Refactoring and reworking dbsetup route

This commit is contained in:
Andreas 2024-06-28 08:03:58 +02:00
parent c076918d05
commit 665da4be29
18 changed files with 390 additions and 162 deletions

0
db/.gitkeep Normal file
View File

29
db/seed/posts.json Normal file
View File

@ -0,0 +1,29 @@
{
"projects": [
{
"name": "Blog",
"readableIdentifier": "blog",
"posts": [
{
"title": "Test Post",
"content": "# Hello <ExampleComponent />\nthis is some **test** markdown and we make some edits\n![](/attachment/788dfc19-55ba-482c-8124-277702296dfb/FB_IMG_1716665756868.jpg)",
"description": "A new post to test the blog system",
"project_id": 1,
"user_id": 1,
"buckets": [
{
"id": "788dfc19-55ba-482c-8124-277702296dfb",
"attachments": [
{
"path": "FB_IMG_1716665756868.jpg"
},{
"path": "patchnotes.jpg"
}
]
}
]
}
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

21
db/seed/users.json Normal file
View File

@ -0,0 +1,21 @@
{
"users": [{
"username": "admin",
"password": "changeme",
"perms": {
"isAdmin": true
}
},{
"username": "notadmin",
"password": "changeme",
"perms": {
"isAdmin": false
}
},{
"username": "theodore",
"password": "changeme",
"perms": {
"isAdmin": false
}
}]
}

View File

@ -5,38 +5,109 @@ import { cookies } from "next/headers";
import { APIError} from "@/util/api/error" import { APIError} from "@/util/api/error"
import { UserAuth, parseBasicAuth, getAssociatedUser } from "@/util/api/user" import { UserAuth, parseBasicAuth, getAssociatedUser } from "@/util/api/user"
import { Attachment, Auth, Bucket, DBState, Post, PostTag, Project, Tag, User, UserPerms,dbSync,sequelize} from "@/models"; import { Attachment, Auth, Bucket, DBState, Post, PostTag, Project, Tag, User, UserPerms,dbSync,sequelize} from "@/models";
import Sequelize, { DataTypes } from "@sequelize/core"; import Sequelize, { CreationAttributes, DataTypes } from "@sequelize/core";
import { SqliteColumnsDescription, SqliteDialect, SqliteQueryInterface } from "@sequelize/sqlite3"; import { SqliteColumnsDescription, SqliteDialect, SqliteQueryInterface } from "@sequelize/sqlite3";
import { hashPassword } from "@/util/Auth"; import { hashPassword } from "@/util/Auth";
import { copyFile, readFileSync } from "fs";
import path from "path";
import { Attributes } from '@sequelize/core';
import { DeepPartial } from "@/util/DeepPartial";
import { UUID } from "crypto";
async function seedUsers(qif: SqliteQueryInterface<SqliteDialect>){
const fp = path.resolve('./db/seed/users.json');
const json: {users: CreationAttributes<User>[]} = JSON.parse(Buffer.from(readFileSync(fp).valueOf()).toString());
const users = json.users.map(async user=>{
user.password = await hashPassword(user.password);
return user;
})
const dbUsers = await User.bulkCreate(await Promise.all(users), {include: User.associations.perms})
}
async function seedPosts(qif: SqliteQueryInterface<SqliteDialect>){
const fp = path.resolve('./db/seed/posts.json');
const json: {users: CreationAttributes<User>[]} = JSON.parse(Buffer.from(readFileSync(fp).valueOf()).toString());
const projects =[
{
"name": "Blog",
"readableIdentifier": "blog",
"posts": [
{
"title": "Test Post",
"content": "# Hello <ExampleComponent />\nthis is some **test** markdown and we make some edits\n![](/attachment/788dfc19-55ba-482c-8124-277702296dfb/FB_IMG_1716665756868.jpg)",
"description": "A new post to test the blog system",
"project_id": 1,
"user_id": 1,
"buckets": [
{
"id": "788dfc19-55ba-482c-8124-277702296dfb",
"attachments": [
{
"filename": "FB_IMG_1716665756868.jpg"
}
]
}
]
}
]
}]
projects.map(project=>{
Project.create({name: project.name, readableIdentifier: project.readableIdentifier});
project.posts.map(async post=>{
const pst = await Post.create({title:post.title, content:post.content, description:post.description, project_id: post.project_id, user_id: post.user_id});
post.buckets.map(async bucket=>{
pst.createBucket({id:bucket.id as UUID});
bucket.attachments.map(attachment=>{
Attachment.create({bucket_id:bucket.id as UUID, filename: attachment.filename}).then((a)=>{
copyFile(
path.resolve('.','db','seed','post',a.filename),
path.resolve('.','bucket',bucket.id,a.filename),
()=>{
}
)
})
})
})
})
})
async function seedDatabase(queryInterface:SqliteQueryInterface<SqliteDialect>){
const password = await hashPassword('changeme');
const project = await Project.findOne({where: { const project = await Project.findOne({where: {
readableIdentifier: 'blog' readableIdentifier: 'blog'
}}).then(e=> e ? e : Project.create({name:'General Blog',readableIdentifier:'blog'})); }}).then(e=> e ? e : Project.create({name:'General Blog',readableIdentifier:'blog'}));
const user = await User.findOne({where: { }
username: 'admin'
}}).then(e=> e ? e : User.create({username: 'admin', password: password, perms: {isAdmin: true}}, {include: User.associations.perms}));
await Post.create({
title: 'Test Post',
content: `
# Hello <ExampleComponent /> async function seedDatabase(qif:SqliteQueryInterface<SqliteDialect>){
this is some **test** markdown await seedUsers(qif);
`, await seedPosts(qif)
project_id: project.id,
user_id: user.id // await Post.create({
}) // title: 'Test Post',
await Post.create({ // content: `
title: 'Test Post 2', // # Hello <ExampleComponent />
content: ` // this is some **test** markdown
# Hello <ExampleComponent /> // `,
this is amother post with some **test** markdown // project_id: project.id,
`, // user_id: user.id
project_id: project.id, // })
user_id: user.id // await Post.create({
}) // title: 'Test Post 2',
// content: `
// # Hello <ExampleComponent />
// this is amother post with some **test** markdown
// `,
// project_id: project.id,
// user_id: user.id
// })
} }
async function trySetup(request:Request){ async function trySetup(request:Request){

View File

@ -1,8 +1,30 @@
import { Attachment, Bucket, dbSync } from "@/models";
import { open, openSync, readFileSync } from "fs"; import { open, openSync, readFileSync } from "fs";
import { NextRequest, NextResponse } from "next/server"; import { NextRequest, NextResponse } from "next/server";
import path from "path"; import path from "path";
export async function GET(req:NextRequest, { params }: {params:{slug: string}}){ export async function GET(req:NextRequest, { params }: {params:{slug: string[]}}){
await dbSync;
const filename: `${string}${'.'}${string}` = params.slug[params.slug.length-1] as `${string}${'.'}${string}`;
const bucket = await Bucket.findOne({
where: {
id:params.slug[0],
},
include: [{
association: Bucket.associations.attachments,
where: {
filename: params.slug[params.slug.length-1]
}
}]
},)
console.log(params.slug);
if(!bucket || !bucket.attachments || !bucket.attachments[0] || bucket.attachments[0].filename != params.slug[params.slug.length-1]){
const headers = new Headers();
headers.set("Content-Type", "application/json");
return new NextResponse(JSON.stringify({
error:"Access Denied"
}),{headers:headers});
}
const fp = path.resolve('.',`bucket`,...params.slug); const fp = path.resolve('.',`bucket`,...params.slug);
return new Response(readFileSync(fp)); return new Response(readFileSync(fp));
} }

View File

@ -71,3 +71,12 @@ export async function updatePost(postAttributes: Partial<Attributes<Post>>): Pro
const post = await Post.update(postAttributes, {where:{id:postAttributes.id}}); const post = await Post.update(postAttributes, {where:{id:postAttributes.id}});
return {result:JSON.parse(JSON.stringify(post))}; return {result:JSON.parse(JSON.stringify(post))};
} }
export type PostServerActions = {
deletePost: (id: number) => Promise<ActionResult<boolean>>;
getPosts: () => Promise<ActionResult<GetPostsAttributes[]>>;
getProjects: () => Promise<ActionResult<Attributes<Project>[]>>;
savePost: (
data: Partial<Attributes<Post>>
) => Promise<ActionResult<Attributes<Post>[]>>;
};

View File

@ -2,6 +2,7 @@ import { GetPostsAttributes } from "@/app/lib/actions/entityManagement/postActio
import { Post, Project, Bucket, PostBucket, Attachment } from "@/models"; import { Post, Project, Bucket, PostBucket, Attachment } from "@/models";
import { Attributes } from "@sequelize/core"; import { Attributes } from "@sequelize/core";
import { UUID } from "crypto"; import { UUID } from "crypto";
import { EntityEditorTextArea } from '../input/EntityEditorTextArea';
import { import {
ChangeEventHandler, ChangeEventHandler,
MouseEventHandler, MouseEventHandler,
@ -49,23 +50,6 @@ export default function PostEditor({
let [postProjectIDState, setPostProjectIDState] = useState( let [postProjectIDState, setPostProjectIDState] = useState(
editorPost.project.id editorPost.project.id
); );
let textbox: any = useRef(undefined);
// Autosize the text area
function textAreaAutoSize(
textbox: MutableRefObject<HTMLTextAreaElement>
): 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<HTMLTextAreaElement> = (e) => {
setPostContentState(e.target.value); // Update State
textAreaAutoSize(textbox); // Autosize the text area
};
// Handle changing the selected project using the dropdown select // Handle changing the selected project using the dropdown select
const projectSelectionChange: ChangeEventHandler<HTMLSelectElement> = (e) => const projectSelectionChange: ChangeEventHandler<HTMLSelectElement> = (e) =>
@ -102,14 +86,14 @@ export default function PostEditor({
<h2 key="label-content" className="m-2"> <h2 key="label-content" className="m-2">
Content Content
</h2> </h2>
<textarea <EntityEditorTextArea
key="input-content" contentHook={{
onChange={onTextAreaChange} state: postContentState,
ref={textbox} setState: setPostContentState
value={postContentState} }}
style={{ height: "100%" }} className={ "w-[100%] min-h-auto h-content align-top text-start text-base line-clamp-6 m-2" }
className="w-[100%] min-h-auto h-content align-top text-start text-base line-clamp-6 m-2" >
/> </EntityEditorTextArea>
<h2 key="label-project" className="m-2"> <h2 key="label-project" className="m-2">
Project Project
</h2> </h2>

View File

@ -1,38 +1,28 @@
"use client"; "use client";
import React, { ReactNode } from "react"; import React from "react";
import EntityManagementTable from "../EntityManagementTable"; import EntityManagementTable from "../EntityManagementTable";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { EditorRenderer, EditorState, PostTableCallbacks } from "./PostEditor"; import { EditorRenderer, EditorState, PostTableCallbacks } from "./PostEditor";
import { Attributes } from "@sequelize/core"; import { Attributes, InferAttributes } from "@sequelize/core";
import { useState } from "react"; import { Project, Post, Bucket, User, PostAttributesWithBuckets } from "@/models";
import { Project, Post, Bucket } from "@/models";
import { ActionResult } from "@/app/lib/actions/ActionResult";
import { handleActionResult } from "@/app/lib/actions/clientActionHandler"; import { handleActionResult } from "@/app/lib/actions/clientActionHandler";
import { import {
getPostsWithBucketsAndProject, getPostsWithBucketsAndProject,
GetPostsAttributes, GetPostsAttributes,
PostServerActions,
} from "@/app/lib/actions/entityManagement/postActions"; } from "@/app/lib/actions/entityManagement/postActions";
import { PostViewProps } from "@/views/admin/ClientPostView";
import { aifa } from "@/util/Utils";
import { StateHook } from "../../../util/types/StateHook";
export type PostTableServerActions = { export type PostTableStateProps = {
deletePost: (id: number) => Promise<ActionResult<boolean>>; posts:StateHook<GetPostsAttributes[]>,
getPosts: () => Promise<ActionResult<GetPostsAttributes[]>>; editor:StateHook<EditorState>
getProjects: () => Promise<ActionResult<Attributes<Project>[]>>; }
savePost: (
data: Partial<Attributes<Post>>
) => Promise<ActionResult<Attributes<Post>[]>>;
// uploadAttachment: ()=> Promise<ActionResult<any>>
};
type PostTableProps = { export type PostTableProps = PostViewProps & {state:PostTableStateProps};
children?: ReactNode;
headings: Array<string>;
posts: GetPostsAttributes[];
projects: Attributes<Project>[];
actions: PostTableServerActions;
};
const aifa = (a: ReactNode, b: ReactNode) => (a ? a : b);
export default function PostTable({ export default function PostTable({
children, children,
@ -40,26 +30,20 @@ export default function PostTable({
posts, posts,
projects, projects,
actions, actions,
state,
}: PostTableProps) { }: PostTableProps) {
// Init editor state. Make sure it is not opened by default
const initEditorState: EditorState = {
isEditorOpen: false,
editorPost: posts[0],
};
// Set up required state hooks
const [postsState, setPostsState] = useState(posts);
const [editorState, setEditorState] = useState(initEditorState);
// Define editor controls // Define editor controls
const editorControls = { const editorControls = {
closeEditor: () => { closeEditor: () => {
setEditorState({ state.editor.setState({
isEditorOpen: false, isEditorOpen: false,
editorPost: posts[0], editorPost: posts[0],
}); });
}, },
showEditor: (entry: GetPostsAttributes) => { showEditor: (entry: GetPostsAttributes) => {
setEditorState({ state.editor.setState({
isEditorOpen: true, isEditorOpen: true,
editorPost: entry, editorPost: entry,
}); });
@ -72,8 +56,8 @@ export default function PostTable({
actions.deletePost(entry.id).then((actionResult) => { actions.deletePost(entry.id).then((actionResult) => {
const result = handleActionResult(actionResult); const result = handleActionResult(actionResult);
if (!result) return; if (!result) return;
postsState.splice(postsState.indexOf(entry), 1); state.posts.state.splice(state.posts.state.indexOf(entry), 1);
setPostsState([...postsState]); state.posts.setState([...state.posts.state]);
toast.success("Removed Post:" + entry.id); toast.success("Removed Post:" + entry.id);
}); });
}, },
@ -86,7 +70,7 @@ export default function PostTable({
getPostsServerActionResult getPostsServerActionResult
); );
// Set Posts state // Set Posts state
if (result) setPostsState(result); if (result) state.posts.setState(result);
}); });
}, },
savePost: (e: Partial<Attributes<Post>>) => { savePost: (e: Partial<Attributes<Post>>) => {
@ -101,26 +85,19 @@ export default function PostTable({
.then(getPostsWithBucketsAndProject) .then(getPostsWithBucketsAndProject)
.then((res) => { .then((res) => {
const result = handleActionResult(res); const result = handleActionResult(res);
if (result) setPostsState(result); if (result) state.posts.setState(result);
}) })
.then(editorControls.closeEditor) .then(editorControls.closeEditor)
.then(postActions.refetch); .then(postActions.refetch);
}, },
}; };
const callbacks: PostTableCallbacks = {
savePost: postActions.savePost,
closeEditor: editorControls.closeEditor,
refetch: postActions.refetch,
uploadAttachment: () => {},
};
return ( return (
<EntityManagementTable <EntityManagementTable
entityName="Post" entityName="Post"
headings={headings} headings={headings}
> >
{postsState.map((post: GetPostsAttributes) => ( {state.posts.state.map((post: GetPostsAttributes) => (
<React.Fragment key={`postrow-${post.id}`}> <React.Fragment key={`postrow-${post.id}`}>
<tr> <tr>
<th key={`rowheading-post-${post.id}`} scope="row"> <th key={`rowheading-post-${post.id}`} scope="row">
@ -177,8 +154,13 @@ export default function PostTable({
headings={headings} headings={headings}
editorPost={post} editorPost={post}
editorControls={editorControls} editorControls={editorControls}
editorState={editorState} editorState={state.editor.state}
callbacks={callbacks} callbacks={{
savePost: postActions.savePost,
closeEditor: editorControls.closeEditor,
refetch: postActions.refetch,
uploadAttachment: () => {},
}}
projects={projects} projects={projects}
/> />
</React.Fragment> </React.Fragment>

View File

@ -0,0 +1,34 @@
'use client'
import { useRef, MutableRefObject, useLayoutEffect, ChangeEventHandler, useState } from "react";
import { StateHook } from '../../../util/types/StateHook';
export function EntityEditorTextArea({contentHook, className}: {contentHook:StateHook<string>, className:string}){
let textbox: any = useRef(undefined);
// Autosize the text area
function textAreaAutoSize(
textbox: MutableRefObject<HTMLTextAreaElement>
): 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<HTMLTextAreaElement> = (e) => {
contentHook?.setState(e.target.value); // Update State
textAreaAutoSize(textbox); // Autosize the text area
};
return <textarea
key="input-content"
onChange={onTextAreaChange}
ref={textbox}
value={contentHook?.state}
style={{ height: "100%" }}
className={className}
/>
}

View File

@ -29,6 +29,7 @@ export class User extends Model<InferAttributes<User>, InferCreationAttributes<U
@Unique @Unique
declare id: CreationOptional<number>; declare id: CreationOptional<number>;
@Attribute(DataTypes.STRING) @Attribute(DataTypes.STRING)
@Unique()
declare username: string; declare username: string;
@Attribute(DataTypes.STRING) @Attribute(DataTypes.STRING)
declare password: string; declare password: string;

View File

@ -1,4 +1,5 @@
'server only' 'server only'
import { ReactNode } from "react";
import Gens from "./gens"; import Gens from "./gens";
// import Auth from "./Auth"; // import Auth from "./Auth";
@ -29,3 +30,4 @@ function truncateString(str:string = '', num:number = 255) {
export { Gens, constructAPIUrl, constructUrl, truncateString } export { Gens, constructAPIUrl, constructUrl, truncateString }
export const aifa = (a: ReactNode, b: ReactNode) => (a ? a : b);

View File

@ -3,7 +3,7 @@
import { validatePassword } from "@/util/Auth"; import { validatePassword } from "@/util/Auth";
import { APIError } from "@/util/api/error"; import { APIError } from "@/util/api/error";
import { User } from "@/model/User"; import { User } from "@/models";
export function parseBasicAuth(authb64:string):UserAuth export function parseBasicAuth(authb64:string):UserAuth
{ {

View File

@ -0,0 +1,9 @@
import { Dispatch, SetStateAction } from "react";
export type StateHook<T> = {
state: T;
setState: Dispatch<SetStateAction<T>>;
};
export type StateHookArray<T> = [
T,Dispatch<SetStateAction<T>>
]

View File

@ -0,0 +1,62 @@
"use client";
import PostTable, {
PostTableStateProps,
} from "@/components/client/admin/PostTable";
import { PostTableProps } from "../../components/client/admin/PostTable";
import {
GetPostsAttributes,
PostServerActions,
} from "@/app/lib/actions/entityManagement/postActions";
import { EditorState } from "@/components/client/admin/PostEditor";
import { Project } from "@/models";
import { Dispatch, ReactNode, SetStateAction, useState } from "react";
import { Attributes } from "@sequelize/core";
import { StateHookArray } from "@/util/types/StateHook";
export type PostViewProps = {
children?: ReactNode;
headings: Array<string>;
posts: GetPostsAttributes[];
projects: Attributes<Project>[];
actions: PostServerActions;
};
export function ClientPostView({
children,
headings,
posts,
projects,
actions,
}: PostViewProps) {
// Init editor state. Make sure it is not opened by default
const initEditorState: EditorState = {
isEditorOpen: false,
editorPost: posts[0],
};
// Set up required state hooks
const [postsState, setPostsState]: StateHookArray<GetPostsAttributes[]> = useState(posts);
const [editorState, setEditorState]: StateHookArray<EditorState> = useState(initEditorState);
const state: PostTableStateProps = {
posts: {
state: postsState,
setState: setPostsState,
},
editor: {
state: editorState,
setState: setEditorState,
},
};
return (
<>
<PostTable
headings={headings}
posts={posts}
projects={projects}
actions={actions}
state={state}
></PostTable>
</>
);
}

View File

@ -2,25 +2,29 @@
cache: "no-store"; cache: "no-store";
import { ReactNode, useEffect } from "react"; import { ReactNode, useEffect } from "react";
import PostTable, { import PostTable from "@/components/client/admin/PostTable";
PostTableServerActions,
} from "@/components/client/admin/PostTable";
import { import {
deletePost, deletePost,
getPostsWithBucketsAndProject, getPostsWithBucketsAndProject,
GetPostsAttributes,
updatePost, updatePost,
GetPostsAttributes,
PostServerActions,
} from "@/app/lib/actions/entityManagement/postActions"; } from "@/app/lib/actions/entityManagement/postActions";
import { getProjects } from "@/app/lib/actions/entityManagement/projectActions"; import { getProjects } from "@/app/lib/actions/entityManagement/projectActions";
import { Bucket, Project, Post, dbSync, Attachment } from "@/models"; import { Bucket, Project, Post, dbSync, Attachment } from "@/models";
import { handleActionResult } from "../../app/lib/actions/clientActionHandler"; import { handleActionResult } from "../../app/lib/actions/clientActionHandler";
import { ClientPostView } from "./ClientPostView";
import { readFileSync } from "fs";
import path from "path";
type Props = { type Props = {
children?: ReactNode; children?: ReactNode;
}; };
async function getHeadings() { export default async function PostView(props: Props) {
let headings: string[] = [ const sync = await dbSync;
const headings:string[] = [
"#", "#",
"Title", "Title",
"Content", "Content",
@ -29,15 +33,7 @@ async function getHeadings() {
"UpdatedAt", "UpdatedAt",
]; ];
return headings; const actions: PostServerActions = {
}
export default async function PostView(props: Props) {
const sync = await dbSync;
const headings = await getHeadings();
const actions: PostTableServerActions = {
deletePost: deletePost, deletePost: deletePost,
getPosts: getPostsWithBucketsAndProject, getPosts: getPostsWithBucketsAndProject,
getProjects: getProjects, getProjects: getProjects,
@ -55,12 +51,12 @@ export default async function PostView(props: Props) {
return ( return (
<div className="w-[100%] min-h-fit bg-gray-100 overflow-scroll"> <div className="w-[100%] min-h-fit bg-gray-100 overflow-scroll">
<div className="w-[100%] m-auto"> <div className="w-[100%] m-auto">
<PostTable <ClientPostView
posts={posts ? posts : []} posts={posts ? posts : []}
projects={projects} projects={projects}
headings={headings} headings={headings}
actions={actions} actions={actions}
></PostTable> ></ClientPostView>
</div> </div>
</div> </div>
); );

View File

@ -1,58 +1,63 @@
cache: 'no-store' cache: "no-store";
import { tryFetchPosts } from "@/app/api/post/route"; import { tryFetchPosts } from "@/app/api/post/route";
import { constructAPIUrl } from "@/util/Utils"; import { constructAPIUrl } from "@/util/Utils";
import { ReactNode, useEffect } from "react"; import { ReactNode, useEffect } from "react";
import EntityManagementTable from "../../components/client/EntityManagementTable"; import EntityManagementTable from "../../components/client/EntityManagementTable";
import PostTable from "@/components/client/admin/PostTable"; import PostTable from "@/components/client/admin/PostTable";
import { deletePost, getPostsWithBucketsAndProject, updatePost } from "@/app/lib/actions/entityManagement/postActions"; import {
deletePost,
getPostsWithBucketsAndProject,
updatePost,
} from "@/app/lib/actions/entityManagement/postActions";
import { getProjects } from "@/app/lib/actions/entityManagement/projectActions"; import { getProjects } from "@/app/lib/actions/entityManagement/projectActions";
import { Bucket, Project, Post, dbSync, Attachment } from "@/models"; import { Bucket, Project, Post, dbSync, Attachment } from "@/models";
import { tryCreateAttachment } from "@/app/api/attachment/route"; import { tryCreateAttachment } from "@/app/api/attachment/route";
import { Attributes } from '@sequelize/core'; import { Attributes } from "@sequelize/core";
import * as util from 'util' import * as util from "util";
type Props = { type Props = {
children?:ReactNode children?: ReactNode;
} };
async function getHeadings() { async function getHeadings() {
let headings:string[] = [] let headings: string[] = [];
for (const key in Project.getAttributes()) for (const key in Project.getAttributes()) {
{ headings.push(key);
headings.push(key)
} }
return headings return headings;
} }
export default async function ProjectView(props: Props) { export default async function ProjectView(props: Props) {
const sync = await dbSync; const sync = await dbSync;
// const headings = [ const actions = {
// '#', deletePost: deletePost,
// 'Title', getProjects:getProjects,
// 'Content', savePost:updatePost,
// 'Project', // uploadAttachment:tryCreateAttachment
// 'Date Created', };
// 'Date Modified', const projects: Project[] = await Project.findAll().then((projects) =>
// 'Edit', projects.map((e) => JSON.parse(JSON.stringify(e)))
// 'Delete', );
// ] return (
// const actions = { <>
// deletePost: deletePost,
// getPosts:getPostsWithBuckets,
// getProjects:getProjects,
// savePost:updatePost,
// // uploadAttachment:tryCreateAttachment
// };
const posts:Post[] = await Post.findAll({include: {model: Bucket, include: {model: Attachment}}}).then(posts=>posts.map((e)=>JSON.parse(JSON.stringify(e))));
const projects = await Project.findAll().then(projects=>projects.map((e)=>JSON.parse(JSON.stringify(e))));
const headings = await getHeadings();
return <>
<div className="w-[100%] min-h-fit bg-gray-100 overflow-scroll"> <div className="w-[100%] min-h-fit bg-gray-100 overflow-scroll">
<div className="w-[100%] m-auto"> <div className="w-[100%] m-auto">
<EntityManagementTable entityName="Project" headings={headings}><></></EntityManagementTable> <EntityManagementTable
entityName="Project"
headings={[
'#',
'Title',
'Content',
'Project',
'Date Created',
'Date Modified',
'Edit',
'Delete',
]}>
{""}
</EntityManagementTable>
{/* <PostTable data={posts} {/* <PostTable data={posts}
projects={projects} projects={projects}
headings={headings} headings={headings}
@ -60,5 +65,6 @@ export default async function ProjectView(props:Props){
</PostTable> */} </PostTable> */}
</div> </div>
</div> </div>
</>; </>
);
} }