implemented post management dashboard and started work on projects
This commit is contained in:
parent
8c56a9421e
commit
a0a938021d
815
package-lock.json
generated
815
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
75
src/app/admin/[...slug]/page.tsx
Normal file
75
src/app/admin/[...slug]/page.tsx
Normal file
@ -0,0 +1,75 @@
|
||||
'use server'
|
||||
import { getCookieAuth } from "@/app/lib/actions";
|
||||
import AdminPanel from "@/components/server/admin/adminPanel";
|
||||
import ServerAdminPanel from "@/components/server/admin/ServerAdminPanel";
|
||||
import AuthHandler from "@/components/server/admin/authHandler";
|
||||
import Sidebar from "@/components/server/admin/views/sidebar";
|
||||
import { cookies } from "next/headers";
|
||||
import PostView from "@/components/server/admin/views/PostView";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
type Props = {
|
||||
params: {
|
||||
slug: string[]
|
||||
};
|
||||
}
|
||||
|
||||
export type SidebarEntry = {
|
||||
label:string;
|
||||
view:string;
|
||||
}
|
||||
|
||||
|
||||
function Home(){
|
||||
return <div>home</div>
|
||||
}
|
||||
function PostManager(){
|
||||
return <div>posts</div>
|
||||
}
|
||||
|
||||
|
||||
async function getViewMap():Promise<Map<string, JSX.Element>>{
|
||||
return new Map([
|
||||
['home', <Home></Home>],
|
||||
['man-post', <PostView></PostView>]
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
async function getSidebarEntries():Promise<Array<SidebarEntry>>{
|
||||
return [
|
||||
{ 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'},
|
||||
]
|
||||
}
|
||||
|
||||
async function getCurrentView(view:string):Promise<JSX.Element>{
|
||||
const viewMap = await getViewMap();
|
||||
const viewJSX = viewMap.get(view);
|
||||
return viewJSX ? viewJSX : <Home></Home>;
|
||||
}
|
||||
|
||||
export default async function Page(props:Props){
|
||||
const sidebarEntries:Array<SidebarEntry> = await getSidebarEntries();
|
||||
|
||||
const slug:string|string[] = props.params.slug ? props.params.slug : 'home';
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<main className="h-screen w-screen flex flex-col p-0 bg-gray-300 box-border m-0">
|
||||
<AuthHandler params={null}>
|
||||
<Sidebar sidebarEntries={sidebarEntries} slug={slug.toString()}></Sidebar>
|
||||
<AdminPanel slug={slug.toString()} auth={await getCookieAuth()}>
|
||||
{await getCurrentView(slug.toString())}
|
||||
</AdminPanel>
|
||||
</AuthHandler>
|
||||
{/* <section>{JSON.stringify(cookies().getAll())}</section> */}
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
import '../globals.css'
|
||||
// import '../globals.css'
|
||||
import { Inter } from 'next/font/google'
|
||||
import StyledJsxRegistry from '../registry';
|
||||
import Providers from '@/providers/providers';
|
||||
|
||||
@ -1,12 +1,45 @@
|
||||
import AdminPanel from "@/components/client/admin/adminPanel";
|
||||
'use server'
|
||||
import { getCookieAuth } from "@/app/lib/actions";
|
||||
import AdminPanel from "@/components/server/admin/adminPanel";
|
||||
import ServerAdminPanel from "@/components/server/admin/ServerAdminPanel";
|
||||
import AuthHandler from "@/components/server/admin/authHandler";
|
||||
import Sidebar from "@/components/server/admin/views/sidebar";
|
||||
import { cookies } from "next/headers";
|
||||
|
||||
type Props = {
|
||||
params: {
|
||||
slug: string[]
|
||||
};
|
||||
}
|
||||
|
||||
export default async function Page(){
|
||||
export type SidebarEntry = {
|
||||
label:string;
|
||||
view:string;
|
||||
}
|
||||
|
||||
|
||||
|
||||
async function getSidebarEntries():Promise<Array<SidebarEntry>>{
|
||||
return [
|
||||
{ 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'},
|
||||
]
|
||||
}
|
||||
|
||||
export default async function Page(props:Props){
|
||||
const sidebarEntries:Array<SidebarEntry> = await getSidebarEntries();
|
||||
|
||||
const slug:string|string[] = props.params.slug ? props.params.slug : 'home';
|
||||
|
||||
return (
|
||||
<main className="h-screen w-screen flex flex-col p-0 bg-background-500 box-border m-0">
|
||||
<AuthHandler params={null}><AdminPanel></AdminPanel></AuthHandler>
|
||||
<AuthHandler params={null}>
|
||||
<Sidebar sidebarEntries={sidebarEntries} slug={""}></Sidebar>
|
||||
<ServerAdminPanel slug={slug.toString()} auth={await getCookieAuth()}></ServerAdminPanel>
|
||||
</AuthHandler>
|
||||
{/* <section>{JSON.stringify(cookies().getAll())}</section> */}
|
||||
</main>
|
||||
);
|
||||
|
||||
14
src/app/admin/page.tsx.bak
Normal file
14
src/app/admin/page.tsx.bak
Normal file
@ -0,0 +1,14 @@
|
||||
import AdminPanel from "@/components/client/admin/adminPanel";
|
||||
import AuthHandler from "@/components/server/admin/authHandler";
|
||||
import { cookies } from "next/headers";
|
||||
import { Router } from "next/router";
|
||||
|
||||
|
||||
export default async function Page(){
|
||||
return (
|
||||
<main className="h-screen w-screen flex flex-col p-0 bg-background-500 box-border m-0">
|
||||
<AuthHandler params={null}><AdminPanel></AdminPanel></AuthHandler>
|
||||
{/* <section>{JSON.stringify(cookies().getAll())}</section> */}
|
||||
</main>
|
||||
);
|
||||
}
|
||||
92
src/app/api/attachment/route.ts
Normal file
92
src/app/api/attachment/route.ts
Normal file
@ -0,0 +1,92 @@
|
||||
'use server'
|
||||
|
||||
import { APIError, attemptAPIAction } from "@/util/api/error";
|
||||
import { Auth, Post, PostTag, Tag, User } from "@/model/Models";
|
||||
import { cookies } from "next/headers";
|
||||
|
||||
|
||||
|
||||
async function tryCreateAttachment(request: Request) {
|
||||
|
||||
// Make sure the DB is ready
|
||||
await PostTag.sync();
|
||||
await Tag.sync();
|
||||
await Post.sync();
|
||||
|
||||
// Prepare data
|
||||
const requestBody = await request.json();
|
||||
const authCkie = await cookies().get("auth");
|
||||
|
||||
// Sanity check auth cookie
|
||||
if ( !authCkie || !authCkie.value) throw new APIError({ status: 500, responseText: "missing auth cookie" });
|
||||
|
||||
// Get JSON from the Cookie
|
||||
const cookieJSON = authCkie.value;
|
||||
const authObject = JSON.parse(cookieJSON);
|
||||
|
||||
// Fetch User Auth from the database
|
||||
const auth = await Auth.findOne({
|
||||
include: [
|
||||
{
|
||||
model: User.withScope(['withPerms']),
|
||||
attributes: {
|
||||
exclude: ['username', 'password', 'updatedAt', 'createdAt']
|
||||
}
|
||||
}
|
||||
],
|
||||
where: { token: authObject.token }
|
||||
});
|
||||
|
||||
// Sanity check the auth and associated user
|
||||
if (!auth || !auth.user) throw new APIError({ status: 401, responseText: "Authentication Error" });
|
||||
|
||||
// Handle incomplete data or other problems
|
||||
if (!requestBody) throw new APIError({ status: 500, responseText: "Empty request body" });
|
||||
if (!requestBody.title) throw new APIError({ status: 500, responseText: "Missing post title" });
|
||||
if (!requestBody.content) throw new APIError({ status: 500, responseText: "Missing post content" });
|
||||
if (!auth.user.id) throw new APIError({ status: 500, responseText: "Missing user id" });
|
||||
if (!auth.user.perms || !auth.user.perms.isAdmin) throw new APIError({ status: 401, responseText: `Unauthorized ${JSON.stringify(auth.user)}` });
|
||||
|
||||
// Create a new Post in the database
|
||||
const post = await Post.create(
|
||||
{
|
||||
content: requestBody.content,
|
||||
user_id: auth.user.id,
|
||||
title: requestBody.title,
|
||||
},{
|
||||
include: {
|
||||
association: Post.associations.user
|
||||
}
|
||||
}).then(post=>post.reload())
|
||||
|
||||
// Return the response
|
||||
return new Response(JSON.stringify(post), { status: 200 });
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
export async function tryFetchAttachments(request: Request) {
|
||||
|
||||
await Post.sync();
|
||||
|
||||
const foundPosts = await Post.findAll({
|
||||
include: [
|
||||
{
|
||||
association: Post.associations.user,
|
||||
attributes: { exclude: ['password', 'createdAt', 'updatedAt'] }
|
||||
},{
|
||||
association: Post.associations.postTags
|
||||
}]
|
||||
});
|
||||
|
||||
return new Response(JSON.stringify(foundPosts), { status: 200 });
|
||||
|
||||
}
|
||||
|
||||
export async function GET(request: Request) {
|
||||
return await attemptAPIAction(tryFetchAttachments,request);
|
||||
}
|
||||
export async function POST(request: Request) {
|
||||
return await attemptAPIAction(tryCreateAttachment,request);
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
'use server'
|
||||
|
||||
import { APIError } from "@/app/lib/api/error";
|
||||
import { APIError } from "@/util/api/error";
|
||||
import { Post } from "@/model/Models";
|
||||
|
||||
|
||||
|
||||
@ -66,7 +66,7 @@ async function tryCreatePost(request: Request) {
|
||||
|
||||
|
||||
|
||||
async function tryFetchPosts(request: Request) {
|
||||
export async function tryFetchPosts(request: Request) {
|
||||
|
||||
await Post.sync();
|
||||
|
||||
|
||||
@ -5,8 +5,10 @@ import { cookies } from "next/headers"
|
||||
import { parseSetCookie } from "@/util/parseSetCookie";
|
||||
import makeFetchCookie from 'fetch-cookie';
|
||||
import fetchCookie from "fetch-cookie";
|
||||
import { Attributes } from "@sequelize/core";
|
||||
import { Attribute, Attributes } from "@sequelize/core";
|
||||
import { User } from "@/model/User";
|
||||
import { AuthProps } from "@/providers/providers";
|
||||
import { Auth } from "@/model/Auth";
|
||||
|
||||
type LoginReturn = {
|
||||
cookie?:unknown,
|
||||
@ -80,6 +82,48 @@ export async function serverValidateSessionCookie(koek:string):Promise<boolean>
|
||||
|
||||
export async function userIsAdmin(user:Partial<Attributes<User>>):Promise<boolean>
|
||||
{
|
||||
const cookieAuthValue = await cookies().get('auth')?.value;
|
||||
const cookieAuthSanitized = decodeURIComponent(cookieAuthValue ? cookieAuthValue: "");
|
||||
|
||||
if(!cookieAuthSanitized) return false;
|
||||
const parsedAuth = JSON.parse(cookieAuthSanitized);
|
||||
|
||||
if(!parsedAuth.id || !parsedAuth.token || !parsedAuth.user_id) return false
|
||||
|
||||
const p:AuthProps = {
|
||||
auth: {
|
||||
id:parsedAuth.id,
|
||||
token:parsedAuth.token,
|
||||
user_id:parsedAuth.user_id
|
||||
}
|
||||
};
|
||||
|
||||
const foundAuth = await Auth.findOne({where: { id: p.auth?.id}});
|
||||
if(!foundAuth || foundAuth.token != p.auth?.token ) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export async function getCookieAuth():Promise<AuthProps>
|
||||
{
|
||||
const cookieAuthValue = await cookies().get('auth')?.value;
|
||||
const cookieAuthSanitized = decodeURIComponent(cookieAuthValue ? cookieAuthValue: "");
|
||||
|
||||
if(!cookieAuthSanitized) return {}
|
||||
|
||||
const kd = JSON.parse(cookieAuthSanitized);
|
||||
if(!kd.id || !kd.token || !kd.user_id) return {};
|
||||
|
||||
const foundAuth = await Auth.findOne({where: { id: kd.id},include:{model:User}});
|
||||
if(!foundAuth) return {};
|
||||
const authObject:AuthProps = {
|
||||
auth: {
|
||||
id:kd.id,
|
||||
token:kd.token,
|
||||
user_id:kd.user_id
|
||||
},
|
||||
user: await foundAuth.user
|
||||
}
|
||||
return authObject;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
15
src/app/lib/postActions.ts
Normal file
15
src/app/lib/postActions.ts
Normal file
@ -0,0 +1,15 @@
|
||||
'use server';
|
||||
import { Post } from "@/model/Post";
|
||||
import { Attributes } from "@sequelize/core";
|
||||
|
||||
|
||||
export async function deletePost(postID: number): Promise<boolean> {
|
||||
const destroy = await Post.destroy({ where: { id: postID } });
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
export async function getPosts(): Promise<string> {
|
||||
const posts = await Post.findAll();
|
||||
return JSON.stringify(posts);
|
||||
}
|
||||
24
src/components/client/TableGen.tsx
Normal file
24
src/components/client/TableGen.tsx
Normal 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>
|
||||
</>
|
||||
}
|
||||
80
src/components/client/admin/PostTable.tsx
Normal file
80
src/components/client/admin/PostTable.tsx
Normal 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>
|
||||
</>
|
||||
}
|
||||
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -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>
|
||||
);
|
||||
}
|
||||
@ -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>
|
||||
);
|
||||
}
|
||||
17
src/components/server/admin/ServerAdminPanel.tsx
Normal file
17
src/components/server/admin/ServerAdminPanel.tsx
Normal 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>
|
||||
}
|
||||
35
src/components/server/admin/adminPanel.tsx
Normal file
35
src/components/server/admin/adminPanel.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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>
|
||||
);
|
||||
}
|
||||
10
src/components/server/admin/views/PostEditor.tsx
Normal file
10
src/components/server/admin/views/PostEditor.tsx
Normal 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>
|
||||
}
|
||||
@ -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>
|
||||
);
|
||||
}
|
||||
33
src/components/server/admin/views/sidebar.tsx
Normal file
33
src/components/server/admin/views/sidebar.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@ -1,9 +1,10 @@
|
||||
import { Association, BelongsToGetAssociationMixin, CreationOptional, DataTypes, ForeignKey, InferAttributes, InferCreationAttributes, Model, NonAttribute, Sequelize } from "@sequelize/core";import { User } from "./User";
|
||||
|
||||
import { SqliteDialect } from '@sequelize/sqlite3';
|
||||
import { Attribute, AutoIncrement, BelongsTo, BelongsToMany, CreatedAt, HasMany, NotNull, PrimaryKey, Unique, UpdatedAt } from "@sequelize/core/decorators-legacy";
|
||||
import { Attribute, AutoIncrement, BelongsTo, BelongsToMany, CreatedAt, Default, HasMany, NotNull, PrimaryKey, Unique, UpdatedAt } from "@sequelize/core/decorators-legacy";
|
||||
import { Tag } from "./Tag";
|
||||
import { PostTag } from "./PostTag";
|
||||
import { Project } from "./Project";
|
||||
|
||||
export class Post extends Model<InferAttributes<Post>, InferCreationAttributes<Post>> {
|
||||
|
||||
@ -14,9 +15,7 @@ export class Post extends Model<InferAttributes<Post>, InferCreationAttributes<P
|
||||
@AutoIncrement
|
||||
@Unique
|
||||
declare id: CreationOptional<number>;
|
||||
@Attribute(DataTypes.INTEGER)
|
||||
@NotNull
|
||||
declare user_id:ForeignKey<User['id']>;
|
||||
|
||||
@Attribute(DataTypes.STRING)
|
||||
declare title:string
|
||||
@Attribute(DataTypes.STRING)
|
||||
@ -31,10 +30,19 @@ export class Post extends Model<InferAttributes<Post>, InferCreationAttributes<P
|
||||
|
||||
|
||||
// Associatoins
|
||||
|
||||
@Attribute(DataTypes.INTEGER)
|
||||
@NotNull
|
||||
declare user_id:ForeignKey<User['id']>;
|
||||
|
||||
@Attribute(DataTypes.INTEGER)
|
||||
declare project_id?:CreationOptional<ForeignKey<User['id']>>;
|
||||
|
||||
@BelongsTo(()=>User, { foreignKey: 'user_id', inverse: { type: 'hasMany', as: 'posts' } })
|
||||
declare user:NonAttribute<User>;
|
||||
|
||||
@BelongsTo(()=>Project, { inverse: { type: 'hasMany', as: 'posts' } })
|
||||
declare project:NonAttribute<User>;
|
||||
|
||||
@BelongsToMany(()=>Tag, { through: { model: ()=>PostTag, unique: false}, inverse: {as: 'taggedPosts'} })
|
||||
declare postTags?:NonAttribute<Tag[]>;
|
||||
|
||||
@ -42,6 +50,7 @@ export class Post extends Model<InferAttributes<Post>, InferCreationAttributes<P
|
||||
|
||||
declare static associations: {
|
||||
user: Association<User, Post>;
|
||||
project: Association<Project, Post>;
|
||||
postTags: Association<Tag, Post>;
|
||||
};
|
||||
}
|
||||
|
||||
@ -4,20 +4,7 @@ import { SqliteDialect } from '@sequelize/sqlite3';
|
||||
import { Attribute, AutoIncrement, PrimaryKey, Unique } from "@sequelize/core/decorators-legacy";
|
||||
import { Post } from "./Post";
|
||||
|
||||
export type ProjectAttributes = {
|
||||
id: number;
|
||||
title: string;
|
||||
description: string;
|
||||
date: number;
|
||||
user_id:ForeignKey<User['id']>;
|
||||
}
|
||||
export type ProjectCreationAttributes = {
|
||||
title: string;
|
||||
description: string;
|
||||
date: number;
|
||||
user_id:number;
|
||||
}
|
||||
export class Project extends Model<ProjectAttributes, ProjectCreationAttributes> {
|
||||
export class Project extends Model<InferAttributes<Project>, InferCreationAttributes<Project>> {
|
||||
@Attribute(DataTypes.INTEGER) @PrimaryKey @Unique @AutoIncrement
|
||||
declare id: number;
|
||||
@Attribute(DataTypes.STRING) @Unique
|
||||
@ -27,7 +14,6 @@ export class Project extends Model<ProjectAttributes, ProjectCreationAttributes>
|
||||
|
||||
declare static associations: {
|
||||
posts: Association<Post, Project>;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@ -7,13 +7,10 @@ import { Attributes, InferAttributes } from "@sequelize/core";
|
||||
|
||||
|
||||
export type AuthProps = {
|
||||
test: string;
|
||||
auth?: Attributes<Auth>
|
||||
user?: Attributes<User>
|
||||
}
|
||||
let p: AuthProps = {
|
||||
test: "lorem",
|
||||
}
|
||||
let p: AuthProps = {}
|
||||
|
||||
export type AdminViewProps = {
|
||||
view: string;
|
||||
@ -27,7 +24,6 @@ export const AdminViewContext = createContext(avp);
|
||||
export const AuthContext = createContext(p);
|
||||
|
||||
|
||||
|
||||
interface Props {
|
||||
children?: ReactNode;
|
||||
params?: any;
|
||||
@ -35,6 +31,6 @@ interface Props {
|
||||
|
||||
export default function Providers(props:Props){
|
||||
return (
|
||||
<AuthContext.Provider value={{test:"freek"}}><AdminViewContext.Provider value={avp}>{props.children}</AdminViewContext.Provider></AuthContext.Provider>
|
||||
<AuthContext.Provider value={{}}><AdminViewContext.Provider value={avp}>{props.children}</AdminViewContext.Provider></AuthContext.Provider>
|
||||
)
|
||||
}
|
||||
@ -1,3 +1,5 @@
|
||||
'server only'
|
||||
|
||||
import { hash, compare } from "bcrypt";
|
||||
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
'server only'
|
||||
import Gens from "./gens";
|
||||
import Auth from "./Auth";
|
||||
// import Auth from "./Auth";
|
||||
|
||||
|
||||
function getAPIEnv(){
|
||||
@ -27,4 +28,4 @@ function truncateString(str:string = '', num:number = 255) {
|
||||
}
|
||||
|
||||
|
||||
export { Gens, Auth, constructAPIUrl, constructUrl, truncateString }
|
||||
export { Gens, constructAPIUrl, constructUrl, truncateString }
|
||||
@ -24,6 +24,6 @@
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "src/app/admin/page.tsx.bak", "src/app/admin/[...slug]/page.tsx.bak"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user