From 40b719806ca70660f556201b604dff7e4d3f1b84 Mon Sep 17 00:00:00 2001 From: morisummer Date: Sat, 19 Jul 2025 21:22:58 +0300 Subject: [PATCH 1/8] =?UTF-8?q?=E2=9C=A8=20Add=20new=20Svelte=20components?= =?UTF-8?q?=20for=20list=20and=20table=20structures,=20and=20update=20user?= =?UTF-8?q?=20model=20with=20ID,=20add=20markdown,=20improve=20overall=20e?= =?UTF-8?q?xpereince.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/models/projects.py | 4 +- backend/app/routes/api/project/data_access.py | 16 + backend/app/routes/api/project/router.py | 11 + backend/app/routes/api/project/schemas.py | 5 + backend/app/routes/api/project/service.py | 24 +- backend/app/schemas/user.py | 1 + frontend/package.json | 3 +- frontend/pnpm-lock.yaml | 59 +++- frontend/src/lib/api/v1.d.ts | 312 ++++++++++++++++- .../src/lib/components/md/Blockquote.svelte | 3 + .../src/lib/components/md/CodeSpan.svelte | 7 + frontend/src/lib/components/md/Heading.svelte | 37 ++ frontend/src/lib/components/md/Link.svelte | 8 + frontend/src/lib/components/md/List.svelte | 10 + .../src/lib/components/md/ListItem.svelte | 1 + .../src/lib/components/md/Markdown.svelte | 31 ++ .../src/lib/components/md/Paragraph.svelte | 1 + frontend/src/lib/components/md/Table.svelte | 3 + .../src/lib/components/md/TableBody.svelte | 1 + .../src/lib/components/md/TableCell.svelte | 18 + .../src/lib/components/md/TableHead.svelte | 1 + .../src/lib/components/md/TableRow.svelte | 1 + frontend/src/lib/components/ui/tabs/index.ts | 16 + .../components/ui/tabs/tabs-content.svelte | 17 + .../lib/components/ui/tabs/tabs-list.svelte | 16 + .../components/ui/tabs/tabs-trigger.svelte | 20 ++ .../src/lib/components/ui/tabs/tabs.svelte | 19 ++ .../src/lib/components/ui/tooltip/index.ts | 21 ++ .../ui/tooltip/tooltip-content.svelte | 47 +++ .../ui/tooltip/tooltip-trigger.svelte | 7 + frontend/src/lib/constants/userRoles.ts | 31 ++ .../app/(components)/ChooseRoleDialog.svelte | 5 +- .../(components)/CreateProjectDialog.svelte | 15 +- .../routes/app/(components)/dataLoaders.ts | 5 +- frontend/src/routes/app/+layout.svelte | 42 ++- .../explore/(components)/ProjectCard.svelte | 74 ++-- frontend/src/routes/app/explore/+page.svelte | 19 +- .../app/profile/(components)/dataLoaders.ts | 10 + frontend/src/routes/app/profile/+page.svelte | 78 +++-- frontend/src/routes/app/profile/+page.ts | 10 + .../app/projects/(components)/dataLoaders.ts | 21 +- .../routes/app/projects/[slug]/+page.svelte | 321 +++++++++++++----- 42 files changed, 1166 insertions(+), 185 deletions(-) create mode 100644 frontend/src/lib/components/md/Blockquote.svelte create mode 100644 frontend/src/lib/components/md/CodeSpan.svelte create mode 100644 frontend/src/lib/components/md/Heading.svelte create mode 100644 frontend/src/lib/components/md/Link.svelte create mode 100644 frontend/src/lib/components/md/List.svelte create mode 100644 frontend/src/lib/components/md/ListItem.svelte create mode 100644 frontend/src/lib/components/md/Markdown.svelte create mode 100644 frontend/src/lib/components/md/Paragraph.svelte create mode 100644 frontend/src/lib/components/md/Table.svelte create mode 100644 frontend/src/lib/components/md/TableBody.svelte create mode 100644 frontend/src/lib/components/md/TableCell.svelte create mode 100644 frontend/src/lib/components/md/TableHead.svelte create mode 100644 frontend/src/lib/components/md/TableRow.svelte create mode 100644 frontend/src/lib/components/ui/tabs/index.ts create mode 100644 frontend/src/lib/components/ui/tabs/tabs-content.svelte create mode 100644 frontend/src/lib/components/ui/tabs/tabs-list.svelte create mode 100644 frontend/src/lib/components/ui/tabs/tabs-trigger.svelte create mode 100644 frontend/src/lib/components/ui/tabs/tabs.svelte create mode 100644 frontend/src/lib/components/ui/tooltip/index.ts create mode 100644 frontend/src/lib/components/ui/tooltip/tooltip-content.svelte create mode 100644 frontend/src/lib/components/ui/tooltip/tooltip-trigger.svelte create mode 100644 frontend/src/lib/constants/userRoles.ts create mode 100644 frontend/src/routes/app/profile/(components)/dataLoaders.ts create mode 100644 frontend/src/routes/app/profile/+page.ts diff --git a/backend/app/models/projects.py b/backend/app/models/projects.py index 153651b..4297fa5 100644 --- a/backend/app/models/projects.py +++ b/backend/app/models/projects.py @@ -16,7 +16,9 @@ class Project(base): ceo_id = Column(Integer, ForeignKey("users.id"), nullable=True, server_default="1") is_opensource = Column(Boolean, nullable=True, default=True, server_default="TRUE") is_dead = Column(Boolean, nullable=True, default=False, server_default="FALSE") - tags = relationship("Tag", secondary="project_tags", back_populates="projects") + tags = relationship( + "Tag", secondary="project_tags", back_populates="projects", lazy="selectin" + ) def __repr__(self): return f"" diff --git a/backend/app/routes/api/project/data_access.py b/backend/app/routes/api/project/data_access.py index f32799d..eb23667 100644 --- a/backend/app/routes/api/project/data_access.py +++ b/backend/app/routes/api/project/data_access.py @@ -70,6 +70,7 @@ async def create_project( project.ceo_id = ceo_id self.db_session.add(project) await self.db_session.flush() + await self.db_session.refresh(project, ["tags"]) return ProjectSchema.model_validate(project) async def get_project_applications( @@ -159,5 +160,20 @@ async def delete_application(self, project_id: int, user_id: int) -> bool: res = await self.db_session.execute(query) return res.rowcount > 0 + async def update_project( + self, project_id: int, brief_description: str, description: str | None = None + ) -> ProjectSchema: + query = ( + update(Project) + .where(Project.id == project_id) + .values(brief_description=brief_description, description=description) + .returning(Project) + ) + res = await self.db_session.execute(query) + project = res.scalars().first() + if not project: + raise ValueError(f"Project with ID {project_id} not found.") + return ProjectSchema.model_validate(project) + ProjectsDataAccessDep = Annotated[ProjectsDataAccess, Depends(ProjectsDataAccess)] diff --git a/backend/app/routes/api/project/router.py b/backend/app/routes/api/project/router.py index fb08152..b090c25 100644 --- a/backend/app/routes/api/project/router.py +++ b/backend/app/routes/api/project/router.py @@ -8,6 +8,7 @@ ApproveApplicationSchema, ProjectMemberSchema, ActionResponse, + UpdateProjectRequest, ) from routes.api.project.service import ProjectServiceDep @@ -97,3 +98,13 @@ async def delete_project( user: AuthUserDep, ) -> ActionResponse: return await service.delete_project(project_id, user) + + +@router.post("/{project_id}/update") +async def update_project( + project_id: int, + data: UpdateProjectRequest, + service: ProjectServiceDep, + user: AuthUserDep, +) -> ProjectSchema: + return await service.update_project(project_id, data, user) diff --git a/backend/app/routes/api/project/schemas.py b/backend/app/routes/api/project/schemas.py index b0b6ec0..4a9b633 100644 --- a/backend/app/routes/api/project/schemas.py +++ b/backend/app/routes/api/project/schemas.py @@ -57,3 +57,8 @@ class ProjectMemberSchema(BaseModel): class Config: from_attributes = True + + +class UpdateProjectRequest(BaseModel): + brief_description: str + description: Optional[str] = None diff --git a/backend/app/routes/api/project/service.py b/backend/app/routes/api/project/service.py index c1c5808..b95ea44 100644 --- a/backend/app/routes/api/project/service.py +++ b/backend/app/routes/api/project/service.py @@ -1,8 +1,6 @@ -from email.message import Message from typing import Annotated from fastapi import Depends from fastapi.exceptions import HTTPException -from pyexpat.errors import messages from models.users import UserRole from routes.api.project.data_access import ProjectsDataAccessDep @@ -13,6 +11,7 @@ ApplicationSchema, ApproveApplicationSchema, ActionResponse, + UpdateProjectRequest, ) from schemas.user import UserInDB @@ -145,7 +144,7 @@ async def delete_project(self, project_id: int, user: UserInDB) -> ActionRespons ) if project.ceo_id != user.id: raise HTTPException( - status_code=403, detail=f"Only the project CEO can delete." + status_code=403, detail="Only the project CEO can delete." ) res = await self.data_access.delete_project(project_id) msg = "Project was successfully deleted" if res else "Project not found" @@ -173,5 +172,24 @@ async def delete_application(self, project_id, user: UserInDB) -> ActionResponse ) return ActionResponse(message=msg, success=res) + async def update_project( + self, + project_id: int, + data: UpdateProjectRequest, + user: UserInDB, + ) -> ProjectSchema: + project = await self.data_access.get_project_by_id(project_id) + if project is None: + raise HTTPException( + status_code=404, detail=f"Project with ID {project_id} not found." + ) + if project.ceo_id != user.id: + raise HTTPException( + status_code=403, detail="Only the project CEO can update the project." + ) + return await self.data_access.update_project( + project_id, data.brief_description, data.description + ) + ProjectServiceDep = Annotated[ProjectService, Depends(ProjectService)] diff --git a/backend/app/schemas/user.py b/backend/app/schemas/user.py index 991a94c..43946cc 100644 --- a/backend/app/schemas/user.py +++ b/backend/app/schemas/user.py @@ -5,6 +5,7 @@ class User(BaseModel): + id: int username: str role: UserRole diff --git a/frontend/package.json b/frontend/package.json index eb38cd1..173f4fb 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -65,6 +65,7 @@ "@tanstack/svelte-query": "^5.81.2", "cobe": "^0.6.4", "jwt-decode": "^4.0.0", - "openapi-fetch": "^0.14.0" + "openapi-fetch": "^0.14.0", + "svelte-markdown": "^0.4.1" } } diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index e459fdc..0e61a37 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -23,6 +23,9 @@ importers: openapi-fetch: specifier: ^0.14.0 version: 0.14.0 + svelte-markdown: + specifier: ^0.4.1 + version: 0.4.1(svelte@5.33.19) devDependencies: '@eslint/compat': specifier: ^1.2.5 @@ -53,10 +56,10 @@ importers: version: 6.6.3 '@testing-library/svelte': specifier: ^5.2.8 - version: 5.2.8(svelte@5.33.19)(vite@6.3.5(jiti@2.4.2)(lightningcss@1.30.1))(vitest@3.2.4(@vitest/ui@3.2.4)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1)) + version: 5.2.8(svelte@5.33.19)(vite@6.3.5(jiti@2.4.2)(lightningcss@1.30.1))(vitest@3.2.4(@types/debug@4.1.12)(@vitest/ui@3.2.4)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1)) '@vitest/coverage-v8': specifier: ^3.2.4 - version: 3.2.4(vitest@3.2.4(@vitest/ui@3.2.4)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1)) + version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@vitest/ui@3.2.4)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1)) '@vitest/ui': specifier: ^3.2.4 version: 3.2.4(vitest@3.2.4) @@ -128,7 +131,7 @@ importers: version: 6.3.5(jiti@2.4.2)(lightningcss@1.30.1) vitest: specifier: ^3.2.4 - version: 3.2.4(@vitest/ui@3.2.4)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1) + version: 3.2.4(@types/debug@4.1.12)(@vitest/ui@3.2.4)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1) packages: @@ -762,6 +765,9 @@ packages: '@types/cookie@0.6.0': resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + '@types/debug@4.1.12': + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} @@ -774,6 +780,12 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@types/marked@5.0.2': + resolution: {integrity: sha512-OucS4KMHhFzhz27KxmWg7J+kIYqyqoW5kdIEI319hqARQQUTqhao3M/F+uFnDXD0Rg72iDDZxZNxq5gvctmLlg==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + '@typescript-eslint/eslint-plugin@8.34.0': resolution: {integrity: sha512-QXwAlHlbcAwNlEEMKQS2RCgJsgXrTJdjXT08xEgbPFa2yYQgVjBymxP5DrfrE7X7iodSzd9qBUHUycdyVJTW1w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1510,6 +1522,11 @@ packages: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} + marked@5.1.2: + resolution: {integrity: sha512-ahRPGXJpjMjwSOlBoTMZAK7ATXkli5qCPxZ21TG44rx1KEo44bii4ekgTDQPNRQ4Kh7JMb9Ub1PVk1NxRSsorg==} + engines: {node: '>= 16'} + hasBin: true + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -1913,6 +1930,11 @@ packages: svelte: optional: true + svelte-markdown@0.4.1: + resolution: {integrity: sha512-pOlLY6EruKJaWI9my/2bKX8PdTeP5CM0s4VMmwmC2prlOkjAf+AOmTM4wW/l19Y6WZ87YmP8+ZCJCCwBChWjYw==} + peerDependencies: + svelte: ^4.0.0 + svelte-sonner@1.0.5: resolution: {integrity: sha512-9dpGPFqKb/QWudYqGnEz93vuY+NgCEvyNvxoCLMVGw6sDN/3oVeKV1xiEirW2E1N3vJEyj5imSBNOGltQHA7mg==} peerDependencies: @@ -2720,13 +2742,13 @@ snapshots: lodash: 4.17.21 redent: 3.0.0 - '@testing-library/svelte@5.2.8(svelte@5.33.19)(vite@6.3.5(jiti@2.4.2)(lightningcss@1.30.1))(vitest@3.2.4(@vitest/ui@3.2.4)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1))': + '@testing-library/svelte@5.2.8(svelte@5.33.19)(vite@6.3.5(jiti@2.4.2)(lightningcss@1.30.1))(vitest@3.2.4(@types/debug@4.1.12)(@vitest/ui@3.2.4)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1))': dependencies: '@testing-library/dom': 10.4.0 svelte: 5.33.19 optionalDependencies: vite: 6.3.5(jiti@2.4.2)(lightningcss@1.30.1) - vitest: 3.2.4(@vitest/ui@3.2.4)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1) + vitest: 3.2.4(@types/debug@4.1.12)(@vitest/ui@3.2.4)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1) '@types/aria-query@5.0.4': {} @@ -2736,6 +2758,11 @@ snapshots: '@types/cookie@0.6.0': {} + '@types/debug@4.1.12': + dependencies: + '@types/ms': 2.1.0 + optional: true + '@types/deep-eql@4.0.2': {} '@types/estree@1.0.7': {} @@ -2744,6 +2771,11 @@ snapshots: '@types/json-schema@7.0.15': {} + '@types/marked@5.0.2': {} + + '@types/ms@2.1.0': + optional: true + '@typescript-eslint/eslint-plugin@8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: '@eslint-community/regexpp': 4.12.1 @@ -2836,7 +2868,7 @@ snapshots: '@typescript-eslint/types': 8.34.0 eslint-visitor-keys: 4.2.1 - '@vitest/coverage-v8@3.2.4(vitest@3.2.4(@vitest/ui@3.2.4)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1))': + '@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@vitest/ui@3.2.4)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1))': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 1.0.2 @@ -2851,7 +2883,7 @@ snapshots: std-env: 3.9.0 test-exclude: 7.0.1 tinyrainbow: 2.0.0 - vitest: 3.2.4(@vitest/ui@3.2.4)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1) + vitest: 3.2.4(@types/debug@4.1.12)(@vitest/ui@3.2.4)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1) transitivePeerDependencies: - supports-color @@ -2900,7 +2932,7 @@ snapshots: sirv: 3.0.1 tinyglobby: 0.2.14 tinyrainbow: 2.0.0 - vitest: 3.2.4(@vitest/ui@3.2.4)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1) + vitest: 3.2.4(@types/debug@4.1.12)(@vitest/ui@3.2.4)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1) '@vitest/utils@3.2.4': dependencies: @@ -3527,6 +3559,8 @@ snapshots: dependencies: semver: 7.7.2 + marked@5.1.2: {} + merge2@1.4.1: {} micromatch@4.0.8: @@ -3858,6 +3892,12 @@ snapshots: optionalDependencies: svelte: 5.33.19 + svelte-markdown@0.4.1(svelte@5.33.19): + dependencies: + '@types/marked': 5.0.2 + marked: 5.1.2 + svelte: 5.33.19 + svelte-sonner@1.0.5(svelte@5.33.19): dependencies: runed: 0.28.0(svelte@5.33.19) @@ -4033,7 +4073,7 @@ snapshots: optionalDependencies: vite: 6.3.5(jiti@2.4.2)(lightningcss@1.30.1) - vitest@3.2.4(@vitest/ui@3.2.4)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1): + vitest@3.2.4(@types/debug@4.1.12)(@vitest/ui@3.2.4)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1): dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 @@ -4059,6 +4099,7 @@ snapshots: vite-node: 3.2.4(jiti@2.4.2)(lightningcss@1.30.1) why-is-node-running: 2.3.0 optionalDependencies: + '@types/debug': 4.1.12 '@vitest/ui': 3.2.4(vitest@3.2.4) jsdom: 26.1.0 transitivePeerDependencies: diff --git a/frontend/src/lib/api/v1.d.ts b/frontend/src/lib/api/v1.d.ts index cad8302..dce346c 100644 --- a/frontend/src/lib/api/v1.d.ts +++ b/frontend/src/lib/api/v1.d.ts @@ -39,6 +39,23 @@ export interface paths { patch?: never; trace?: never; }; + '/api/projects/all-applications': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get User Applications */ + get: operations['get_user_applications_api_projects_all_applications_get']; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; '/api/projects/{project_id}': { parameters: { query?: never; @@ -50,7 +67,8 @@ export interface paths { get: operations['get_project_by_id_api_projects__project_id__get']; put?: never; post?: never; - delete?: never; + /** Delete Project */ + delete: operations['delete_project_api_projects__project_id__delete']; options?: never; head?: never; patch?: never; @@ -124,6 +142,40 @@ export interface paths { patch: operations['approve_application_api_projects__project_id__applications_approve_patch']; trace?: never; }; + '/api/projects/{project_id}/applications/cancel': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + /** Delete Application */ + delete: operations['delete_application_api_projects__project_id__applications_cancel_delete']; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + '/api/projects/{project_id}/update': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Update Project */ + post: operations['update_project_api_projects__project_id__update_post']; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; '/api/auth/token': { parameters: { query?: never; @@ -192,10 +244,51 @@ export interface paths { patch?: never; trace?: never; }; + '/api/tags/generate/{project_id}': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get Tags By Description */ + get: operations['get_tags_by_description_api_tags_generate__project_id__get']; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + '/api/tags/add/{project_id}': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Add Project Tags */ + post: operations['add_project_tags_api_tags_add__project_id__post']; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; } export type webhooks = Record; export interface components { schemas: { + /** ActionResponse */ + ActionResponse: { + /** Message */ + message: string; + /** Success */ + success: boolean; + }; /** ApplicationSchema */ ApplicationSchema: { /** Project Id */ @@ -209,6 +302,8 @@ export interface components { * Format: date-time */ created_at: string; + /** Feedback */ + feedback: string | null; }; /** ApproveApplicationSchema */ ApproveApplicationSchema: { @@ -216,6 +311,8 @@ export interface components { is_approved: boolean; /** User Id */ user_id: number; + /** Feedback */ + feedback: string | null; }; /** Body_login_for_access_token_api_auth_token_post */ Body_login_for_access_token_api_auth_token_post: { @@ -235,6 +332,11 @@ export interface components { /** Client Secret */ client_secret?: string | null; }; + /** GeneratedTagsResponse */ + GeneratedTagsResponse: { + /** Tags */ + tags: string[]; + }; /** HTTPValidationError */ HTTPValidationError: { /** Detail */ @@ -244,6 +346,8 @@ export interface components { NewProjectSchema: { /** Title */ title: string; + /** Brief Description */ + brief_description: string; /** Description */ description: string | null; /** Is Public */ @@ -264,6 +368,8 @@ export interface components { ProjectSchema: { /** Title */ title: string; + /** Brief Description */ + brief_description: string; /** Description */ description: string | null; /** Is Public */ @@ -281,11 +387,23 @@ export interface components { created_at: string; /** Ceo Id */ ceo_id: number | null; + /** Tags */ + tags?: components['schemas']['Tag'][] | null; }; /** SetUserRoleSchema */ SetUserRoleSchema: { role: components['schemas']['UserRole']; }; + /** Tag */ + Tag: { + /** Name */ + name: string; + }; + /** TagSchema */ + TagSchema: { + /** Name */ + name: string; + }; /** Token */ Token: { /** Access Token */ @@ -293,8 +411,17 @@ export interface components { /** Token Type */ token_type: string; }; + /** UpdateProjectRequest */ + UpdateProjectRequest: { + /** Brief Description */ + brief_description: string; + /** Description */ + description?: string | null; + }; /** User */ User: { + /** Id */ + id: number; /** Username */ username: string; role: components['schemas']['UserRole']; @@ -402,6 +529,26 @@ export interface operations { }; }; }; + get_user_applications_api_projects_all_applications_get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['ApplicationSchema'][]; + }; + }; + }; + }; get_project_by_id_api_projects__project_id__get: { parameters: { query?: never; @@ -433,6 +580,37 @@ export interface operations { }; }; }; + delete_project_api_projects__project_id__delete: { + parameters: { + query?: never; + header?: never; + path: { + project_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['ActionResponse']; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['HTTPValidationError']; + }; + }; + }; + }; apply_to_project_api_projects__project_id__apply_post: { parameters: { query?: never; @@ -561,6 +739,72 @@ export interface operations { }; }; }; + delete_application_api_projects__project_id__applications_cancel_delete: { + parameters: { + query?: never; + header?: never; + path: { + project_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['ActionResponse']; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['HTTPValidationError']; + }; + }; + }; + }; + update_project_api_projects__project_id__update_post: { + parameters: { + query?: never; + header?: never; + path: { + project_id: number; + }; + cookie?: never; + }; + requestBody: { + content: { + 'application/json': components['schemas']['UpdateProjectRequest']; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['ProjectSchema']; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['HTTPValidationError']; + }; + }; + }; + }; login_for_access_token_api_auth_token_post: { parameters: { query?: never; @@ -682,4 +926,70 @@ export interface operations { }; }; }; + get_tags_by_description_api_tags_generate__project_id__get: { + parameters: { + query?: never; + header?: never; + path: { + project_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['GeneratedTagsResponse']; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['HTTPValidationError']; + }; + }; + }; + }; + add_project_tags_api_tags_add__project_id__post: { + parameters: { + query?: never; + header?: never; + path: { + project_id: number; + }; + cookie?: never; + }; + requestBody: { + content: { + 'application/json': components['schemas']['TagSchema'][]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['TagSchema'][]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['HTTPValidationError']; + }; + }; + }; + }; } diff --git a/frontend/src/lib/components/md/Blockquote.svelte b/frontend/src/lib/components/md/Blockquote.svelte new file mode 100644 index 0000000..f72617f --- /dev/null +++ b/frontend/src/lib/components/md/Blockquote.svelte @@ -0,0 +1,3 @@ +
+ +
diff --git a/frontend/src/lib/components/md/CodeSpan.svelte b/frontend/src/lib/components/md/CodeSpan.svelte new file mode 100644 index 0000000..76b8ed5 --- /dev/null +++ b/frontend/src/lib/components/md/CodeSpan.svelte @@ -0,0 +1,7 @@ + + + + {raw.replace(/`/g, '')} + diff --git a/frontend/src/lib/components/md/Heading.svelte b/frontend/src/lib/components/md/Heading.svelte new file mode 100644 index 0000000..0b53cce --- /dev/null +++ b/frontend/src/lib/components/md/Heading.svelte @@ -0,0 +1,37 @@ + + +{#if depth === 1} +

+ +

+{:else if depth === 2} +

+ +

+{:else if depth === 3} +

+ +

+{:else if depth === 4} +

+ +

+{:else if depth === 5} +
+ +
+{:else if depth === 6} +
+ +
+{:else} + {raw} +{/if} diff --git a/frontend/src/lib/components/md/Link.svelte b/frontend/src/lib/components/md/Link.svelte new file mode 100644 index 0000000..bc6b39e --- /dev/null +++ b/frontend/src/lib/components/md/Link.svelte @@ -0,0 +1,8 @@ + + + + + diff --git a/frontend/src/lib/components/md/List.svelte b/frontend/src/lib/components/md/List.svelte new file mode 100644 index 0000000..9a0eef0 --- /dev/null +++ b/frontend/src/lib/components/md/List.svelte @@ -0,0 +1,10 @@ + + +{#if ordered} +
+{:else} +
+{/if} diff --git a/frontend/src/lib/components/md/ListItem.svelte b/frontend/src/lib/components/md/ListItem.svelte new file mode 100644 index 0000000..bc52876 --- /dev/null +++ b/frontend/src/lib/components/md/ListItem.svelte @@ -0,0 +1 @@ +
  • diff --git a/frontend/src/lib/components/md/Markdown.svelte b/frontend/src/lib/components/md/Markdown.svelte new file mode 100644 index 0000000..0526d77 --- /dev/null +++ b/frontend/src/lib/components/md/Markdown.svelte @@ -0,0 +1,31 @@ + + + diff --git a/frontend/src/lib/components/md/Paragraph.svelte b/frontend/src/lib/components/md/Paragraph.svelte new file mode 100644 index 0000000..59f4aad --- /dev/null +++ b/frontend/src/lib/components/md/Paragraph.svelte @@ -0,0 +1 @@ +

    diff --git a/frontend/src/lib/components/md/Table.svelte b/frontend/src/lib/components/md/Table.svelte new file mode 100644 index 0000000..8dc3192 --- /dev/null +++ b/frontend/src/lib/components/md/Table.svelte @@ -0,0 +1,3 @@ +
    +
    +
    diff --git a/frontend/src/lib/components/md/TableBody.svelte b/frontend/src/lib/components/md/TableBody.svelte new file mode 100644 index 0000000..3ecda87 --- /dev/null +++ b/frontend/src/lib/components/md/TableBody.svelte @@ -0,0 +1 @@ + diff --git a/frontend/src/lib/components/md/TableCell.svelte b/frontend/src/lib/components/md/TableCell.svelte new file mode 100644 index 0000000..322b9bb --- /dev/null +++ b/frontend/src/lib/components/md/TableCell.svelte @@ -0,0 +1,18 @@ + + +{#if header} + +{:else} + +{/if} diff --git a/frontend/src/lib/components/md/TableHead.svelte b/frontend/src/lib/components/md/TableHead.svelte new file mode 100644 index 0000000..659bbbf --- /dev/null +++ b/frontend/src/lib/components/md/TableHead.svelte @@ -0,0 +1 @@ + diff --git a/frontend/src/lib/components/md/TableRow.svelte b/frontend/src/lib/components/md/TableRow.svelte new file mode 100644 index 0000000..187b478 --- /dev/null +++ b/frontend/src/lib/components/md/TableRow.svelte @@ -0,0 +1 @@ + diff --git a/frontend/src/lib/components/ui/tabs/index.ts b/frontend/src/lib/components/ui/tabs/index.ts new file mode 100644 index 0000000..d2a7939 --- /dev/null +++ b/frontend/src/lib/components/ui/tabs/index.ts @@ -0,0 +1,16 @@ +import Root from './tabs.svelte'; +import Content from './tabs-content.svelte'; +import List from './tabs-list.svelte'; +import Trigger from './tabs-trigger.svelte'; + +export { + Root, + Content, + List, + Trigger, + // + Root as Tabs, + Content as TabsContent, + List as TabsList, + Trigger as TabsTrigger +}; diff --git a/frontend/src/lib/components/ui/tabs/tabs-content.svelte b/frontend/src/lib/components/ui/tabs/tabs-content.svelte new file mode 100644 index 0000000..92044c8 --- /dev/null +++ b/frontend/src/lib/components/ui/tabs/tabs-content.svelte @@ -0,0 +1,17 @@ + + + diff --git a/frontend/src/lib/components/ui/tabs/tabs-list.svelte b/frontend/src/lib/components/ui/tabs/tabs-list.svelte new file mode 100644 index 0000000..e875fe4 --- /dev/null +++ b/frontend/src/lib/components/ui/tabs/tabs-list.svelte @@ -0,0 +1,16 @@ + + + diff --git a/frontend/src/lib/components/ui/tabs/tabs-trigger.svelte b/frontend/src/lib/components/ui/tabs/tabs-trigger.svelte new file mode 100644 index 0000000..8dac6c5 --- /dev/null +++ b/frontend/src/lib/components/ui/tabs/tabs-trigger.svelte @@ -0,0 +1,20 @@ + + + diff --git a/frontend/src/lib/components/ui/tabs/tabs.svelte b/frontend/src/lib/components/ui/tabs/tabs.svelte new file mode 100644 index 0000000..b275bda --- /dev/null +++ b/frontend/src/lib/components/ui/tabs/tabs.svelte @@ -0,0 +1,19 @@ + + + diff --git a/frontend/src/lib/components/ui/tooltip/index.ts b/frontend/src/lib/components/ui/tooltip/index.ts new file mode 100644 index 0000000..273d831 --- /dev/null +++ b/frontend/src/lib/components/ui/tooltip/index.ts @@ -0,0 +1,21 @@ +import { Tooltip as TooltipPrimitive } from 'bits-ui'; +import Trigger from './tooltip-trigger.svelte'; +import Content from './tooltip-content.svelte'; + +const Root = TooltipPrimitive.Root; +const Provider = TooltipPrimitive.Provider; +const Portal = TooltipPrimitive.Portal; + +export { + Root, + Trigger, + Content, + Provider, + Portal, + // + Root as Tooltip, + Content as TooltipContent, + Trigger as TooltipTrigger, + Provider as TooltipProvider, + Portal as TooltipPortal +}; diff --git a/frontend/src/lib/components/ui/tooltip/tooltip-content.svelte b/frontend/src/lib/components/ui/tooltip/tooltip-content.svelte new file mode 100644 index 0000000..60d68f5 --- /dev/null +++ b/frontend/src/lib/components/ui/tooltip/tooltip-content.svelte @@ -0,0 +1,47 @@ + + + + + {@render children?.()} + + {#snippet child({ props })} +
    + {/snippet} +
    +
    +
    diff --git a/frontend/src/lib/components/ui/tooltip/tooltip-trigger.svelte b/frontend/src/lib/components/ui/tooltip/tooltip-trigger.svelte new file mode 100644 index 0000000..5631d1b --- /dev/null +++ b/frontend/src/lib/components/ui/tooltip/tooltip-trigger.svelte @@ -0,0 +1,7 @@ + + + diff --git a/frontend/src/lib/constants/userRoles.ts b/frontend/src/lib/constants/userRoles.ts new file mode 100644 index 0000000..aaf6290 --- /dev/null +++ b/frontend/src/lib/constants/userRoles.ts @@ -0,0 +1,31 @@ +import { View, CodeXml, Crown, CircleDollarSign, type Icon } from '@lucide/svelte'; +import type { Component } from 'svelte'; + +export const USER_ROLES: { + [key: string]: { + label: string; + description: string; + icon: Component; + }; +} = { + VIEWER: { + label: 'Viewer', + description: 'Can view projects and data, but cannot make changes.', + icon: View + }, + FOUNDER: { + label: 'Founder', + description: 'Can create and manage projects', + icon: Crown + }, + DEVELOPER: { + label: 'Developer', + description: 'Can contribute to projects and manage code.', + icon: CodeXml + }, + INVESTOR: { + label: 'Investor', + description: 'Can financially support projects.', + icon: CircleDollarSign + } +}; diff --git a/frontend/src/routes/app/(components)/ChooseRoleDialog.svelte b/frontend/src/routes/app/(components)/ChooseRoleDialog.svelte index c866635..e48a334 100644 --- a/frontend/src/routes/app/(components)/ChooseRoleDialog.svelte +++ b/frontend/src/routes/app/(components)/ChooseRoleDialog.svelte @@ -46,7 +46,8 @@ } ]; - let { open = $bindable(false) }: { open: boolean } = $props(); + let { open = $bindable(false), dismissable = false }: { open: boolean; dismissable?: boolean } = + $props(); let selectedRole: UserRole | null = $state(null); const setRoleMutation = createMutation({ @@ -63,7 +64,7 @@ - + Choose Your Role diff --git a/frontend/src/routes/app/(components)/CreateProjectDialog.svelte b/frontend/src/routes/app/(components)/CreateProjectDialog.svelte index 2b79615..8001a90 100644 --- a/frontend/src/routes/app/(components)/CreateProjectDialog.svelte +++ b/frontend/src/routes/app/(components)/CreateProjectDialog.svelte @@ -59,12 +59,13 @@
    - +