DEV Community

Sokhavuth TIN
Sokhavuth TIN

Posted on

1 1

Blog Engine with Fresh: Listing Posts on Homepage


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


// routes/index.tsx

/** @jsx h */
import { h } from "preact";
import { Handlers, PageProps } from "$fresh/server.ts";
import VHome from '../components/front/home.jsx';
import CHome from "../controllers/front/home.js";


export const handler: Handlers = {
  async GET(req, ctx) {
      return await CHome.getPosts(req, ctx);
  },
}


export default function Template(props: PageProps){
  return (
    <VHome data={props.data} />
  )
}
Enter fullscreen mode Exit fullscreen mode
// controllers/front/home.js

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


class Home{
    async getPosts(req, ctx){
        const config = setting();
        config.items = await postdb.getPosts(config.homePostAmount);
        return await ctx.render({"setting": config});
    }
}

export default new Home();
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
// components/front/home.jsx

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


function HomeJsx(props){ 
    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 class="date">{(new Date(item.date)).toLocaleDateString('it-IT')}</div>
        <div class="text" dangerouslySetInnerHTML={{ __html: `${ item.content }` }} />
      </div>
    </li>
    )

    return(
        <section class="Home">
            <link href="/styles/front/home.css" rel="stylesheet" />
            <script src="/scripts/menu.js"></script>
            <header>
                <div class="inner region">
                    <div class="title"><a href="/">{ props.data.setting.site_title }</a></div>
                    <form action="search" method="post">
                        <select class="category" name="frontSearch">
                            <option>Posts</option>
                            <option>Books</option>
                        </select>
                        <input type="text" name="q" required placeholder="Search" />
                        <input type="submit" value="Submit" />
                    </form>
                    <div class="login">
                        <a href="/login">Login</a> | <a href="#">Register</a>
                    </div>
                </div>
            </header>
            <div class="menu">
                <div class="inner region">
                    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" />
                    <link rel="stylesheet" href="/styles/front/menu.css" />

                    <div class="topnav" id="myTopnav" dangerouslySetInnerHTML={{__html: `
                        <a href="/" class="active">Home</a>
                        <a href="#news">News</a>
                        <a href="#contact">Contact</a>
                        <a href="#about">About</a>
                        <a href="javascript:void(0);" class="icon" onclick="mobileMenu()">
                            <i class="fa fa-bars"></i>
                        </a>
                    `}}/>
                </div>
            </div>
            <link rel="stylesheet" href="styles/front/main.css" />
            <div class="main region">
                <div class="content">
                    <ul> { listItems } </ul>
                </div>
                <div class="sidebar">Sidebar</div>
            </div>
        </section>
    )
}


export default function Home(props){
    props.data.page = HomeJsx;
    return(
        <Base data={props.data} />
    )
}
Enter fullscreen mode Exit fullscreen mode
/* static/styles/front/main.css */

.Home .main{
    padding-top: 20px;
    display: grid;
    grid-template-columns: 70% calc(30% - 15px);
    grid-gap: 15px;
    padding-bottom: 30px;
}

.Home .main .content,
.Home .main .sidebar{
    background: white;
    padding: 20px;
}

.Home .main .content ul{
    list-style-type: none;
}

.Home .main .content ul li{
    display: grid;
    grid-template-columns: 35% 65%;
    margin-bottom: 20px;
    background: rgb(243, 243, 243);
}

.Home .main .content ul li:last-child{
    margin-bottom: 0;
}

.Home .main .content ul li .thumb{
    position: relative;
    padding-top: 56.25%
}

.Home .main .content ul li .thumb img{
    position: absolute;
    top: 0%;
    left: 0;
    width: 100%;
    height: 100%;
}

.Home .main .content ul li .thumb .play-icon{
    width: 20%;
    height: auto;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}

.Home .main .content ul li .title{
    padding: 5px 10px;
}

.Home .main .content ul li .title a{
    font: 22px/1.5 Oswald, Limonf3;
    padding-bottom: 5px;
    display: block;
    width: 100%;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis
}

.Home .main .content ul li .title .text{
    padding-top: 10px;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
    text-overflow: ellipsis;
}

@media only screen and (max-width: 600px){
    .Home .main{
        grid-template-columns: 100%;
    }

    .Home .main .content ul li{
        grid-template-columns: 100%;
    }
}
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