From a52a002b0ff09b09cc0de917e8e945028ac345ac Mon Sep 17 00:00:00 2001 From: Andreas Date: Thu, 27 Jun 2024 11:19:05 +0200 Subject: [PATCH] Minor refactors and fixes --- src/app/admin/[[...slug]]/page.tsx | 103 ++-- src/components/client/admin/PostEditor.tsx | 446 ++++++++++++------ src/components/client/admin/PostTable.tsx | 352 +++++++------- .../server/admin/views/PostView.tsx | 51 -- src/components/server/admin/views/sidebar.tsx | 16 +- src/providers/providers.tsx | 3 +- src/views/admin/PostView.tsx | 67 +++ .../views => views/admin}/ProjectView.tsx | 2 +- src/views/index.ts | 0 9 files changed, 612 insertions(+), 428 deletions(-) delete mode 100644 src/components/server/admin/views/PostView.tsx create mode 100644 src/views/admin/PostView.tsx rename src/{components/server/admin/views => views/admin}/ProjectView.tsx (96%) create mode 100644 src/views/index.ts diff --git a/src/app/admin/[[...slug]]/page.tsx b/src/app/admin/[[...slug]]/page.tsx index 6fc3735..c6feb31 100644 --- a/src/app/admin/[[...slug]]/page.tsx +++ b/src/app/admin/[[...slug]]/page.tsx @@ -1,72 +1,67 @@ -'use server' -import { getCookieAuth } from "@/app/lib/actions/actions"; +"use server"; +cache: "no-store"; import AuthHandler from "@/components/server/admin/authHandler"; import Sidebar, { SidebarEntry } from "@/components/server/admin/views/sidebar"; -import { cookies } from "next/headers"; -import PostView from "@/components/server/admin/views/PostView"; -import { ReactNode } from "react"; -import ProjectView from "@/components/server/admin/views/ProjectView"; - -type Props = { - params: { - slug: string[] - }; -} - - +import ProjectView from "@/views/admin/ProjectView"; +import PostView from "@/views/admin/PostView"; +import { redirect } from 'next/navigation' function Home() { - return
home
+ return
home
; } function PostManager() { - return
posts
+ return ; } function ProjectManager() { - return
projects
+ return ; } - async function getViewMap(): Promise> { - return new Map([ - ['home', ], - ['man-post', ], - ['man-proj', ] - ]); + return new Map([ + ["home", ], + ["man-post", ], + ["man-proj", ], + ]); } - - - -async function getSidebarEntries(): Promise> { - 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' }, - ] -} +const sidebarEntries: 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" }, +]; async function getCurrentView(view: string): Promise { - const viewMap = await getViewMap(); - const viewJSX = viewMap.get(view); - return viewJSX ? viewJSX : ; + const viewMap = await getViewMap(); + const viewJSX = viewMap.get(view || "home"); + return viewJSX || ; } -export default async function Page(props: Props) { - const sidebarEntries: Array = await getSidebarEntries(); +type Props = { + params: { + slug: string[]; + }; +}; - const slug: string | string[] = props.params.slug ? props.params.slug : 'home'; - - return ( -
- - -
- {await getCurrentView(slug.toString())} -
-
- {/*
{JSON.stringify(cookies().getAll())}
*/} -
- ); -} \ No newline at end of file +export default async function Page({ params: { slug = ["home"] } }: Props) { + if ( + (await sidebarEntries) + .map((entry) => entry.view) + .indexOf(slug.toString()) < 0 + ) redirect("/admin/") + return ( +
+ + +
+ {await getCurrentView(slug.toString())} +
+
+ {/*
{JSON.stringify(cookies().getAll())}
*/} +
+ ); +} diff --git a/src/components/client/admin/PostEditor.tsx b/src/components/client/admin/PostEditor.tsx index e443f7b..7923ad6 100644 --- a/src/components/client/admin/PostEditor.tsx +++ b/src/components/client/admin/PostEditor.tsx @@ -1,153 +1,315 @@ -import { ActionResult } from "@/app/lib/actions/ActionResult"; -import { handleActionResult } from "@/app/lib/actions/clientActionHandler"; import { GetPostsAttributes } from "@/app/lib/actions/entityManagement/postActions"; -import { Post, Project, Bucket, PostBucket, Attachment } from "@/models" -import { PostAttributesWithBuckets } from "@/models"; -import { DeepPartial } from "@/util/DeepPartial"; +import { Post, Project, Bucket, PostBucket, Attachment } from "@/models"; import { Attributes } from "@sequelize/core"; import { UUID } from "crypto"; -import { ChangeEventHandler, MouseEventHandler, useLayoutEffect, useRef, useState } from "react"; -import { Accordion, AccordionBody, AccordionHeader, AccordionItem } from "react-bootstrap"; +import { + ChangeEventHandler, + MouseEventHandler, + MutableRefObject, + useLayoutEffect, + useRef, + useState, +} from "react"; +import { + Accordion, + AccordionBody, + AccordionHeader, + AccordionItem, +} from "react-bootstrap"; export type PostTableCallbacks = { - savePost: (p:Partial>)=>void; - closeEditor: ()=>any; - uploadAttachment : ()=>any; -} + savePost: (p: Partial>) => void; + refetch: () => any; + closeEditor: () => any; + uploadAttachment: () => any; +}; + +export type EditorState = { + isEditorOpen: boolean; + editorPost: GetPostsAttributes; +}; export type EditorProps = { - editorOpenState:boolean; - openedPost?:GetPostsAttributes; - projects?:Attributes[]; - callbacks?:PostTableCallbacks; + editorPost: GetPostsAttributes; + projectList: { + id: number; + readableIdentifier: string; + }[]; + callbacks: PostTableCallbacks; +}; + +export default function PostEditor({ + editorPost, + projectList, + callbacks, +}: EditorProps) { + // Setup State Hooks + let [postContentState, setPostContentState] = useState(editorPost.content); + let [postTitleState, setPostTitleState] = useState(editorPost.title); + let [postProjectIDState, setPostProjectIDState] = useState( + editorPost.project.id + ); + let textbox: any = useRef(undefined); + + // Autosize the text area + function textAreaAutoSize( + textbox: MutableRefObject + ): void { + if (!textbox.current || !textbox.current.style) return; + textbox.current.style.height = "fit-content"; + textbox.current.style.height = `${textbox.current.scrollHeight}px`; + } + useLayoutEffect(() => textAreaAutoSize(textbox)); + + // Handle user input on the text area by updating state and autosizing the textfield + const onTextAreaChange: ChangeEventHandler = (e) => { + setPostContentState(e.target.value); // Update State + textAreaAutoSize(textbox); // Autosize the text area + }; + + // Handle changing the selected project using the dropdown select + const projectSelectionChange: ChangeEventHandler = (e) => + setPostProjectIDState(parseInt(e.target.value)); + // Handle clicking the save button + const onClickSaveButton: MouseEventHandler = (e) => { + callbacks?.savePost({ + id: editorPost?.id as number, + content: postContentState as string, + title: postTitleState as string, + project_id: postProjectIDState, + }); + }; + const onClickCancelButton: MouseEventHandler = (e) => { + callbacks?.closeEditor(); + }; + + return ( + <> +
+

+ Edit Post +

+

+ Title +

+ setPostTitleState(e.target.value)} + type="text" + className="m-2" + /> +

+ Content +

+