Repository: SashenJayathilaka/ChatGPT-Clone Branch: master Commit: 630988faa7ee Files: 160 Total size: 266.0 KB Directory structure: gitextract__q2wgi47/ ├── README.md ├── aws-bucket-policy.json ├── client/ │ ├── .eslintrc.json │ ├── .gitignore │ ├── .prettierrc │ ├── README.md │ ├── app/ │ │ ├── globals.css │ │ ├── layout.tsx │ │ ├── loading.tsx │ │ ├── page.tsx │ │ ├── priority/ │ │ │ ├── backlog/ │ │ │ │ └── page.tsx │ │ │ ├── high/ │ │ │ │ └── page.tsx │ │ │ ├── low/ │ │ │ │ └── page.tsx │ │ │ ├── medium/ │ │ │ │ └── page.tsx │ │ │ └── urgent/ │ │ │ └── page.tsx │ │ ├── projects/ │ │ │ └── [id]/ │ │ │ └── page.tsx │ │ ├── search/ │ │ │ └── page.tsx │ │ ├── settings/ │ │ │ └── page.tsx │ │ ├── teams/ │ │ │ └── page.tsx │ │ ├── timeline/ │ │ │ └── page.tsx │ │ └── users/ │ │ └── page.tsx │ ├── components/ │ │ ├── Header/ │ │ │ └── index.tsx │ │ ├── data/ │ │ │ └── columns.tsx │ │ ├── global/ │ │ │ ├── auth-provider/ │ │ │ │ └── index.tsx │ │ │ ├── border-view/ │ │ │ │ └── index.tsx │ │ │ ├── loader/ │ │ │ │ └── spinner.tsx │ │ │ ├── project-header/ │ │ │ │ └── index.tsx │ │ │ ├── tab-button/ │ │ │ │ └── index.tsx │ │ │ ├── task/ │ │ │ │ └── index.tsx │ │ │ ├── task-card/ │ │ │ │ └── index.tsx │ │ │ └── task-column/ │ │ │ └── index.tsx │ │ ├── home-page/ │ │ │ └── index.tsx │ │ ├── list-view/ │ │ │ └── index.tsx │ │ ├── modal/ │ │ │ ├── index.tsx │ │ │ ├── modal-new-project/ │ │ │ │ └── index.tsx │ │ │ └── model-new-task/ │ │ │ └── index.tsx │ │ ├── navbar/ │ │ │ └── index.tsx │ │ ├── priorityPage/ │ │ │ └── index.tsx │ │ ├── project-card/ │ │ │ └── index.tsx │ │ ├── project-page/ │ │ │ └── index.tsx │ │ ├── search-page/ │ │ │ └── index.tsx │ │ ├── settings-page/ │ │ │ └── index.tsx │ │ ├── sidebar/ │ │ │ └── index.tsx │ │ ├── table-view/ │ │ │ └── index.tsx │ │ ├── team-page/ │ │ │ └── index.tsx │ │ ├── timeline/ │ │ │ └── index.tsx │ │ ├── timeline-view/ │ │ │ └── index.tsx │ │ ├── user-card/ │ │ │ └── index.tsx │ │ ├── user-page/ │ │ │ └── index.tsx │ │ └── wrapper/ │ │ ├── dashboardWrapper.tsx │ │ └── redux.tsx │ ├── lib/ │ │ └── utils.ts │ ├── next.config.mjs │ ├── package.json │ ├── postcss.config.mjs │ ├── state/ │ │ ├── api.ts │ │ └── index.ts │ ├── tailwind.config.ts │ ├── tsconfig.json │ └── types/ │ └── type.ts ├── lamda_trigger.mjs ├── production-level-application/ │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── actions/ │ │ └── index.ts │ ├── app/ │ │ ├── (auth)/ │ │ │ ├── layout.tsx │ │ │ ├── sign-in/ │ │ │ │ └── [[...sign-in]]/ │ │ │ │ └── page.tsx │ │ │ └── sign-up/ │ │ │ └── [[...sign-up]]/ │ │ │ └── page.tsx │ │ ├── api/ │ │ │ ├── move/ │ │ │ │ └── [taskId]/ │ │ │ │ └── route.tsx │ │ │ ├── projects/ │ │ │ │ ├── [projectId]/ │ │ │ │ │ └── route.ts │ │ │ │ └── route.ts │ │ │ ├── tasks/ │ │ │ │ ├── [projectId]/ │ │ │ │ │ └── route.ts │ │ │ │ ├── route.ts │ │ │ │ └── user/ │ │ │ │ └── [userId]/ │ │ │ │ └── route.ts │ │ │ └── user/ │ │ │ ├── [userId]/ │ │ │ │ └── route.ts │ │ │ └── route.ts │ │ ├── dashboard/ │ │ │ ├── layout.tsx │ │ │ ├── loading.tsx │ │ │ ├── page.tsx │ │ │ ├── priority/ │ │ │ │ ├── backlog/ │ │ │ │ │ └── page.tsx │ │ │ │ ├── high/ │ │ │ │ │ └── page.tsx │ │ │ │ ├── low/ │ │ │ │ │ └── page.tsx │ │ │ │ ├── medium/ │ │ │ │ │ └── page.tsx │ │ │ │ └── urgent/ │ │ │ │ └── page.tsx │ │ │ ├── projects/ │ │ │ │ └── [id]/ │ │ │ │ └── page.tsx │ │ │ └── timeline/ │ │ │ └── page.tsx │ │ ├── globals.css │ │ ├── layout.tsx │ │ ├── loading.tsx │ │ └── page.tsx │ ├── components/ │ │ ├── data/ │ │ │ └── columns.tsx │ │ ├── global/ │ │ │ ├── border-view/ │ │ │ │ └── index.tsx │ │ │ ├── client-only/ │ │ │ │ └── index.tsx │ │ │ ├── image-card/ │ │ │ │ └── index.tsx │ │ │ ├── image-upload/ │ │ │ │ └── index.tsx │ │ │ ├── list-view/ │ │ │ │ └── index.tsx │ │ │ ├── loader/ │ │ │ │ ├── buttonLoader.tsx │ │ │ │ └── spinner.tsx │ │ │ ├── project-header/ │ │ │ │ └── index.tsx │ │ │ ├── project-page/ │ │ │ │ └── index.tsx │ │ │ ├── tab-button/ │ │ │ │ └── index.tsx │ │ │ ├── table-view/ │ │ │ │ └── index.tsx │ │ │ ├── task-card/ │ │ │ │ └── index.tsx │ │ │ ├── task-column/ │ │ │ │ └── index.tsx │ │ │ └── timeline-view/ │ │ │ └── index.tsx │ │ ├── header/ │ │ │ └── index.tsx │ │ ├── home-page/ │ │ │ ├── index.tsx │ │ │ └── root-home-page/ │ │ │ └── index.tsx │ │ ├── modal/ │ │ │ ├── index.tsx │ │ │ ├── modal-new-project/ │ │ │ │ └── index.tsx │ │ │ ├── model-new-task/ │ │ │ │ └── index.tsx │ │ │ └── model-share-project/ │ │ │ └── index.tsx │ │ ├── navbar/ │ │ │ ├── home/ │ │ │ │ └── index.tsx │ │ │ └── index.tsx │ │ ├── priority-page/ │ │ │ └── index.tsx │ │ ├── sidebar/ │ │ │ └── index.tsx │ │ ├── task/ │ │ │ └── index.tsx │ │ ├── timeline/ │ │ │ └── index.tsx │ │ └── wrapper/ │ │ ├── dashboardWrapper.tsx │ │ └── redux.tsx │ ├── lib/ │ │ ├── prismadb.ts │ │ └── utils.ts │ ├── middleware.ts │ ├── next.config.mjs │ ├── package.json │ ├── postcss.config.mjs │ ├── prisma/ │ │ └── schema.prisma │ ├── provider/ │ │ └── reduxProvider.tsx │ ├── state/ │ │ ├── api.ts │ │ └── index.ts │ ├── tailwind.config.ts │ ├── tsconfig.json │ └── types/ │ └── type.ts └── server/ ├── .gitignore ├── ecosystem.config.js ├── package.json ├── prisma/ │ ├── migrations/ │ │ ├── 20250103002536_init/ │ │ │ └── migration.sql │ │ └── migration_lock.toml │ ├── schema.prisma │ ├── seed.ts │ └── seedData/ │ ├── attachment.json │ ├── comment.json │ ├── project.json │ ├── projectTeam.json │ ├── task.json │ ├── taskAssignment.json │ ├── team.json │ └── user.json ├── src/ │ ├── controllers/ │ │ ├── projectControllers.ts │ │ ├── searchControllers.ts │ │ ├── taskControllers.ts │ │ ├── teamControllers.ts │ │ └── usercontrollers.ts │ ├── index.ts │ └── routes/ │ ├── projectRoutes.ts │ ├── searchRoutes.ts │ ├── taskRoute.ts │ ├── teamRoutes.ts │ └── userRoutes.ts └── tsconfig.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: README.md ================================================
logo # 🗂️ Project Management App (Full Stack + AWS) This is a **Project Management Dashboard** built using modern web technologies. It includes a powerful frontend, a backend server, and secure cloud integration with AWS. Perfect for managing tasks, tracking projects, and working with teams.
--- ### 🚀 Live Demo 👉 [View the App Live](https://pm-app-tan.vercel.app/) --- ### 📚 Table of Contents * [About](#about) * [Features](#features) * [Tech Stack](#tech-stack) * [Screenshots](#screenshots) * [Getting Started](#getting-started) * [Requirements](#requirements) * [Installation](#installation) * [Running Locally](#running-locally) * [Environment Variables](#environment-variables) * [Deployment](#deployment) * [Contact](#contact) --- ## 📌 About This app is a **full-stack project management tool**. It helps teams stay organized with features like task tracking, user authentication, cloud hosting, and more. --- ## ✨ Features ✅ Create and manage projects ✅ Add tasks and assign team members ✅ User authentication (with AWS Cognito) ✅ Cloud functions using AWS Lambda ✅ Beautiful UI with charts and data tables --- ## 🛠 Tech Stack ### Frontend * Next.js * TypeScript * React.js * Tailwind CSS * Material UI Data Grid ### Backend * Node.js * Express.js * PostgreSQL * Prisma ORM ### Cloud & DevOps * AWS Lambda * AWS Cognito * Vercel (Deployment) --- ## 📷 Screenshots > Here’s a preview of the dashboard (Click to view):
screenshot
--- ## 🧰 Getting Started ### ✅ Requirements Before you start, make sure you have: * [Node.js](https://nodejs.org/en/) installed * An [AWS account](https://aws.amazon.com/free/) * A [Clerk account](https://clerk.com/) for user auth (optional but recommended) --- ### 📦 Installation Clone the project: ```bash git clone https://github.com/SashenJayathilaka/Project-Management-App.git cd Project-Management-App ``` Install the dependencies: ```bash npm install ``` --- ### 🧪 Running Locally To start the app locally: ```bash npm run dev ``` Then open your browser and go to: 👉 [http://localhost:3000](http://localhost:3000) --- ### 🔐 Environment Variables Create a `.env` file in the root directory and add the required keys. Example: ```env DATABASE_URL=your_postgres_db_url AWS_REGION=your_aws_region COGNITO_USER_POOL_ID=your_cognito_pool_id COGNITO_CLIENT_ID=your_cognito_client_id ``` --- ## 🚀 Deployment ### Deploy with Vercel The easiest way to deploy is using **Vercel**: 1. Go to [https://vercel.com/new](https://vercel.com/new) 2. Import your GitHub repository 3. Add environment variables in the Vercel dashboard 4. Deploy and enjoy! You can also deploy manually using other platforms like AWS, Netlify, or Docker. --- ## 📬 Contact **Developer**: Sashen Jayathilaka 📧 Email: [sashenjayathilaka95@gmail.com](mailto:sashenjayathilaka95@gmail.com) 🐦 Twitter: [@SashenHasinduJ](https://twitter.com/SashenHasinduJ) 🔗 Project Repository: [GitHub Link](https://github.com/SashenJayathilaka/Project-Management-App.git) ---
⭐️ If you found this project helpful, feel free to star it!
================================================ FILE: aws-bucket-policy.json ================================================ { "Version": "2012-10-17", "Statement": [ { "Sid": "PublicReadGetObject", "Effect": "Allow", "Principal": "*", "Action": "s3:GetObject", "Resource": "*" } ] } ================================================ FILE: client/.eslintrc.json ================================================ { "extends": "next/core-web-vitals" } ================================================ FILE: client/.gitignore ================================================ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies /node_modules /.pnp .pnp.js .yarn/install-state.gz # testing /coverage # next.js /.next/ /out/ # production /build # misc .DS_Store *.pem # debug npm-debug.log* yarn-debug.log* yarn-error.log* # local env files .env*.local # vercel .vercel # typescript *.tsbuildinfo next-env.d.ts ================================================ FILE: client/.prettierrc ================================================ { "plugins": ["prettier-plugin-tailwindcss"] } ================================================ FILE: client/README.md ================================================ This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). ## Getting Started First, run the development server: ```bash npm run dev # or yarn dev # or pnpm dev # or bun dev ``` Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. ## Learn More To learn more about Next.js, take a look at the following resources: - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! ## Deploy on Vercel The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. ================================================ FILE: client/app/globals.css ================================================ @tailwind base; @tailwind components; @tailwind utilities; *, *::before, *::after { box-sizing: border-box; } html, body, #root, .app { height: 100%; width: 100%; @apply text-sm; @apply bg-gray-500; @apply text-gray-900; } .timeline ._3_ygE { @apply rounded-tl-md border border-r-0 border-[#e6e4e4] dark:border-stroke-dark; } .timeline ._2eZzQ { @apply border-[#e6e4e4] dark:border-stroke-dark; } .timeline ._2dZTy { @apply fill-white dark:fill-dark-secondary; } .timeline ._2dZTy:nth-child(even) { @apply fill-[#f5f5f5] dark:fill-dark-tertiary; } .timeline ._35nLX { @apply fill-white stroke-[#e6e4e4] dark:fill-dark-secondary dark:stroke-stroke-dark; } .timeline ._9w8d5 { @apply fill-[#333] dark:fill-white; } .timeline ._34SS0 { @apply bg-white dark:bg-dark-secondary; } .timeline ._34SS0:nth-of-type(even) { @apply bg-[#f5f5f5] dark:bg-dark-tertiary; } .timeline ._RuwuK, .timeline ._3rUKi, .timeline ._1rLuZ { @apply stroke-[#e6e4e4] dark:stroke-stroke-dark; } .timeline ._3ZbQT { @apply border-l-0 border-[#e6e4e4] dark:border-stroke-dark; } .timeline ._3T42e { @apply bg-white dark:bg-dark-bg; } .timeline ._29NTg { @apply dark:text-neutral-500; } ::-webkit-scrollbar { width: 0px; height: 0px; } ================================================ FILE: client/app/layout.tsx ================================================ import DashboardWrapper from "@/components/wrapper/dashboardWrapper"; import type { Metadata } from "next"; import { Plus_Jakarta_Sans } from "next/font/google"; import "./globals.css"; const jakarta = Plus_Jakarta_Sans({ subsets: ["latin"] }); export const metadata: Metadata = { title: "Create Next App", description: "Generated by create next app", }; export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { return ( {children} ); } ================================================ FILE: client/app/loading.tsx ================================================ import { Spinner } from "@/components/global/loader/spinner"; type Props = {}; function Loading({}: Props) { return (
); } export default Loading; ================================================ FILE: client/app/page.tsx ================================================ import HomePage from "@/components/home-page"; export default function Home() { return ; } ================================================ FILE: client/app/priority/backlog/page.tsx ================================================ import PriorityPage from "@/components/priorityPage"; import { Priority } from "@/types/type"; type Props = {}; function Page({}: Props) { return ; } export default Page; ================================================ FILE: client/app/priority/high/page.tsx ================================================ import PriorityPage from "@/components/priorityPage"; import { Priority } from "@/types/type"; type Props = {}; function Page({}: Props) { return ; } export default Page; ================================================ FILE: client/app/priority/low/page.tsx ================================================ import PriorityPage from "@/components/priorityPage"; import { Priority } from "@/types/type"; type Props = {}; function Page({}: Props) { return ; } export default Page; ================================================ FILE: client/app/priority/medium/page.tsx ================================================ import PriorityPage from "@/components/priorityPage"; import { Priority } from "@/types/type"; type Props = {}; function Page({}: Props) { return ; } export default Page; ================================================ FILE: client/app/priority/urgent/page.tsx ================================================ import PriorityPage from "@/components/priorityPage"; import { Priority } from "@/types/type"; type Props = {}; function Page({}: Props) { return ; } export default Page; ================================================ FILE: client/app/projects/[id]/page.tsx ================================================ import ProjectPage from "@/components/project-page"; type Props = { params: { id: string; }; }; function Page({ params }: Props) { const { id } = params; return ; } export default Page; ================================================ FILE: client/app/search/page.tsx ================================================ import SearchPage from "@/components/search-page"; type Props = {}; function Page({}: Props) { return ; } export default Page; ================================================ FILE: client/app/settings/page.tsx ================================================ import SettingsPage from "@/components/settings-page"; import React from "react"; type Props = {}; function Page({}: Props) { return ; } export default Page; ================================================ FILE: client/app/teams/page.tsx ================================================ import TeamPage from "@/components/team-page"; type Props = {}; function Page({}: Props) { return ; } export default Page; ================================================ FILE: client/app/timeline/page.tsx ================================================ import TimeLinePage from "@/components/timeline"; import React from "react"; type Props = {}; function Page({}: Props) { return ; } export default Page; ================================================ FILE: client/app/users/page.tsx ================================================ import UserPage from "@/components/user-page"; type Props = {}; function Page({}: Props) { return ; } export default Page; ================================================ FILE: client/components/Header/index.tsx ================================================ import React from "react"; type Props = { name: string; buttonComponent?: any; isSmallText?: boolean; }; function Header({ name, buttonComponent, isSmallText }: Props) { return (

{name}

{buttonComponent}
); } export default Header; ================================================ FILE: client/components/data/columns.tsx ================================================ import { GridColDef, GridToolbarContainer, GridToolbarExport, GridToolbarFilterButton, } from "@mui/x-data-grid"; export const columns: GridColDef[] = [ { field: "title", headerName: "Title", width: 100, }, { field: "description", headerName: "Description", width: 200, }, { field: "status", headerName: "Status", width: 130, renderCell: (params) => ( {params.value} ), }, { field: "priority", headerName: "Priority", width: 75, }, { field: "tags", headerName: "Tags", width: 130, }, { field: "startDate", headerName: "Start Date", width: 130, }, { field: "dueDate", headerName: "Due Date", width: 130, }, { field: "author", headerName: "Author", width: 150, renderCell: (params) => params.value?.author || "Unknown", }, { field: "assignee", headerName: "Assignee", width: 150, renderCell: (params) => params.value?.assignee || "Unassigned", }, ]; export const userSettings = { username: "johndoe", email: "john.doe@example.com", teamName: "Development Team", roleName: "Developer", }; export const CustomToolbar = () => ( ); export const taskColumns: GridColDef[] = [ { field: "title", headerName: "Title", width: 200 }, { field: "status", headerName: "Status", width: 150 }, { field: "priority", headerName: "Priority", width: 150 }, { field: "dueDate", headerName: "Due Date", width: 150 }, ]; export const COLORS = ["#0088FE", "#00C49F", "#FFBB28", "#FF8042"]; export const priorityColumns: GridColDef[] = [ { field: "title", headerName: "Title", width: 100, }, { field: "description", headerName: "Description", width: 200, }, { field: "status", headerName: "Status", width: 130, renderCell: (params) => ( {params.value} ), }, { field: "priority", headerName: "Priority", width: 75, }, { field: "tags", headerName: "Tags", width: 130, }, { field: "startDate", headerName: "Start Date", width: 130, }, { field: "dueDate", headerName: "Due Date", width: 130, }, { field: "author", headerName: "Author", width: 150, renderCell: (params) => params.value?.username || "Unknown", }, { field: "assignee", headerName: "Assignee", width: 150, renderCell: (params) => params.value?.username || "Unassigned", }, ]; export const formFields = { signUp: { username: { order: 1, placeholder: "Choose a username", label: "Username", inputProps: { required: true }, }, email: { order: 1, placeholder: "Enter your email address", label: "Email", inputProps: { type: "email", required: true }, }, password: { order: 3, placeholder: "Enter your password", label: "Password", inputProps: { type: "password", required: true }, }, confirm_password: { order: 4, placeholder: "Confirm your password", label: "Confirm Password", inputProps: { type: "password", required: true }, }, }, }; ================================================ FILE: client/components/global/auth-provider/index.tsx ================================================ import { formFields } from "@/components/data/columns"; import { Authenticator } from "@aws-amplify/ui-react"; import "@aws-amplify/ui-react/styles.css"; import { Amplify } from "aws-amplify"; import React from "react"; type Props = { children: React.ReactNode; }; Amplify.configure({ Auth: { Cognito: { userPoolId: process.env.NEXT_PUBLIC_COGNITO_USER_POOL_ID!, userPoolClientId: process.env.NEXT_PUBLIC_COGNITO_USER_CLIENT_ID!, }, }, }); function AuthProvider({ children }: Props) { return (
{({ user }: any) => user ? (
{children}
) : (

Please Sign In Below

) }
); } export default AuthProvider; ================================================ FILE: client/components/global/border-view/index.tsx ================================================ import { useGetTasksQuery, useUpdateTasksMutation } from "@/state/api"; import React from "react"; import { DndProvider } from "react-dnd"; import { HTML5Backend } from "react-dnd-html5-backend"; import { Spinner } from "../loader/spinner"; import TaskColumn from "../task-column"; type Props = { id: string; setIsModelNewTasOpen: (isOpen: boolean) => void; }; const taskStatus = ["To Do", "Work In Progress", "Under Review", "Completed"]; function BorderView({ id, setIsModelNewTasOpen }: Props) { const { data: tasks, isLoading, error, } = useGetTasksQuery({ projectId: Number(id) }); const [updateTaskStatus] = useUpdateTasksMutation(); const moveTask = (taskId: number, toStatus: string) => { updateTaskStatus({ taskId, status: toStatus }); }; if (isLoading) return ; if (error) return
An error has occurred
; return (
{taskStatus.map((status) => ( ))}
); } export default BorderView; ================================================ FILE: client/components/global/loader/spinner.tsx ================================================ type SpinnerProps = { color?: string; }; export const Spinner = ({ color }: SpinnerProps) => { return (
); }; ================================================ FILE: client/components/global/project-header/index.tsx ================================================ import TabButton from "@/components/global/tab-button"; import Header from "@/components/Header"; import ModalNewProject from "@/components/modal/modal-new-project"; import { Clock, Filter, Grid3x3, Grid3X3, List, PlusSquare, Share2, Table, } from "lucide-react"; import { useState } from "react"; type Props = { activeTab: string; setActiveTab: (tabName: string) => void; }; function ProjectHeader({ activeTab, setActiveTab }: Props) { const [isModalNameProjectOpen, setIsModalNameProjectOpen] = useState(false); return (
setIsModalNameProjectOpen(false)} />
setIsModalNameProjectOpen(true)} > New Board } />
} activeTab={activeTab} setActiveTab={setActiveTab} /> } activeTab={activeTab} setActiveTab={setActiveTab} /> } activeTab={activeTab} setActiveTab={setActiveTab} /> } activeTab={activeTab} setActiveTab={setActiveTab} />
); } export default ProjectHeader; ================================================ FILE: client/components/global/tab-button/index.tsx ================================================ import React from "react"; type Props = { name: string; icon: React.ReactNode; setActiveTab: (tabName: string) => void; activeTab: string; }; function TabButton({ activeTab, icon, name, setActiveTab }: Props) { const isActive = activeTab === name; return ( ); } export default TabButton; ================================================ FILE: client/components/global/task/index.tsx ================================================ import { TasksTypes } from "@/types/type"; import { format } from "date-fns"; import { EllipsisVertical, MessageSquareMore } from "lucide-react"; import Image from "next/image"; import { useDrag } from "react-dnd"; type Props = { task: TasksTypes; }; function Task({ task }: Props) { const [{ isDragging }, drop] = useDrag(() => ({ type: "task", item: { id: task.id }, collect: (monitor: any) => ({ isDragging: !!monitor.isDragging(), }), })); const taskTagsSplit = task.tags ? task.tags.split(",") : []; const formattedStartDate = task.startDate ? format(new Date(task.startDate), "P") : ""; const formattedDueDate = task.dueDate ? format(new Date(task.dueDate), "P") : ""; const numberOfComments = (task.comments && task.comments.length) || 0; const PriorityTags = ({ priority }: { priority: TasksTypes["priority"] }) => (
{priority}
); return (
{ drop(instance); }} className={`mb-4 rounded-md bg-white shadow dark:bg-dark-secondary ${ isDragging ? "opacity-50" : "opacity-100" }`} > {task.attachments && task.attachments.length > 0 && ( {task.attachments[0].fileName} )}
{task.priority && }
{taskTagsSplit.map((tag) => (
{" "} {tag}
))}

{task.title}

{typeof task.points === "number" && (
{task.points} pts
)}
{formattedStartDate && {formattedStartDate} - } {formattedDueDate && {formattedDueDate}}

{task.description}

{task.assignee && ( {task.assignee.username} )} {task.author && ( {task.author.username} )}
{numberOfComments}
); } export default Task; ================================================ FILE: client/components/global/task-card/index.tsx ================================================ import { TasksTypes } from "@/types/type"; import { format } from "date-fns"; import Image from "next/image"; import React from "react"; type Props = { task: TasksTypes; }; function TaskCard({ task }: Props) { return (
{task.attachments && task.attachments.length > 0 && (
Attachments:
{task.attachments && task.attachments.length > 0 && ( {task.attachments[0].fileName} )}
)}

ID: {task.id}

Title: {task.title}

Description:{" "} {task.description || "No description provided"}

Status: {task.status}

Priority: {task.priority}

Tags: {task.tags || "No tags"}

Start Date:{" "} {task.startDate ? format(new Date(task.startDate), "P") : "Not set"}

Due Date:{" "} {task.dueDate ? format(new Date(task.dueDate), "P") : "Not set"}

Author:{" "} {task.author ? task.author.username : "Unknown"}

Assignee:{" "} {task.assignee ? task.assignee.username : "Unassigned"}

); } export default TaskCard; ================================================ FILE: client/components/global/task-column/index.tsx ================================================ import { EllipsisVertical, Plus } from "lucide-react"; import React from "react"; import { useDrop } from "react-dnd"; import Task from "../task"; import { TasksTypes } from "@/types/type"; type Props = { status: string; tasks: TasksTypes[]; moveTask: (taskId: number, toStatus: string) => void; setIsModelNewTasOpen: (isOpen: boolean) => void; }; function TaskColumn({ moveTask, setIsModelNewTasOpen, status, tasks }: Props) { const [{ isOver }, drop] = useDrop(() => ({ accept: "task", drop: (item: { id: number }) => moveTask(item.id, status), collect: (monitor: any) => ({ isOver: !!monitor.isOver(), }), })); const tasksCount = tasks.filter((task) => task.status === status).length; const statusColor: any = { "To Do": "#2563EB", "Work In Progress": "#059669", "Under Review": "#D97706", Completed: "#000000", }; return (
{ drop(instance); }} className={`sl:py-4 rounded-lg py-2 xl:px-2 ${isOver ? "bg-blue-100 dark:bg-neutral-950" : ""}`} >

{status}{" "} {tasksCount}

{tasks .filter((task) => task.status === status) .map((task) => ( ))}
); } export default TaskColumn; ================================================ FILE: client/components/home-page/index.tsx ================================================ "use client"; import { dataGridClassNames, dataGridSxStyles } from "@/lib/utils"; import { useGetProjectsQuery, useGetTasksQuery } from "@/state/api"; import { Priority, ProjectTypes, TasksTypes } from "@/types/type"; import { DataGrid } from "@mui/x-data-grid"; import { Bar, BarChart, CartesianGrid, Cell, Legend, Pie, PieChart, ResponsiveContainer, Tooltip, XAxis, YAxis, } from "recharts"; import { COLORS, taskColumns } from "../data/columns"; import { Spinner } from "../global/loader/spinner"; import Header from "../Header"; import { useAppSelector } from "../wrapper/redux"; type Props = {}; function HomePage({}: Props) { const { data: tasks, isLoading: tasksLoading, isError: tasksError, } = useGetTasksQuery({ projectId: parseInt("1"), }); const { data: projects, isLoading: isProjectsLoading } = useGetProjectsQuery(); const isDarkMode = useAppSelector((state) => state.global.isDarkMode); if (tasksLoading || isProjectsLoading) return ; if (tasksError || !tasks || !projects) return
An error has occurred
; const priorityCount = tasks.reduce( (acc: Record, task: TasksTypes) => { const { priority } = task; acc[priority as Priority] = (acc[priority as Priority] || 0) + 1; return acc; }, {}, ); const taskDistribution = Object.keys(priorityCount).map((key) => ({ name: key, count: priorityCount[key], })); const statusCount = projects.reduce( (acc: Record, project: ProjectTypes) => { const status = project.endDate ? "Completed" : "Active"; acc[status] = (acc[status] || 0) + 1; return acc; }, {}, ); const projectStatus = Object.keys(statusCount).map((key) => ({ name: key, count: statusCount[key], })); const chartColors = isDarkMode ? { bar: "#8884d8", barGrid: "#303030", pieFill: "#4A90E2", text: "#FFFFFF", } : { bar: "#8884d8", barGrid: "#E0E0E0", pieFill: "#82ca9d", text: "#000000", }; return (

Task Priority Distribution

Project Status

{projectStatus.map((entry, index) => ( ))}

Your Tasks

"data-grid-row"} getCellClassName={() => "data-grid-cell"} className={dataGridClassNames} sx={dataGridSxStyles(isDarkMode)} />
); } export default HomePage; ================================================ FILE: client/components/list-view/index.tsx ================================================ import { useGetTasksQuery } from "@/state/api"; import { TasksTypes } from "@/types/type"; import { Spinner } from "../global/loader/spinner"; import TaskCard from "../global/task-card"; import Header from "../Header"; type Props = { id: string; setIsModelNewTasOpen: (isOpen: boolean) => void; }; function ListView({ id, setIsModelNewTasOpen }: Props) { const { data: tasks, error, isLoading, } = useGetTasksQuery({ projectId: Number(id) }); if (isLoading) return ; if (error) return
An error has occurred
; return (
setIsModelNewTasOpen(true)} > Add Task } isSmallText />
{tasks?.map((task: TasksTypes) => ( ))}
); } export default ListView; ================================================ FILE: client/components/modal/index.tsx ================================================ import Header from "@/components/Header"; import { X } from "lucide-react"; import React from "react"; import ReactDOM from "react-dom"; type Props = { children: React.ReactNode; isOpen: boolean; onClose: () => void; name: string; }; function Modal({ children, isOpen, onClose, name }: Props) { if (!isOpen) return null; return ReactDOM.createPortal(
} isSmallText /> {children}
, document.body, ); } export default Modal; ================================================ FILE: client/components/modal/modal-new-project/index.tsx ================================================ import { Spinner } from "@/components/global/loader/spinner"; import { useCreateProjectMutation } from "@/state/api"; import { formatISO } from "date-fns"; import { useState } from "react"; import Modal from ".."; type Props = { isOpen: boolean; onClose: () => void; }; function ModalNewProject({ isOpen, onClose }: Props) { const [createProject, { isLoading }] = useCreateProjectMutation(); const [projectName, setProjectName] = useState(""); const [description, setDescription] = useState(""); const [startDate, setStartDate] = useState(""); const [endDate, setEndDate] = useState(""); const handleSubmit = async () => { if (!projectName || !startDate || !endDate) return; const formateStartDate = formatISO(new Date(startDate), { representation: "complete", }); const formateEndDate = formatISO(new Date(endDate), { representation: "complete", }); await createProject({ name: projectName, description, startDate: formateStartDate, endDate: formateEndDate, }); onClose(); }; const isFormValid = () => { return projectName && description && startDate && endDate; }; const inputStyle = "w-full rounded-md border border-gray-300 p-2 shadow-sm dark:border-dark-tertiary dark:bg-dark-tertiary dark:text-white dark:focus:outline-none"; return (
{ e.preventDefault(); handleSubmit(); }} > setProjectName(e.target.value)} />