implemented post management dashboard and started work on projects

This commit is contained in:
2024-06-02 00:58:11 +02:00
parent 8c56a9421e
commit a0a938021d
29 changed files with 969 additions and 595 deletions

View File

@@ -0,0 +1,24 @@
'use client'
import { ReactNode } from "react";
type Props = {
headings:Array<string>;
children:ReactNode;
}
export default function TableGen(props:Props){
return <>
<table className="table overflow-scroll">
<thead>
<tr>
{props.headings.map((h)=>
<th key={h} scope="col">{h}</th>
)}
</tr>
</thead>
<tbody>
{props.children}
</tbody>
</table>
</>
}

View File

@@ -0,0 +1,80 @@
'use client'
import { ReactNode } from "react"
import TableGen from "../TableGen"
import { Attributes } from "@sequelize/core";
import { Post } from "@/model/Post";
import { useState } from "react";
type Actions = {
deletePost:any
getPosts:()=>Promise<string>
}
type Props = {
children?:ReactNode;
headings:Array<string>;
data:any[];
actions?:Actions;
}
type EditorProps = {
open:boolean;
post?:Attributes<Post>;
}
function RenderEditor(props:EditorProps){
let [content,setContent] = useState(props.post?.content)
let [title,setTitle] = useState(props.post?.title)
return <form className="bg-light w-[100%] h-content p-1">
<h1 className="m-2">Edit Post</h1>
<h2 className="m-2">Title</h2>
<input value={title} onChange={e => setTitle(e.target.value)} type='text' className="m-2"></input>
<h2 className="m-2">Content</h2>
<textarea value={content} onChange={e => setContent(e.target.value)} className="w-[100%] h-content align-top text-start text-base line-clamp-6 m-2"></textarea>
<button type="button" className="m-2 btn btn-primary">Preview</button>
<button type="button" className="m-2 btn btn-success">Save</button>
<button type="button" className="m-2 btn btn-danger">Cancel</button>
</form>
}
export default function PostTable(props:Props){
function showEditor(entry:Attributes<Post>){
setEditor({
open: true,
post: entry
})
}
const initEditorState:EditorProps = {
open: false
}
const [posts, setPosts] = useState(props.data);
const [editor, setEditor] = useState(initEditorState)
function deletePost(entry:Attributes<Post>){
props.actions?.deletePost(entry.id);
props.actions?.getPosts().then((p)=>setPosts(JSON.parse(p)));
}
return <>
<TableGen headings={props.headings}>
{posts.map((d)=>{
return <><tr key={d.id}>
<th scope="row">{d.id}</th>
<td key='titlefield'>{d.title}</td>
<td key='contentfield'>{d.content.length<255 ? d.content :`${d.content.substring(0,255)}...`}</td>
<td key='createdatfield'>{d.createdAt}</td>
<td key='updatedatfield'>{d.updatedAt}</td>
<td key='btnedit'><button type="button" className="btn btn-primary" onClick={()=>showEditor(d)}>Edit</button></td>
<td key='btndelete'><button type="button" className="btn btn-danger" onClick={()=>deletePost(d)}> Delete</button></td>
</tr>
{(editor.open && editor.post && editor.post.id == d.id)?
<tr key={'activeEditor'}><th scope="row" colSpan={props.headings.length}><RenderEditor open={editor.open} post={editor.post}></RenderEditor></th></tr>:""}
</>
})}
</TableGen>
</>
}

View File

@@ -1,76 +0,0 @@
'use client'
import { ReactNode, useContext, useState } from "react";
import { AuthContext, AuthProps } from "@/providers/providers";
import SomeServerSubComponent from "@/components/server/admin/serverContextUserTest";
import PostView from "@/components/server/admin/views/PostView";
import Sidebar from "./views/sidebar";
import ClientPostView from "./views/ClientPostView";
import ServerComponentTest from "@/components/server/ServerComponentTest";
interface Props {
children?: ReactNode;
auth?: AuthProps;
}
export type SidebarEntry = {
label:string;
view:string;
}
export const sidebarEntries:Array<SidebarEntry> = [
{ label: 'Home', view: 'home'},
{ label: 'Post Management', view: 'man-post'},
{ label: 'Project Management', view: 'man-proj'},
{ label: 'Tag Management', view: 'man-tags'},
{ label: 'User Management', view: 'man-user'},
]
function Home(){
return <div>home</div>
}
function PostManager(){
return <div>posts</div>
}
const viewMap = new Map<string, ReactNode>([
['home', <Home></Home>],
['man-post', <ClientPostView></ClientPostView>]
])
export default function AdminPanel(props:Props){
const [view, setView] = useState((<Home></Home>) as ReactNode);
const switchView = function (value:string){
setView(viewMap.get(value));
}
return (
<div className="AdminPanelWrapper w-[100%] h-[100%] flex flex-row">
{/* <h1>Super Secret Admin Panel!</h1> */}
{/* <h2>this is where we use the context test:<SomeSubComponent></SomeSubComponent></h2> */}
{/* <SomeServerSubComponent></SomeServerSubComponent> */}
{/* <section> */}
{/* {props.children} */}
{/* <PostView></PostView> */}
{/* </section> */}
<Sidebar sidebarEntries={sidebarEntries} switchView={switchView}></Sidebar>
<div>{view}</div>
<ServerComponentTest></ServerComponentTest>
</div>
)
}
function SomeSubComponent(props:Props){
let { test, auth } = useContext(AuthContext);
return (
<span>{test}{JSON.stringify(auth)}</span>
);
}

View File

@@ -1,16 +0,0 @@
'use client'
import ServerComponentTest from "@/components/server/ServerComponentTest";
import { ReactNode } from "react";
type Props = {
children?:ReactNode
}
export default function ClientPostView(props:Props){
return (
<div>
test
</div>
);
}

View File

@@ -1,30 +0,0 @@
import './sidebar.css'
import { Button, NavLink } from 'react-bootstrap';
import { SidebarEntry } from '../adminPanel';
import React, { ReactNode, useState } from 'react';
type Props = {
children?:ReactNode;
sidebarEntries:Array<SidebarEntry>;
switchView:any;
}
export default function Sidebar(props:Props){
return (
<div className='w[100%] h[100%] bg-secondary-800'>
<ul className='nav nav-pills flex-column mb-auto container-fluid'>
{props.sidebarEntries.map((sidebarEntry)=>{
return <li className='nav-item'><NavLink className='nav-link bg-purple-400 m-2' onClick={(e)=>{props.switchView(sidebarEntry.view)}}>
{sidebarEntry.label}
</NavLink></li>
})}
</ul>
</div>
);
}

View File

@@ -0,0 +1,17 @@
'use server'
import AdminPanel from "@/components/server/admin/adminPanel";
import { AuthProps } from "@/providers/providers";
import { ReactNode } from "react";
interface Props {
children?: ReactNode;
auth?: AuthProps;
slug?: string;
}
export default async function ServerAdminPanel(props:Props){
return <AdminPanel slug={props.slug?props.slug:'home'} auth={props.auth}>{props.children}</AdminPanel>
}

View File

@@ -0,0 +1,35 @@
'use server'
import { ReactNode, useContext, useState } from "react";
import { AuthProps } from "@/providers/providers";
import SomeServerSubComponent from "@/components/server/admin/serverContextUserTest";
import Sidebar from "./views/sidebar";
import PostView from "./views/PostView";
interface Props {
children?: ReactNode;
auth?: AuthProps;
slug?: string;
}
export default async function AdminPanel(props:Props){
const slug = props.slug ? props.slug : 'home'
return (
<div className="AdminPanelWrapper w-[100%] h-[100%] flex flex-row m-auto">
{/* <h1>Super Secret Admin Panel!</h1> */}
{/* <h2>this is where we use the context test:<SomeSubComponent></SomeSubComponent></h2> */}
{/* <SomeServerSubComponent></SomeServerSubComponent> */}
{/* <section> */}
{/* {props.children} */}
{/* <PostView></PostView> */}
{/* </section> */}
{props.children}
{/* <div>{JSON.stringify(props.auth)}</div> */}
</div>
)
}

View File

@@ -2,7 +2,7 @@
import { cookies } from "next/headers";
import LoginForm from "@/components/client/admin/loginForm";
import AdminPanel from "@/components/client/admin/adminPanel";
import AdminPanel from "@/components/server/admin/adminPanel";
import ClientAuthHandler from "@/components/client/admin/clientAuthHandler";
import { serverValidateSessionCookie } from "@/app/lib/actions";
@@ -23,13 +23,11 @@ export default async function AuthHandler(props: Props) {
const koek = decodeURIComponent(protoKoek ? protoKoek: "");
console.log("koekje:" + koek)
let p:AuthProps = {
test:"not pog"
};
if(koek){
const kd = JSON.parse(koek);
if(kd.id && kd.token && kd.user_id){
p = {
test:"success!",
auth: {
id:kd.id,
token:kd.token,

View File

@@ -1,47 +0,0 @@
'use server'
import { cookies } from "next/headers";
import LoginForm from "@/components/client/admin/loginForm";
import AdminPanel from "@/components/client/admin/adminPanel";
import ClientAuthHandler from "@/components/client/admin/clientAuthHandler";
import { serverValidateSessionCookie } from "@/app/lib/actions";
import { constructAPIUrl } from "@/util/Utils";
import { ReactNode } from "react";
import { AuthContext, AuthProps } from "@/providers/providers";
interface Props {
children?: ReactNode;
params?: any;
requiredRole?: number;
} // We interfacing lads
export default async function AuthHandler(props: Props) {
const protoKoek = await cookies().get('auth')?.value;
const koek = decodeURIComponent(protoKoek ? protoKoek: "");
console.log("koekje:" + koek)
let p:AuthProps = {
test:"not pog"
};
if(koek){
const kd = JSON.parse(koek);
if(kd.id && kd.token && kd.user_id){
p = {
test:"success!",
auth: {
id:kd.id,
token:kd.token,
user_id:kd.user_id
}
}
}
}
return (
<div className="flex flex-row">
{!(koek && await serverValidateSessionCookie(koek)) ? <LoginForm>{ }</LoginForm> : <ClientAuthHandler authProps={p}><section>{props.children}</section></ClientAuthHandler>}
</div>
);
}

View File

@@ -0,0 +1,10 @@
export default async function(){
<form className="bg-light w-[100%] h-[100%]">
<h1>Edit Post</h1>
<h2>Title</h2>
<input type='text'></input>
</form>
}

View File

@@ -1,21 +1,35 @@
'use server'
import { ReactNode } from "react"
import AuthHandler from "../authHandler"
import { Post } from "@/model/Post"
import { constructAPIUrl } from "@/util/Utils"
import { tryFetchPosts } from "@/app/api/post/route";
import { Post } from "@/model/Post";
import { constructAPIUrl } from "@/util/Utils";
import { ReactNode, useEffect } from "react";
import TableGen from "../../../client/TableGen";
import PostTable from "@/components/client/admin/PostTable";
import { deletePost, getPosts } from "@/app/lib/postActions";
type Props = {
children?:ReactNode
}
export default async function PostView(props:Props){
// const posts = await Post.findAll();
export default async function PostView(props:Props){
const headings = [
'#',
'Title',
'Content',
'Date Created',
'Date Modified',
'Edit',
'Delete',
]
const data = await Post.findAll();
const passData:any[] = data.map((e)=>JSON.parse(JSON.stringify(e)));
return (
<div>
test
<div className="w-[100%] min-h-fit bg-gray-100 overflow-scroll">
<span className="flex flex-row flex-grow w-[100%] pl-2 pr-2"><h1 className="p-2 inline-block">Post Management</h1><section className="flex-grow"></section><button className='btn btn-success h-12 mt-auto mb-auto self-end'>New</button></span>
<div className="w-[100%] m-auto">
<PostTable data={passData} headings={headings} actions={{deletePost: deletePost, getPosts:getPosts}}></PostTable>
</div>
</div>
);
}

View File

@@ -0,0 +1,33 @@
import './sidebar.css'
import { Button, NavLink } from 'react-bootstrap';
import { SidebarEntry } from '@/app/admin/page';
import React, { ReactNode, useState } from 'react';
import Link from 'next/link';
import { headers } from 'next/headers';
type Props = {
children?:ReactNode;
sidebarEntries:Array<SidebarEntry>;
slug:string
}
export default async function Sidebar(props:Props){
return (
<div className='w-fit h[100%]'>
<ul className={`navbar-light bg-light nav nav-pills flex-column mb-auto container-fluid h-[100%] items-start justify-start p-0 w-fit`}>
{props.sidebarEntries.map((sidebarEntry)=>{
return <li key={sidebarEntry.view} className='nav-item w-[100%]'><Link className={`nav-link m-2 whitespace-nowrap outline outline-1 ${(props.slug == sidebarEntry.view) ? 'active' : ''}`} href={`/admin/${sidebarEntry.view}`}>
{sidebarEntry.label}
</Link></li>
})}
</ul>
</div>
);
}