From 01081ee83451f76d8fed20250b97107419b627cf Mon Sep 17 00:00:00 2001 From: Xevion Date: Sun, 10 Nov 2024 13:50:07 -0600 Subject: [PATCH] begin adding shadcn components for frontend --- CHANGELOG.md | 1 + frontend/components.json | 21 +++++++ frontend/package.json | 7 ++- frontend/pnpm-lock.yaml | 82 +++++++++++++++++++++++++++ frontend/src/App.css | 6 ++ frontend/src/components/ui/button.tsx | 57 +++++++++++++++++++ frontend/tailwind.config.cjs | 19 +++++-- frontend/tsconfig.json | 6 +- frontend/vite.config.ts | 6 ++ 9 files changed, 197 insertions(+), 8 deletions(-) create mode 100644 frontend/components.json create mode 100644 frontend/src/components/ui/button.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a50523..f9a878c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - A release checklist to the `CHANGELOG.md` file, as a reminder for procedure. - An action workflow for invoking `pytest`, with coverage report generation in CI/CD +- backend: Login & Logout routes - backend: Rate Limiting via custom `RateLimiter` dependency - backend: `User` model, `Session` model with migration script - backend: `Session` model constraints for `token` length, `expiry` & `last_used` timestamps diff --git a/frontend/components.json b/frontend/components.json new file mode 100644 index 0000000..2d871b9 --- /dev/null +++ b/frontend/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "tailwind.config.cjs", + "css": "src/App.css", + "baseColor": "zinc", + "cssVariables": false, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index 255c944..e54e173 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -8,9 +8,14 @@ "pnpm": ">=9.0.0" }, "dependencies": { + "@radix-ui/react-slot": "^1.1.0", + "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", + "lucide-react": "^0.456.0", "react": "^18.3.1", - "react-dom": "^18.3.1" + "react-dom": "^18.3.1", + "tailwind-merge": "^2.5.4", + "tailwindcss-animate": "^1.0.7" }, "devDependencies": { "@ianvs/prettier-plugin-sort-imports": "^4.2.1", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 31dc745..97b339e 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -8,15 +8,30 @@ importers: .: dependencies: + '@radix-ui/react-slot': + specifier: ^1.1.0 + version: 1.1.0(@types/react@18.3.11)(react@18.3.1) + class-variance-authority: + specifier: ^0.7.0 + version: 0.7.0 clsx: specifier: ^2.1.1 version: 2.1.1 + lucide-react: + specifier: ^0.456.0 + version: 0.456.0(react@18.3.1) react: specifier: ^18.3.1 version: 18.3.1 react-dom: specifier: ^18.3.1 version: 18.3.1(react@18.3.1) + tailwind-merge: + specifier: ^2.5.4 + version: 2.5.4 + tailwindcss-animate: + specifier: ^1.0.7 + version: 1.0.7(tailwindcss@3.4.14(ts-node@10.9.2(@swc/core@1.7.36)(@types/node@22.9.0)(typescript@5.6.3))) devDependencies: '@ianvs/prettier-plugin-sort-imports': specifier: ^4.2.1 @@ -417,6 +432,24 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} + '@radix-ui/react-compose-refs@1.1.0': + resolution: {integrity: sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-slot@1.1.0': + resolution: {integrity: sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@rollup/rollup-android-arm-eabi@4.24.0': resolution: {integrity: sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==} cpu: [arm] @@ -766,6 +799,13 @@ packages: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} + class-variance-authority@0.7.0: + resolution: {integrity: sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==} + + clsx@2.0.0: + resolution: {integrity: sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==} + engines: {node: '>=6'} + clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} @@ -1141,6 +1181,11 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lucide-react@0.456.0: + resolution: {integrity: sha512-DIIGJqTT5X05sbAsQ+OhA8OtJYyD4NsEMCA/HQW/Y6ToPQ7gwbtujIoeAaup4HpHzV35SQOarKAWH8LYglB6eA==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc + magic-string@0.30.12: resolution: {integrity: sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==} @@ -1548,6 +1593,14 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + tailwind-merge@2.5.4: + resolution: {integrity: sha512-0q8cfZHMu9nuYP/b5Shb7Y7Sh1B7Nnl5GqNr1U+n2p6+mybvRtayrQ+0042Z5byvTA8ihjlP8Odo8/VnHbZu4Q==} + + tailwindcss-animate@1.0.7: + resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders' + tailwindcss@3.4.14: resolution: {integrity: sha512-IcSvOcTRcUtQQ7ILQL5quRDg7Xs93PdJEk1ZLbhhvJc7uj/OAhYOnruEiwnGgBvUtaUAJ8/mhSw1o8L2jCiENA==} engines: {node: '>=14.0.0'} @@ -2056,6 +2109,19 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true + '@radix-ui/react-compose-refs@1.1.0(@types/react@18.3.11)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.11 + + '@radix-ui/react-slot@1.1.0(@types/react@18.3.11)(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.11)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.11 + '@rollup/rollup-android-arm-eabi@4.24.0': optional: true @@ -2375,6 +2441,12 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + class-variance-authority@0.7.0: + dependencies: + clsx: 2.0.0 + + clsx@2.0.0: {} + clsx@2.1.1: {} color-convert@1.9.3: @@ -2738,6 +2810,10 @@ snapshots: dependencies: yallist: 3.1.1 + lucide-react@0.456.0(react@18.3.1): + dependencies: + react: 18.3.1 + magic-string@0.30.12: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 @@ -3071,6 +3147,12 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + tailwind-merge@2.5.4: {} + + tailwindcss-animate@1.0.7(tailwindcss@3.4.14(ts-node@10.9.2(@swc/core@1.7.36)(@types/node@22.9.0)(typescript@5.6.3))): + dependencies: + tailwindcss: 3.4.14(ts-node@10.9.2(@swc/core@1.7.36)(@types/node@22.9.0)(typescript@5.6.3)) + tailwindcss@3.4.14(ts-node@10.9.2(@swc/core@1.7.36)(@types/node@22.9.0)(typescript@5.6.3)): dependencies: '@alloc/quick-lru': 5.2.0 diff --git a/frontend/src/App.css b/frontend/src/App.css index dacd573..df9d556 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -18,3 +18,9 @@ body { background-color: var(--background-color); color: var(--text-color); } + +@layer base { + :root { + --radius: 0.5rem; + } +} diff --git a/frontend/src/components/ui/button.tsx b/frontend/src/components/ui/button.tsx new file mode 100644 index 0000000..4f52e23 --- /dev/null +++ b/frontend/src/components/ui/button.tsx @@ -0,0 +1,57 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const buttonVariants = cva( + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-zinc-950 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 dark:focus-visible:ring-zinc-300", + { + variants: { + variant: { + default: + "bg-zinc-900 text-zinc-50 shadow hover:bg-zinc-900/90 dark:bg-zinc-50 dark:text-zinc-900 dark:hover:bg-zinc-50/90", + destructive: + "bg-red-500 text-zinc-50 shadow-sm hover:bg-red-500/90 dark:bg-red-900 dark:text-zinc-50 dark:hover:bg-red-900/90", + outline: + "border border-zinc-200 bg-white shadow-sm hover:bg-zinc-100 hover:text-zinc-900 dark:border-zinc-800 dark:bg-zinc-950 dark:hover:bg-zinc-800 dark:hover:text-zinc-50", + secondary: + "bg-zinc-100 text-zinc-900 shadow-sm hover:bg-zinc-100/80 dark:bg-zinc-800 dark:text-zinc-50 dark:hover:bg-zinc-800/80", + ghost: "hover:bg-zinc-100 hover:text-zinc-900 dark:hover:bg-zinc-800 dark:hover:text-zinc-50", + link: "text-zinc-900 underline-offset-4 hover:underline dark:text-zinc-50", + }, + size: { + default: "h-9 px-4 py-2", + sm: "h-8 rounded-md px-3 text-xs", + lg: "h-10 rounded-md px-8", + icon: "h-9 w-9", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean +} + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "button" + return ( + + ) + } +) +Button.displayName = "Button" + +export { Button, buttonVariants } diff --git a/frontend/tailwind.config.cjs b/frontend/tailwind.config.cjs index a5782a2..612174a 100644 --- a/frontend/tailwind.config.cjs +++ b/frontend/tailwind.config.cjs @@ -1,13 +1,20 @@ /** @type {import('tailwindcss').Config} */ module.exports = { content: ['./src/**/*.{js,jsx,ts,tsx}', './*.html'], - darkMode: 'media', + darkMode: ['media', 'class'], mode: 'jit', theme: { - extend: { - fontFamily: { - inter: ['Inter', 'sans-serif'], - }, - }, + extend: { + fontFamily: { + inter: ['Inter', 'sans-serif'] + }, + borderRadius: { + lg: 'var(--radius)', + md: 'calc(var(--radius) - 2px)', + sm: 'calc(var(--radius) - 4px)' + }, + colors: {} + } }, + plugins: [require("tailwindcss-animate")] }; diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 6b6b7d7..d50276b 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -17,7 +17,11 @@ "skipLibCheck": true, "strict": true, "target": "es2017", - "types": ["vite/client"] + "types": ["vite/client"], + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } }, "exclude": ["node_modules"], "include": ["**/*.ts", "**/*.tsx"], diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index fabde1a..78caf99 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -1,6 +1,12 @@ +import path from 'path'; import react from '@vitejs/plugin-react'; import { defineConfig } from 'vite'; export default defineConfig({ plugins: [react()], + resolve: { + alias: { + '@': path.resolve(__dirname, './src'), + }, + }, });