// 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">© <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} />
)
}
//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')
}
// 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"}));
};
// 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()
GitHub: https://github.com/Sokhavuth/khmerweb-fresh
Deno Deploy: https://khmerweb.deno.dev/login