From 25fc0127b2512e615c3c293769b9843b28a406d7 Mon Sep 17 00:00:00 2001 From: Mathis Date: Thu, 22 Aug 2024 15:24:18 +0200 Subject: [PATCH] Ajoute des composants UI et des packages Radix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ajout des nouveaux composants UI : Alert, Badge, Form, Sheet, Tabs et Input dans le répertoire frontend. Mise à jour du fichier package.json avec des nouvelles dépendances Radix UI et des autres bibliothèques nécessaires. Ajout d'une configuration pour TailwindCSS dans components.json. --- .gitignore | 2 +- apps/backend/src/app/app.service.ts | 4 + .../src/app/storage/storage.service.ts | 47 +- apps/frontend/jest.config.ts | 1 - apps/frontend/src/app/global.css | 478 +- apps/frontend/src/app/page.tsx | 463 +- apps/frontend/src/components/ui/accordion.tsx | 57 + .../src/components/ui/alert-dialog.tsx | 140 + apps/frontend/src/components/ui/alert.tsx | 58 + .../src/components/ui/aspect-ratio.tsx | 7 + apps/frontend/src/components/ui/avatar.tsx | 50 + apps/frontend/src/components/ui/badge.tsx | 35 + .../frontend/src/components/ui/breadcrumb.tsx | 114 + apps/frontend/src/components/ui/button.tsx | 55 + apps/frontend/src/components/ui/calendar.tsx | 66 + apps/frontend/src/components/ui/card.tsx | 78 + apps/frontend/src/components/ui/carousel.tsx | 261 + apps/frontend/src/components/ui/chart.tsx | 364 ++ apps/frontend/src/components/ui/checkbox.tsx | 29 + .../src/components/ui/collapsible.tsx | 11 + apps/frontend/src/components/ui/command.tsx | 155 + .../src/components/ui/context-menu.tsx | 199 + apps/frontend/src/components/ui/dialog.tsx | 121 + apps/frontend/src/components/ui/drawer.tsx | 118 + .../src/components/ui/dropdown-menu.tsx | 199 + apps/frontend/src/components/ui/form.tsx | 178 + .../frontend/src/components/ui/hover-card.tsx | 28 + apps/frontend/src/components/ui/input-otp.tsx | 70 + apps/frontend/src/components/ui/input.tsx | 24 + apps/frontend/src/components/ui/label.tsx | 25 + apps/frontend/src/components/ui/menubar.tsx | 235 + .../src/components/ui/navigation-menu.tsx | 127 + .../frontend/src/components/ui/pagination.tsx | 117 + apps/frontend/src/components/ui/popover.tsx | 30 + apps/frontend/src/components/ui/progress.tsx | 27 + .../src/components/ui/radio-group.tsx | 43 + apps/frontend/src/components/ui/resizable.tsx | 44 + .../src/components/ui/scroll-area.tsx | 47 + apps/frontend/src/components/ui/select.tsx | 160 + apps/frontend/src/components/ui/separator.tsx | 30 + apps/frontend/src/components/ui/sheet.tsx | 139 + apps/frontend/src/components/ui/skeleton.tsx | 16 + apps/frontend/src/components/ui/slider.tsx | 27 + apps/frontend/src/components/ui/sonner.tsx | 31 + apps/frontend/src/components/ui/switch.tsx | 28 + apps/frontend/src/components/ui/table.tsx | 116 + apps/frontend/src/components/ui/tabs.tsx | 54 + apps/frontend/src/components/ui/textarea.tsx | 23 + apps/frontend/src/components/ui/toast.tsx | 128 + apps/frontend/src/components/ui/toaster.tsx | 35 + .../src/components/ui/toggle-group.tsx | 61 + apps/frontend/src/components/ui/toggle.tsx | 44 + apps/frontend/src/components/ui/tooltip.tsx | 29 + apps/frontend/src/components/ui/use-toast.ts | 195 + apps/frontend/src/lib/utils.ts | 6 + apps/frontend/tailwind.config.js | 80 +- components.json | 15 + nx.json | 3 + package.json | 53 +- pnpm-lock.yaml | 4624 ++++++++++++++++- tsconfig.base.json | 17 +- 61 files changed, 9094 insertions(+), 927 deletions(-) create mode 100644 apps/frontend/src/components/ui/accordion.tsx create mode 100644 apps/frontend/src/components/ui/alert-dialog.tsx create mode 100644 apps/frontend/src/components/ui/alert.tsx create mode 100644 apps/frontend/src/components/ui/aspect-ratio.tsx create mode 100644 apps/frontend/src/components/ui/avatar.tsx create mode 100644 apps/frontend/src/components/ui/badge.tsx create mode 100644 apps/frontend/src/components/ui/breadcrumb.tsx create mode 100644 apps/frontend/src/components/ui/button.tsx create mode 100644 apps/frontend/src/components/ui/calendar.tsx create mode 100644 apps/frontend/src/components/ui/card.tsx create mode 100644 apps/frontend/src/components/ui/carousel.tsx create mode 100644 apps/frontend/src/components/ui/chart.tsx create mode 100644 apps/frontend/src/components/ui/checkbox.tsx create mode 100644 apps/frontend/src/components/ui/collapsible.tsx create mode 100644 apps/frontend/src/components/ui/command.tsx create mode 100644 apps/frontend/src/components/ui/context-menu.tsx create mode 100644 apps/frontend/src/components/ui/dialog.tsx create mode 100644 apps/frontend/src/components/ui/drawer.tsx create mode 100644 apps/frontend/src/components/ui/dropdown-menu.tsx create mode 100644 apps/frontend/src/components/ui/form.tsx create mode 100644 apps/frontend/src/components/ui/hover-card.tsx create mode 100644 apps/frontend/src/components/ui/input-otp.tsx create mode 100644 apps/frontend/src/components/ui/input.tsx create mode 100644 apps/frontend/src/components/ui/label.tsx create mode 100644 apps/frontend/src/components/ui/menubar.tsx create mode 100644 apps/frontend/src/components/ui/navigation-menu.tsx create mode 100644 apps/frontend/src/components/ui/pagination.tsx create mode 100644 apps/frontend/src/components/ui/popover.tsx create mode 100644 apps/frontend/src/components/ui/progress.tsx create mode 100644 apps/frontend/src/components/ui/radio-group.tsx create mode 100644 apps/frontend/src/components/ui/resizable.tsx create mode 100644 apps/frontend/src/components/ui/scroll-area.tsx create mode 100644 apps/frontend/src/components/ui/select.tsx create mode 100644 apps/frontend/src/components/ui/separator.tsx create mode 100644 apps/frontend/src/components/ui/sheet.tsx create mode 100644 apps/frontend/src/components/ui/skeleton.tsx create mode 100644 apps/frontend/src/components/ui/slider.tsx create mode 100644 apps/frontend/src/components/ui/sonner.tsx create mode 100644 apps/frontend/src/components/ui/switch.tsx create mode 100644 apps/frontend/src/components/ui/table.tsx create mode 100644 apps/frontend/src/components/ui/tabs.tsx create mode 100644 apps/frontend/src/components/ui/textarea.tsx create mode 100644 apps/frontend/src/components/ui/toast.tsx create mode 100644 apps/frontend/src/components/ui/toaster.tsx create mode 100644 apps/frontend/src/components/ui/toggle-group.tsx create mode 100644 apps/frontend/src/components/ui/toggle.tsx create mode 100644 apps/frontend/src/components/ui/tooltip.tsx create mode 100644 apps/frontend/src/components/ui/use-toast.ts create mode 100644 apps/frontend/src/lib/utils.ts create mode 100644 components.json diff --git a/.gitignore b/.gitignore index f625cab..38ffc69 100644 --- a/.gitignore +++ b/.gitignore @@ -46,4 +46,4 @@ Thumbs.db out # ENV VAR -.env +.env \ No newline at end of file diff --git a/apps/backend/src/app/app.service.ts b/apps/backend/src/app/app.service.ts index 613794f..6643ef5 100644 --- a/apps/backend/src/app/app.service.ts +++ b/apps/backend/src/app/app.service.ts @@ -6,3 +6,7 @@ export class AppService { return { message: "Hello API" }; } } + +const hello: object = { + name: "ABC", +}; diff --git a/apps/backend/src/app/storage/storage.service.ts b/apps/backend/src/app/storage/storage.service.ts index f0f6382..c24b452 100644 --- a/apps/backend/src/app/storage/storage.service.ts +++ b/apps/backend/src/app/storage/storage.service.ts @@ -1,13 +1,17 @@ -import { BadRequestException, Injectable, InternalServerErrorException, NotFoundException } from "@nestjs/common"; -import * as crypto from "node:crypto"; -import { readFile, writeFile } from 'node:fs/promises'; -import { join } from "node:path"; import * as console from "node:console"; -import FileType from 'file-type'; +import * as crypto from "node:crypto"; +import { readFile, writeFile } from "node:fs/promises"; +import { join } from "node:path"; +import { + BadRequestException, + Injectable, + InternalServerErrorException, + NotFoundException, +} from "@nestjs/common"; +import FileType from "file-type"; @Injectable() export class StorageService { - /** * Saves the given file with a generated file name that includes a prefix, * the checksum of the file, and the file type extension. @@ -17,10 +21,10 @@ export class StorageService { * @returns {Promise} A promise that resolves with the generated file name after the file is saved. */ private async save(prefix: string, file: Buffer) { - const checksum = await this.checkConditions(file) - const fileType = await FileType.fileTypeFromBuffer(file) + const checksum = await this.checkConditions(file); + const fileType = await FileType.fileTypeFromBuffer(file); const fileName = `${prefix.toLowerCase()}-${checksum}.${fileType.ext.toLowerCase()}`; - await this.saveFile(fileName, file) + await this.saveFile(fileName, file); return fileName; //SEC TODO Catch case } @@ -32,7 +36,7 @@ export class StorageService { * @return {string} - The hexadecimal representation of the checksum. */ private getChecksum(file: Buffer): string { - return crypto.createHash('sha256').update(file).digest('hex').toLowerCase(); + return crypto.createHash("sha256").update(file).digest("hex").toLowerCase(); } /** @@ -47,10 +51,10 @@ export class StorageService { */ private async saveFile(fileName: string, file: Buffer): Promise { try { - await writeFile(join(process.cwd(), 'files/', fileName), file, 'utf8'); + await writeFile(join(process.cwd(), "files/", fileName), file, "utf8"); } catch (err) { console.error(err); - throw new InternalServerErrorException("File save failed !") + throw new InternalServerErrorException("File save failed !"); } } @@ -62,17 +66,21 @@ export class StorageService { * @throws BadRequestException - If the file type is invalid or the file size exceeds the limit. */ private async checkConditions(file: Buffer): Promise { - const checksum = this.getChecksum(file) - const fileType = await FileType.fileTypeFromBuffer(file) + const checksum = this.getChecksum(file); + const fileType = await FileType.fileTypeFromBuffer(file); //TODO make file type configurable by admin - if (!fileType || !fileType.mime.startsWith('image/')) { - throw new BadRequestException('Invalid file type. Only images are allowed.'); + if (!fileType || !fileType.mime.startsWith("image/")) { + throw new BadRequestException( + "Invalid file type. Only images are allowed.", + ); } //check file size is less than 2mb const fileSize = file.byteLength; //TODO make file size configurable by admin if (fileSize > 2 * 1024 * 1024) { - throw new BadRequestException('File size exceeds the limit. Maximum file size allowed is 2MB.'); + throw new BadRequestException( + "File size exceeds the limit. Maximum file size allowed is 2MB.", + ); } return checksum; } @@ -86,10 +94,9 @@ export class StorageService { */ private async getFile(fileName: string): Promise { try { - return await readFile(join(process.cwd(), 'files/', fileName)); + return await readFile(join(process.cwd(), "files/", fileName)); } catch (err) { - throw new NotFoundException("File not found") + throw new NotFoundException("File not found"); } } - } diff --git a/apps/frontend/jest.config.ts b/apps/frontend/jest.config.ts index ce9edd7..0936b61 100644 --- a/apps/frontend/jest.config.ts +++ b/apps/frontend/jest.config.ts @@ -1,4 +1,3 @@ -/* eslint-disable */ export default { displayName: "frontend", preset: "../../jest.preset.js", diff --git a/apps/frontend/src/app/global.css b/apps/frontend/src/app/global.css index 1d6827b..e11e133 100644 --- a/apps/frontend/src/app/global.css +++ b/apps/frontend/src/app/global.css @@ -2,412 +2,80 @@ @tailwind components; @tailwind utilities; -html { - -webkit-text-size-adjust: 100%; - font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, - Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif, - Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji; - line-height: 1.5; - tab-size: 4; - scroll-behavior: smooth; -} -body { - font-family: inherit; - line-height: inherit; - margin: 0; -} -h1, -h2, -p, -pre { - margin: 0; -} -*, -::before, -::after { - box-sizing: border-box; - border-width: 0; - border-style: solid; - border-color: currentColor; -} -h1, -h2 { - font-size: inherit; - font-weight: inherit; -} -a { - color: inherit; - text-decoration: inherit; -} -pre { - font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, - Liberation Mono, Courier New, monospace; -} -svg { - display: block; - vertical-align: middle; - shape-rendering: auto; - text-rendering: optimizeLegibility; -} -pre { - background-color: rgba(55, 65, 81, 1); - border-radius: 0.25rem; - color: rgba(229, 231, 235, 1); - font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, - Liberation Mono, Courier New, monospace; - overflow: scroll; - padding: 0.5rem 0.75rem; +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 222.2 47.4% 11.2%; + + --muted: 210 40% 96.1%; + --muted-foreground: 215.4 16.3% 46.9%; + + --popover: 0 0% 100%; + --popover-foreground: 222.2 47.4% 11.2%; + + --border: 214.3 31.8% 91.4%; + --input: 214.3 31.8% 91.4%; + + --card: 0 0% 100%; + --card-foreground: 222.2 47.4% 11.2%; + + --primary: 222.2 47.4% 11.2%; + --primary-foreground: 210 40% 98%; + + --secondary: 210 40% 96.1%; + --secondary-foreground: 222.2 47.4% 11.2%; + + --accent: 210 40% 96.1%; + --accent-foreground: 222.2 47.4% 11.2%; + + --destructive: 0 100% 50%; + --destructive-foreground: 210 40% 98%; + + --ring: 215 20.2% 65.1%; + + --radius: 0.5rem; + } + + .dark { + --background: 224 71% 4%; + --foreground: 213 31% 91%; + + --muted: 223 47% 11%; + --muted-foreground: 215.4 16.3% 56.9%; + + --accent: 216 34% 17%; + --accent-foreground: 210 40% 98%; + + --popover: 224 71% 4%; + --popover-foreground: 215 20.2% 65.1%; + + --border: 216 34% 17%; + --input: 216 34% 17%; + + --card: 224 71% 4%; + --card-foreground: 213 31% 91%; + + --primary: 210 40% 98%; + --primary-foreground: 222.2 47.4% 1.2%; + + --secondary: 222.2 47.4% 11.2%; + --secondary-foreground: 210 40% 98%; + + --destructive: 0 63% 31%; + --destructive-foreground: 210 40% 98%; + + --ring: 216 34% 17%; + + --radius: 0.5rem; + } } -.shadow { - box-shadow: 0 0 #0000, 0 0 #0000, 0 10px 15px -3px rgba(0, 0, 0, 0.1), - 0 4px 6px -2px rgba(0, 0, 0, 0.05); -} -.rounded { - border-radius: 1.5rem; -} -.wrapper { - width: 100%; -} -.container { - margin-left: auto; - margin-right: auto; - max-width: 768px; - padding-bottom: 3rem; - padding-left: 1rem; - padding-right: 1rem; - color: rgba(55, 65, 81, 1); - width: 100%; -} -#welcome { - margin-top: 2.5rem; -} -#welcome h1 { - font-size: 3rem; - font-weight: 500; - letter-spacing: -0.025em; - line-height: 1; -} -#welcome span { - display: block; - font-size: 1.875rem; - font-weight: 300; - line-height: 2.25rem; - margin-bottom: 0.5rem; -} -#hero { - align-items: center; - background-color: hsla(214, 62%, 21%, 1); - border: none; - box-sizing: border-box; - color: rgba(55, 65, 81, 1); - display: grid; - grid-template-columns: 1fr; - margin-top: 3.5rem; -} -#hero .text-container { - color: rgba(255, 255, 255, 1); - padding: 3rem 2rem; -} -#hero .text-container h2 { - font-size: 1.5rem; - line-height: 2rem; - position: relative; -} -#hero .text-container h2 svg { - color: hsla(162, 47%, 50%, 1); - height: 2rem; - left: -0.25rem; - position: absolute; - top: 0; - width: 2rem; -} -#hero .text-container h2 span { - margin-left: 2.5rem; -} -#hero .text-container a { - background-color: rgba(255, 255, 255, 1); - border-radius: 0.75rem; - color: rgba(55, 65, 81, 1); - display: inline-block; - margin-top: 1.5rem; - padding: 1rem 2rem; - text-decoration: inherit; -} -#hero .logo-container { - display: none; - justify-content: center; - padding-left: 2rem; - padding-right: 2rem; -} -#hero .logo-container svg { - color: rgba(255, 255, 255, 1); - width: 66.666667%; -} -#middle-content { - align-items: flex-start; - display: grid; - gap: 4rem; - grid-template-columns: 1fr; - margin-top: 3.5rem; -} -#learning-materials { - padding: 2.5rem 2rem; -} -#learning-materials h2 { - font-weight: 500; - font-size: 1.25rem; - letter-spacing: -0.025em; - line-height: 1.75rem; - padding-left: 1rem; - padding-right: 1rem; -} -.list-item-link { - align-items: center; - border-radius: 0.75rem; - display: flex; - margin-top: 1rem; - padding: 1rem; - transition-property: background-color, border-color, color, fill, stroke, - opacity, box-shadow, transform, filter, backdrop-filter, - -webkit-backdrop-filter; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; - width: 100%; -} -.list-item-link svg:first-child { - margin-right: 1rem; - height: 1.5rem; - transition-property: background-color, border-color, color, fill, stroke, - opacity, box-shadow, transform, filter, backdrop-filter, - -webkit-backdrop-filter; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; - width: 1.5rem; -} -.list-item-link > span { - flex-grow: 1; - font-weight: 400; - transition-property: background-color, border-color, color, fill, stroke, - opacity, box-shadow, transform, filter, backdrop-filter, - -webkit-backdrop-filter; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; -} -.list-item-link > span > span { - color: rgba(107, 114, 128, 1); - display: block; - flex-grow: 1; - font-size: 0.75rem; - font-weight: 300; - line-height: 1rem; - transition-property: background-color, border-color, color, fill, stroke, - opacity, box-shadow, transform, filter, backdrop-filter, - -webkit-backdrop-filter; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; -} -.list-item-link svg:last-child { - height: 1rem; - transition-property: all; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; - width: 1rem; -} -.list-item-link:hover { - color: rgba(255, 255, 255, 1); - background-color: hsla(162, 47%, 50%, 1); -} -.list-item-link:hover > span { -} -.list-item-link:hover > span > span { - color: rgba(243, 244, 246, 1); -} -.list-item-link:hover svg:last-child { - transform: translateX(0.25rem); -} -#other-links { -} -.button-pill { - padding: 1.5rem 2rem; - transition-duration: 300ms; - transition-property: background-color, border-color, color, fill, stroke, - opacity, box-shadow, transform, filter, backdrop-filter, - -webkit-backdrop-filter; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - align-items: center; - display: flex; -} -.button-pill svg { - transition-property: background-color, border-color, color, fill, stroke, - opacity, box-shadow, transform, filter, backdrop-filter, - -webkit-backdrop-filter; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; - flex-shrink: 0; - width: 3rem; -} -.button-pill > span { - letter-spacing: -0.025em; - font-weight: 400; - font-size: 1.125rem; - line-height: 1.75rem; - padding-left: 1rem; - padding-right: 1rem; -} -.button-pill span span { - display: block; - font-size: 0.875rem; - font-weight: 300; - line-height: 1.25rem; -} -.button-pill:hover svg, -.button-pill:hover { - color: rgba(255, 255, 255, 1) !important; -} -#nx-console:hover { - background-color: rgba(0, 122, 204, 1); -} -#nx-console svg { - color: rgba(0, 122, 204, 1); -} -#nx-console-jetbrains { - margin-top: 2rem; -} -#nx-console-jetbrains:hover { - background-color: rgba(255, 49, 140, 1); -} -#nx-console-jetbrains svg { - color: rgba(255, 49, 140, 1); -} -#nx-repo:hover { - background-color: rgba(24, 23, 23, 1); -} -#nx-repo svg { - color: rgba(24, 23, 23, 1); -} -#nx-cloud { - margin-bottom: 2rem; - margin-top: 2rem; - padding: 2.5rem 2rem; -} -#nx-cloud > div { - align-items: center; - display: flex; -} -#nx-cloud > div svg { - border-radius: 0.375rem; - flex-shrink: 0; - width: 3rem; -} -#nx-cloud > div h2 { - font-size: 1.125rem; - font-weight: 400; - letter-spacing: -0.025em; - line-height: 1.75rem; - padding-left: 1rem; - padding-right: 1rem; -} -#nx-cloud > div h2 span { - display: block; - font-size: 0.875rem; - font-weight: 300; - line-height: 1.25rem; -} -#nx-cloud p { - font-size: 1rem; - line-height: 1.5rem; - margin-top: 1rem; -} -#nx-cloud pre { - margin-top: 1rem; -} -#nx-cloud a { - color: rgba(107, 114, 128, 1); - display: block; - font-size: 0.875rem; - line-height: 1.25rem; - margin-top: 1.5rem; - text-align: right; -} -#nx-cloud a:hover { - text-decoration: underline; -} -#commands { - padding: 2.5rem 2rem; - margin-top: 3.5rem; -} -#commands h2 { - font-size: 1.25rem; - font-weight: 400; - letter-spacing: -0.025em; - line-height: 1.75rem; - padding-left: 1rem; - padding-right: 1rem; -} -#commands p { - font-size: 1rem; - font-weight: 300; - line-height: 1.5rem; - margin-top: 1rem; - padding-left: 1rem; - padding-right: 1rem; -} -details { - align-items: center; - display: flex; - margin-top: 1rem; - padding-left: 1rem; - padding-right: 1rem; - width: 100%; -} -details pre > span { - color: rgba(181, 181, 181, 1); - display: block; -} -summary { - border-radius: 0.5rem; - display: flex; - font-weight: 400; - padding: 0.5rem; - cursor: pointer; - transition-property: background-color, border-color, color, fill, stroke, - opacity, box-shadow, transform, filter, backdrop-filter, - -webkit-backdrop-filter; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; -} -summary:hover { - background-color: rgba(243, 244, 246, 1); -} -summary svg { - height: 1.5rem; - margin-right: 1rem; - width: 1.5rem; -} -#love { - color: rgba(107, 114, 128, 1); - font-size: 0.875rem; - line-height: 1.25rem; - margin-top: 3.5rem; - opacity: 0.6; - text-align: center; -} -#love svg { - color: rgba(252, 165, 165, 1); - width: 1.25rem; - height: 1.25rem; - display: inline; - margin-top: -0.25rem; -} -@media screen and (min-width: 768px) { - #hero { - grid-template-columns: repeat(2, minmax(0, 1fr)); +@layer base { + * { + @apply border-border; } - #hero .logo-container { - display: flex; + body { + @apply bg-background text-foreground; + font-feature-settings: "rlig" 1, "calt" 1; } - #middle-content { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } -} +} \ No newline at end of file diff --git a/apps/frontend/src/app/page.tsx b/apps/frontend/src/app/page.tsx index aa6f19e..7f1cb1c 100644 --- a/apps/frontend/src/app/page.tsx +++ b/apps/frontend/src/app/page.tsx @@ -1,467 +1,12 @@ -export default function Index() { +export default function HomePage() { /* * Replace the elements below with your own. * * Note: The corresponding styles are in the ./index.tailwind file. */ return ( -
-
-
-
-

- Hello there, - Welcome frontend 👋 -

-
- -
-
-

- - - - You're up and running -

- What's next? -
-
- - - -
-
- - - -
-

Next steps

-

Here are some things you can do with Nx:

-
- - - - - Add UI library - -
-                # Generate UI lib
-                nx g @nx/next:library ui
-                # Add a component
-                nx g @nx/next:component ui/src/lib/button
-              
-
-
- - - - - View project details - -
nx show project frontend --web
-
-
- - - - - View interactive project graph - -
nx graph
-
-
- - - - - Run affected commands - -
-                # see what's been affected by changes
-                nx affected:graph
-                # run tests for current changes
-                nx affected:test
-                # run e2e tests for current changes
-                nx affected:e2e
-              
-
-
- -

- Carefully crafted with - - - -

-
-
-
+
+

Hello world

+
); } diff --git a/apps/frontend/src/components/ui/accordion.tsx b/apps/frontend/src/components/ui/accordion.tsx new file mode 100644 index 0000000..2c401f4 --- /dev/null +++ b/apps/frontend/src/components/ui/accordion.tsx @@ -0,0 +1,57 @@ +"use client" + +import * as React from "react" +import * as AccordionPrimitive from "@radix-ui/react-accordion" +import { ChevronDown } from "lucide-react" +import {cn} from "apps/frontend/src/lib/utils"; + +const Accordion = AccordionPrimitive.Root + +const AccordionItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AccordionItem.displayName = "AccordionItem" + +const AccordionTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + svg]:rotate-180", + className + )} + {...props} + > + {children} + + + +)) +AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName + +const AccordionContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + +
{children}
+
+)) + +AccordionContent.displayName = AccordionPrimitive.Content.displayName + +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } diff --git a/apps/frontend/src/components/ui/alert-dialog.tsx b/apps/frontend/src/components/ui/alert-dialog.tsx new file mode 100644 index 0000000..1c6478c --- /dev/null +++ b/apps/frontend/src/components/ui/alert-dialog.tsx @@ -0,0 +1,140 @@ +"use client" + +import * as React from "react" +import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" +import {cn} from "apps/frontend/src/lib/utils"; +import {buttonVariants} from "apps/frontend/src/components/ui/button"; + +const AlertDialog = AlertDialogPrimitive.Root + +const AlertDialogTrigger = AlertDialogPrimitive.Trigger + +const AlertDialogPortal = AlertDialogPrimitive.Portal + +const AlertDialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName + +const AlertDialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + +)) +AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName + +const AlertDialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +AlertDialogHeader.displayName = "AlertDialogHeader" + +const AlertDialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +AlertDialogFooter.displayName = "AlertDialogFooter" + +const AlertDialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName + +const AlertDialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogDescription.displayName = + AlertDialogPrimitive.Description.displayName + +const AlertDialogAction = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName + +const AlertDialogCancel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName + +export { + AlertDialog, + AlertDialogPortal, + AlertDialogOverlay, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +} diff --git a/apps/frontend/src/components/ui/alert.tsx b/apps/frontend/src/components/ui/alert.tsx new file mode 100644 index 0000000..04bfe0b --- /dev/null +++ b/apps/frontend/src/components/ui/alert.tsx @@ -0,0 +1,58 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" +import {cn} from "apps/frontend/src/lib/utils"; + +const alertVariants = cva( + "relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground", + { + variants: { + variant: { + default: "bg-background text-foreground", + destructive: + "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +const Alert = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & VariantProps +>(({ className, variant, ...props }, ref) => ( +
+)) +Alert.displayName = "Alert" + +const AlertTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertTitle.displayName = "AlertTitle" + +const AlertDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertDescription.displayName = "AlertDescription" + +export { Alert, AlertTitle, AlertDescription } diff --git a/apps/frontend/src/components/ui/aspect-ratio.tsx b/apps/frontend/src/components/ui/aspect-ratio.tsx new file mode 100644 index 0000000..d6a5226 --- /dev/null +++ b/apps/frontend/src/components/ui/aspect-ratio.tsx @@ -0,0 +1,7 @@ +"use client" + +import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio" + +const AspectRatio = AspectRatioPrimitive.Root + +export { AspectRatio } diff --git a/apps/frontend/src/components/ui/avatar.tsx b/apps/frontend/src/components/ui/avatar.tsx new file mode 100644 index 0000000..51e507b --- /dev/null +++ b/apps/frontend/src/components/ui/avatar.tsx @@ -0,0 +1,50 @@ +"use client" + +import * as React from "react" +import * as AvatarPrimitive from "@radix-ui/react-avatar" + +import { cn } from "@/lib/utils" + +const Avatar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +Avatar.displayName = AvatarPrimitive.Root.displayName + +const AvatarImage = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarImage.displayName = AvatarPrimitive.Image.displayName + +const AvatarFallback = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName + +export { Avatar, AvatarImage, AvatarFallback } diff --git a/apps/frontend/src/components/ui/badge.tsx b/apps/frontend/src/components/ui/badge.tsx new file mode 100644 index 0000000..24436d4 --- /dev/null +++ b/apps/frontend/src/components/ui/badge.tsx @@ -0,0 +1,35 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" +import {cn} from "apps/frontend/src/lib/utils"; + +const badgeVariants = cva( + "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", + { + variants: { + variant: { + default: + "border-transparent bg-primary text-primary-foreground hover:bg-primary/80", + secondary: + "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", + destructive: + "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80", + outline: "text-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +export interface BadgeProps + extends React.HTMLAttributes, + VariantProps {} + +function Badge({ className, variant, ...props }: BadgeProps) { + return ( +
+ ) +} + +export { Badge, badgeVariants } diff --git a/apps/frontend/src/components/ui/breadcrumb.tsx b/apps/frontend/src/components/ui/breadcrumb.tsx new file mode 100644 index 0000000..89650ac --- /dev/null +++ b/apps/frontend/src/components/ui/breadcrumb.tsx @@ -0,0 +1,114 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { ChevronRight, MoreHorizontal } from "lucide-react" +import {cn} from "apps/frontend/src/lib/utils"; + +const Breadcrumb = React.forwardRef< + HTMLElement, + React.ComponentPropsWithoutRef<"nav"> & { + separator?: React.ReactNode + } +>(({ ...props }, ref) =>