From 6c1efa572f248a025d34017f693e63a5554e53ad Mon Sep 17 00:00:00 2001 From: Ifeoluwa Date: Wed, 4 Feb 2026 18:42:57 +0100 Subject: [PATCH 1/3] add projects to the frontend --- frontend/.astro/data-store.json | 2 +- frontend/.astro/settings.json | 2 +- frontend/.astro/types.d.ts | 1 + frontend/src/components/AppSidebar.tsx | 7 +- frontend/src/components/pages/ProjectPage.tsx | 12 + .../src/components/pages/ProjectsPage.tsx | 20 ++ .../projects/CreateProjectModal.tsx | 155 +++++++++++ .../projects/DeleteConfirmationDialogue.tsx | 98 +++++++ .../components/projects/EditProjectModal.tsx | 162 ++++++++++++ .../src/components/projects/ProjectCard.tsx | 136 ++++++++++ .../src/components/projects/ProjectsList.tsx | 191 ++++++++++++++ .../src/components/projects/ProjectsMain.tsx | 196 ++++++++++++++ .../projects/project-page/ProjectActions.tsx | 96 +++++++ .../project-page/ProjectDescription.tsx | 20 ++ .../projects/project-page/ProjectHeader.tsx | 88 +++++++ .../projects/project-page/ProjectMain.tsx | 71 +++++ frontend/src/hooks/projects/useprojects.ts | 248 ++++++++++++++++++ .../src/lib/constants/projectConstants.ts | 99 +++++++ frontend/src/lib/types/projects.d.ts | 27 ++ frontend/src/pages/project/[id].astro | 10 + frontend/src/pages/projects.astro | 8 + 21 files changed, 1646 insertions(+), 3 deletions(-) create mode 100644 frontend/src/components/pages/ProjectPage.tsx create mode 100644 frontend/src/components/pages/ProjectsPage.tsx create mode 100644 frontend/src/components/projects/CreateProjectModal.tsx create mode 100644 frontend/src/components/projects/DeleteConfirmationDialogue.tsx create mode 100644 frontend/src/components/projects/EditProjectModal.tsx create mode 100644 frontend/src/components/projects/ProjectCard.tsx create mode 100644 frontend/src/components/projects/ProjectsList.tsx create mode 100644 frontend/src/components/projects/ProjectsMain.tsx create mode 100644 frontend/src/components/projects/project-page/ProjectActions.tsx create mode 100644 frontend/src/components/projects/project-page/ProjectDescription.tsx create mode 100644 frontend/src/components/projects/project-page/ProjectHeader.tsx create mode 100644 frontend/src/components/projects/project-page/ProjectMain.tsx create mode 100644 frontend/src/hooks/projects/useprojects.ts create mode 100644 frontend/src/lib/constants/projectConstants.ts create mode 100644 frontend/src/lib/types/projects.d.ts create mode 100644 frontend/src/pages/project/[id].astro create mode 100644 frontend/src/pages/projects.astro diff --git a/frontend/.astro/data-store.json b/frontend/.astro/data-store.json index 805c3fe..4af4b92 100644 --- a/frontend/.astro/data-store.json +++ b/frontend/.astro/data-store.json @@ -1 +1 @@ -[["Map",1,2],"meta::meta",["Map",3,4,5,6],"astro-version","5.14.1","astro-config-digest","{\"root\":{},\"srcDir\":{},\"publicDir\":{},\"outDir\":{},\"cacheDir\":{},\"compressHTML\":true,\"base\":\"/\",\"trailingSlash\":\"ignore\",\"output\":\"server\",\"scopedStyleStrategy\":\"attribute\",\"build\":{\"format\":\"directory\",\"client\":{},\"server\":{},\"assets\":\"_astro\",\"serverEntry\":\"entry.mjs\",\"redirects\":true,\"inlineStylesheets\":\"auto\",\"concurrency\":1},\"server\":{\"open\":false,\"host\":\"0.0.0.0\",\"port\":4321,\"streaming\":true,\"allowedHosts\":[]},\"redirects\":{},\"image\":{\"endpoint\":{\"route\":\"/_image\",\"entrypoint\":\"astro/assets/endpoint/node\"},\"service\":{\"entrypoint\":\"astro/assets/services/sharp\",\"config\":{}},\"domains\":[],\"remotePatterns\":[],\"responsiveStyles\":false},\"devToolbar\":{\"enabled\":true},\"markdown\":{\"syntaxHighlight\":{\"type\":\"shiki\",\"excludeLangs\":[\"math\"]},\"shikiConfig\":{\"langs\":[],\"langAlias\":{},\"theme\":\"github-dark\",\"themes\":{},\"wrap\":false,\"transformers\":[]},\"remarkPlugins\":[],\"rehypePlugins\":[],\"remarkRehype\":{},\"gfm\":true,\"smartypants\":true},\"security\":{\"checkOrigin\":true},\"env\":{\"schema\":{},\"validateSecrets\":false},\"experimental\":{\"clientPrerender\":false,\"contentIntellisense\":false,\"headingIdCompat\":false,\"preserveScriptOrder\":false,\"liveContentCollections\":false,\"csp\":false,\"staticImportMetaEnv\":false,\"chromeDevtoolsWorkspace\":false,\"failOnPrerenderConflict\":false},\"legacy\":{\"collections\":false},\"session\":{\"driver\":\"fs-lite\",\"options\":{\"base\":\"/app/node_modules/.astro/sessions\"}}}"] \ No newline at end of file +[["Map",1,2],"meta::meta",["Map",3,4,5,6],"astro-version","5.14.1","astro-config-digest","{\"root\":{},\"srcDir\":{},\"publicDir\":{},\"outDir\":{},\"cacheDir\":{},\"compressHTML\":true,\"base\":\"/\",\"trailingSlash\":\"ignore\",\"output\":\"server\",\"scopedStyleStrategy\":\"attribute\",\"build\":{\"format\":\"directory\",\"client\":{},\"server\":{},\"assets\":\"_astro\",\"serverEntry\":\"entry.mjs\",\"redirects\":true,\"inlineStylesheets\":\"auto\",\"concurrency\":1},\"server\":{\"open\":false,\"host\":\"0.0.0.0\",\"port\":4321,\"streaming\":true,\"allowedHosts\":[]},\"redirects\":{},\"image\":{\"endpoint\":{\"route\":\"/_image\",\"entrypoint\":\"astro/assets/endpoint/node\"},\"service\":{\"entrypoint\":\"astro/assets/services/sharp\",\"config\":{}},\"domains\":[],\"remotePatterns\":[],\"responsiveStyles\":false},\"devToolbar\":{\"enabled\":true},\"markdown\":{\"syntaxHighlight\":{\"type\":\"shiki\",\"excludeLangs\":[\"math\"]},\"shikiConfig\":{\"langs\":[],\"langAlias\":{},\"theme\":\"github-dark\",\"themes\":{},\"wrap\":false,\"transformers\":[]},\"remarkPlugins\":[],\"rehypePlugins\":[],\"remarkRehype\":{},\"gfm\":true,\"smartypants\":true},\"security\":{\"checkOrigin\":true},\"env\":{\"schema\":{},\"validateSecrets\":false},\"experimental\":{\"clientPrerender\":false,\"contentIntellisense\":false,\"headingIdCompat\":false,\"preserveScriptOrder\":false,\"liveContentCollections\":false,\"csp\":false,\"staticImportMetaEnv\":false,\"chromeDevtoolsWorkspace\":false,\"failOnPrerenderConflict\":false},\"legacy\":{\"collections\":false},\"session\":{\"driver\":\"fs-lite\",\"options\":{\"base\":\"/home/phoebe/TheGuildGenesis/frontend/node_modules/.astro/sessions\"}}}"] \ No newline at end of file diff --git a/frontend/.astro/settings.json b/frontend/.astro/settings.json index 2e8ce89..822e808 100644 --- a/frontend/.astro/settings.json +++ b/frontend/.astro/settings.json @@ -1,6 +1,6 @@ { "_variables": { - "lastUpdateCheck": 1761559074026 + "lastUpdateCheck": 1770210836100 }, "eslint.validate": [ "javascript", diff --git a/frontend/.astro/types.d.ts b/frontend/.astro/types.d.ts index f964fe0..03d7cc4 100644 --- a/frontend/.astro/types.d.ts +++ b/frontend/.astro/types.d.ts @@ -1 +1,2 @@ /// +/// \ No newline at end of file diff --git a/frontend/src/components/AppSidebar.tsx b/frontend/src/components/AppSidebar.tsx index 870225d..b172a0a 100644 --- a/frontend/src/components/AppSidebar.tsx +++ b/frontend/src/components/AppSidebar.tsx @@ -1,4 +1,4 @@ -import { Smile, BadgeCheck, Home, BookOpen , Table2 } from "lucide-react"; +import { Smile, BadgeCheck, Home, BookOpen , Table2, FolderKanban } from "lucide-react"; import { Sidebar, @@ -38,6 +38,11 @@ const items = [ url: "/leaderboard", icon: Table2, }, + { + title: "Projects", + url: "/projects", + icon: FolderKanban, + }, ]; export function AppSidebar() { diff --git a/frontend/src/components/pages/ProjectPage.tsx b/frontend/src/components/pages/ProjectPage.tsx new file mode 100644 index 0000000..103c1fc --- /dev/null +++ b/frontend/src/components/pages/ProjectPage.tsx @@ -0,0 +1,12 @@ +import { AppWrapper } from "@/components/AppWrapper"; +import { ProjectMain } from "@/components/projects/project-page/ProjectMain"; + +type Props = { id?: string }; + +export default function ProjectPage({ id }: Props) { + return ( + + + + ); +} \ No newline at end of file diff --git a/frontend/src/components/pages/ProjectsPage.tsx b/frontend/src/components/pages/ProjectsPage.tsx new file mode 100644 index 0000000..ac9ba0d --- /dev/null +++ b/frontend/src/components/pages/ProjectsPage.tsx @@ -0,0 +1,20 @@ +import { AppWrapper } from "@/components/AppWrapper"; +import { ProjectsMain } from "@/components/projects/ProjectsMain"; + +export function ProjectsPage() { + return ( + +
+

+ Projects +

+

+ Showcase your work. Projects can be created by anyone and are managed by their owners. +

+ +
+
+ ); +} + +export default ProjectsPage; \ No newline at end of file diff --git a/frontend/src/components/projects/CreateProjectModal.tsx b/frontend/src/components/projects/CreateProjectModal.tsx new file mode 100644 index 0000000..c64d151 --- /dev/null +++ b/frontend/src/components/projects/CreateProjectModal.tsx @@ -0,0 +1,155 @@ +import React, { useState, useEffect } from 'react'; +import { useCreateProject } from '@/hooks/projects/useprojects'; +import { Button } from '@/components/ui/button'; +import { Loader2, X } from 'lucide-react'; + +interface CreateProjectModalProps { + isOpen: boolean; + onClose: () => void; +} + +export default function CreateProjectModal({ isOpen, onClose }: CreateProjectModalProps) { + const [name, setName] = useState(''); + const [description, setDescription] = useState(''); + const [validationError, setValidationError] = useState(''); + + const createProjectMutation = useCreateProject(); + + useEffect(() => { + if (isOpen) { + setName(''); + setDescription(''); + setValidationError(''); + } + }, [isOpen]); + + useEffect(() => { + if (createProjectMutation.isSuccess) { + onClose(); + } + }, [createProjectMutation.isSuccess, onClose]); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setValidationError(''); + + // Validation + if (!name.trim()) { + setValidationError('Project name is required'); + return; + } + + if (name.length > 100) { + setValidationError('Project name must be 100 characters or less'); + return; + } + + if (!description.trim()) { + setValidationError('Project description is required'); + return; + } + + if (description.length > 500) { + setValidationError('Description must be 500 characters or less'); + return; + } + + createProjectMutation.mutate({ input: { name: name.trim(), description: description.trim() } }); + }; + + if (!isOpen) return null; + + return ( +
+
+
+

Create Project

+ +
+ +
+ {/* Project Name */} +
+ + setName(e.target.value)} + className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" + placeholder="My Awesome Project" + maxLength={100} + disabled={createProjectMutation.isPending} + /> +

{name.length}/100 characters

+
+ + {/* Description */} +
+ +