DEV Community

Sokhavuth TIN
Sokhavuth TIN

Posted on

2 1

Blog Engine with Fresh: Paginating Post Item


GitHub: https://github.com/Sokhavuth/deno-fresh
Deno Deploy: https://khmerweb-fresh.deno.dev/login


// components/admin/index.jsx

/** @jsx h */
import { h } from "preact";
import Base from "../base.jsx"


function IndexJsx(props){
    const Page = props.data.pageInner;
    const items = props.data.setting.items;

    const listItems = items.map((item) =>
    <li>
      <a class="thumb" href={`/post/${item.id}`}>
        <img src={item.thumb} />
        {((item.videos !== "" )&&(item.videos !== "[]")) &&
          <img class="play-icon" src={`/images/play.png`} />
        }
      </a>
      <div class="title">
        <a href={`/post/${item.id}`}>{item.title}</a>
        <div>{(new Date(item.date)).toLocaleDateString('it-IT')}</div>
      </div>
      <div class="edit">
        <a href={`/admin/post/edit/${item.id}`}><img src={`/images/edit.png`} /></a>
        <a href={`/admin/post/delete/${item.id}`}><img src={`/images/delete.png`} /></a>
      </div>
    </li>
    )
    return(
        <section class="Index" >
            <link rel="stylesheet" href="/styles/admin/index.css" />
            <script src="/scripts/paginate.js"></script>
        <header>
          <div class="inner region">
              <div class="title">{props.data.setting.page_title}</div>
              <form action="/admin/search" method="post">
                <select name="admin_search">
                  <option>posts</option>
                  <option>books</option>
                </select>
                <input type="text" name="admin_q" required placeholder="Search" />
                <input type="submit" value="Search" />
              </form>
              <div class="logout"><span>{props.data.setting.username}</span> | <a href="/">Home</a> | <a href="/logout">Logout</a></div>
          </div>
        </header>

        <div class="main region">
          <div class="sidebar">
            <div class="inner">
              <a href="/admin/post"><img src="/images/movie.png" /></a>
              <a href="/admin/post">Post</a>

              <a href="/admin/book"><img src="/images/books.png" /></a>
              <a href="/admin/book">Book</a>

              <a href="/admin/category"><img src="/images/category.png" /></a>
              <a href="/admin/category">Category</a>

              <a href="/admin/upload"><img src="/images/upload.png" /></a>
              <a href="/admin/upload">Upload</a>

              <a href="/admin/user"><img src="/images/users.png" /></a>
              <a href="/admin/user">User</a>

              <a href="/admin/setting"><img src="/images/setting.png" /></a>
              <a href="/admin/setting">Setting</a>
            </div>
          </div>
          <div class="content">
            <Page data={props.data} />
          </div>
        </div>

        <div class="footer region">
          <div class="info">Total amount of items: {props.data.setting.count}</div>
          <ul class="list">
              { listItems }
          </ul>
          <div class="pagination" dangerouslySetInnerHTML={{__html: `
              <img onclick="paginate('${props.data.setting.route}')" src="/images/load-more.png" />
          `}}/>

          <div class="credit">&copy; <a href="https://khmerweb.vercel.app/">Khmer Web 2022</a></div>
        </div>
        </section>
    )
}

export default function Index(props){
    props.data.page = IndexJsx
    return(
        <Base data={props.data} />
    )
}
Enter fullscreen mode Exit fullscreen mode
//static/scripts/paginate.js

let page = 0

async function paginate(route){
    $('.pagination img').attr('src', '/images/loading.gif')
    page += 1

    const resp = await fetch(`/api/paginate/${page}`);
    if (resp.status === 404) {
        alert('no post');
        return;
    }

    const data = await resp.json();
    appendItem(data.items, route, data);
}

function appendItem(items, route,data){
    let html = ''

    if(items){
        for(const item of items){
            html += `<li>`
                html += `<div class='thumb'>`
                    html += `<a href="/${data.type}/${item.id}">
                                <img src="${item.thumb}"/>`
                                if((item.video)&&(item.video !== '[]')){
                                    html += `<img class="play-icon" src="/images/play.png"/>`
                                }
                    html += `</a>`
                html += `</div>`
                html += `<div class="title">`
                    html += `<a href="/${data.type}/${item.id}">${item.title}</a>`
                    html += `<div>${new Date(item.date).toLocaleDateString('it-IT')}</div>`
                html += `</div>`
                html += `<div class="edit">`
                    html += `<a href="${route}/edit/${item.id}"><img src="/images/edit.png"/></a>`
                    html += `<a href="${route}/delete/${item.id}"><img src="/images/delete.png"/></a>`
                html += `</div>` 
            html += `</li>`
        }
    }
    $('.list').append(html)

    if(route === '/admin/user'){
        $('.Footer .list li').css({'grid-template-columns':'13% auto 25%'})
        $('.Footer .list li .thumb').css({'padding-top':'100%'})
        $('.Footer .list li .thumb img').css({'border-radius':'50%'})
    }

    $('.pagination img').attr('src', '/images/load-more.png')
}
Enter fullscreen mode Exit fullscreen mode
// routes/api/paginate/[page].js

import { setting } from 'setting';
import postdb from "../../../models/post.ts";


export const handler = async (_req, ctx) => {
    const config = setting();
    const posts = await postdb.paginatePosts(config.post_amount, ctx.params.page);
    return new Response(JSON.stringify({items: posts, type: "post"}));
};
Enter fullscreen mode Exit fullscreen mode
// models/post.ts

import { mydb } from "setting"

interface PostSchema {
    _id: ObjectId;
    id: string; 
    title: string;
    content: string;
    categories: string[];
    thumb: string;
    date: string;
    videos: string;
    userid: string;
}

class Post{
    async count(query={}){
        const posts = mydb.collection<PostSchema>("posts")
        return await posts.countDocuments(query)
    }

    async insertPost(req, user_id: string){
        const id = crypto.randomUUID()
        const formData = await req.formData()

        let categories: string[]

        if(formData.get("categories").includes(',')){
            categories = formData.get("categories").split(',')
        }else{
            categories = [formData.get("categories")]
        }

        const new_post = {
            id: id, 
            title: formData.get("title"),
            content: formData.get("content"),
            categories: categories,
            thumb: formData.get("thumb"),
            date: formData.get("datetime"),
            videos: formData.get("videos"),
            userid: user_id,
        }

        const posts = mydb.collection<PostSchema>("posts")
        await posts.insertOne(new_post)
    }

    async getPosts(amount: number, query={}){
        const posts = mydb.collection<PostSchema>("posts")
        return await posts.find(query).sort({date:-1,_id:-1}).limit(amount).toArray()
    }

    async getPost(post_id: string){
        const posts = mydb.collection<PostSchema>("posts")
        return await posts.findOne({id: post_id})
    }

    async updatePost(req, post_id: string){
        const formData = await req.formData()

        let categories: string[]

        if(formData.get("categories").includes(',')){
            categories = formData.get("categories").split(',')
        }else{
            categories = [formData.get("categories")]
        }

        const edited_post = {$set:{
            title: formData.get("title"),
            content: formData.get("content"),
            categories: categories,
            thumb: formData.get("thumb"),
            date: formData.get("datetime"),
            videos: formData.get("videos"),
        }}

        const posts = mydb.collection<PostSchema>("posts")
        await posts.updateOne({id: post_id}, edited_post)
    }

    async deletePost(post_id: string){
        const posts = mydb.collection<PostSchema>("posts")
        await posts.deleteOne({id: post_id})
    }

    async paginatePosts(amount: number, page: number){
        const posts = mydb.collection<PostSchema>("posts")
        return await posts.find().skip(amount*page).sort({date:-1,_id:-1}).limit(amount).toArray();
    }

}

export default new Post()
Enter fullscreen mode Exit fullscreen mode

SurveyJS custom survey software

Build Your Own Forms without Manual Coding

SurveyJS UI libraries let you build a JSON-based form management system that integrates with any backend, giving you full control over your data with no user limits. Includes support for custom question types, skip logic, an integrated CSS editor, PDF export, real-time analytics, and more.

Learn more

Top comments (0)

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more