diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..d94a7b04 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,13 @@ +module.exports = { + extends: [ + 'next/core-web-vitals', + 'plugin:@typescript-eslint/recommended', + ], + parser: '@typescript-eslint/parser', + plugins: ['@typescript-eslint'], + root: true, + env: { + node: true, + browser: true, + }, +}; \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 00000000..4227aa79 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,9 @@ +{ + "extends": [ + "next/core-web-vitals", + "plugin:@typescript-eslint/recommended" + ], + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"], + "root": true +} \ No newline at end of file diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 00000000..9e198ba5 --- /dev/null +++ b/.github/workflows/playwright.yml @@ -0,0 +1,20 @@ +#name: Playwright Tests +# +#on: +# deployment_status: # Trigger on deployment status changes +# +#jobs: +# run-e2es: # Job name for end-to-end tests +# if: github.event_name == 'deployment_status' && github.event.deployment_status.state == 'success' # Run only if deployment is successful +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v3 +# - uses: actions/setup-node@v3 +# - name: Install dependencies +# run: npm ci && npx playwright install --with-deps # Install dependencies and Playwright browsers +# - name: Set BASE_URL +# run: echo "BASE_URL=${{ github.event.deployment_status.environment_url }}" >> $GITHUB_ENV # Set BASE_URL from deployment status +# - name: Set Bypass Token +# run: echo "BYPASS_TOKEN=${{ secrets.BYPASS_TOKEN }}" >> $GITHUB_ENV # Set Bypass Token from GitHub Secrets +# - name: Run tests +# run: npx playwright test # Execute Playwright tests diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml new file mode 100644 index 00000000..f74c9584 --- /dev/null +++ b/.github/workflows/unit-tests.yml @@ -0,0 +1,11 @@ +name: Unit Tests +on: + push: + branches: [main] + pull_request: + branches: [main] +jobs: + test: + runs-on: ubuntu-latest + steps: + - run: echo "No unit tests configured yet" \ No newline at end of file diff --git a/.gitignore b/.gitignore index fd3dbb57..4d6ca31a 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,8 @@ /.next/ /out/ +.env* + # production /build @@ -34,3 +36,11 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts +.aider* +node_modules/ +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ + +package-lock.json diff --git a/.idea/front.iml b/.idea/front.iml index 24643cc3..c4400eb7 100644 --- a/.idea/front.iml +++ b/.idea/front.iml @@ -2,9 +2,12 @@ + + + diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 00000000..35f9ffc4 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/.snaplet/config.json b/.snaplet/config.json new file mode 100644 index 00000000..a25c325f --- /dev/null +++ b/.snaplet/config.json @@ -0,0 +1,3 @@ +{ + "adapter": "pg" +} \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..09cf720d --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "denoland.vscode-deno" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json index d3fdae9a..521fc97e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,26 @@ { - "typescript.tsdk": "../../node_modules/typescript/lib", - "typescript.enablePromptUseWorkspaceTsdk": true -} \ No newline at end of file + "[typescript]": { + "editor.defaultFormatter": "denoland.vscode-deno" + }, + "deno.enablePaths": [ + "supabase/functions" + ], + "deno.lint": true, + "deno.unstable": [ + "bare-node-builtins", + "byonm", + "sloppy-imports", + "unsafe-proto", + "webgpu", + "broadcast-channel", + "worker-options", + "cron", + "kv", + "ffi", + "fs", + "http", + "net" + ], + "typescript.enablePromptUseWorkspaceTsdk": true, + "typescript.tsdk": "../../node_modules/typescript/lib" +} diff --git a/@types/global.d.ts b/@types/global.d.ts new file mode 100644 index 00000000..5bc7365f --- /dev/null +++ b/@types/global.d.ts @@ -0,0 +1,9 @@ +import { MetaMaskInpageProvider } from "@metamask/providers"; + +declare global { + interface Window { + ethereum?: MetaMaskInpageProvider; + } +} + +declare module '@web3-react/injected-connector'; \ No newline at end of file diff --git a/@types/radix-ui__react-accordion/index.d.ts b/@types/radix-ui__react-accordion/index.d.ts new file mode 100644 index 00000000..8df36125 --- /dev/null +++ b/@types/radix-ui__react-accordion/index.d.ts @@ -0,0 +1,10 @@ +declare module '@radix-ui/react-accordion' { + // Add the necessary type declarations here + import * as React from 'react'; + + export const Root: React.FC; + export const Item: React.FC; + export const Trigger: React.FC; + export const Content: React.FC; + export const Header: React.FC; +} \ No newline at end of file diff --git a/README.md b/README.md index bb7418dd..d9e2aa4e 100644 --- a/README.md +++ b/README.md @@ -1,201 +1,103 @@ - - Next.js and Supabase Starter Kit - the fastest way to build apps with Next.js and Supabase -

Next.js and Supabase Starter Kit

-
- -

- The fastest way to build apps with Next.js and Supabase -

- -

- Features Β· - Demo Β· - Deploy to Vercel Β· - Clone and run locally Β· - Feedback and issues - More Examples -

-
+# QuantumLabs Task Management Application πŸš€ + +## Table of Contents +- [Overview](#overview) +- [Features](#features) +- [Backend Database](#backend-database) +- [Installation](#installation) +- [Usage](#usage) +- [Testing](#testing) +- [To Do](#to-do) +- [Inspiration](#inspiration) +- [Live Application](#live-application) +- [AI Disclosure](#ai-disclosure) +- [Contributing](#contributing) +- [License](#license) +![alt text](public/images/TaskFlow.gif) +## Overview +The **QuantumLabs Task Management Application** is a powerful and intuitive task management board designed to help users efficiently create, manage, and organize tasks. With features like drag-and-drop functionality and customizable task cards, users can easily track progress and collaborate effectively. 🌟 ## Features +- **Task Management**: Create, edit, and delete tasks with essential fields such as title, description, priority, and due date. πŸ“ +- **Drag-and-Drop Functionality**: Seamlessly move tasks between lists (To Do, In Progress, Done) to update their status. πŸ”„ +- **User Authentication**: Secure login and signup using Supabase Auth for user management. πŸ” +- **Real-time Updates**: Changes are reflected in real-time across the application, enhancing collaboration. ⏱️ +- **Responsive Design**: Built with Tailwind CSS, ensuring a clean and intuitive user interface that works on all devices. πŸ“±πŸ’» +- **Custom Jobs**: Define and manage custom job types tailored to specific project needs, allowing for greater flexibility and organization. βš™οΈ -- Works across the entire [Next.js](https://nextjs.org) stack - - App Router - - Pages Router - - Middleware - - Client - - Server - - It just works! -- supabase-ssr. A package to configure Supabase Auth to use cookies -- Styling with [Tailwind CSS](https://tailwindcss.com) -- Optional deployment with [Supabase Vercel Integration and Vercel deploy](#deploy-your-own) - - Environment variables automatically assigned to Vercel project - -## Demo - -You can view a fully working demo at [demo-nextjs-with-supabase.vercel.app](https://demo-nextjs-with-supabase.vercel.app/). - -## Deploy to Vercel - -Vercel deployment will guide you through creating a Supabase account and project. - -After installation of the Supabase integration, all relevant environment variables will be assigned to the project so the deployment is fully functioning. +## Backend Database +- **Supabase**: The application utilizes Supabase as the backend database, providing a PostgreSQL database with real-time capabilities. πŸ—„οΈ +- **Database Setup**: Ensure that your Supabase project is configured with the necessary tables and schemas to support task management features. -[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fnext.js%2Ftree%2Fcanary%2Fexamples%2Fwith-supabase&project-name=nextjs-with-supabase&repository-name=nextjs-with-supabase&demo-title=nextjs-with-supabase&demo-description=This%20starter%20configures%20Supabase%20Auth%20to%20use%20cookies%2C%20making%20the%20user's%20session%20available%20throughout%20the%20entire%20Next.js%20app%20-%20Client%20Components%2C%20Server%20Components%2C%20Route%20Handlers%2C%20Server%20Actions%20and%20Middleware.&demo-url=https%3A%2F%2Fdemo-nextjs-with-supabase.vercel.app%2F&external-id=https%3A%2F%2Fgithub.com%2Fvercel%2Fnext.js%2Ftree%2Fcanary%2Fexamples%2Fwith-supabase&demo-image=https%3A%2F%2Fdemo-nextjs-with-supabase.vercel.app%2Fopengraph-image.png&integration-ids=oac_VqOgBHqhEoFTPzGkPd7L0iH6) - -The above will also clone the Starter kit to your GitHub, you can clone that locally and develop locally. - -If you wish to just develop locally and not deploy to Vercel, [follow the steps below](#clone-and-run-locally). - -## Clone and run locally - -1. You'll first need a Supabase project which can be made [via the Supabase dashboard](https://database.new) - -2. Create a Next.js app using the Supabase Starter template npx command +## Installation +To set up the project locally, follow these steps: +1. **Clone the repository**: ```bash - npx create-next-app -e with-supabase + git clone https://github.com/khaosans/taskboard.git + cd taskboard ``` -3. Use `cd` to change into the app's directory - +2. **Install dependencies**: ```bash - cd name-of-new-app + pnpm install ``` -4. Rename `.env.local.example` to `.env.local` and update the following: +3. **Set up Supabase**: + - Create a Supabase project at [Supabase Dashboard](https://app.supabase.com). + - Obtain your Supabase URL and Anon Key from the API settings. - ``` - NEXT_PUBLIC_SUPABASE_URL=[INSERT SUPABASE PROJECT URL] - NEXT_PUBLIC_SUPABASE_ANON_KEY=[INSERT SUPABASE PROJECT API ANON KEY] - ``` - - Both `NEXT_PUBLIC_SUPABASE_URL` and `NEXT_PUBLIC_SUPABASE_ANON_KEY` can be found in [your Supabase project's API settings](https://app.supabase.com/project/_/settings/api) - -5. You can now run the Next.js local development server: +4. **Configure environment variables**: + - Create a `.env.local` file in the root directory and add the following: + ```plaintext + SUPABASE_URL= + SUPABASE_ANON_KEY= + ``` +## Usage +1. **Run the development server**: ```bash - npm run dev + pnpm run dev ``` - The starter kit should now be running on [localhost:3000](http://localhost:3000/). - -> Check out [the docs for Local Development](https://supabase.com/docs/guides/getting-started/local-development) to also run Supabase locally. - -## Feedback and issues +2. **Open your browser** and navigate to [http://localhost:3000](http://localhost:3000) to view the application. 🌐 -Please file feedback and issues over on the [Supabase GitHub org](https://github.com/supabase/supabase/issues/new/choose). +3. **Create an account** or **log in** to start managing your tasks. -## More Supabase examples - -- [Next.js Subscription Payments Starter](https://github.com/vercel/nextjs-subscription-payments) -- [Cookie-based Auth and the Next.js 13 App Router (free course)](https://youtube.com/playlist?list=PL5S4mPUpp4OtMhpnp93EFSo42iQ40XjbF) -- [Supabase Auth and the Next.js App Router](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs) - - -```markdown -# Simplified Trello-like ToDo Board App with Drag-and-Drop and Jira-like Fields (MVP) - -## Overview -This app will be a streamlined task management board with drag-and-drop functionality and task cards that include essential fields similar to Jira but simplified. The goal is to create an MVP with core features that allow easy task management. - -## User Stories and Specifications - ---- - -### 1. Board Management - -- **Story 1.1**: - - **Title**: Create a ToDo Board - - **Description**: As a user, I want to create a new ToDo board so that I can organize my tasks. - - **Acceptance Criteria**: - - The user can create a single board with predefined lists (To Do, In Progress, Done). - - The board is automatically saved to Supabase. - - **Design**: - - A simple interface where the user starts with a default ToDo board. - - The board includes three columns (To Do, In Progress, Done) to organize tasks. - ---- - -### 2. List Management - -- **Story 2.1**: - - **Title**: Manage Task Lists - - **Description**: As a user, I want to create, rename, or delete task lists within my board to organize my tasks into different categories. - - **Acceptance Criteria**: - - The user can create additional lists. - - Lists are displayed horizontally on the board. - - Lists can be renamed or deleted, and changes are saved to Supabase. - - **Design**: - - β€œAdd New List” button on the board. - - Inline editing for list names and a delete option in the list menu. - ---- - -### 3. Task (Card) Management - -- **Story 3.1**: - - **Title**: Add a New Task with Essential Fields - - **Description**: As a user, I want to add tasks to my ToDo board, including key details similar to Jira, but simplified. - - **Acceptance Criteria**: - - The user can add tasks (cards) to any list. - - Each task has the following fields: - - **Title**: The name of the task. - - **Description**: A brief summary of the task. - - **Priority**: A dropdown to set the priority (Low, Medium, High). - - **Status**: The current status of the task (linked to the list it’s in). - - **Assignee**: The person responsible for the task (single user for MVP). - - **Due Date**: Optional due date for the task. - - The task is saved to Supabase and appears in the selected list. - - **Design**: - - An "Add Task" button at the bottom of each list. - - A task creation modal with input fields for the title, description, priority, and due date. - -- **Story 3.2**: - - **Title**: Drag and Drop Tasks Between Lists - - **Description**: As a user, I want to drag and drop tasks between lists to update their status easily. - - **Acceptance Criteria**: - - The user can drag and drop tasks between lists (e.g., from To Do to In Progress). - - The task’s new status (based on the list) is saved in Supabase. - - **Design**: - - Smooth drag-and-drop functionality with visual feedback. - - The task’s position and list are updated upon drop. - -- **Story 3.3**: - - **Title**: Edit or Delete a Task - - **Description**: As a user, I want to edit or delete tasks to keep my board up to date. - - **Acceptance Criteria**: - - The user can edit task details like title, description, priority, and due date. - - The user can delete a task, and it is removed from Supabase. - - **Design**: - - Inline editing for quick changes. - - Delete option available via a button on the task or within the task detail view. - ---- - -### 4. Basic Interface - -- **Story 4.1**: - - **Title**: Simple and Intuitive User Interface - - **Description**: As a user, I want a simple and intuitive interface so that I can easily manage my tasks. - - **Acceptance Criteria**: - - The interface should be clean and easy to navigate. - - Tasks and lists should be clearly displayed and easily accessible. - - **Design**: - - Minimalistic design with a focus on usability. - - Responsive layout that adapts to different screen sizes (desktop, tablet, mobile). - -## Design Specifications - -- **Layout**: The app will have a single-page layout with a board that spans the width of the screen. The board will have a header with the board title and options for adding new lists or tasks. -- **Lists**: Lists will be displayed horizontally across the board. Each list will have a title and an area where tasks can be added. -- **Tasks**: Tasks (cards) will be displayed vertically within each list. Each task will show its title, and clicking on it will expand the card to show additional details like description, priority, and due date. - -## Next Steps - -- **Back-End Setup**: Configure Supabase to store boards, lists, and tasks with the necessary fields. -- **Front-End Development**: Begin with the basic UI layout and integrate drag-and-drop functionality. -- **Integration**: Connect the front-end with the Supabase back-end to ensure seamless data flow. - -This MVP should provide a solid foundation for your task management app with essential features and a clean, intuitive interface. +## Testing +To ensure the application functions as expected, unit tests are included. You can run the tests using the following command: +```bash +pnpm test ``` +Make sure all tests pass before making any changes to the codebase. βœ… + +## To Do +- ~~**Fix Authentication**: Review and resolve any issues related to user authentication with Supabase.~~ βœ”οΈ +- ~~**Enhance Unit Testing**: Add more tests to cover edge cases and ensure robust functionality.~~ βœ”οΈ +- **Implement AI Agent System**: Integrate AI capabilities for task management and automation. πŸ€– +- **Add Task Dependencies**: Allow users to set dependencies between tasks. πŸ”— +- **Milestone Tracking**: Enable users to define and track project milestones. 🎯 +- **Customizable Workflows**: Allow users to create tailored workflows for projects. βš™οΈ +- **Resource Allocation**: Track and manage resources associated with tasks. πŸ“Š +- **Cost Tracking**: Implement budget management and cost analysis features. πŸ’° + +## Inspiration +The QuantumLabs Task Management Application is inspired by the need for efficient task management in a world increasingly reliant on AI and automation. By integrating advanced AI capabilities with user-friendly design, we aim to empower users to manage their tasks more effectively and focus on what truly matters. πŸ’‘ + +## Live Application +You can access the live application at [QuantumLabs Task Management App](https://the-front-1.vercel.app/landing). 🌟 + +## AI Disclosure +The QuantumLabs Task Management Application utilizes AI agents to enhance task management and automation. These AI agents analyze data, automate repetitive tasks, and provide insights to improve productivity. While AI agents offer significant benefits, they operate based on probabilistic models and may not always produce deterministic outcomes. Users are encouraged to review and validate AI-generated suggestions to ensure accuracy and relevance. πŸ€– + +## Contributing +Contributions are welcome! Please follow these steps to contribute: + +1. Fork the repository. +2. Create a new branch (`git checkout -b feature/YourFeature`). +3. Make your changes and commit them (`git commit -m 'Add some feature'`). +4. Push to the branch (`git push origin feature/YourFeature`). +5. Open a pull request. + +## License +This project is licensed under the MIT License. πŸ“„ \ No newline at end of file diff --git a/app/components/LoginForm.tsx b/__mocks__/next/server.ts similarity index 100% rename from app/components/LoginForm.tsx rename to __mocks__/next/server.ts diff --git a/__mocks__/supabaseClient.ts b/__mocks__/supabaseClient.ts new file mode 100644 index 00000000..1a5f5394 --- /dev/null +++ b/__mocks__/supabaseClient.ts @@ -0,0 +1,6 @@ +export const createClient = jest.fn(() => ({ + auth: { + getUser: jest.fn().mockResolvedValue({ data: { user: null }, error: null }), + }, + // Add other methods you use in your components +})); \ No newline at end of file diff --git a/__mocks__/themeContext.tsx b/__mocks__/themeContext.tsx new file mode 100644 index 00000000..862581c0 --- /dev/null +++ b/__mocks__/themeContext.tsx @@ -0,0 +1,12 @@ +import React from 'react'; + +export const ThemeContext = React.createContext({ + theme: 'light', + toggleTheme: () => {}, +}); + +export const ThemeProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => ( + + {children} + +); \ No newline at end of file diff --git a/__tests__/api/ollama.test.ts b/__tests__/api/ollama.test.ts new file mode 100644 index 00000000..bcc93668 --- /dev/null +++ b/__tests__/api/ollama.test.ts @@ -0,0 +1,101 @@ +import { createMocks } from 'node-mocks-http'; +import { POST as handler } from '../../app/api/ollama/route'; +import { render } from '@testing-library/react'; // Use @testing-library/react +import ChatModal from '../../components/ChatbotModal'; // Update the path if necessary + +// Mock a callback function +const mockCallback = jest.fn(); + +jest.mock('../../app/api/ollama/route', () => ({ + POST: jest.fn((req, res) => { + // Simulate calling a callback + mockCallback(req.body.message); + res.status(200).json({ reply: `Ollama received: ${req.body.message || ''}` }); + }), +})); + +describe('Ollama API', () => { + it('should return a response with the message', async () => { + const { req, res } = createMocks({ + method: 'POST', + body: { message: 'Hello, Ollama!' }, + }); + + await handler(req as any, res as any); + + expect(res._getStatusCode()).toBe(200); + const data = JSON.parse(res._getData()); + expect(data.reply).toContain('Ollama received: Hello, Ollama!'); + }); + + it('should handle missing message', async () => { + const { req, res } = createMocks({ + method: 'POST', + body: {}, + }); + + await handler(req as any, res as any); + + expect(res._getStatusCode()).toBe(200); + const data = JSON.parse(res._getData()); + expect(data.reply).toContain('Ollama received: '); + }); + + it('should handle error in Ollama API', async () => { + const { req, res } = createMocks({ + method: 'POST', + body: { message: 'Hello, Ollama!' }, + }); + + await handler(req as any, res as any); + + expect(res._getStatusCode()).toBe(200); + const data = JSON.parse(res._getData()); + expect(data.reply).toContain('Ollama received: Hello, Ollama!'); + }); + + it('should call the callback with the correct message', async () => { + const { req, res } = createMocks({ + method: 'POST', + body: { message: 'Hello, Ollama!' }, + }); + + await handler(req as any, res as any); + + // Verify the callback was called with the correct argument + expect(mockCallback).toHaveBeenCalledWith('Hello, Ollama!'); + }); + + //add unit tet for other rout api + it('should handle a different message correctly', async () => { + const { req, res } = createMocks({ + method: 'POST', + body: { message: 'How are you, Ollama?' }, + }); + + await handler(req as any, res as any); + + expect(res._getStatusCode()).toBe(200); + const data = JSON.parse(res._getData()); + expect(data.reply).toContain('Ollama received: How are you, Ollama?'); + }); + + //add a unit test for callback function + it('should call the callback with the correct message', async () => { + const { req, res } = createMocks({ + method: 'POST', + body: { message: 'Hello, Ollama!' }, + }); + + await handler(req as any, res as any); + + // Verify the callback was called with the correct argument + expect(mockCallback).toHaveBeenCalledWith('Hello, Ollama!'); + }); + + + + + + +}); diff --git a/__tests__/directory-structure.test.ts b/__tests__/directory-structure.test.ts new file mode 100644 index 00000000..02823820 --- /dev/null +++ b/__tests__/directory-structure.test.ts @@ -0,0 +1,15 @@ +import fs from 'fs'; +import path from 'path'; +import { describe, it, expect } from '@jest/globals'; + +describe('Directory Structure', () => { + // eslint-disable-next-line no-undef + const appDir = path.join(__dirname, '..', 'app'); + + it('has the correct app directory structure', () => { + expect(fs.existsSync(appDir)).toBe(true); + expect(fs.existsSync(path.join(appDir, 'layout.tsx'))).toBe(true); + expect(fs.existsSync(path.join(appDir, 'page.tsx'))).toBe(true); + // Add more checks for expected directories and files + }); +}); diff --git a/api.md b/api.md new file mode 100644 index 00000000..6dc0fac5 --- /dev/null +++ b/api.md @@ -0,0 +1,283 @@ +# API Specification + +## Overview +This API allows for the management of users, tasks, and comments in a task management application. It provides endpoints for creating, reading, updating, and deleting (CRUD) resources, as well as additional functionalities for user authentication and task assignment. + +## Base URL + +## Authentication +- All endpoints require authentication via a Bearer token in the Authorization header. + +## Endpoints + +### Users + +#### Create User +- **POST** `/users` +- **Request Body**: + ```json + { + "username": "string", + "email": "string", + "password": "string" + } + ``` +- **Response**: + - **201 Created** + ```json + { + "id": "uuid", + "username": "string", + "email": "string", + "created_at": "timestamp", + "updated_at": "timestamp" + } + ``` + +#### Get User +- **GET** `/users/{id}` +- **Response**: + - **200 OK** + ```json + { + "id": "uuid", + "username": "string", + "email": "string", + "created_at": "timestamp", + "updated_at": "timestamp" + } + ``` + +#### Update User +- **PUT** `/users/{id}` +- **Request Body**: + ```json + { + "username": "string", + "email": "string", + "password": "string" // Optional + } + ``` +- **Response**: + - **200 OK** + ```json + { + "id": "uuid", + "username": "string", + "email": "string", + "created_at": "timestamp", + "updated_at": "timestamp" + } + ``` + +#### Delete User +- **DELETE** `/users/{id}` +- **Response**: + - **204 No Content** + +#### User Login +- **POST** `/users/login` +- **Request Body**: + ```json + { + "email": "string", + "password": "string" + } + ``` +- **Response**: + - **200 OK** + ```json + { + "token": "string", + "user": { + "id": "uuid", + "username": "string", + "email": "string" + } + } + ``` + +### Tasks + +#### Create Task +- **POST** `/tasks` +- **Request Body**: + ```json + { + "title": "string", + "description": "string", + "status": "pending | in_progress | completed", + "user_id": "uuid" // Optional, for task assignment + } + ``` +- **Response**: + - **201 Created** + ```json + { + "id": "uuid", + "title": "string", + "description": "string", + "status": "pending | in_progress | completed", + "user_id": "uuid", + "created_at": "timestamp", + "updated_at": "timestamp" + } + ``` + +#### Get Task +- **GET** `/tasks/{id}` +- **Response**: + - **200 OK** + ```json + { + "id": "uuid", + "title": "string", + "description": "string", + "status": "pending | in_progress | completed", + "user_id": "uuid", + "created_at": "timestamp", + "updated_at": "timestamp" + } + ``` + +#### Update Task +- **PUT** `/tasks/{id}` +- **Request Body**: + ```json + { + "title": "string", + "description": "string", + "status": "pending | in_progress | completed", + "user_id": "uuid" // Optional, for reassigning + } + ``` +- **Response**: + - **200 OK** + ```json + { + "id": "uuid", + "title": "string", + "description": "string", + "status": "pending | in_progress | completed", + "user_id": "uuid", + "created_at": "timestamp", + "updated_at": "timestamp" + } + ``` + +#### Delete Task +- **DELETE** `/tasks/{id}` +- **Response**: + - **204 No Content** + +#### Get All Tasks +- **GET** `/tasks` +- **Response**: + - **200 OK** + ```json + [ + { + "id": "uuid", + "title": "string", + "description": "string", + "status": "pending | in_progress | completed", + "user_id": "uuid", + "created_at": "timestamp", + "updated_at": "timestamp" + } + ] + ``` + +### Comments + +#### Create Comment +- **POST** `/comments` +- **Request Body**: + ```json + { + "task_id": "uuid", + "content": "string" + } + ``` +- **Response**: + - **201 Created** + ```json + { + "id": "uuid", + "task_id": "uuid", + "user_id": "uuid", + "content": "string", + "created_at": "timestamp", + "updated_at": "timestamp" + } + ``` + +#### Get Comments for Task +- **GET** `/tasks/{task_id}/comments` +- **Response**: + - **200 OK** + ```json + [ + { + "id": "uuid", + "task_id": "uuid", + "user_id": "uuid", + "content": "string", + "created_at": "timestamp", + "updated_at": "timestamp" + } + ] + ``` + +#### Delete Comment +- **DELETE** `/comments/{id}` +- **Response**: + - **204 No Content** + +### Chat History + +#### Add Chat Entry +- **POST** `/chat` +- **Request Body**: + ```json + { + "userId": "uuid", + "taskId": "uuid", + "message": "string" + } + ``` +- **Response**: + - **201 Created** + ```json + { + "id": "uuid", + "userId": "uuid", + "taskId": "uuid", + "message": "string", + "createdAt": "timestamp" + } + ``` + +#### Get Chat History +- **GET** `/chat/{taskId}` +- **Response**: + - **200 OK** + ```json + [ + { + "id": "uuid", + "userId": "uuid", + "taskId": "uuid", + "message": "string", + "createdAt": "timestamp" + } + ] + ``` + +#### Delete Chat Entry +- **DELETE** `/chat/{id}` +- **Response**: + - **204 No Content** + +## Error Handling +- All responses will include an error object in the following format: \ No newline at end of file diff --git a/app/404.tsx b/app/404.tsx new file mode 100644 index 00000000..0519ecba --- /dev/null +++ b/app/404.tsx @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/TaskBoard.tsx b/app/TaskBoard.tsx deleted file mode 100644 index b28aab4e..00000000 --- a/app/TaskBoard.tsx +++ /dev/null @@ -1,173 +0,0 @@ -'use client' - -import { useState, useEffect } from 'react' -import { createClientComponentClient } from '@supabase/auth-helpers-nextjs' -import { DragDropContext, Droppable, Draggable, DropResult } from 'react-beautiful-dnd' -import { useRouter } from 'next/navigation' -import styles from './TaskBoard.module.css' - -interface Task { - id: string - title: string - status: 'TODO' | 'IN_PROGRESS' | 'DONE' -} - -const COLUMNS = ['TODO', 'IN_PROGRESS', 'DONE'] - -export default function TaskBoard() { - const [tasks, setTasks] = useState([]) - const [newTaskTitle, setNewTaskTitle] = useState('') - const [user, setUser] = useState(null) - const [loading, setLoading] = useState(true) - const supabase = createClientComponentClient() - const router = useRouter() - - useEffect(() => { - const checkUser = async () => { - const { data: { user } } = await supabase.auth.getUser() - if (user) { - setUser(user) - fetchTasks() - } else { - router.push('/login') // Redirect to login page if not authenticated - } - setLoading(false) - } - checkUser() - }, [supabase, router]) - - async function fetchTasks() { - const { data, error } = await supabase - .from('tasks') - .select('*') - .order('created_at', { ascending: true }) - - if (error) { - console.error('Error fetching tasks:', error) - } else { - setTasks(data || []) - } - } - - async function addTask(e: React.FormEvent) { - e.preventDefault() - if (!newTaskTitle.trim()) return - - const newTask = { - title: newTaskTitle.trim(), - status: 'TODO' as const, - user_id: user.id // Associate task with the current user - } - - const { data, error } = await supabase - .from('tasks') - .insert([newTask]) - .select() - - if (error) { - console.error('Error adding task:', error) - } else if (data) { - setTasks([...tasks, data[0]]) - setNewTaskTitle('') - } - } - - async function updateTaskStatus(taskId: string, newStatus: 'TODO' | 'IN_PROGRESS' | 'DONE') { - const { error } = await supabase - .from('tasks') - .update({ status: newStatus }) - .eq('id', taskId) - .eq('user_id', user.id) // Ensure user can only update their own tasks - - if (error) { - console.error('Error updating task:', error) - } else { - setTasks(tasks.map(task => - task.id === taskId ? { ...task, status: newStatus } : task - )) - } - } - - const onDragEnd = async (result: DropResult) => { - const { source, destination, draggableId } = result - - if (!destination) return - - if ( - source.droppableId === destination.droppableId && - source.index === destination.index - ) { - return - } - - const newStatus = destination.droppableId as 'TODO' | 'IN_PROGRESS' | 'DONE' - await updateTaskStatus(draggableId, newStatus) - } - - async function handleSignOut() { - await supabase.auth.signOut() - router.push('/login') - } - - if (loading) { - return
Loading...
- } - - if (!user) { - return null // This should not happen as we redirect to login, but just in case - } - - return ( - -
-

Task Board

-

Welcome, {user.email}

- -
- setNewTaskTitle(e.target.value)} - placeholder="Enter new task" - className={styles.addTaskInput} - /> - -
-
- {COLUMNS.map(column => ( -
-

{column}

- - {(provided, snapshot) => ( -
- {tasks - .filter(task => task.status === column) - .map((task, index) => ( - - {(provided, snapshot) => ( -
- {task.title} -
- )} -
- ))} - {provided.placeholder} -
- )} -
-
- ))} -
-
-
- ) -} \ No newline at end of file diff --git a/app/Typescript-task.code-workspace b/app/Typescript-task.code-workspace new file mode 100644 index 00000000..fd36f7bc --- /dev/null +++ b/app/Typescript-task.code-workspace @@ -0,0 +1,11 @@ +{ + "folders": [ + { + "path": "../.." + } + ], + "settings": { + "typescript.enablePromptUseWorkspaceTsdk": true, + "typescript.tsdk": "../../node_modules/typescript/lib" + } +} \ No newline at end of file diff --git a/app/_app.tsx b/app/_app.tsx new file mode 100644 index 00000000..6f3daa19 --- /dev/null +++ b/app/_app.tsx @@ -0,0 +1,17 @@ +import { AppProps } from 'next/app'; +import { Web3ReactProvider } from '@web3-react/core'; +import { Web3Provider } from '@ethersproject/providers'; + +function getLibrary(provider: any): Web3Provider { + return new Web3Provider(provider); +} + +function MyApp({ Component, pageProps }: AppProps) { + return ( + + + + ); +} + +export default MyApp; \ No newline at end of file diff --git a/app/about/page.tsx b/app/about/page.tsx new file mode 100644 index 00000000..2ab34d5c --- /dev/null +++ b/app/about/page.tsx @@ -0,0 +1,10 @@ +import React from 'react'; + +export default function AboutPage() { + return ( +
+

About Us

+

Welcome to our about page. We are a company dedicated to creating amazing products.

+
+ ); +} \ No newline at end of file diff --git a/app/agent-design/page.tsx b/app/agent-design/page.tsx new file mode 100644 index 00000000..9424f3e4 --- /dev/null +++ b/app/agent-design/page.tsx @@ -0,0 +1,161 @@ +'use client'; + +import React, { useState } from 'react'; +import { motion } from 'framer-motion'; +import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; +import { Badge } from "@/components/ui/badge"; +import { ScrollArea } from "@/components/ui/scroll-area"; +import { Switch } from "@/components/ui/switch"; +import { Bot, Send, Zap, Settings, Calendar, Clock, BarChart } from 'lucide-react'; +import Slider from '@geist-ui/react/esm/slider/slider'; + +const AgentDesignPage: React.FC = () => { + const [messages, setMessages] = useState([ + { role: 'agent', content: 'Hello! I\'m your AI task assistant. How can I help you today?' }, + { role: 'user', content: 'Can you help me prioritize my tasks for today?' }, + { role: 'agent', content: 'I\'ll analyze your current tasks and deadlines to suggest a prioritized list. Give me a moment...' }, + { role: 'agent', content: 'Based on your deadlines and task importance, here\'s a suggested priority list for today:\n\n1. Complete project proposal (due tomorrow)\n2. Review and respond to client emails\n3. Prepare for team meeting at 2 PM\n4. Start research for upcoming presentation\n5. Update task board with current progress\n\nWould you like me to create these tasks in your task list?' }, + ]); + + const [inputMessage, setInputMessage] = useState(''); + + const handleSendMessage = () => { + if (inputMessage.trim()) { + setMessages([...messages, { role: 'user', content: inputMessage }]); + setInputMessage(''); + // Simulate AI response + setTimeout(() => { + setMessages(prev => [...prev, { role: 'agent', content: 'I understand. I\'ll work on that right away and update your task list accordingly.' }]); + }, 1000); + } + }; + + return ( +
+

AI Agent Design

+ +
+ + + AI Task Assistant + Interact with your AI agent to manage tasks efficiently + + + + {messages.map((message, index) => ( + +
+ + {message.role === 'agent' ? ( + + ) : ( + + )} + {message.role === 'agent' ? 'AI' : 'You'} + +
+ {message.content} +
+
+
+ ))} +
+
+ setInputMessage(e.target.value)} + placeholder="Type your message here..." + onKeyPress={(e) => e.key === 'Enter' && handleSendMessage()} + /> + +
+
+
+ +
+ + + Agent Capabilities + + +
    +
  • + + + Scheduling + + Manage your calendar and deadlines +
  • +
  • + + + Analytics + + Provide insights on productivity +
  • +
  • + + + Automation + + Automate repetitive tasks +
  • +
  • + + + Reminders + + Send timely notifications +
  • +
+
+
+ + + + Agent Settings + + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+
+
+ ); +}; + +export default AgentDesignPage; \ No newline at end of file diff --git a/app/agent-manager/[id]/page.tsx b/app/agent-manager/[id]/page.tsx new file mode 100644 index 00000000..db8a9bfd --- /dev/null +++ b/app/agent-manager/[id]/page.tsx @@ -0,0 +1,46 @@ + 'use client'; + + import React, { useState, useEffect } from 'react'; // Import React + import { useRouter } from 'next/navigation'; + import AgentDetailView from '@/components/agent-detail-view'; // Ensure this path is correct + import { Agent } from '@/types'; + + // Mock data for agents + const mockAgents: Agent[] = [ + // ... (mock agents) + { id: "1", name: "Alice Johnson", avatar: "/placeholder.svg?height=40&width=40", expertise: "Frontend Development", backstory: "Alice is a frontend developer with a passion for creating beautiful and functional user interfaces.", tools: ["React", "Next.js", "Tailwind CSS"] }, + { id: "2", name: "Bob Smith", avatar: "/placeholder.svg?height=40&width=40", expertise: "Backend Development", backstory: "Bob is a backend developer with a passion for creating scalable and secure web applications.", tools: ["Node.js", "Express", "MongoDB"] }, + { id: "3", name: "Charlie Brown", avatar: "/placeholder.svg?height=40&width=40", expertise: "DevOps", backstory: "Charlie is a devops engineer with a passion for creating scalable and secure web applications.", tools: ["Docker", "Kubernetes", "Jenkins"] }, + { id: "4", name: "Diana Prince", avatar: "/placeholder.svg?height=40&width=40", expertise: "UI/UX Design", backstory: "Diana is a ui/ux designer with a passion for creating beautiful and functional user interfaces.", tools: ["Figma", "Adobe XD", "Sketch"] }, + ]; + + export default function AgentDetailPage({ params }: { params: { id: string } }) { + const id = params.id; + const [agent, setAgent] = useState(null); + const [loading, setLoading] = useState(true); // Loading state + const router = useRouter(); + + useEffect(() => { + const fetchedAgent = mockAgents.find(a => a.id === id) || null; + setAgent(fetchedAgent); + setLoading(false); // Set loading to false after fetching + }, [id]); + + const handleClose = () => { + router.push('/agent-manager'); + }; + + if (loading) { + return
Loading...
; // Show loading state + } + + if (!agent) { + return
Agent not found
; + } + + return ( +
+ +
+ ); + } \ No newline at end of file diff --git a/app/agent-manager/page.tsx b/app/agent-manager/page.tsx new file mode 100644 index 00000000..9bfa8807 --- /dev/null +++ b/app/agent-manager/page.tsx @@ -0,0 +1,61 @@ +'use client'; + +import React from 'react'; +import { Wrench, Globe, Database, FileText, User } from 'lucide-react'; // Importing the new icons +import Link from 'next/link'; +import { useUser } from '@clerk/nextjs'; + +const AgentManager: React.FC = () => { + const { isSignedIn, user } = useUser(); + + if (!isSignedIn) { + return
Please sign in to access the Agent Manager.
; + } + + // Sample agent data for demonstration + const agents = [ + { id: 1, avatar: 'AJ', name: 'Alice Johnson', type: 'Researcher', tools: ['Web Scraper', 'Database'], tasks: 15, successRate: '93.0%', score: 87, rank: 'Expert' }, + { id: 2, avatar: 'BS', name: 'Bob Smith', type: 'Front End', tools: ['Web Scraper'], tasks: 8, successRate: '75.0%', score: 60, rank: 'Intermediate' }, + { id: 3, avatar: 'CB', name: 'Charlie Brown', type: 'Product Manager', tools: ['Database', 'File Creator'], tasks: 20, successRate: '95.0%', score: 95, rank: 'Master' }, + ]; + + return ( +
+

Agent Manager

+ + Add New Agent +
+ {agents.map(agent => ( + +
+ +
+

{agent.name}

+

{agent.type}

+
+ {agent.tools.map(tool => ( + + {tool === 'Web Scraper' && } + {tool === 'Database' && } + {tool === 'File Creator' && } + + ))} +
+
+ + {agent.rank} + +
+
+

Tasks: {agent.tasks}

+

Success Rate: {agent.successRate}

+

Score: {agent.score}

+
+ + ))} +
+
+ ); +}; + +export default AgentManager; + diff --git a/app/agents-tasks/page.tsx b/app/agents-tasks/page.tsx new file mode 100644 index 00000000..69655942 --- /dev/null +++ b/app/agents-tasks/page.tsx @@ -0,0 +1,169 @@ +'use client'; + +import React, { useState } from 'react'; +import RobotTransformerWallpaper from '../../components/RobotTransformerWallpaper'; + +interface Task { + id: number; + title: string; + completed: boolean; + category: string; + dueDate: string; + priority: string; + assignedTo: string; + estimatedTime: string; + dependencies: string[]; +} + +const AgentTaskManager: React.FC = () => { + const [tasks, setTasks] = useState([ + { id: 1, title: "Implement new UI component", completed: false, category: 'Frontend', dueDate: '', priority: 'High', assignedTo: 'Alice', estimatedTime: '3h', dependencies: [] }, + { id: 2, title: "Optimize database queries", completed: false, category: 'Backend', dueDate: '', priority: 'Medium', assignedTo: 'Bob', estimatedTime: '2h', dependencies: ['Task 1'] }, + // Add more default tasks here + ]); + const [newTask, setNewTask] = useState(''); + const [category, setCategory] = useState('Frontend'); + const [dueDate, setDueDate] = useState(''); + const [priority, setPriority] = useState('Medium'); + const [assignedTo, setAssignedTo] = useState(''); + const [estimatedTime, setEstimatedTime] = useState(''); + const [dependencies, setDependencies] = useState(''); + + const addTask = () => { + if (newTask.trim()) { + setTasks([...tasks, { + id: Date.now(), + title: newTask, + completed: false, + category, + dueDate, + priority, + assignedTo, + estimatedTime, + dependencies: dependencies.split(',').map(dep => dep.trim()), + }]); + setNewTask(''); + setDueDate(''); + setAssignedTo(''); + setEstimatedTime(''); + setDependencies(''); + } + }; + + const toggleTaskCompletion = (id: number) => { + setTasks(tasks.map(task => + task.id === id ? { ...task, completed: !task.completed } : task + )); + }; + + return ( +
+ +
+

Agent Task Manager

+
+

Background Story

+

+ Welcome to the Agent Task Manager. This tool is designed to help agents efficiently manage their tasks across various domains such as Frontend, Backend, DevOps, QA Automation, UAT, and Product Management. By organizing tasks and setting priorities, agents can streamline their workflow and achieve their goals effectively. +

+

Expected Output

+

+ The expected outcome is a well-organized task list that allows agents to track progress, meet deadlines, and ensure high-quality deliverables. By using this task manager, agents can enhance productivity and collaboration within their teams. +

+
+
+
+ setNewTask(e.target.value)} + /> +
+ + setDueDate(e.target.value)} + /> + +
+ setAssignedTo(e.target.value)} + /> + setEstimatedTime(e.target.value)} + /> + setDependencies(e.target.value)} + /> + +
+
    + {tasks.map(task => ( +
  • +
    + toggleTaskCompletion(task.id)} + > + {task.title} + + + {task.category} | Due: {task.dueDate} | Priority: {task.priority} | Assigned to: {task.assignedTo} | Estimated time: {task.estimatedTime} | Dependencies: {task.dependencies.join(', ')} + +
    + +
  • + ))} +
+
+
+
+ ); +}; + +export default AgentTaskManager; \ No newline at end of file diff --git a/app/analytics/page.tsx b/app/analytics/page.tsx new file mode 100644 index 00000000..82b0659d --- /dev/null +++ b/app/analytics/page.tsx @@ -0,0 +1,142 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { AreaChart, Area, BarChart, Bar, PieChart, Pie, Cell, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Legend } from 'recharts'; + +const initialTaskCompletionData = [ + { date: '2023-01', completed: 45, total: 60 }, + { date: '2023-02', completed: 52, total: 65 }, + { date: '2023-03', completed: 48, total: 70 }, + { date: '2023-04', completed: 70, total: 80 }, + { date: '2023-05', completed: 65, total: 75 }, + { date: '2023-06', completed: 80, total: 90 }, +]; + +const initialTeamPerformanceData = [ + { name: 'Development', performance: 85 }, + { name: 'Design', performance: 78 }, + { name: 'Marketing', performance: 92 }, + { name: 'Product', performance: 88 }, +]; + +const initialTaskDistributionData = [ + { name: 'To Do', value: 30, color: '#FF6384' }, + { name: 'In Progress', value: 45, color: '#36A2EB' }, + { name: 'Done', value: 25, color: '#FFCE56' }, +]; + +export default function AnalyticsPage() { + const [taskCompletionData, setTaskCompletionData] = useState(initialTaskCompletionData); + const [teamPerformanceData, setTeamPerformanceData] = useState(initialTeamPerformanceData); + const [taskDistributionData, setTaskDistributionData] = useState(initialTaskDistributionData); + + useEffect(() => { + const interval = setInterval(() => { + setTaskCompletionData((prevData) => + prevData.map((entry) => ({ + ...entry, + completed: Math.max(0, Math.min(entry.total, entry.completed + Math.floor(Math.random() * 10 - 5))), + })) + ); + + setTeamPerformanceData((prevData) => + prevData.map((entry) => ({ + ...entry, + performance: Math.max(0, Math.min(100, entry.performance + Math.floor(Math.random() * 10 - 5))), + })) + ); + + setTaskDistributionData((prevData) => { + const total = prevData.reduce((sum, entry) => sum + entry.value, 0); + return prevData.map((entry) => ({ + ...entry, + value: Math.max(0, Math.min(total, entry.value + Math.floor(Math.random() * 10 - 5))), + })); + }); + }, Math.floor(Math.random() * 6000) + 4000); + + return () => clearInterval(interval); + }, []); + + return ( +
+

Analytics Dashboard

+
+ + + Task Completion Over Time + Completed tasks vs total tasks + + +
+ + + + + + + + + + + +
+
+
+ + + + Team Performance + Performance score by team + + +
+ + + + + + + + + + +
+
+
+ + + + Task Distribution + Current status of all tasks + + +
+ + + `${name} ${(percent * 100).toFixed(0)}%`} + > + {taskDistributionData.map((entry, index) => ( + + ))} + + + + + +
+
+
+
+
+ ); +} \ No newline at end of file diff --git a/app/api/auth/callback/route.ts b/app/api/auth/callback/route.ts new file mode 100644 index 00000000..ee94fe4b --- /dev/null +++ b/app/api/auth/callback/route.ts @@ -0,0 +1,17 @@ +import { NextRequest, NextResponse } from 'next/server'; + +export async function POST(req: NextRequest) { + try { + const { token } = await req.json(); + + // Simulate token verification or processing + if (!token) { + return NextResponse.json({ error: 'Token is required' }, { status: 400 }); + } + + // Mock successful authentication + return NextResponse.json({ message: 'Authentication successful', token }, { status: 200 }); + } catch (error) { + return NextResponse.json({ error: 'Internal server error' }, { status: 500 }); + } +} \ No newline at end of file diff --git a/app/api/auth/route.ts b/app/api/auth/route.ts new file mode 100644 index 00000000..c5ef00fa --- /dev/null +++ b/app/api/auth/route.ts @@ -0,0 +1,11 @@ +//@ts-nocheck +import { NextResponse } from 'next/server'; + +import { handleLogin } from '@/components/session'; + +export async function POST(request: Request) { + console.log('Received login request'); // Debug log + const { email, password } = await request.json(); + console.log('Email:', email); // Log the email for debugging + return handleLogin(email, password); +} \ No newline at end of file diff --git a/app/api/auth/session.ts b/app/api/auth/session.ts new file mode 100644 index 00000000..94f6e6bd --- /dev/null +++ b/app/api/auth/session.ts @@ -0,0 +1,12 @@ +import { NextResponse } from 'next/server'; +import { supabase } from '@/utils/supabase/client'; + +export async function GET() { + const { data, error } = await supabase.auth.getSession(); + + if (error || !data.session) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); + } + + return NextResponse.json({ user: data.session.user }); +} diff --git a/app/api/auth/status/route.ts b/app/api/auth/status/route.ts deleted file mode 100644 index 27dadfcd..00000000 --- a/app/api/auth/status/route.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { NextResponse } from 'next/server' - -export async function GET() { - // Replace this with your actual authentication logic - const isAuthenticated = true // This should be determined by your auth mechanism - - return NextResponse.json({ isAuthenticated }) -} \ No newline at end of file diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts new file mode 100644 index 00000000..24c1b165 --- /dev/null +++ b/app/api/chat/route.ts @@ -0,0 +1,43 @@ +import { NextRequest } from 'next/server'; + +export async function POST(req: NextRequest) { + const { message, model } = await req.json(); + + const response = await fetch('http://localhost:11434/api/chat', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + model, + messages: [{ role: 'user', content: message }], + stream: true, + }), + }); + + if (!response.ok) { + throw new Error('Failed to fetch from Ollama'); + } + + const stream = new ReadableStream({ + async start(controller) { + const reader = response.body?.getReader(); + while (true) { + const { done, value } = await reader!.read(); + if (done) break; + const chunk = new TextDecoder().decode(value); + try { + const parsed = JSON.parse(chunk); + if (parsed.message?.content) { + controller.enqueue(parsed.message.content); + } + } catch (e) { + console.error('Error parsing JSON:', e); + } + } + controller.close(); + }, + }); + + return new Response(stream, { + headers: { 'Content-Type': 'text/plain' }, + }); +} diff --git a/app/api/debank/debank.ts b/app/api/debank/debank.ts new file mode 100644 index 00000000..4eeb9795 --- /dev/null +++ b/app/api/debank/debank.ts @@ -0,0 +1,48 @@ +import { NextRequest, NextResponse } from 'next/server'; +import logger from '@/lib/logger'; + +export async function GET(request: NextRequest) { + logger.info('Starting API request...'); + logger.info(`DEBANK_API_KEY: ${process.env.DEBANK_API_KEY ? 'Set' : 'Not Set'}`); + + const id = request.nextUrl.searchParams.get('id'); + + logger.info(`Received request for wallet ID: ${id}`); + + if (!id) { + logger.warn('Missing wallet address in request'); + return NextResponse.json({ error: 'Missing wallet address' }, { status: 400 }); + } + + const apiKey = process.env.DEBANK_API_KEY; + if (!apiKey) { + logger.error('DEBANK_API_KEY is not set'); + return NextResponse.json({ error: 'API key not configured' }, { status: 500 }); + } + + try { + const url = `https://pro-openapi.debank.com/v1/user/total_balance?id=${id}`; + logger.info(`Fetching data from DeBank API: ${url}`); + + const response = await fetch(url, { + headers: { + 'AccessKey': apiKey, + }, + }); + + logger.info(`DeBank API response status: ${response.status}`); + + if (!response.ok) { + const errorData = await response.text(); // Log the response text for debugging + logger.error(`DeBank API error: ${errorData}`); + return NextResponse.json({ error: 'Failed to fetch data from DeBank API' }, { status: response.status }); + } + + const data = await response.json(); + logger.info(`Successfully fetched data from DeBank API: ${JSON.stringify(data)}`); + return NextResponse.json(data); + } catch (error) { + logger.error(`Error in DeBank API route: ${(error as Error).message}`); + return NextResponse.json({ error: 'Internal server error' }, { status: 500 }); + } +} \ No newline at end of file diff --git a/app/api/debank/portfolio.ts b/app/api/debank/portfolio.ts new file mode 100644 index 00000000..ef8f791b --- /dev/null +++ b/app/api/debank/portfolio.ts @@ -0,0 +1,51 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + if (req.method === 'GET') { + const { id } = req.query; + + // Validate the wallet ID + if (!id || typeof id !== 'string') { + console.error("Invalid wallet ID:", id); // Log invalid wallet ID + return res.status(400).json({ error: 'Invalid wallet ID' }); + } + + console.log("Received request for wallet ID:", id); // Log the received wallet ID + + try { + const total_usd_value = await getTotalBalance(id as string); // Ensure id is treated as a string + console.log("Total USD Value:", total_usd_value); // Log the total USD value + res.status(200).json({ total_usd_value }); + } catch (error) { + console.error("Error fetching balance:", error); // Log error + res.status(500).json({ error: 'Failed to fetch balance data' }); + } + } else { + // Handle any other HTTP method + res.setHeader('Allow', ['GET']); + res.status(405).end(`Method ${req.method} Not Allowed`); + } +} + +// Function to fetch balance from Debank +async function getTotalBalance(walletId: string): Promise { + console.log("Fetching balance from Debank for wallet ID:", walletId); // Log the wallet ID being fetched + const response = await fetch(`https://api.debank.com/v1/user/total_balance?id=${walletId}`, { + headers: { + 'Authorization': `Bearer ${process.env.DEBANK_API_KEY}`, // Include the API key in the headers + }, + }); + + console.log("Debank API Response Status:", response.status); // Log Debank response status + + if (!response.ok) { + const errorData = await response.json(); // Get error details + console.error("Debank Error Response Data:", errorData); // Log error response data + throw new Error('Failed to fetch data from Debank'); + } + + const data = await response.json(); + console.log("Debank Response Data:", data); // Log the response from Debank + + return data.total_usd_value || 0; // Return the total balance or 0 if not available +} \ No newline at end of file diff --git a/app/api/debank/route.ts b/app/api/debank/route.ts new file mode 100644 index 00000000..f04a3829 --- /dev/null +++ b/app/api/debank/route.ts @@ -0,0 +1,47 @@ +import { NextResponse } from 'next/server'; +import logger from '@/lib/logger'; + +export async function GET(request: Request) { + const { searchParams } = new URL(request.url); + const id = searchParams.get('id'); + + logger.info('Starting API request...'); + logger.info(`Received request for wallet ID: ${id}`); + + if (!id) { + logger.warn('Missing wallet address in request'); + return NextResponse.json({ error: 'Missing wallet address' }, { status: 400 }); + } + + const apiKey = process.env.DEBANK_API_KEY; + if (!apiKey) { + logger.error('DEBANK_API_KEY is not set'); + return NextResponse.json({ error: 'API key not configured' }, { status: 500 }); + } + + try { + const url = `https://pro-openapi.debank.com/v1/user/total_balance?id=${id}`; + logger.info(`Fetching data from DeBank API: ${url}`); + + const response = await fetch(url, { + headers: { + 'AccessKey': apiKey, + }, + }); + + logger.info(`DeBank API response status: ${response.status}`); + + if (!response.ok) { + const errorData = await response.text(); // Log the response text for debugging + logger.error(`DeBank API error: ${errorData}`); + return NextResponse.json({ error: 'Failed to fetch data from DeBank API' }, { status: response.status }); + } + + const data = await response.json(); + logger.info(`Successfully fetched data from DeBank API: ${JSON.stringify(data)}`); + return NextResponse.json(data); + } catch (error) { + logger.error(`Error in DeBank API route: ${(error as Error).message}`); + return NextResponse.json({ error: 'Internal server error' }, { status: 500 }); + } +} \ No newline at end of file diff --git a/app/api/notifications/index.ts b/app/api/notifications/index.ts new file mode 100644 index 00000000..923d27f7 --- /dev/null +++ b/app/api/notifications/index.ts @@ -0,0 +1,16 @@ +import { NextApiRequest, NextApiResponse } from 'next'; + +// Mock data for notifications +const mockNotifications = [ + { id: '1', message: 'New task assigned to you', createdAt: new Date().toISOString() }, + { id: '2', message: 'Project deadline approaching', createdAt: new Date(Date.now() - 86400000).toISOString() }, + { id: '3', message: 'Team meeting scheduled for tomorrow', createdAt: new Date(Date.now() - 172800000).toISOString() }, +]; + +export default async function getNotifications(req: NextApiRequest, res: NextApiResponse) { + // Simulate a delay to mimic API call + await new Promise(resolve => setTimeout(resolve, 500)); + + // Return mock notifications + res.status(200).json({ notifications: mockNotifications }); +} \ No newline at end of file diff --git a/app/api/ollama/route.ts b/app/api/ollama/route.ts new file mode 100644 index 00000000..e39d9c25 --- /dev/null +++ b/app/api/ollama/route.ts @@ -0,0 +1,18 @@ +import { NextResponse } from 'next/server'; + +// Example settings for the current model +const modelSettings = { + temperature: 0.7, + maxTokens: 150, + // Add other settings as needed +}; + +export async function POST(request: Request, p0: any) { + const { message } = await request.json(); + + // Here you would typically call your Ollama service or logic + // For demonstration, we'll just echo the message back + const reply = `Ollama received: ${message} with settings: ${JSON.stringify(modelSettings)}`; + + return NextResponse.json({ reply }); +} \ No newline at end of file diff --git a/app/api/tags/route.ts b/app/api/tags/route.ts new file mode 100644 index 00000000..50fbfb4f --- /dev/null +++ b/app/api/tags/route.ts @@ -0,0 +1,16 @@ +import { NextRequest, NextResponse } from 'next/server'; + +export async function GET(req: NextRequest) { + try { + const response = await fetch('http://localhost:11434/api/tags'); + if (!response.ok) { + throw new Error('Failed to fetch models from Ollama'); + } + const data = await response.json(); + const models = data.models.map((model: { name: string }) => model.name); + return NextResponse.json({ models }); + } catch (error) { + console.error('Error fetching models:', error); + return NextResponse.json({ error: 'Failed to fetch models' }, { status: 500 }); + } +} \ No newline at end of file diff --git a/app/api/tasks/[id]/route.ts b/app/api/tasks/[id]/route.ts deleted file mode 100644 index 5d9f6cc0..00000000 --- a/app/api/tasks/[id]/route.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { NextResponse } from 'next/server'; -import { Pool } from 'pg'; - -const pool = new Pool({ - connectionString: process.env.DATABASE_URL, -}); - -export async function PATCH(request: Request, { params }: { params: { id: string } }) { - const { id } = params; - const { status, position } = await request.json(); - - try { - const client = await pool.connect(); - await client.query( - 'UPDATE tasks SET status = $1, position = $2 WHERE id = $3', - [status, position, id] - ); - client.release(); - return NextResponse.json({ message: 'Task updated successfully' }); - } catch (error) { - console.error('Error updating task:', error); - return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 }); - } -} \ No newline at end of file diff --git a/app/api/tasks/route.ts b/app/api/tasks/route.ts deleted file mode 100644 index e1d9c15d..00000000 --- a/app/api/tasks/route.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { NextResponse } from 'next/server'; -import { Pool } from 'pg'; - -const pool = new Pool({ - connectionString: process.env.DATABASE_URL, -}); - -export async function GET() { - try { - const client = await pool.connect(); - const result = await client.query('SELECT * FROM tasks ORDER BY position'); - client.release(); - return NextResponse.json(result.rows); - } catch (error) { - console.error('Error fetching tasks:', error); - return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 }); - } -} \ No newline at end of file diff --git a/app/api/user.ts b/app/api/user.ts new file mode 100644 index 00000000..ceb41463 --- /dev/null +++ b/app/api/user.ts @@ -0,0 +1,17 @@ +import { NextApiRequest, NextApiResponse } from 'next'; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + if (req.method === 'GET') { + // Simulate fetching user data + const user = { id: 1, name: 'John Doe', email: 'john@example.com' }; + return res.status(200).json(user); + } + + if (req.method === 'POST') { + // Simulate creating a user + const { name, email } = req.body; + return res.status(201).json({ id: 2, name, email }); + } + + return res.status(405).end(); // Method Not Allowed +} \ No newline at end of file diff --git a/app/auth/callback/route.ts b/app/auth/callback/route.ts index 930a0e02..e337b8ac 100644 --- a/app/auth/callback/route.ts +++ b/app/auth/callback/route.ts @@ -8,15 +8,9 @@ export async function GET(request: Request) { if (code) { const supabase = createRouteHandlerClient({ cookies }); - try { - await supabase.auth.exchangeCodeForSession(code); - console.log('Successfully exchanged code for session'); - } catch (error) { - console.error('Error exchanging code for session:', error); - // You might want to redirect to an error page here - return NextResponse.redirect(`${requestUrl.origin}/auth-error`); - } + await supabase.auth.exchangeCodeForSession(code); } - return NextResponse.redirect(`${requestUrl.origin}/taskboard`); + // URL to redirect to after sign in process completes + return NextResponse.redirect(requestUrl.origin); } \ No newline at end of file diff --git a/app/board/page.tsx b/app/board/page.tsx deleted file mode 100644 index 5a34246a..00000000 --- a/app/board/page.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import AuthenticatedLayout from '@/app/layouts/AuthenticatedLayout'; -import Board from "@/components/todo/Board"; - -interface Params { - id: string; -} - -interface WorkboardPageProps { - params: Params; -} - -const WorkboardPage: React.FC = ({ params }) => { - return ( - - - - ); -}; - -export default WorkboardPage; diff --git a/app/boards/page.tsx b/app/boards/page.tsx deleted file mode 100644 index b4031377..00000000 --- a/app/boards/page.tsx +++ /dev/null @@ -1,96 +0,0 @@ -'use client'; - -import { useState, useEffect } from 'react'; -import { createClientComponentClient } from "@supabase/auth-helpers-nextjs"; -import AuthenticatedLayout from '../components/AuthenticatedLayout'; -import Link from 'next/link'; - -const BoardsPage = () => { - const [boards, setBoards] = useState([]); - const [newBoardName, setNewBoardName] = useState(''); - const supabase = createClientComponentClient(); - - useEffect(() => { - fetchBoards(); - }, []); - - const fetchBoards = async () => { - const { data, error } = await supabase.from('boards').select('*'); - if (error) { - console.error('Error fetching boards:', error); - } else { - setBoards(data || []); - } - }; - - const createBoard = async (e: React.FormEvent) => { - e.preventDefault(); - if (!newBoardName.trim()) return; - - const { data, error } = await supabase - .from('boards') - .insert({ name: newBoardName }) - .select() - .single(); - - if (error) { - console.error('Error creating board:', error); - } else { - setBoards([...boards, data]); - setNewBoardName(''); - } - }; - - const deleteBoard = async (boardId: string) => { - const { error } = await supabase - .from('boards') - .delete() - .eq('id', boardId); - - if (error) { - console.error('Error deleting board:', error); - } else { - setBoards(boards.filter(board => board.id !== boardId)); - } - }; - - return ( - -
-

My Boards

-
- setNewBoardName(e.target.value)} - placeholder="New board name" - className="mr-2 p-2 border rounded" - /> - -
-
- {boards.map((board) => ( -
-

{board.name}

-
- - Open Board - - -
-
- ))} -
-
-
- ); -}; - -export default BoardsPage; \ No newline at end of file diff --git a/app/bot-tasks/page.tsx b/app/bot-tasks/page.tsx new file mode 100644 index 00000000..e628dc63 --- /dev/null +++ b/app/bot-tasks/page.tsx @@ -0,0 +1,77 @@ +'use client'; + +import React, { useState } from 'react'; +import RobotTransformerWallpaper from '../../components/RobotTransformerWallpaper'; +import ChatbotModal from '../../components/ChatbotModal'; + +interface Task { + id: number; + title: string; + completed: boolean; + category: string; + dueDate: string; + priority: string; +} + +const BotTaskManager: React.FC = () => { + const [tasks, setTasks] = useState([]); + const [isChatOpen, setIsChatOpen] = useState(false); + + const handleChatSubmit = (message: string) => { + // Simulate task creation from chatbot input + const newTask: Task = { + id: Date.now(), + title: message, + completed: false, + category: 'General', + dueDate: '', + priority: 'Medium', + }; + setTasks([...tasks, newTask]); + }; + + return ( +
+ +
+

Bot Task Manager

+
+

Task Management Bot

+

+ Use the chatbot to manage your tasks. You can add new tasks, update existing ones, and track your progress through a conversational interface. +

+
+
+

Task List

+
    + {tasks.map(task => ( +
  • +
    + + {task.title} + + + {task.category} | Due: {task.dueDate} | Priority: {task.priority} + +
    + +
  • + ))} +
+
+
+ setIsChatOpen(false)} + /> +
+ ); +}; + +export default BotTaskManager; \ No newline at end of file diff --git a/app/chat/layout.tsx b/app/chat/layout.tsx new file mode 100644 index 00000000..15cd0899 --- /dev/null +++ b/app/chat/layout.tsx @@ -0,0 +1,32 @@ +'use client'; + +import React, { useState } from 'react'; +import { ThemeProvider } from '@/app/contexts/ThemeContext'; +import '@/styles/globals.css'; +import ChatbotModal from '@/components/ChatbotModal'; +import RobotTransformerWallpaper from '@/components/RobotTransformerWallpaper'; + +interface ChatLayoutProps { + children: React.ReactNode; +} + +const ChatLayout: React.FC = ({ children }) => { + const [isChatbotOpen, setIsChatbotOpen] = useState(false); + + return ( + + + + +
+ {children} + + setIsChatbotOpen(false)} /> +
+
+ + + ); +}; + +export default ChatLayout; diff --git a/app/chat/page.tsx b/app/chat/page.tsx new file mode 100644 index 00000000..ac87adff --- /dev/null +++ b/app/chat/page.tsx @@ -0,0 +1,27 @@ +'use client'; + +import React, { useState } from 'react'; +import ChatbotModal from '@/components/ChatbotModal'; + +const ChatPage: React.FC = () => { + const [isChatOpen, setIsChatOpen] = useState(false); + + const toggleChat = () => { + setIsChatOpen(!isChatOpen); + }; + + return ( +
+

Chat Room

+ + {isChatOpen && } +
+ ); +}; + +export default ChatPage; \ No newline at end of file diff --git a/app/client-layout.tsx b/app/client-layout.tsx new file mode 100644 index 00000000..15324cd4 --- /dev/null +++ b/app/client-layout.tsx @@ -0,0 +1,49 @@ +'use client' + +import React, { useState } from 'react' +import { useTheme } from './contexts/ThemeContext' +import ChatIcon from '../components/chat-icon' +import { ChatBotModal } from '@/components/chat-bot-modal' +import CodeEditorIcon from '../components/code-editor-icon' +import { MoncacoEditor } from '@/components/moncaco-editor' +import { Button } from '@/components/ui/button' +import { X } from 'lucide-react' +import Footer from "@/components/footer" +import RobotTransformerWallpaper from '@/components/RobotTransformerWallpaper'; // Ensure this path is correct + +export function ClientLayout({ children }: { children: React.ReactNode }) { + const { theme } = useTheme() + + const [isChatOpen, setIsChatOpen] = useState(false) + const [isEditorOpen, setIsEditorOpen] = useState(false) + + return ( +
+ {/* Add the wallpaper component */} +
{children}
+
+ setIsChatOpen(true)} /> + setIsEditorOpen(true)} /> + setIsChatOpen(false)} /> + {isEditorOpen && ( +
+
+ + { + console.log('Saved:', value) + setIsEditorOpen(false) + }} + /> +
+
+ )} +
+ ) +} \ No newline at end of file diff --git a/app/components/AuthLayout.tsx b/app/components/AuthLayout.tsx deleted file mode 100644 index e39515f8..00000000 --- a/app/components/AuthLayout.tsx +++ /dev/null @@ -1,29 +0,0 @@ -'use client'; - -import { useEffect, useState } from 'react'; -import { useRouter } from 'next/navigation'; -import { createClientComponentClient } from "@supabase/auth-helpers-nextjs"; - -export default function AuthLayout({ children }: { children: React.ReactNode }) { - const [isLoading, setIsLoading] = useState(true); - const router = useRouter(); - const supabase = createClientComponentClient(); - - useEffect(() => { - const checkAuth = async () => { - const { data: { session } } = await supabase.auth.getSession(); - if (!session) { - router.push('/'); - } else { - setIsLoading(false); - } - }; - checkAuth(); - }, [router, supabase]); - - if (isLoading) { - return
Loading...
; - } - - return <>{children}; -} \ No newline at end of file diff --git a/app/components/AuthenticatedLayout.tsx b/app/components/AuthenticatedLayout.tsx deleted file mode 100644 index ab9406e1..00000000 --- a/app/components/AuthenticatedLayout.tsx +++ /dev/null @@ -1,55 +0,0 @@ -'use client'; - -import { useEffect, useState } from 'react'; -import { createClientComponentClient } from "@supabase/auth-helpers-nextjs"; -import Header from './Header'; -import { useRouter } from 'next/navigation'; - -interface AuthenticatedLayoutProps { - children: React.ReactNode; -} - -const AuthenticatedLayout: React.FC = ({ children }) => { - const [isAuthenticated, setIsAuthenticated] = useState(false); - const supabase = createClientComponentClient(); - const router = useRouter(); - - useEffect(() => { - const checkAuth = async () => { - const { data: { session } } = await supabase.auth.getSession(); - if (session) { - setIsAuthenticated(true); - } else { - router.push('/'); - } - }; - - checkAuth(); - - const { data: authListener } = supabase.auth.onAuthStateChange((event, session) => { - if (event === 'SIGNED_OUT') { - setIsAuthenticated(false); - router.push('/'); - } else if (event === 'SIGNED_IN' && session) { - setIsAuthenticated(true); - } - }); - - return () => { - authListener.subscription.unsubscribe(); - }; - }, [supabase, router]); - - if (!isAuthenticated) { - return null; // or a loading spinner - } - - return ( - <> -
- {children} - - ); -}; - -export default AuthenticatedLayout; \ No newline at end of file diff --git a/app/components/Board.tsx b/app/components/Board.tsx deleted file mode 100644 index 2b090a8e..00000000 --- a/app/components/Board.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React, { useState } from 'react'; -import { DragDropContext, Droppable } from 'react-beautiful-dnd'; - -interface Column { - id: string; - title: string; - taskIds: string[]; -} - -interface Task { - id: string; - content: string; -} - -const Board: React.FC = () => { - const [columns, setColumns] = useState<{ [key: string]: Column }>({ - // Initialize your columns here - }); - const [tasks, setTasks] = useState<{ [key: string]: Task }>({ - // Initialize your tasks here - }); - - const onDragEnd = (result: any) => { - // Implement drag and drop logic here - }; - - return ( - - {/* Implement your board structure here */} - - ); -}; - -export default Board; \ No newline at end of file diff --git a/app/components/Header.tsx b/app/components/Header.tsx deleted file mode 100644 index 63c2bb52..00000000 --- a/app/components/Header.tsx +++ /dev/null @@ -1,124 +0,0 @@ -'use client'; - -import { useState, useEffect } from 'react'; -import { createClientComponentClient } from "@supabase/auth-helpers-nextjs"; -import { useRouter } from 'next/navigation'; -import Link from 'next/link'; -import { useTheme } from '../contexts/ThemeContext'; - -const Header = () => { - const [user, setUser] = useState(null); - const [isDropdownOpen, setIsDropdownOpen] = useState(false); - const [isNavOpen, setIsNavOpen] = useState(false); - const supabase = createClientComponentClient(); - const router = useRouter(); - const { theme, setTheme } = useTheme(); - - useEffect(() => { - const fetchUser = async () => { - const { data: { user } } = await supabase.auth.getUser(); - if (user) { - const { data: userData } = await supabase - .from('users') - .select('full_name, initials') - .eq('id', user.id) - .single(); - setUser({ ...user, ...userData }); - } - }; - fetchUser(); - }, []); - - const handleSignOut = async () => { - await supabase.auth.signOut(); - router.push('/'); - }; - - const toggleTheme = () => { - setTheme(theme === 'light' ? 'dark' : 'light'); - }; - - return ( - <> -
-
-
- - - Dashboard - -
-
- - {user && ( -
- - {isDropdownOpen && ( -
- Profile - Settings - -
- )} -
- )} -
-
-
- - {/* Left Navigation Bar */} -
- - -
- - {/* Overlay to close nav when clicking outside */} - {isNavOpen && ( -
setIsNavOpen(false)} - >
- )} - - ); -}; - -export default Header; \ No newline at end of file diff --git a/app/components/Login.tsx b/app/components/Login.tsx deleted file mode 100644 index ce19013f..00000000 --- a/app/components/Login.tsx +++ /dev/null @@ -1,47 +0,0 @@ -'use client'; - -import { useState } from 'react'; -import { createClientComponentClient } from '@supabase/auth-helpers-nextjs'; -import { useRouter } from 'next/navigation'; - -export default function Login() { - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const router = useRouter(); - const supabase = createClientComponentClient(); - - const handleLogin = async (e: React.FormEvent) => { - e.preventDefault(); - try { - const { error } = await supabase.auth.signInWithPassword({ - email, - password, - }); - if (error) throw error; - router.refresh(); - } catch (error) { - console.error('Error logging in:', error); - alert('Error logging in. Please try again.'); - } - }; - - return ( -
- setEmail(e.target.value)} - required - /> - setPassword(e.target.value)} - required - /> - -
- ); -} \ No newline at end of file diff --git a/app/components/TaskBoard.tsx b/app/components/TaskBoard.tsx deleted file mode 100644 index 02a30195..00000000 --- a/app/components/TaskBoard.tsx +++ /dev/null @@ -1,486 +0,0 @@ -'use client'; - -import React, { useState, useEffect } from 'react'; -import { createClientComponentClient } from '@supabase/auth-helpers-nextjs' -import InviteUserModal from './InviteUserModal'; -import { useTheme } from '../contexts/ThemeContext'; - -interface Task { - id: string; - title: string; - description: string; - status: string; - position: number; - board_id: string; -} - -interface Column { - id: string; - name: string; - position: number; - board_id: string; -} - -interface Board { - id: string; - name: string; -} - -const TaskBoard: React.FC = () => { - const [board, setBoard] = useState(null); - const [boardName, setBoardName] = useState(''); - const [isEditingBoardName, setIsEditingBoardName] = useState(false); - const [tasks, setTasks] = useState([]); - const [columns, setColumns] = useState([]); - const [newTaskTitle, setNewTaskTitle] = useState(''); - const [newTaskDescription, setNewTaskDescription] = useState(''); - const [newColumnName, setNewColumnName] = useState(''); - const [error, setError] = useState(null); - const [editingTasks, setEditingTasks] = useState<{ [key: string]: Task }>({}); - const [saveMessage, setSaveMessage] = useState<{ [key: string]: string }>({}); - const [saveFeedback, setSaveFeedback] = useState<{ [key: string]: string }>({}); - const [openMenu, setOpenMenu] = useState(null); - const [isAddingColumn, setIsAddingColumn] = useState(false); - const [isInviteModalOpen, setIsInviteModalOpen] = useState(false); - const supabase = createClientComponentClient(); - const { theme } = useTheme(); - - useEffect(() => { - fetchOrCreateBoard(); - }, []); - - const fetchOrCreateBoard = async () => { - try { - let { data: boards, error } = await supabase - .from('boards') - .select('*') - .limit(1); - - if (error) throw error; - - if (boards && boards.length > 0) { - setBoard(boards[0]); - setBoardName(boards[0].name); - await fetchColumns(boards[0].id); - await fetchTasks(boards[0].id); - } else { - const { data, error } = await supabase - .from('boards') - .insert({ name: 'New Board' }) - .select() - .single(); - - if (error) throw error; - - setBoard(data); - setBoardName(data.name); - await fetchColumns(data.id); - } - } catch (error) { - console.error('Error fetching or creating board:', error); - setError('Failed to fetch or create board. Please try again.'); - } - }; - - const fetchColumns = async (boardId: string) => { - try { - const { data, error } = await supabase - .from('columns') - .select('*') - .eq('board_id', boardId) - .order('position'); - if (error) throw error; - console.log('Fetched columns:', data); // Debug log - setColumns(data || []); - } catch (error) { - console.error('Error fetching columns:', error); - setError('Failed to fetch columns. Please try again.'); - } - }; - - const fetchTasks = async (boardId: string) => { - try { - const { data, error } = await supabase - .from('tasks') - .select('*') - .eq('board_id', boardId) - .order('position'); - if (error) throw error; - setTasks(data || []); - } catch (error) { - console.error('Error fetching tasks:', error); - setError('Failed to fetch tasks. Please try again.'); - } - }; - - const addColumn = async (e: React.FormEvent) => { - e.preventDefault(); - if (!newColumnName.trim() || !board) return; - - const newColumn = { - name: newColumnName, - position: columns.length, - board_id: board.id, - }; - - try { - const { data, error } = await supabase - .from('columns') - .insert(newColumn) - .select() - .single(); - - if (error) throw error; - - setColumns([...columns, data]); - setNewColumnName(''); - setIsAddingColumn(false); // Hide the input field after adding - fetchTasks(board.id); - } catch (error) { - console.error('Error adding column:', error); - setError('Failed to add column. Please try again.'); - } - }; - - const addTask = async (columnName: string) => { - if (!board) return; - - const newTask = { - title: "New Task", - description: "", - status: columnName, - position: tasks.filter(t => t.status === columnName).length, - board_id: board.id, - }; - - const { data, error } = await supabase - .from('tasks') - .insert(newTask) - .select() - .single(); - - if (error) { - console.error('Error adding task:', error); - } else if (data) { - setTasks([...tasks, data]); - } - }; - - const updateTask = async (taskId: string) => { - const updatedTask = editingTasks[taskId]; - if (!updatedTask) return; - - try { - const { error } = await supabase - .from('tasks') - .update(updatedTask) - .eq('id', taskId); - - if (error) throw error; - - setTasks(tasks.map(t => t.id === taskId ? updatedTask : t)); - setEditingTasks(prev => { - const newState = { ...prev }; - delete newState[taskId]; - return newState; - }); - - // Set success message - setSaveMessage(prev => ({ ...prev, [taskId]: 'Saved successfully!' })); - setSaveFeedback(prev => ({ ...prev, [taskId]: 'Saved successfully!' })); - - // Clear success message after 3 seconds - setTimeout(() => { - setSaveMessage(prev => { - const newState = { ...prev }; - delete newState[taskId]; - return newState; - }); - }, 3000); - - } catch (error) { - console.error('Error updating task:', error); - setSaveMessage(prev => ({ ...prev, [taskId]: 'Error saving. Please try again.' })); - } - }; - - const deleteTask = async (id: string) => { - const { error } = await supabase - .from('tasks') - .delete() - .eq('id', id); - - if (error) { - console.error('Error deleting task:', error); - } else { - setTasks(tasks.filter(t => t.id !== id)); - } - }; - - const handleInputChange = (task: Task, field: keyof Task, value: string) => { - setEditingTasks(prev => ({ - ...prev, - [task.id]: { ...task, [field]: value } - })); - }; - - const deleteColumn = async (columnId: string) => { - if (!board) return; - - try { - const { error } = await supabase - .from('columns') - .delete() - .eq('id', columnId); - - if (error) throw error; - - setColumns(columns.filter(c => c.id !== columnId)); - // Move tasks from deleted column to the first available column - const firstColumn = columns.find(c => c.id !== columnId); - if (firstColumn) { - const updatedTasks = tasks.map(task => - task.status === columns.find(c => c.id === columnId)?.name - ? { ...task, status: firstColumn.name } - : task - ); - setTasks(updatedTasks); - // Update tasks in the database - await Promise.all(updatedTasks.map(task => - supabase.from('tasks').update({ status: task.status }).eq('id', task.id) - )); - } - } catch (error) { - console.error('Error deleting column:', error); - setError('Failed to delete column. Please try again.'); - } - }; - - const handleInviteUser = async (email: string) => { - if (!board) return; - - try { - const { data, error } = await supabase - .from('board_invitations') - .insert({ board_id: board.id, invited_email: email, status: 'pending' }) - .select() - .single(); - - if (error) throw error; - - // Here you would typically send an email to the invited user - // For now, we'll just log the invitation - console.log(`Invitation sent to ${email}`); - setIsInviteModalOpen(false); - } catch (error) { - console.error('Error inviting user:', error); - setError('Failed to invite user. Please try again.'); - } - }; - - const saveBoardName = async () => { - if (!board) return; - - try { - const { error } = await supabase - .from('boards') - .update({ name: boardName }) - .eq('id', board.id); - - if (error) throw error; - - setBoard({ ...board, name: boardName }); - setIsEditingBoardName(false); - } catch (error) { - console.error('Error saving board name:', error); - setError('Failed to save board name. Please try again.'); - } - }; - - return ( -
- {error &&

{error}

} - -
-
- {isEditingBoardName ? ( - setBoardName(e.target.value)} - onBlur={saveBoardName} - onKeyPress={(e) => e.key === 'Enter' && saveBoardName()} - className="text-2xl font-bold bg-transparent border-b border-gray-300 focus:outline-none focus:border-blue-500 mr-2" - autoFocus - /> - ) : ( - <> -

- {board?.name || 'Unnamed Board'} -

- - - )} -
- -
- -
- {columns.length === 0 ? ( -

No columns available. Add a column to get started.

- ) : ( - columns.map((column, index) => ( -
-
-
-

{column.name}

-
- - {openMenu === column.id && ( -
- -
- )} -
-
-
- {tasks - .filter((task) => task.status === column.name) - .map((task) => ( -
- handleInputChange(task, 'title', e.target.value)} - className={`w-full mb-2 p-1 border-b border-transparent hover:border-gray-300 focus:border-blue-500 font-semibold focus:outline-none ${theme === 'dark' ? 'bg-gray-700 text-gray-200' : ''}`} - /> - +
+ ); +}; + +export default CodeEditor; \ No newline at end of file diff --git a/app/favicon.ico b/app/favicon.ico index 718d6fea..01137982 100644 Binary files a/app/favicon.ico and b/app/favicon.ico differ diff --git a/app/forgot-password/page.tsx b/app/forgot-password/page.tsx index f90f7e24..a666344f 100644 --- a/app/forgot-password/page.tsx +++ b/app/forgot-password/page.tsx @@ -1,94 +1,72 @@ -import Link from "next/link"; -import { createClient } from "@/utils/supabase/server"; -import { redirect } from "next/navigation"; -import { SubmitButton } from "../../components/forms/submit-button"; -import { Label } from "@/components/forms/label"; -import { Input } from "@/components/forms/input"; -import { FormMessage, Message } from "@/components/forms/form-message"; -import { headers } from "next/headers"; -import { encodedRedirect } from "@/utils/utils"; +'use client'; -export default function ForgotPassword({ - searchParams, -}: { - searchParams: Message; -}) { - const forgotPassword = async (formData: FormData) => { - "use server"; +import { useState } from 'react' +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" +import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card" +import { AlertCircle, ArrowLeft } from "lucide-react" +import Link from "next/link" - const email = formData.get("email")?.toString(); - const supabase = createClient(); - const origin = headers().get("origin"); - const callbackUrl = formData.get("callbackUrl")?.toString(); +export default function ForgotPassword() { + const [email, setEmail] = useState('') + const [isSubmitting, setIsSubmitting] = useState(false) + const [isSubmitted, setIsSubmitted] = useState(false) - if (!email) { - return encodedRedirect("error", "/forgot-password", "Email is required"); - } - - const { error } = await supabase.auth.resetPasswordForEmail(email, { - redirectTo: `${origin}/auth/callback?redirect_to=/protected/reset-password`, - }); - - if (error) { - console.error(error.message); - return encodedRedirect( - "error", - "/forgot-password", - "Could not reset password", - ); - } - - if (callbackUrl) { - return redirect(callbackUrl); - } - - return encodedRedirect( - "success", - "/forgot-password", - "Check your email for a link to reset your password.", - ); - }; + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault() + setIsSubmitting(true) + // TODO: Implement actual password reset logic here + await new Promise(resolve => setTimeout(resolve, 1500)) // Simulating API call + setIsSubmitting(false) + setIsSubmitted(true) + } return ( -
- - - - {" "} - Back - - -
-

Reset Password

-

- Already have an account?{" "} - - Log in +

+ + + Forgot Password + Enter your email to reset your password + + + {!isSubmitted ? ( + +
+
+ + setEmail(e.target.value)} + required + disabled={isSubmitting} + /> +
+ +
+ + ) : ( +
+ +

Check your email

+

+ We've sent a password reset link to {email} +

+
+ )} +
+ + + + Back to login -

-
- - - - Reset Password - - -
- +
+
- ); + ) } diff --git a/app/globals.css b/app/globals.css index 7216135d..91ec90d2 100644 --- a/app/globals.css +++ b/app/globals.css @@ -8,16 +8,18 @@ /* Your existing global styles */ :root { - --primary-color: #0079BF; - --secondary-color: #1d338b; - --text-color: #172b4d; - --background-color: #485d86; + --primary-color: #000000; + --secondary-color: #6B7280; + --background-color: #F3F4F6; + --text-color: #111827; } body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; color: var(--text-color); background-color: var(--background-color); - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Noto Sans', 'Ubuntu', 'Droid Sans', 'Helvetica Neue', sans-serif; + margin: 0; + padding: 0; } .page-container { @@ -29,21 +31,41 @@ body { justify-content: center; } +.card { + background-color: white; + border-radius: 12px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + padding: 2rem; + width: 100%; + max-width: 400px; +} + .page-title { + color: var(--text-color); font-size: 2rem; font-weight: 700; - color: var(--primary-color); - margin-bottom: 2rem; - text-align: center; + margin-bottom: 0.5rem; } -.card { - background-color: white; - border-radius: 8px; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - padding: 2rem; +.page-subtitle { + color: var(--secondary-color); + font-size: 1rem; + margin-bottom: 1.5rem; +} + +.input-label { + display: block; + font-weight: 600; + margin-bottom: 0.5rem; +} + +.input { width: 100%; - max-width: 400px; + padding: 0.75rem; + margin-bottom: 1rem; + border: 1px solid #D1D5DB; + border-radius: 8px; + font-size: 1rem; } .btn { @@ -52,80 +74,126 @@ body { background-color: var(--primary-color); color: white; border: none; - border-radius: 4px; + border-radius: 8px; font-size: 1rem; font-weight: 600; cursor: pointer; transition: background-color 0.3s ease; + margin-bottom: 0.5rem; } .btn:hover { - background-color: #026AA7; + background-color: #1F2937; } -.input { - width: 100%; - padding: 0.75rem; +.text-link { + color: var(--primary-color); + text-decoration: none; + font-weight: 600; +} + +.text-link:hover { + text-decoration: underline; +} + +.tab-container { + display: flex; margin-bottom: 1rem; - border: 1px solid #dfe1e6; - border-radius: 4px; - font-size: 1rem; + border: 1px solid #D1D5DB; + border-radius: 8px; + overflow: hidden; } -.text-primary { - color: var(--primary-color); +.tab { + flex: 1; + text-align: center; + padding: 0.75rem; +} + +.tab:first-child { + border-top-left-radius: 8px; + border-bottom-left-radius: 8px; } -/* Add this new class */ -.draggable-card { - cursor: move; - user-select: none; +.tab:last-child { + border-top-right-radius: 8px; + border-bottom-right-radius: 8px; } -/* Dark mode styles */ -.dark { - --primary-color: #4C9AFF; - --secondary-color: #2C5282; - --text-color: #E2E8F0; - --background-color: #1A202C; +.tab.active { + background-color: white; + border-bottom: none; +} + +/* Add this to the end of your globals.css file */ + +.atom-spinner { + position: relative; + width: var(--size); + height: var(--size); + overflow: hidden; } -.dark body { - @apply bg-gray-900 text-gray-200; +.spinner-inner { + position: relative; + display: block; + height: 100%; + width: 100%; } -.dark input, -.dark textarea, -.dark select { - @apply bg-gray-700 text-gray-200 border-gray-600; +.spinner-circle { + display: block; + position: absolute; + color: var(--color); + font-size: calc(var(--size) * 0.24); + top: 50%; + left: 50%; + transform: translate(-50%, -50%); } -.dark input:focus, -.dark textarea:focus, -.dark select:focus { - @apply border-blue-500; +.spinner-line { + position: absolute; + width: 100%; + height: 100%; + border-radius: 50%; + animation-duration: 1s; + border-left-width: calc(var(--size) / 25); + border-top-width: calc(var(--size) / 25); + border-left-color: var(--color); + border-left-style: solid; + border-top-style: solid; + border-top-color: transparent; } -.dark button { - @apply bg-blue-600 hover:bg-blue-700 text-white; +.spinner-line:nth-child(1) { + animation: atom-spinner-animation-1 1s linear infinite; + transform: rotateZ(120deg) rotateX(66deg) rotateZ(0deg); } -.dark button:disabled { - @apply bg-gray-600 text-gray-400; +.spinner-line:nth-child(2) { + animation: atom-spinner-animation-2 1s linear infinite; + transform: rotateZ(240deg) rotateX(66deg) rotateZ(0deg); } -.dark a { - @apply text-blue-400 hover:text-blue-300; +.spinner-line:nth-child(3) { + animation: atom-spinner-animation-3 1s linear infinite; + transform: rotateZ(360deg) rotateX(66deg) rotateZ(0deg); } -.dark .card { - @apply bg-gray-800 shadow-lg; +@keyframes atom-spinner-animation-1 { + 100% { + transform: rotateZ(120deg) rotateX(66deg) rotateZ(360deg); + } } -.dark .btn { - @apply bg-blue-600 hover:bg-blue-700; +@keyframes atom-spinner-animation-2 { + 100% { + transform: rotateZ(240deg) rotateX(66deg) rotateZ(360deg); + } } -.dark .text-primary { - @apply text-blue-400; +@keyframes atom-spinner-animation-3 { + 100% { + transform: rotateZ(360deg) rotateX(66deg) rotateZ(360deg); + } } diff --git a/app/header.tsx b/app/header.tsx new file mode 100644 index 00000000..61807abc --- /dev/null +++ b/app/header.tsx @@ -0,0 +1,85 @@ +'use client'; + +import React from 'react'; +import Link from 'next/link'; +import { useTheme } from './contexts/ThemeContext'; +import { useRouter, usePathname } from 'next/navigation'; +import { Button } from "@/components/ui/button"; +import { Settings } from 'lucide-react'; + +const isUserLoggedIn = () => { + return true; // Change this to false to test logged-out state +}; + +const signOut = async () => { + console.log('User signed out'); +}; + +export function Header() { + const { theme, toggleTheme } = useTheme(); + const router = useRouter(); + const pathname = usePathname(); + + const handleSignOut = async () => { + await signOut(); + router.push('/login'); + }; + + const isAuthPage = pathname === '/login' || pathname === '/signup'; + + return ( +
+
+ + QuantumLabs Logo + + {!isAuthPage && ( + + )} +
+
+ ); +} \ No newline at end of file diff --git a/app/home/page.tsx b/app/home/page.tsx new file mode 100644 index 00000000..f44eb6b0 --- /dev/null +++ b/app/home/page.tsx @@ -0,0 +1,26 @@ +'use client'; + +import React from 'react'; +import Link from 'next/link'; +import SharedLayout from '../../components/SharedLayout'; + +const HomePage: React.FC = () => { + return ( + +

Welcome to Our App

+ +
+ ); +}; + +export default HomePage; \ No newline at end of file diff --git a/app/landing/page.tsx b/app/landing/page.tsx new file mode 100644 index 00000000..31cfba25 --- /dev/null +++ b/app/landing/page.tsx @@ -0,0 +1,227 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import Link from 'next/link'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Brain, Shield, Coins } from 'lucide-react'; + +export default function LandingPage() { + const [scrollY, setScrollY] = useState(0); + + useEffect(() => { + const handleScroll = () => setScrollY(window.scrollY); + window.addEventListener('scroll', handleScroll); + return () => window.removeEventListener('scroll', handleScroll); + }, []); + + return ( +
+
+
50 ? '0 2px 4px rgba(0,0,0,.1)' : 'none' + }} + > +
+ + + + + + + + + + + + BlockFlow + +
+ +
+
+
+
+
+
+

+ Revolutionize Your Workflow with BlockFlow +

+

+ Harness the power of AI, blockchain, and DeFi for unparalleled project management and productivity. +

+
+
+
+ + +
+

+ Start your free 14-day trial. No credit card required. +

+
+
+
+
+
+
+

Key Features

+
+
+ +

AI-Powered Assistance

+

Intelligent task prioritization and resource allocation.

+
+
+ +

Blockchain Security

+

Immutable task records and enhanced data integrity.

+
+
+ +

DeFi Integration

+

Tokenized rewards and decentralized project funding.

+
+
+
+
+
+
+

About BlockFlow

+
+
+

Our Mission

+

+ At BlockFlow, we're on a mission to transform project management through cutting-edge technology. + By combining AI, blockchain, and DeFi, we're creating a platform that not only enhances productivity + but also ensures security, transparency, and fair rewards for all team members. +

+
+
+

Why Choose BlockFlow?

+
    +
  • + + AI-driven insights for smarter decision making +
  • +
  • + + Unparalleled security with blockchain technology +
  • +
  • + + Innovative reward system with DeFi integration +
  • +
+
+
+
+
+
+
+

Pricing Plans

+
+
+

Starter

+

$19/month

+
    +
  • + + Basic AI task prioritization +
  • +
  • + + Blockchain-secured task records +
  • +
+ +
+
+

Pro

+

$49/month

+
    +
  • + + Advanced AI agents +
  • +
  • + + Smart contract integration +
  • +
  • + + Basic DeFi features +
  • +
+ +
+
+

Enterprise

+

Custom

+
    +
  • + + Custom AI model training +
  • +
  • + + Advanced blockchain features +
  • +
  • + + Full DeFi suite with custom token +
  • +
+ +
+
+
+
+
+
+
+

Β© 2024 BlockFlow. All rights reserved.

+ +
+
+ +
+ ); +} \ No newline at end of file diff --git a/app/layout.tsx b/app/layout.tsx index fd00dd12..24b9b585 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,18 +1,70 @@ -import './globals.css' -import { ThemeProvider } from './contexts/ThemeContext' +'use client'; + +import React from 'react'; +import { ThemeProvider } from 'next-themes'; +import '@/styles/globals.css'; +import Layout from '@/components/Layout'; +import { Web3ReactProvider } from '@web3-react/core'; +import { Web3Provider } from '@ethersproject/providers'; +import { Toaster } from 'react-hot-toast'; +import { ClerkProvider } from '@clerk/nextjs' +import { dark } from '@clerk/themes'; export default function RootLayout({ - children, + children, }: { - children: React.ReactNode + children: React.ReactNode }) { - return ( - - - - {children} - - - - ) + return ( + + + + + new Web3Provider(provider)}> + + {children} + + + + + + + + ) } \ No newline at end of file diff --git a/app/layouts/AuthenticatedLayout.module.css b/app/layouts/AuthenticatedLayout.module.css deleted file mode 100644 index 226578d9..00000000 --- a/app/layouts/AuthenticatedLayout.module.css +++ /dev/null @@ -1,10 +0,0 @@ -.layout { - min-height: 100vh; - display: flex; - flex-direction: column; -} - -.main { - flex-grow: 1; - padding: 2rem; -} diff --git a/app/layouts/AuthenticatedLayout.tsx b/app/layouts/AuthenticatedLayout.tsx deleted file mode 100644 index 4d2eee11..00000000 --- a/app/layouts/AuthenticatedLayout.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import React from 'react'; -import Header from '@/components/Header'; -import styles from './AuthenticatedLayout.module.css'; - -interface AuthenticatedLayoutProps { - children: React.ReactNode; -} - -const AuthenticatedLayout: React.FC = ({ children }) => { - return ( -
-
-
{children}
-
- ); -}; - -export default AuthenticatedLayout; diff --git a/app/layouts/CustomLayout.tsx b/app/layouts/CustomLayout.tsx new file mode 100644 index 00000000..bc97adda --- /dev/null +++ b/app/layouts/CustomLayout.tsx @@ -0,0 +1,21 @@ +import React, { ReactNode } from 'react'; +import { ThemeProvider } from '@/app/contexts/ThemeContext'; +import RobotTransformerWallpaper from '@/components/RobotTransformerWallpaper'; // Ensure this path is correct + +interface CustomLayoutProps { + children: ReactNode; +} + +const CustomLayout: React.FC = ({ children }) => { + return ( + + {/* Add the wallpaper component */} +
+ {/* Custom layout structure */} + {children} +
+
+ ); +}; + +export default CustomLayout; \ No newline at end of file diff --git a/app/loading.tsx b/app/loading.tsx new file mode 100644 index 00000000..b83ed902 --- /dev/null +++ b/app/loading.tsx @@ -0,0 +1,10 @@ +import Spinner from '@/components/ui/spinner'; +import React from 'react'; + +export default function Loading() { + return ( +
+ +
+ ); +} \ No newline at end of file diff --git a/app/login/[[...login]]/page.tsx b/app/login/[[...login]]/page.tsx new file mode 100644 index 00000000..d99c618d --- /dev/null +++ b/app/login/[[...login]]/page.tsx @@ -0,0 +1,9 @@ +import { SignIn } from "@clerk/nextjs"; + +export default function LoginPage() { + return ( +
+ +
+ ); +} \ No newline at end of file diff --git a/app/login/login.module.css b/app/login/login.module.css deleted file mode 100644 index 4f9fd773..00000000 --- a/app/login/login.module.css +++ /dev/null @@ -1,114 +0,0 @@ -.container { - display: flex; - justify-content: center; - align-items: center; - min-height: 100vh; - background-color: #1a202c; -} - -.formCard { - background-color: #2d3748; - padding: 2rem; - border-radius: 8px; - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); - width: 100%; - max-width: 400px; -} - -.title { - font-size: 24px; - font-weight: bold; - color: #63b3ed; - margin-bottom: 0.5rem; - text-align: center; -} - -.subtitle { - font-size: 14px; - color: #a0aec0; - margin-bottom: 1.5rem; - text-align: center; -} - -.form { - display: flex; - flex-direction: column; -} - -.inputGroup { - margin-bottom: 1rem; -} - -.label { - display: block; - font-size: 14px; - margin-bottom: 0.5rem; - color: #e2e8f0; -} - -.input { - width: 100%; - padding: 0.75rem; - border: 1px solid #4a5568; - border-radius: 6px; - font-size: 16px; - background-color: #2d3748; - color: #e2e8f0; -} - -.input:focus { - outline: none; - border-color: #63b3ed; - box-shadow: 0 0 0 2px rgba(99, 179, 237, 0.2); -} - -.options { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 1rem; -} - -.rememberMe { - display: flex; - align-items: center; -} - -.checkbox { - margin-right: 0.5rem; - background-color: #2d3748; - border: 1px solid #4a5568; -} - -.checkboxLabel { - font-size: 14px; - color: #e2e8f0; -} - -.link { - color: #63b3ed; - text-decoration: none; - font-size: 14px; -} - -.link:hover { - text-decoration: underline; -} - -.button { - background-color: #4299e1; - color: #e2e8f0; - padding: 0.75rem; - border: none; - border-radius: 6px; - font-size: 16px; - font-weight: bold; - cursor: pointer; - transition: background-color 0.3s ease; -} - -.button:hover { - background-color: #3182ce; -} - -/* Add more styles as needed */ diff --git a/app/login/page.tsx b/app/login/page.tsx deleted file mode 100644 index eacd2d54..00000000 --- a/app/login/page.tsx +++ /dev/null @@ -1,151 +0,0 @@ -'use client'; - -import { useState } from 'react'; -import { useRouter } from 'next/navigation'; -import { createClientComponentClient } from '@supabase/auth-helpers-nextjs'; -import Link from 'next/link'; -import { useTheme } from '../contexts/ThemeContext'; -import Image from 'next/image'; -import toast from 'react-hot-toast'; - -export default function LoginPage() { - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const [error, setError] = useState(null); - const router = useRouter(); - const supabase = createClientComponentClient(); - const { theme } = useTheme(); - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - setError(null); - try { - const { error } = await supabase.auth.signInWithPassword({ email, password }); - if (error) throw error; - toast.success('Logged in successfully'); - router.push('/taskboard'); - } catch (error: any) { - setError(error.message); - toast.error(error.message); - } - }; - - const handleGoogleSignIn = async () => { - try { - const { error } = await supabase.auth.signInWithOAuth({ - provider: 'google', - options: { - redirectTo: `${window.location.origin}/auth/callback`, - }, - }); - if (error) throw error; - } catch (error: any) { - toast.error(error.message); - } - }; - - return ( -
-
-
-

- Sign in to your account -

-
-
- -
-
- - setEmail(e.target.value)} - /> -
-
- - setPassword(e.target.value)} - /> -
-
- - {error &&
{error}
} - -
- -
-
-
-
-
-
-
-
- - Or continue with - -
-
- -
- -
-
- -
-

- Don't have an account?{' '} - - Sign up - -

-
-
-
- ); -} \ No newline at end of file diff --git a/app/members/page.tsx b/app/members/page.tsx index a91f1569..d698d8ae 100644 --- a/app/members/page.tsx +++ b/app/members/page.tsx @@ -1,92 +1,171 @@ 'use client'; -import { useState, useEffect } from 'react'; -import { createClientComponentClient } from "@supabase/auth-helpers-nextjs"; -import AuthenticatedLayout from '../components/AuthenticatedLayout'; +import { useState } from 'react'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; +import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"; +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; +import { Badge } from "@/components/ui/badge"; +import { Plus, Search, Mail, MoreHorizontal } from 'lucide-react'; -const MembersPage = () => { - const [members, setMembers] = useState([]); - const [newMemberEmail, setNewMemberEmail] = useState(''); - const supabase = createClientComponentClient(); +const initialMembers = [ + { id: 1, name: 'John Doe', email: 'john@example.com', role: 'Developer', team: 'Development', avatar: 'JD' }, + { id: 2, name: 'Jane Smith', email: 'jane@example.com', role: 'Designer', team: 'Design', avatar: 'JS' }, + { id: 3, name: 'Bob Johnson', email: 'bob@example.com', role: 'Product Manager', team: 'Product', avatar: 'BJ' }, + { id: 4, name: 'Alice Brown', email: 'alice@example.com', role: 'Marketing Specialist', team: 'Marketing', avatar: 'AB' }, + { id: 5, name: 'Charlie Wilson', email: 'charlie@example.com', role: 'Developer', team: 'Development', avatar: 'CW' }, +]; - useEffect(() => { - fetchMembers(); - }, []); +export default function MembersPage() { + const [members, setMembers] = useState(initialMembers); + const [searchQuery, setSearchQuery] = useState(''); + const [newMember, setNewMember] = useState({ name: '', email: '', role: '', team: '' }); - const fetchMembers = async () => { - const { data, error } = await supabase.from('users').select('*'); - if (error) { - console.error('Error fetching members:', error); - } else { - setMembers(data || []); - } - }; - - const inviteMember = async (e: React.FormEvent) => { - e.preventDefault(); - if (!newMemberEmail.trim()) return; - - // Here you would typically send an invitation email - // For now, we'll just add the email to the members list - const { data, error } = await supabase - .from('users') - .insert({ email: newMemberEmail }) - .select() - .single(); - - if (error) { - console.error('Error inviting member:', error); - } else { - setMembers([...members, data]); - setNewMemberEmail(''); - } - }; - - const removeMember = async (memberId: string) => { - const { error } = await supabase - .from('users') - .delete() - .eq('id', memberId); + const filteredMembers = members.filter(member => + member.name.toLowerCase().includes(searchQuery.toLowerCase()) || + member.email.toLowerCase().includes(searchQuery.toLowerCase()) || + member.role.toLowerCase().includes(searchQuery.toLowerCase()) || + member.team.toLowerCase().includes(searchQuery.toLowerCase()) + ); - if (error) { - console.error('Error removing member:', error); - } else { - setMembers(members.filter(member => member.id !== memberId)); + const addMember = () => { + if (newMember.name && newMember.email && newMember.role && newMember.team) { + setMembers([...members, { ...newMember, id: members.length + 1, avatar: newMember.name.split(' ').map(n => n[0]).join('') }]); + setNewMember({ name: '', email: '', role: '', team: '' }); } }; return ( - -
-

Team Members

-
- setNewMemberEmail(e.target.value)} - placeholder="New member email" - className="mr-2 p-2 border rounded" - /> - -
-
    - {members.map((member) => ( -
  • - {member.email} {member.full_name ? `(${member.full_name})` : ''} - -
  • - ))} -
-
-
+
+ + +
+
+ Members + Manage all members across teams +
+
+
+ + setSearchQuery(e.target.value)} + className="pl-8 w-64" + /> +
+ + + + + + + Add New Member + Add a new member to your organization. + +
+
+ + setNewMember({ ...newMember, name: e.target.value })} + className="col-span-3" + /> +
+
+ + setNewMember({ ...newMember, email: e.target.value })} + className="col-span-3" + /> +
+
+ + setNewMember({ ...newMember, role: e.target.value })} + className="col-span-3" + /> +
+
+ + +
+
+ + + +
+
+
+
+
+ + + + + Name + Email + Role + Team + Actions + + + + {filteredMembers.map((member) => ( + + +
+ + + {member.avatar} + + {member.name} +
+
+ {member.email} + {member.role} + + {member.team} + + +
+ + +
+
+
+ ))} +
+
+
+
+
); -}; - -export default MembersPage; \ No newline at end of file +} \ No newline at end of file diff --git a/app/metadata.ts b/app/metadata.ts new file mode 100644 index 00000000..425ce37c --- /dev/null +++ b/app/metadata.ts @@ -0,0 +1,6 @@ +import { Metadata } from 'next' + +export const metadata: Metadata = { + title: 'QuantumLabs', + description: 'Manage your tasks and projects efficiently with QuantumLabs', +} \ No newline at end of file diff --git a/app/next.config.js b/app/next.config.js new file mode 100644 index 00000000..74137e85 --- /dev/null +++ b/app/next.config.js @@ -0,0 +1,4 @@ +module.exports = { + // Other Next.js config options + metadataBase: 'https://yourdomain.com', // Replace with your actual domain +}; \ No newline at end of file diff --git a/app/not-found/page.tsx b/app/not-found/page.tsx new file mode 100644 index 00000000..c4e1f33e --- /dev/null +++ b/app/not-found/page.tsx @@ -0,0 +1,14 @@ +'use client'; + +import React from 'react'; + +const NotFoundPage = () => { + return ( +
+

404 - Page Not Found

+

Sorry, the page you are looking for does not exist.

+
+ ); +}; + +export default NotFoundPage; \ No newline at end of file diff --git a/app/notifications/page.tsx b/app/notifications/page.tsx new file mode 100644 index 00000000..82cadfed --- /dev/null +++ b/app/notifications/page.tsx @@ -0,0 +1,97 @@ +'use client'; + +import { useState } from 'react'; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Avatar, AvatarFallback } from "@/components/ui/avatar"; +import { Badge } from "@/components/ui/badge"; +import { ScrollArea } from "@/components/ui/scroll-area"; +import { Bell, MessageSquare, CheckCircle } from 'lucide-react'; + +interface Notification { + id: string; + type: 'assignment' | 'comment' | 'update'; + content: string; + project: string; + timestamp: string; + read: boolean; +} + +export default function NotificationsPage() { + const [notifications, setNotifications] = useState([ + { + id: '1', + type: 'assignment', + content: 'You have been assigned to the task "Design new logo"', + project: 'Branding Refresh', + timestamp: '2023-07-15T10:30:00Z', + read: false, + }, + { + id: '2', + type: 'comment', + content: 'Alice commented on your task "Implement login functionality"', + project: 'User Authentication', + timestamp: '2023-07-14T15:45:00Z', + read: true, + }, + { + id: '3', + type: 'update', + content: 'The project "Backend Development" has been marked as complete', + project: 'Backend Development', + timestamp: '2023-07-13T09:15:00Z', + read: false, + }, + ]); + + const markAsRead = (id: string) => { + setNotifications(notifications.map(notification => + notification.id === id ? { ...notification, read: true } : notification + )); + }; + + const getIcon = (type: Notification['type']) => { + switch (type) { + case 'assignment': + return ; + case 'comment': + return ; + case 'update': + return ; + } + }; + + return ( +
+

Notifications

+ + + Recent Notifications + + + + {notifications.map((notification) => ( +
markAsRead(notification.id)} + > + + {getIcon(notification.type)} + +
+

{notification.content}

+

Project: {notification.project}

+

{new Date(notification.timestamp).toLocaleString()}

+
+ {!notification.read && ( + New + )} +
+ ))} +
+
+
+
+ ); +} \ No newline at end of file diff --git a/app/page.tsx b/app/page.tsx index f01d8fa6..9fd9be95 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,7 +1,103 @@ 'use client'; -import { redirect } from 'next/navigation'; +import React from 'react'; +import Link from 'next/link'; +import { useUser } from '@clerk/nextjs'; +import { ArrowRight, Brain, Cog, Users, Zap, BarChart, Shield } from 'lucide-react'; -export default function Home() { - redirect('/login'); -} \ No newline at end of file +const WelcomePage: React.FC = () => { + const { isSignedIn, user } = useUser(); + + return ( +
+
+
+

Welcome to Quantum Labs

+

Empowering your projects with AI-driven solutions

+
+ + {isSignedIn ? ( +
+

Hello, {user?.firstName}! Ready to dive in?

+ + Go to Dashboard + + +
+ ) : ( +
+
+ + Sign In + + + Sign Up + +
+
+ )} + +
+

Our Features

+
+ } + title="AI Agents" + description="Leverage our intelligent AI agents for various tasks, from data analysis to content creation." + /> + } + title="Task Management" + description="Efficiently manage and track your projects with our intuitive task management system." + /> + } + title="Collaboration" + description="Work seamlessly with your team members in real-time, enhancing productivity and communication." + /> + } + title="Automation" + description="Automate repetitive tasks and workflows to save time and reduce errors." + /> + } + title="Analytics" + description="Gain valuable insights with our advanced analytics and reporting tools." + /> + } + title="Security" + description="Rest easy knowing your data is protected with our state-of-the-art security measures." + /> +
+
+ +
+

© 2023 Quantum Labs. All rights reserved.

+
+
+
+ ); +}; + +const FeatureCard: React.FC<{ icon: React.ReactNode; title: string; description: string }> = ({ icon, title, description }) => ( +
+
+ {icon} +

{title}

+
+

{description}

+
+); + +export default WelcomePage; \ No newline at end of file diff --git a/app/portfolio/page.tsx b/app/portfolio/page.tsx new file mode 100644 index 00000000..94dc07d2 --- /dev/null +++ b/app/portfolio/page.tsx @@ -0,0 +1,135 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import { useUser } from '@clerk/nextjs'; +import { Button } from '@/components/ui/button'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { useRouter } from 'next/navigation'; +import logger from '@/lib/logger'; + +interface ChainData { + id: string; + name: string; + logo_url: string; + usd_value: number; +} + +interface PortfolioData { + total_usd_value: number; + chain_list: ChainData[]; +} + +export default function PortfolioPage() { + const { isLoaded: isUserLoaded, isSignedIn, user } = useUser(); + const [portfolioData, setPortfolioData] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const router = useRouter(); + + const walletAddress = user?.web3Wallets?.[0]?.web3Wallet; + + useEffect(() => { + if (isSignedIn && walletAddress) { + logger.info(`Fetching balance for wallet: ${walletAddress}`); + const fetchPortfolioData = async () => { + try { + const response = await fetch(`/api/debank?id=${walletAddress}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }); + + logger.info(`Response Status: ${response.status}`); + + if (response.status === 404) { + const errorText = await response.text(); + logger.error(`404 Error: ${errorText}`); + throw new Error('API endpoint not found'); + } + + if (!response.ok) { + const errorData = await response.json(); + logger.error(`Error Response Data: ${JSON.stringify(errorData)}`); + throw new Error(`Network response was not ok: ${errorData.error}`); + } + + const data: PortfolioData = await response.json(); + logger.info(`Response Data: ${JSON.stringify(data)}`); + setPortfolioData(data); + } catch (error) { + logger.error(`Error fetching portfolio data: ${(error as Error).message}`); + setError((error as Error).message); + } finally { + setLoading(false); + } + }; + + fetchPortfolioData(); + } else { + logger.info("User is not signed in or wallet address is not available."); + setLoading(false); + } + }, [isSignedIn, user, walletAddress]); + + if (!isUserLoaded) { + return
Loading...
; + } + + if (!isSignedIn) { + return ( +
+

Please sign in to view your portfolio

+ +
+ ); + } + + if (loading) { + return
Loading portfolio data...
; + } + + if (error) { + return
Error: {error}
; + } + + return ( +
+

Your Portfolio

+
+ {'User'} +

{user.username}

+
+ {portfolioData ? ( +
+ + + Portfolio Overview + + +

Total Balance: ${portfolioData.total_usd_value.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}

+
+
+

Chain Breakdown

+
+ {portfolioData.chain_list.map((chain) => ( + + + + {chain.name} + {chain.name} + + + +

${chain.usd_value.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}

+
+
+ ))} +
+
+ ) : ( +

No portfolio data available.

+ )} +
+ ); +} \ No newline at end of file diff --git a/app/profile/page.tsx b/app/profile/page.tsx index 9342f63c..7fecfd9a 100644 --- a/app/profile/page.tsx +++ b/app/profile/page.tsx @@ -1,88 +1,29 @@ 'use client'; -import { useState, useEffect } from 'react'; -import { createClientComponentClient } from "@supabase/auth-helpers-nextjs"; -import AuthenticatedLayout from '../components/AuthenticatedLayout'; -import Link from 'next/link'; +import React, { useState, useEffect } from 'react'; +import CenteredAtomSpinner from "@/components/CenteredAtomSpinner"; // Import the spinner -const ProfilePage = () => { - const [user, setUser] = useState(null); - const [fullName, setFullName] = useState(''); - const [initials, setInitials] = useState(''); - const supabase = createClientComponentClient(); +const Profile: React.FC = () => { + const [loading, setLoading] = useState(true); useEffect(() => { - const fetchUser = async () => { - const { data: { user } } = await supabase.auth.getUser(); - if (user) { - const { data } = await supabase - .from('users') - .select('*') - .eq('id', user.id) - .single(); - setUser({ ...user, ...data }); - setFullName(data?.full_name || ''); - setInitials(data?.initials || ''); - } - }; - fetchUser(); - }, []); + const timer = setTimeout(() => { + setLoading(false); + }, 2000); // Simulate loading for 2 seconds - const updateProfile = async () => { - const { error } = await supabase - .from('users') - .update({ full_name: fullName, initials }) - .eq('id', user.id); + return () => clearTimeout(timer); + }, []); - if (error) { - console.error('Error updating profile:', error); - } else { - alert('Profile updated successfully!'); - } - }; + if (loading) { + return ; + } return ( - -
-
-

Profile

- - Back to Dashboard - -
- {user && ( -
-

Email: {user.email}

-
- - setFullName(e.target.value)} - className="w-full p-2 border rounded" - /> -
-
- - setInitials(e.target.value)} - className="w-full p-2 border rounded" - maxLength={2} - /> -
- -
- )} -
-
+
+

Profile

+ {/* Profile content goes here */} +
); }; -export default ProfilePage; \ No newline at end of file +export default Profile; \ No newline at end of file diff --git a/app/progress/page.tsx b/app/progress/page.tsx new file mode 100644 index 00000000..d5057435 --- /dev/null +++ b/app/progress/page.tsx @@ -0,0 +1,104 @@ +'use client' + +import { useState } from 'react' +import { Progress } from "@/components/ui/progress" +import { ScrollArea } from "@/components/ui/scroll-area" +import { Badge } from "@/components/ui/badge" +import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts' +import Link from 'next/link' +import { Button } from "@/components/ui/button" +import { CheckCircle, Users, Folder, List } from 'lucide-react' +import { Card } from '@/components/ui/card' +import { CardContent } from '@/components/ui/card-content' +import { CardHeader } from '@/components/ui/card-header' +import { CardTitle } from '@/components/ui/card-title' + +interface Project { + id: string + name: string + progress: number + tasksCompleted: number + totalTasks: number +} + +export default function ProgressPage() { + const [projects] = useState([ + { id: '1', name: 'Project Alpha', progress: 65, tasksCompleted: 13, totalTasks: 20 }, + { id: '2', name: 'Project Beta', progress: 30, tasksCompleted: 6, totalTasks: 20 }, + { id: '3', name: 'Project Gamma', progress: 90, tasksCompleted: 18, totalTasks: 20 }, + { id: '4', name: 'Project Delta', progress: 45, tasksCompleted: 9, totalTasks: 20 }, + { id: '5', name: 'Project Epsilon', progress: 10, tasksCompleted: 2, totalTasks: 20 }, + ]) + + const chartData = projects.map(project => ({ + name: project.name, + progress: project.progress, + })) + + return ( +
+
+
+
+ QuantumLabs +
+ Log in + +
+
+
+
+ +
+
+
+

Track Your Progress

+

Monitor your tasks and projects in real-time.

+ +
+
+ +
+
+

Key Features

+
+ {[ + { icon: , title: "Team Collaboration", description: "Work seamlessly with your team members, assign tasks, and track progress together." }, + { icon: , title: "Project Management", description: "Organize your work into projects, set milestones, and manage resources effectively." }, + { icon: , title: "Task Tracking", description: "Create, assign, and monitor tasks with ease. Set priorities and deadlines to stay on top of your work." }, + ].map((feature, index) => ( + + ))} +
+
+
+
+ +
+
+

© {new Date().getFullYear()} QuantumLabs. All rights reserved.

+
+
+
+ ) +} + +function FeatureCard({ icon, title, description }: { icon: React.ReactNode, title: string, description: string }) { + return ( + + + +
{icon}
+ {title} +
+
+ +

{description}

+
+
+ ) +} \ No newline at end of file diff --git a/app/project-board/page.tsx b/app/project-board/page.tsx new file mode 100644 index 00000000..78cf7529 --- /dev/null +++ b/app/project-board/page.tsx @@ -0,0 +1,14 @@ +'use client'; + +import React from 'react'; + +const ProjectBoardPage: React.FC = () => { + return ( +
+

Project Board

+ {/* Your project board content */} +
+ ); +}; + +export default ProjectBoardPage; \ No newline at end of file diff --git a/app/projects/[ProjectId]/board/page.tsx b/app/projects/[ProjectId]/board/page.tsx new file mode 100644 index 00000000..ce40b408 --- /dev/null +++ b/app/projects/[ProjectId]/board/page.tsx @@ -0,0 +1,112 @@ +'use client' + +import { useParams } from 'next/navigation' +import { useState, useEffect } from 'react' +import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd' +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" +import { Badge } from "@/components/ui/badge" + +interface Task { + id: string + content: string + status: 'To Do' | 'In Progress' | 'Done' + priority: 'Low' | 'Medium' | 'High' +} + +const initialTasks: Task[] = [ + { id: 'task-1', content: 'Design user interface', status: 'To Do', priority: 'High' }, + { id: 'task-2', content: 'Implement authentication', status: 'In Progress', priority: 'Medium' }, + { id: 'task-3', content: 'Set up database', status: 'Done', priority: 'High' }, + { id: 'task-4', content: 'Create API endpoints', status: 'To Do', priority: 'Medium' }, + { id: 'task-5', content: 'Write unit tests', status: 'In Progress', priority: 'Low' }, +] + +const getStatusColor = (status: string) => { + switch (status) { + case 'To Do': + return 'bg-blue-100 border-blue-300'; + case 'In Progress': + return 'bg-yellow-100 border-yellow-300'; + case 'Done': + return 'bg-green-100 border-green-300'; + default: + return 'bg-gray-100 border-gray-300'; + } +}; + +const getPriorityColor = (priority: string) => { + switch (priority) { + case 'Low': + return 'bg-green-200 text-green-800'; + case 'Medium': + return 'bg-yellow-200 text-yellow-800'; + case 'High': + return 'bg-red-200 text-red-800'; + default: + return 'bg-gray-200 text-gray-800'; + } +}; + +export default function ProjectBoard() { + const params = useParams() + const projectId = params.projectId + const [tasks, setTasks] = useState(initialTasks) + + const onDragEnd = (result: any) => { + if (!result.destination) return + + const newTasks = Array.from(tasks) + const [reorderedItem] = newTasks.splice(result.source.index, 1) + reorderedItem.status = result.destination.droppableId as 'To Do' | 'In Progress' | 'Done' + newTasks.splice(result.destination.index, 0, reorderedItem) + + setTasks(newTasks) + } + + return ( +
+

Project Board

+

Project ID: {projectId}

+ + +
+ {['To Do', 'In Progress', 'Done'].map((status) => ( + + + {status} + + + + {(provided) => ( +
    + {tasks.filter(task => task.status === status).map((task, index) => ( + + {(provided) => ( +
  • +
    + {task.content} + + {task.priority} + +
    +
  • + )} +
    + ))} + {provided.placeholder} +
+ )} +
+
+
+ ))} +
+
+
+ ) +} \ No newline at end of file diff --git a/app/projects/[ProjectId]/tasks/page.tsx b/app/projects/[ProjectId]/tasks/page.tsx new file mode 100644 index 00000000..4d8bc4c7 --- /dev/null +++ b/app/projects/[ProjectId]/tasks/page.tsx @@ -0,0 +1,168 @@ +'use client' + +import { useState, useEffect } from 'react' +import { useParams } from 'next/navigation' +import { Plus, Edit, Trash2 } from 'lucide-react' +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog" +import { Label } from "@/components/ui/label" +import { Textarea } from "@/components/ui/textarea" +import { ScrollArea } from "@/components/ui/scroll-area" +import { Badge } from "@/components/ui/badge" + +interface Task { + id: string + title: string + description: string + due_date: string + status: 'todo' | 'inprogress' | 'done' + project_id: string +} + +// Mock data +const mockTasks: Task[] = [ + { id: '1', title: 'Task 1', description: 'Description 1', due_date: '2023-05-01', status: 'todo', project_id: 'project1' }, + { id: '2', title: 'Task 2', description: 'Description 2', due_date: '2023-05-02', status: 'inprogress', project_id: 'project1' }, + { id: '3', title: 'Task 3', description: 'Description 3', due_date: '2023-05-03', status: 'done', project_id: 'project1' }, +] + +export default function ProjectTasksPage() { + const { projectId } = useParams() + const [tasks, setTasks] = useState([]) + const [isDialogOpen, setIsDialogOpen] = useState(false) + const [currentTask, setCurrentTask] = useState(null) + const [searchTerm, setSearchTerm] = useState('') + const [isLoading, setIsLoading] = useState(true) + + useEffect(() => { + fetchTasks() + }, [projectId]) + + async function fetchTasks() { + setIsLoading(true) + // Simulate API call + setTimeout(() => { + setTasks(mockTasks.filter(task => task.project_id === projectId)) + setIsLoading(false) + }, 500) + } + + const handleAddEditTask = async (task: Omit) => { + if (currentTask?.id) { + // Update existing task + setTasks(tasks.map(t => t.id === currentTask.id ? { ...t, ...task } : t)) + } else { + // Add new task + const newTask = { ...task, id: Date.now().toString(), project_id: projectId as string } + setTasks([...tasks, newTask]) + } + setIsDialogOpen(false) + setCurrentTask(null) + } + + const handleDeleteTask = async (id: string) => { + setTasks(tasks.filter(t => t.id !== id)) + } + + const filteredTasks = tasks.filter(task => + task.title.toLowerCase().includes(searchTerm.toLowerCase()) || + task.description.toLowerCase().includes(searchTerm.toLowerCase()) + ) + + if (isLoading) { + return
Loading tasks...
+ } + + return ( +
+

Project Tasks

+
+ setSearchTerm(e.target.value)} + /> + +
+ + {filteredTasks.map(task => ( + + + + {task.title} + + {task.status} + + +
+ + +
+
+ +

{task.description}

+

Due: {task.due_date}

+
+
+ ))} +
+ + + + {currentTask ? 'Edit Task' : 'Add New Task'} + +
{ + e.preventDefault() + const formData = new FormData(e.currentTarget) + const task = { + title: formData.get('title') as string, + description: formData.get('description') as string, + due_date: formData.get('due_date') as string, + status: formData.get('status') as 'todo' | 'inprogress' | 'done', + project_id: projectId as string, + } + handleAddEditTask(task) + }}> +
+
+ + +
+
+ + +
+ ); +}; + +export default CodeEditor; + +================ +File: app/forgot-password/page.tsx +================ +'use client'; + +import { useState } from 'react' +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" +import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card" +import { AlertCircle, ArrowLeft } from "lucide-react" +import Link from "next/link" + +export default function ForgotPassword() { + const [email, setEmail] = useState('') + const [isSubmitting, setIsSubmitting] = useState(false) + const [isSubmitted, setIsSubmitted] = useState(false) + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault() + setIsSubmitting(true) + // TODO: Implement actual password reset logic here + await new Promise(resolve => setTimeout(resolve, 1500)) // Simulating API call + setIsSubmitting(false) + setIsSubmitted(true) + } + + return ( +
+ + + Forgot Password + Enter your email to reset your password + + + {!isSubmitted ? ( + +
+
+ + setEmail(e.target.value)} + required + disabled={isSubmitting} + /> +
+ +
+ + ) : ( +
+ +

Check your email

+

+ We've sent a password reset link to {email} +

+
+ )} +
+ + + + Back to login + + +
+
+ ) +} + +================ +File: app/home/page.tsx +================ +'use client'; + +import React from 'react'; +import Link from 'next/link'; +import SharedLayout from '../../components/SharedLayout'; + +const HomePage: React.FC = () => { + return ( + +

Welcome to Our App

+ +
+ ); +}; + +export default HomePage; + +================ +File: app/landing/layout.tsx +================ +'use client'; + +import React, { useState } from 'react'; +import { ThemeProvider } from '@/app/contexts/ThemeContext'; +import '@/styles/globals.css'; +import ChatbotModal from "@/components/ChatbotModal"; +import RobotTransformerWallpaper from '@/components/RobotTransformerWallpaper'; + +export default function LandingLayout({ + children, +}: { + children: React.ReactNode; +}) { + const [isChatbotOpen, setIsChatbotOpen] = useState(false); + + return ( + + + + +
+ {children} + + setIsChatbotOpen(false)} /> +
+
+ + + ); +} + +================ +File: app/landing/page.tsx +================ +'use client'; + +import React from 'react'; +import { motion } from 'framer-motion'; +import Link from 'next/link'; +import { Button } from "@/components/ui/button"; +import { Users, Folder, List, Calendar, BarChart2, CheckCircle, Star, MessageSquare, Database, Bot } from 'lucide-react'; +import { useTheme } from '@/app/contexts/ThemeContext'; +import SharedLayout from '@/components/SharedLayout'; + +export default function LandingPage() { + const { theme } = useTheme(); + + const features = [ + { icon: , title: "Team Collaboration", description: "Work seamlessly with your team members, assign tasks, and track progress together." }, + { icon: , title: "Project Management", description: "Organize your work into projects, set milestones, and manage resources effectively." }, + { icon: , title: "Task Tracking", description: "Create, assign, and monitor tasks with ease. Set priorities and deadlines to stay on top of your work." }, + { icon: , title: "Scheduling", description: "Plan your work with an intuitive calendar view. Never miss a deadline again." }, + { icon: , title: "Analytics", description: "Gain insights into your team's performance with detailed reports and analytics." }, + { icon: , title: "Goal Tracking", description: "Set and track goals for your team. Celebrate achievements and identify areas for improvement." }, + { icon: , title: "AI Chatbot Assistant", description: "Get instant help and automate tasks with our intelligent chatbot." }, + { icon: , title: "Blockchain Integration", description: "Secure and transparent task management with blockchain technology." }, + { icon: , title: "Agent Automation", description: "Leverage AI agents to automate repetitive tasks and enhance productivity." }, + ]; + + const testimonials = [ + { name: "John Doe", role: "CEO, TechCorp", quote: "QuantumLabs has revolutionized our workflow. The AI chatbot and blockchain features are game-changers!" }, + { name: "Jane Smith", role: "Project Manager, InnovateCo", quote: "The best task management tool I've used. The chatbot assistant saves us hours every week." }, + { name: "Mike Johnson", role: "Team Lead, CreativeSolutions", quote: "QuantumLabs's blockchain integration gives us unparalleled security and transparency." }, + ]; + + return ( + +
+ {/* Hero Section */} +
+ + AI-Powered Task Management + + + TaskFlow: Revolutionizing team collaboration with AI chatbots and blockchain technology. + + + + + +
+ + {/* Features Section */} +
+

Cutting-Edge Features for Modern Teams

+
+ {features.map((feature, index) => ( + +
{feature.icon}
+

{feature.title}

+

{feature.description}

+
+ ))} +
+
+ + {/* AI Chatbot Section */} +
+
+

AI-Powered Chatbot Assistant

+

Our intelligent chatbot helps you manage tasks, answer questions, and automate workflows.

+
    +
  • βœ“ Instant task creation and assignment
  • +
  • βœ“ Quick access to project information
  • +
  • βœ“ Automated reminders and notifications
  • +
  • βœ“ Natural language processing for ease of use
  • +
+
+
+ + {/* Blockchain Integration Section */} +
+

Blockchain-Powered Security

+
+
+

Unparalleled Security and Transparency

+
    +
  • Immutable task records
  • +
  • Transparent project history
  • +
  • Secure data storage
  • +
  • Decentralized access control
  • +
+
+
+

+ Our blockchain integration ensures that your project data is secure, transparent, and tamper-proof. + Every action is recorded on the blockchain, providing an auditable trail of all project activities. +

+
+
+
+ + {/* Agent Automation Section */} +
+

AI-Powered Agent Automation

+
+
+

Supercharge Your Workflow with AI Agents

+
    +
  • Automate repetitive tasks
  • +
  • Intelligent task assignment and prioritization
  • +
  • 24/7 productivity with always-on AI agents
  • +
  • Customizable automation workflows
  • +
+
+
+

+ Our AI agents work tirelessly to optimize your workflow, automating routine tasks and + providing intelligent suggestions to boost your team's productivity. +

+
+
+
+ + {/* Testimonials Section */} +
+

What Our Users Say

+
+ {testimonials.map((testimonial, index) => ( + +

"{testimonial.quote}"

+
+
+ +
+
+

{testimonial.name}

+

{testimonial.role}

+
+
+
+ ))} +
+
+ + {/* CTA Section */} +
+
+

Ready to revolutionize your team's productivity?

+

Join thousands of teams already using QuantumLabs's AI and blockchain-powered platform.

+ +
+
+
+ +
+
+

© 2023 QuantumLabs. All rights reserved.

+
+
+
+ ); +} + +================ +File: app/layouts/CustomLayout.tsx +================ +import React, { ReactNode } from 'react'; +import { ThemeProvider } from '@/app/contexts/ThemeContext'; +import RobotTransformerWallpaper from '@/components/RobotTransformerWallpaper'; // Ensure this path is correct + +interface CustomLayoutProps { + children: ReactNode; +} + +const CustomLayout: React.FC = ({ children }) => { + return ( + + {/* Add the wallpaper component */} +
+ {/* Custom layout structure */} + {children} +
+
+ ); +}; + +export default CustomLayout; + +================ +File: app/login/page.tsx +================ +'use client'; + +import React, { useState } from 'react'; +import { useRouter } from 'next/navigation'; +import Link from 'next/link'; +import Image from 'next/image'; +import { Button } from "@/components/ui/button"; +import SharedLayout from '../../components/SharedLayout'; + +export default function LoginPage() { + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [error, setError] = useState(null); + const router = useRouter(); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setError(null); + // Implement login logic here + }; + + return ( + +
+
+

+ Log in to your account +

+
+
+ +
+
+ + setEmail(e.target.value)} + /> +
+
+ + setPassword(e.target.value)} + /> +
+
+ + {error &&
{error}
} + +
+ +
+
+
+
+
+
+
+
+ + Or continue with + +
+
+ +
+ +
+
+ +
+

+ Don't have an account?{' '} + + Sign up + +

+
+
+
+ ); +} + +================ +File: app/members/page.tsx +================ +'use client'; + +import { useState } from 'react'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; +import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"; +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; +import { Badge } from "@/components/ui/badge"; +import { Plus, Search, Mail, MoreHorizontal } from 'lucide-react'; + +const initialMembers = [ + { id: 1, name: 'John Doe', email: 'john@example.com', role: 'Developer', team: 'Development', avatar: 'JD' }, + { id: 2, name: 'Jane Smith', email: 'jane@example.com', role: 'Designer', team: 'Design', avatar: 'JS' }, + { id: 3, name: 'Bob Johnson', email: 'bob@example.com', role: 'Product Manager', team: 'Product', avatar: 'BJ' }, + { id: 4, name: 'Alice Brown', email: 'alice@example.com', role: 'Marketing Specialist', team: 'Marketing', avatar: 'AB' }, + { id: 5, name: 'Charlie Wilson', email: 'charlie@example.com', role: 'Developer', team: 'Development', avatar: 'CW' }, +]; + +export default function MembersPage() { + const [members, setMembers] = useState(initialMembers); + const [searchQuery, setSearchQuery] = useState(''); + const [newMember, setNewMember] = useState({ name: '', email: '', role: '', team: '' }); + + const filteredMembers = members.filter(member => + member.name.toLowerCase().includes(searchQuery.toLowerCase()) || + member.email.toLowerCase().includes(searchQuery.toLowerCase()) || + member.role.toLowerCase().includes(searchQuery.toLowerCase()) || + member.team.toLowerCase().includes(searchQuery.toLowerCase()) + ); + + const addMember = () => { + if (newMember.name && newMember.email && newMember.role && newMember.team) { + setMembers([...members, { ...newMember, id: members.length + 1, avatar: newMember.name.split(' ').map(n => n[0]).join('') }]); + setNewMember({ name: '', email: '', role: '', team: '' }); + } + }; + + return ( +
+ + +
+
+ Members + Manage all members across teams +
+
+
+ + setSearchQuery(e.target.value)} + className="pl-8 w-64" + /> +
+ + + + + + + Add New Member + Add a new member to your organization. + +
+
+ + setNewMember({ ...newMember, name: e.target.value })} + className="col-span-3" + /> +
+
+ + setNewMember({ ...newMember, email: e.target.value })} + className="col-span-3" + /> +
+
+ + setNewMember({ ...newMember, role: e.target.value })} + className="col-span-3" + /> +
+
+ + +
+
+ + + +
+
+
+
+
+ + + + + Name + Email + Role + Team + Actions + + + + {filteredMembers.map((member) => ( + + +
+ + + {member.avatar} + + {member.name} +
+
+ {member.email} + {member.role} + + {member.team} + + +
+ + +
+
+
+ ))} +
+
+
+
+
+ ); +} + +================ +File: app/not-found/page.tsx +================ +'use client'; + +import React from 'react'; + +const NotFoundPage = () => { + return ( +
+

404 - Page Not Found

+

Sorry, the page you are looking for does not exist.

+
+ ); +}; + +export default NotFoundPage; + +================ +File: app/notifications/page.tsx +================ +'use client'; + +import { useState } from 'react'; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Avatar, AvatarFallback } from "@/components/ui/avatar"; +import { Badge } from "@/components/ui/badge"; +import { ScrollArea } from "@/components/ui/scroll-area"; +import { Bell, MessageSquare, CheckCircle } from 'lucide-react'; + +interface Notification { + id: string; + type: 'assignment' | 'comment' | 'update'; + content: string; + project: string; + timestamp: string; + read: boolean; +} + +export default function NotificationsPage() { + const [notifications, setNotifications] = useState([ + { + id: '1', + type: 'assignment', + content: 'You have been assigned to the task "Design new logo"', + project: 'Branding Refresh', + timestamp: '2023-07-15T10:30:00Z', + read: false, + }, + { + id: '2', + type: 'comment', + content: 'Alice commented on your task "Implement login functionality"', + project: 'User Authentication', + timestamp: '2023-07-14T15:45:00Z', + read: true, + }, + { + id: '3', + type: 'update', + content: 'The project "Backend Development" has been marked as complete', + project: 'Backend Development', + timestamp: '2023-07-13T09:15:00Z', + read: false, + }, + ]); + + const markAsRead = (id: string) => { + setNotifications(notifications.map(notification => + notification.id === id ? { ...notification, read: true } : notification + )); + }; + + const getIcon = (type: Notification['type']) => { + switch (type) { + case 'assignment': + return ; + case 'comment': + return ; + case 'update': + return ; + } + }; + + return ( +
+

Notifications

+ + + Recent Notifications + + + + {notifications.map((notification) => ( +
markAsRead(notification.id)} + > + + {getIcon(notification.type)} + +
+

{notification.content}

+

Project: {notification.project}

+

{new Date(notification.timestamp).toLocaleString()}

+
+ {!notification.read && ( + New + )} +
+ ))} +
+
+
+
+ ); +} + +================ +File: app/profile/page.tsx +================ +'use client'; + +import React, { useState, useEffect } from 'react'; +import CenteredAtomSpinner from "@/components/CenteredAtomSpinner"; // Import the spinner + +const Profile: React.FC = () => { + const [loading, setLoading] = useState(true); + + useEffect(() => { + const timer = setTimeout(() => { + setLoading(false); + }, 2000); // Simulate loading for 2 seconds + + return () => clearTimeout(timer); + }, []); + + if (loading) { + return ; + } + + return ( +
+

Profile

+ {/* Profile content goes here */} +
+ ); +}; + +export default Profile; + +================ +File: app/progress/page.tsx +================ +'use client' + +import { useState } from 'react' +import { Progress } from "@/components/ui/progress" +import { ScrollArea } from "@/components/ui/scroll-area" +import { Badge } from "@/components/ui/badge" +import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts' +import Link from 'next/link' +import { Button } from "@/components/ui/button" +import { CheckCircle, Users, Folder, List } from 'lucide-react' +import { Card } from '@/components/ui/card' +import { CardContent } from '@/components/ui/card-content' +import { CardHeader } from '@/components/ui/card-header' +import { CardTitle } from '@/components/ui/card-title' + +interface Project { + id: string + name: string + progress: number + tasksCompleted: number + totalTasks: number +} + +export default function ProgressPage() { + const [projects] = useState([ + { id: '1', name: 'Project Alpha', progress: 65, tasksCompleted: 13, totalTasks: 20 }, + { id: '2', name: 'Project Beta', progress: 30, tasksCompleted: 6, totalTasks: 20 }, + { id: '3', name: 'Project Gamma', progress: 90, tasksCompleted: 18, totalTasks: 20 }, + { id: '4', name: 'Project Delta', progress: 45, tasksCompleted: 9, totalTasks: 20 }, + { id: '5', name: 'Project Epsilon', progress: 10, tasksCompleted: 2, totalTasks: 20 }, + ]) + + const chartData = projects.map(project => ({ + name: project.name, + progress: project.progress, + })) + + return ( +
+
+
+
+ QuantumLabs +
+ Log in + +
+
+
+
+ +
+
+
+

Track Your Progress

+

Monitor your tasks and projects in real-time.

+ +
+
+ +
+
+

Key Features

+
+ {[ + { icon: , title: "Team Collaboration", description: "Work seamlessly with your team members, assign tasks, and track progress together." }, + { icon: , title: "Project Management", description: "Organize your work into projects, set milestones, and manage resources effectively." }, + { icon: , title: "Task Tracking", description: "Create, assign, and monitor tasks with ease. Set priorities and deadlines to stay on top of your work." }, + ].map((feature, index) => ( + + ))} +
+
+
+
+ +
+
+

© {new Date().getFullYear()} QuantumLabs. All rights reserved.

+
+
+
+ ) +} + +function FeatureCard({ icon, title, description }: { icon: React.ReactNode, title: string, description: string }) { + return ( + + + +
{icon}
+ {title} +
+
+ +

{description}

+
+
+ ) +} + +================ +File: app/project-board/page.tsx +================ +'use client'; + +import React from 'react'; + +const ProjectBoardPage: React.FC = () => { + return ( +
+

Project Board

+ {/* Your project board content */} +
+ ); +}; + +export default ProjectBoardPage; + +================ +File: app/projects/[ProjectId]/board/page.tsx +================ +'use client' + +import { useParams } from 'next/navigation' +import { useState, useEffect } from 'react' +import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd' +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" +import { Badge } from "@/components/ui/badge" + +interface Task { + id: string + content: string + status: 'To Do' | 'In Progress' | 'Done' + priority: 'Low' | 'Medium' | 'High' +} + +const initialTasks: Task[] = [ + { id: 'task-1', content: 'Design user interface', status: 'To Do', priority: 'High' }, + { id: 'task-2', content: 'Implement authentication', status: 'In Progress', priority: 'Medium' }, + { id: 'task-3', content: 'Set up database', status: 'Done', priority: 'High' }, + { id: 'task-4', content: 'Create API endpoints', status: 'To Do', priority: 'Medium' }, + { id: 'task-5', content: 'Write unit tests', status: 'In Progress', priority: 'Low' }, +] + +const getStatusColor = (status: string) => { + switch (status) { + case 'To Do': + return 'bg-blue-100 border-blue-300'; + case 'In Progress': + return 'bg-yellow-100 border-yellow-300'; + case 'Done': + return 'bg-green-100 border-green-300'; + default: + return 'bg-gray-100 border-gray-300'; + } +}; + +const getPriorityColor = (priority: string) => { + switch (priority) { + case 'Low': + return 'bg-green-200 text-green-800'; + case 'Medium': + return 'bg-yellow-200 text-yellow-800'; + case 'High': + return 'bg-red-200 text-red-800'; + default: + return 'bg-gray-200 text-gray-800'; + } +}; + +export default function ProjectBoard() { + const params = useParams() + const projectId = params.projectId + const [tasks, setTasks] = useState(initialTasks) + + const onDragEnd = (result: any) => { + if (!result.destination) return + + const newTasks = Array.from(tasks) + const [reorderedItem] = newTasks.splice(result.source.index, 1) + reorderedItem.status = result.destination.droppableId as 'To Do' | 'In Progress' | 'Done' + newTasks.splice(result.destination.index, 0, reorderedItem) + + setTasks(newTasks) + } + + return ( +
+

Project Board

+

Project ID: {projectId}

+ + +
+ {['To Do', 'In Progress', 'Done'].map((status) => ( + + + {status} + + + + {(provided) => ( +
    + {tasks.filter(task => task.status === status).map((task, index) => ( + + {(provided) => ( +
  • +
    + {task.content} + + {task.priority} + +
    +
  • + )} +
    + ))} + {provided.placeholder} +
+ )} +
+
+
+ ))} +
+
+
+ ) +} + +================ +File: app/projects/[ProjectId]/tasks/page.tsx +================ +'use client' + +import { useState, useEffect } from 'react' +import { useParams } from 'next/navigation' +import { Plus, Edit, Trash2 } from 'lucide-react' +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog" +import { Label } from "@/components/ui/label" +import { Textarea } from "@/components/ui/textarea" +import { ScrollArea } from "@/components/ui/scroll-area" +import { Badge } from "@/components/ui/badge" + +interface Task { + id: string + title: string + description: string + due_date: string + status: 'todo' | 'inprogress' | 'done' + project_id: string +} + +// Mock data +const mockTasks: Task[] = [ + { id: '1', title: 'Task 1', description: 'Description 1', due_date: '2023-05-01', status: 'todo', project_id: 'project1' }, + { id: '2', title: 'Task 2', description: 'Description 2', due_date: '2023-05-02', status: 'inprogress', project_id: 'project1' }, + { id: '3', title: 'Task 3', description: 'Description 3', due_date: '2023-05-03', status: 'done', project_id: 'project1' }, +] + +export default function ProjectTasksPage() { + const { projectId } = useParams() + const [tasks, setTasks] = useState([]) + const [isDialogOpen, setIsDialogOpen] = useState(false) + const [currentTask, setCurrentTask] = useState(null) + const [searchTerm, setSearchTerm] = useState('') + const [isLoading, setIsLoading] = useState(true) + + useEffect(() => { + fetchTasks() + }, [projectId]) + + async function fetchTasks() { + setIsLoading(true) + // Simulate API call + setTimeout(() => { + setTasks(mockTasks.filter(task => task.project_id === projectId)) + setIsLoading(false) + }, 500) + } + + const handleAddEditTask = async (task: Omit) => { + if (currentTask?.id) { + // Update existing task + setTasks(tasks.map(t => t.id === currentTask.id ? { ...t, ...task } : t)) + } else { + // Add new task + const newTask = { ...task, id: Date.now().toString(), project_id: projectId as string } + setTasks([...tasks, newTask]) + } + setIsDialogOpen(false) + setCurrentTask(null) + } + + const handleDeleteTask = async (id: string) => { + setTasks(tasks.filter(t => t.id !== id)) + } + + const filteredTasks = tasks.filter(task => + task.title.toLowerCase().includes(searchTerm.toLowerCase()) || + task.description.toLowerCase().includes(searchTerm.toLowerCase()) + ) + + if (isLoading) { + return
Loading tasks...
+ } + + return ( +
+

Project Tasks

+
+ setSearchTerm(e.target.value)} + /> + +
+ + {filteredTasks.map(task => ( + + + + {task.title} + + {task.status} + + +
+ + +
+
+ +

{task.description}

+

Due: {task.due_date}

+
+
+ ))} +
+ + + + {currentTask ? 'Edit Task' : 'Add New Task'} + +
{ + e.preventDefault() + const formData = new FormData(e.currentTarget) + const task = { + title: formData.get('title') as string, + description: formData.get('description') as string, + due_date: formData.get('due_date') as string, + status: formData.get('status') as 'todo' | 'inprogress' | 'done', + project_id: projectId as string, + } + handleAddEditTask(task) + }}> +
+
+ + +
+
+ +