diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 0000000..972e541 --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,125 @@ +# Design System Inspired by Kraken + +## 1. Visual Theme & Atmosphere + +Kraken's website is a clean, trustworthy crypto exchange that uses purple as its commanding brand color. The design operates on white backgrounds with Kraken Purple (`#7132f5`, `#5741d8`, `#5b1ecf`) creating a distinctive, professional crypto identity. The proprietary Kraken-Brand font handles display headings with bold (700) weight and negative tracking, while Kraken-Product (with IBM Plex Sans fallback) serves as the UI workhorse. + +**Key Characteristics:** +- Kraken Purple (`#7132f5`) as primary brand with darker variants (`#5741d8`, `#5b1ecf`) +- Kraken-Brand (display) + Kraken-Product (UI) dual font system +- Near-black (`#101114`) text with cool blue-gray neutral scale +- 12px radius buttons (rounded but not pill) +- Subtle shadows (`rgba(0,0,0,0.03) 0px 4px 24px`) — whisper-level +- Green accent (`#149e61`) for positive/success states + +## 2. Color Palette & Roles + +### Primary +- **Kraken Purple** (`#7132f5`): Primary CTA, brand accent, links +- **Purple Dark** (`#5741d8`): Button borders, outlined variants +- **Purple Deep** (`#5b1ecf`): Deepest purple +- **Purple Subtle** (`rgba(133,91,251,0.16)`): Purple at 16% — subtle button backgrounds +- **Near Black** (`#101114`): Primary text + +### Neutral +- **Cool Gray** (`#686b82`): Primary neutral, borders at 24% opacity +- **Silver Blue** (`#9497a9`): Secondary text, muted elements +- **White** (`#ffffff`): Primary surface +- **Border Gray** (`#dedee5`): Divider borders + +### Semantic +- **Green** (`#149e61`): Success/positive at 16% opacity for badges +- **Green Dark** (`#026b3f`): Badge text + +## 3. Typography Rules + +### Font Families +- **Display**: `Kraken-Brand`, fallbacks: `IBM Plex Sans, Helvetica, Arial` +- **UI / Body**: `Kraken-Product`, fallbacks: `Helvetica Neue, Helvetica, Arial` + +### Hierarchy + +| Role | Font | Size | Weight | Line Height | Letter Spacing | +|------|------|------|--------|-------------|----------------| +| Display Hero | Kraken-Brand | 48px | 700 | 1.17 | -1px | +| Section Heading | Kraken-Brand | 36px | 700 | 1.22 | -0.5px | +| Sub-heading | Kraken-Brand | 28px | 700 | 1.29 | -0.5px | +| Feature Title | Kraken-Product | 22px | 600 | 1.20 | normal | +| Body | Kraken-Product | 16px | 400 | 1.38 | normal | +| Body Medium | Kraken-Product | 16px | 500 | 1.38 | normal | +| Button | Kraken-Product | 16px | 500–600 | 1.38 | normal | +| Caption | Kraken-Product | 14px | 400–700 | 1.43–1.71 | normal | +| Small | Kraken-Product | 12px | 400–500 | 1.33 | normal | +| Micro | Kraken-Product | 7px | 500 | 1.00 | uppercase | + +## 4. Component Stylings + +### Buttons + +**Primary Purple** +- Background: `#7132f5` +- Text: `#ffffff` +- Padding: 13px 16px +- Radius: 12px + +**Purple Outlined** +- Background: `#ffffff` +- Text: `#5741d8` +- Border: `1px solid #5741d8` +- Radius: 12px + +**Purple Subtle** +- Background: `rgba(133,91,251,0.16)` +- Text: `#7132f5` +- Padding: 8px +- Radius: 12px + +**White Button** +- Background: `#ffffff` +- Text: `#101114` +- Radius: 10px +- Shadow: `rgba(0,0,0,0.03) 0px 4px 24px` + +**Secondary Gray** +- Background: `rgba(148,151,169,0.08)` +- Text: `#101114` +- Radius: 12px + +### Badges +- Success: `rgba(20,158,97,0.16)` bg, `#026b3f` text, 6px radius +- Neutral: `rgba(104,107,130,0.12)` bg, `#484b5e` text, 8px radius + +## 5. Layout Principles + +### Spacing: 1px, 2px, 3px, 4px, 5px, 6px, 8px, 10px, 12px, 13px, 15px, 16px, 20px, 24px, 25px +### Border Radius: 3px, 6px, 8px, 10px, 12px, 16px, 9999px, 50% + +## 6. Depth & Elevation +- Subtle: `rgba(0,0,0,0.03) 0px 4px 24px` +- Micro: `rgba(16,24,40,0.04) 0px 1px 4px` + +## 7. Do's and Don'ts + +### Do +- Use Kraken Purple (#7132f5) for CTAs and links +- Apply 12px radius on all buttons +- Use Kraken-Brand for headings, Kraken-Product for body + +### Don't +- Don't use pill buttons — 12px is the max radius for buttons +- Don't use other purples outside the defined scale + +## 8. Responsive Behavior +Breakpoints: 375px, 425px, 640px, 768px, 1024px, 1280px, 1536px + +## 9. Agent Prompt Guide + +### Quick Color Reference +- Brand: Kraken Purple (`#7132f5`) +- Dark variant: `#5741d8` +- Text: Near Black (`#101114`) +- Secondary text: `#9497a9` +- Background: White (`#ffffff`) + +### Example Component Prompts +- "Create hero: white background. Kraken-Brand 48px weight 700, letter-spacing -1px. Purple CTA (#7132f5, 12px radius, 13px 16px padding)." diff --git a/package-lock.json b/package-lock.json index 16eed35..39f105f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,11 +8,17 @@ "name": "aplp.frontend.lms", "version": "0.0.0", "dependencies": { - "@heroui/react": "^3.2.4", - "@heroui/styles": "^3.2.4", + "@radix-ui/react-label": "^2.1.15", + "@radix-ui/react-separator": "^1.1.15", + "@radix-ui/react-slot": "^1.3.3", + "@radix-ui/react-tabs": "^1.1.21", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", "react": "^19.2.8", "react-dom": "^19.2.8", + "react-icons": "^5.7.0", "react-router-dom": "^7.18.2", + "tailwind-merge": "^3.6.0", "tailwindcss": "^4.3.3" }, "devDependencies": { @@ -28,50 +34,6 @@ "vite": "^8.2.0" } }, - "node_modules/@adobe/react-spectrum": { - "version": "3.47.3", - "resolved": "https://registry.npmjs.org/@adobe/react-spectrum/-/react-spectrum-3.47.3.tgz", - "integrity": "sha512-tWZG59+xTbXIqeZB4uyuo2qljk3nRK66IkdBmMgbl2p6peApkKbrtJ/YOM8rW4gemdmv7qoJlbIHvy8aJkYsCQ==", - "license": "Apache-2.0", - "dependencies": { - "@internationalized/date": "^3.12.3", - "@react-types/shared": "^3.36.1", - "@spectrum-icons/ui": "^3.7.1", - "@spectrum-icons/workflow": "^4.3.1", - "@swc/helpers": "^0.5.0", - "client-only": "^0.0.1", - "clsx": "^2.0.0", - "react-aria": "3.51.0", - "react-aria-components": "1.20.0", - "react-stately": "3.49.0", - "react-transition-group": "^4.4.5", - "use-sync-external-store": "^1.6.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@adobe/react-spectrum-ui": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@adobe/react-spectrum-ui/-/react-spectrum-ui-1.2.1.tgz", - "integrity": "sha512-wcrbEE2O/9WnEn6avBnaVRRx88S5PLFsPLr4wffzlbMfXeQsy+RMQwaJd3cbzrn18/j04Isit7f7Emfn0dhrJA==", - "license": "Apache-2.0", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/@adobe/react-spectrum-workflow": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@adobe/react-spectrum-workflow/-/react-spectrum-workflow-2.3.5.tgz", - "integrity": "sha512-b53VIPwPWKb/T5gzE3qs+QlGP5gVrw/LnWV3xMksDU+CRl3rzOKUwxIGiZO8ICyYh1WiyqY4myGlPU/nAynBUg==", - "license": "Apache-2.0", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0" - } - }, "node_modules/@babel/code-frame": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", @@ -264,15 +226,6 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/runtime": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", - "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/template": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", @@ -321,6 +274,40 @@ "node": ">=6.9.0" } }, + "node_modules/@emnapi/core": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.3.tgz", + "integrity": "sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.3", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.3.tgz", + "integrity": "sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", @@ -449,101 +436,6 @@ "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@formatjs/ecma402-abstract": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.6.tgz", - "integrity": "sha512-HJnTFeRM2kVFVr5gr5kH1XP6K0JcJtE7Lzvtr3FS/so5f1kpsqqqxy5JF+FRaO6H2qmcMfAUIox7AJteieRtVw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@formatjs/fast-memoize": "2.2.7", - "@formatjs/intl-localematcher": "0.6.2", - "decimal.js": "^10.4.3", - "tslib": "^2.8.0" - } - }, - "node_modules/@formatjs/fast-memoize": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.7.tgz", - "integrity": "sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "tslib": "^2.8.0" - } - }, - "node_modules/@formatjs/icu-messageformat-parser": { - "version": "2.11.4", - "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.4.tgz", - "integrity": "sha512-7kR78cRrPNB4fjGFZg3Rmj5aah8rQj9KPzuLsmcSn4ipLXQvC04keycTI1F7kJYDwIXtT2+7IDEto842CfZBtw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@formatjs/ecma402-abstract": "2.3.6", - "@formatjs/icu-skeleton-parser": "1.8.16", - "tslib": "^2.8.0" - } - }, - "node_modules/@formatjs/icu-skeleton-parser": { - "version": "1.8.16", - "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.16.tgz", - "integrity": "sha512-H13E9Xl+PxBd8D5/6TVUluSpxGNvFSlN/b3coUp0e0JpuWXXnQDiavIpY3NnvSp4xhEMoXyyBvVfdFX8jglOHQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "@formatjs/ecma402-abstract": "2.3.6", - "tslib": "^2.8.0" - } - }, - "node_modules/@formatjs/intl-localematcher": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.6.2.tgz", - "integrity": "sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA==", - "license": "MIT", - "peer": true, - "dependencies": { - "tslib": "^2.8.0" - } - }, - "node_modules/@heroui/react": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@heroui/react/-/react-3.2.4.tgz", - "integrity": "sha512-ZXCTyz1G7bgCdHVxRPy4vIRI/wdb0ZvzEUJGG7CAEp0zqQsKrTH1o9i3Ns8yM6EidtAyfMosULN29gFA3wegBA==", - "license": "MIT", - "dependencies": { - "@heroui/styles": "3.2.4", - "@radix-ui/react-avatar": "1.1.11", - "@react-stately/utils": "3.12.1", - "@react-types/color": "3.2.0", - "@react-types/shared": "3.36.1", - "input-otp": "1.4.2", - "tailwind-merge": "3.4.0", - "tailwind-variants": "3.3.1" - }, - "peerDependencies": { - "@react-aria/i18n": "^3.13.1", - "@react-aria/ssr": "^3.10.1", - "@react-aria/utils": "^3.34.1", - "react": ">=19.0.0", - "react-aria": "^3.51.0", - "react-aria-components": "^1.20.0", - "react-dom": ">=19.0.0", - "tailwindcss": ">=4.0.0" - } - }, - "node_modules/@heroui/styles": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@heroui/styles/-/styles-3.2.4.tgz", - "integrity": "sha512-tjiT7VFUSjw7TA6ZjeT+D3oX62+D27l7dhPwW+emwPnkdkRoKALFJEPBsxpmEI/x18uNkKFIoTk85bICyOO8Vg==", - "license": "MIT", - "dependencies": { - "tailwind-variants": "3.3.1", - "tw-animate-css": "1.4.0" - }, - "peerDependencies": { - "tailwindcss": ">=4.0.0" - } - }, "node_modules/@humanfs/core": { "version": "0.19.2", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", @@ -610,44 +502,6 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@internationalized/date": { - "version": "3.12.3", - "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.12.3.tgz", - "integrity": "sha512-fuLX+3ZKLsxI73y8b01EG/WjHb6gE6weCqlfawPO27kBWGMh9G1yH6Csv1uU7/cac9H2GHmOMt6CjmuQ1aia4Q==", - "license": "Apache-2.0", - "dependencies": { - "@swc/helpers": "^0.5.0" - } - }, - "node_modules/@internationalized/message": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/@internationalized/message/-/message-3.1.10.tgz", - "integrity": "sha512-nc0Or6EdWHqZRcsXb6P9hBIpLsfSl/ILh0rk5h/OVBpzmhdExXtPy2cQtWsq8XKRBpRHwDNnAHt4OpolcB7dog==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@swc/helpers": "^0.5.0", - "intl-messageformat": "^10.1.0" - } - }, - "node_modules/@internationalized/number": { - "version": "3.6.7", - "resolved": "https://registry.npmjs.org/@internationalized/number/-/number-3.6.7.tgz", - "integrity": "sha512-3ji1fcrT+FPAK86UqEhB/psHixYo6niWPJtt7+qRaYFynt/BaJG8GhAPimtWUpEiVSTq8ZM8L5psMxGquiB/Vg==", - "license": "Apache-2.0", - "dependencies": { - "@swc/helpers": "^0.5.0" - } - }, - "node_modules/@internationalized/string": { - "version": "3.2.10", - "resolved": "https://registry.npmjs.org/@internationalized/string/-/string-3.2.10.tgz", - "integrity": "sha512-PDx6//vHSpRnHfxqMqto11zQvhsaU74O3mKv2F/0eicGZcl9NLjQmGlbHz/LsJh5tLKp4A4L7ZVTzN1/MmMTvA==", - "license": "Apache-2.0", - "dependencies": { - "@swc/helpers": "^0.5.0" - } - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -698,6 +552,28 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.3.tgz", + "integrity": "sha512-UMduMbqO5s5zF2NkNacMT/yK5Y5QiKvWr2+50bzIIxFDwVJ2h49b+oyjaCGPhJxd2/gC2x39EHv/gHVuu36x2Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4", + "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4" + } + }, "node_modules/@oxc-project/types": { "version": "0.144.0", "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.144.0.tgz", @@ -708,17 +584,22 @@ "url": "https://github.com/sponsors/Boshen" } }, - "node_modules/@radix-ui/react-avatar": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.11.tgz", - "integrity": "sha512-0Qk603AHGV28BOBO34p7IgD5m+V5Sg/YovfayABkoDDBM5d3NCx0Mp4gGrjzLGes1jV5eNOE1r3itqOR33VC6Q==", + "node_modules/@radix-ui/primitive": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.7.tgz", + "integrity": "sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.15.tgz", + "integrity": "sha512-9W+B9NPF0NaaPh/1NJd3+KqsnlLqU9H7T2rvww+fp+T/evVXdNAyYcnfRQZFOjkR1ajQp3yORlqnI8soawLvNA==", "license": "MIT", "dependencies": { - "@radix-ui/react-context": "1.1.3", - "@radix-ui/react-primitive": "2.1.4", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-is-hydrated": "0.1.0", - "@radix-ui/react-use-layout-effect": "1.1.1" + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-slot": "1.3.3" }, "peerDependencies": { "@types/react": "*", @@ -736,9 +617,9 @@ } }, "node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", - "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.5.tgz", + "integrity": "sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -751,9 +632,9 @@ } }, "node_modules/@radix-ui/react-context": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.3.tgz", - "integrity": "sha512-ieIFACdMpYfMEjF0rEf5KLvfVyIkOz6PDGyNnP+u+4xQ6jny3VCgA4OgXOwNx2aUkxn8zx9fiVcM8CfFYv9Lxw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.2.2.tgz", + "integrity": "sha512-RHCUGwKHDr0hDGg4X7ma4JG4/+12qxw8rkh5QKdDldlCvtja6nUx1Ef/8HVrJze81lEsgLQlqjzjGNHantgnQA==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -765,13 +646,148 @@ } } }, - "node_modules/@radix-ui/react-primitive": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz", - "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==", + "node_modules/@radix-ui/react-direction": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.4.tgz", + "integrity": "sha512-5pzg4FGQNpExhnhT2zlrP1wZFaYCd1K0nYWoFAdcYoYK868IEigqMX3B3f8yIoRlAhAeDWciLI6ZdCKHF9P4Vg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.4.tgz", + "integrity": "sha512-TMQp2llA+RYn7JcjnrMnz7wN4pcVttPZnRZo52PLQsoLVKzNlVwUeHmfePgTgRluXFvlD3GD5g5MOVVTJCO0qA==", "license": "MIT", "dependencies": { - "@radix-ui/react-slot": "1.2.4" + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-label": { + "version": "2.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.15.tgz", + "integrity": "sha512-o/rdYEwZTTo5tjknnPeyQFU45kUC4i/XyeDPP+HGyi6XqpOP6Zf5Ya5vh/Yfe9Id5JiuWnnAx2XqIeD3UYZt0g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.10" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.10.tgz", + "integrity": "sha512-3wyzCQ6+ubRA+D4uv9m95JYLXxmOHp05qjrkjeA7uKHHtjpPggQzc6DAb0URl7j67oR0K2foO4ip27TiX037Bw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.10.tgz", + "integrity": "sha512-MucOnzh6hR5mid6VpkbglRAMYMjKLqRnGBbjXkzjK52fuQDd1qbkx78a5P40mkcnVXJdEVxm26E9OPAiUq7nBg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.3.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.19.tgz", + "integrity": "sha512-V9jI6hDjT7l3jsCQD9bLNvDLM3tH/gdbOTp7Tefp3hbbgCGQoK7tUvrWiRlcoBHIZ809ElXwNQwVo0B98LuTXQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-is-hydrated": "0.1.3", + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-separator": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.15.tgz", + "integrity": "sha512-jOLO4lssEzWpoDu7G+Ze4VjwMRUBt291pnZD0gmalREZipnTX3wadQo7Fy48GCTfe14/YRN6rw/rOJqrE85Wxw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.10" }, "peerDependencies": { "@types/react": "*", @@ -789,12 +805,12 @@ } }, "node_modules/@radix-ui/react-slot": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz", - "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.3.3.tgz", + "integrity": "sha512-qx7oqnYbxnK9kYI9m317qmFmEgo6ywqWvbTogdj7cL9p3/yx4M48p7Rnw5z3H890cL/ow/EeWJsuTykeZVXP5Q==", "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" + "@radix-ui/react-compose-refs": "1.1.5" }, "peerDependencies": { "@types/react": "*", @@ -806,11 +822,79 @@ } } }, - "node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", - "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "node_modules/@radix-ui/react-tabs": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.21.tgz", + "integrity": "sha512-UKxJlZid7FVtsk/WTxj4i4uSEgj2Au+KBbS7SQyTlzMhhn+86Cz3tISZdTa87bfEfcuvZezf2ZsxD4xuEKtkog==", "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-roving-focus": "1.1.19", + "@radix-ui/react-use-controllable-state": "1.2.6" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.4.tgz", + "integrity": "sha512-R6OUY2e2fA6Yn6s+VSx5KBV6Nx8LQEhu+cz7LCej18rQ1HLyg9PSC9jP/ZNx0o6FAIK9c0F1kHylzSxKsdlkrQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.6.tgz", + "integrity": "sha512-uEQJGT97ZA/TgP/Hydw47lHu+/vQj6z/0jA+WeTbK1o9Rx45GImjpD0tc3W5ad3D6XTSR6e1yEO0FvGq6WQfVQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-use-effect-event": "0.0.5", + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.5.tgz", + "integrity": "sha512-7cshFL8HGS/7HEiHH+9kL9HBwp2sa9yX18Knwek6KYWmXwM7pegMgta2AXMQKI+rq3JnfSj9x8wYqFMTdG1Jgg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.4" + }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -822,13 +906,10 @@ } }, "node_modules/@radix-ui/react-use-is-hydrated": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.0.tgz", - "integrity": "sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==", + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.3.tgz", + "integrity": "sha512-umO/aJ+82CpOnhDZUTbILCQf7kU/g0iv+oGs/Q8jw7IkhWBzaEP4sA268PhFAJTFetbwp3ICc6ktpI4TqtxcIw==", "license": "MIT", - "dependencies": { - "use-sync-external-store": "^1.5.0" - }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -840,9 +921,9 @@ } }, "node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", - "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.4.tgz", + "integrity": "sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -854,155 +935,6 @@ } } }, - "node_modules/@react-aria/color": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@react-aria/color/-/color-3.2.1.tgz", - "integrity": "sha512-v/pZh1yBNeayQ5Kio0xRIX0gBf6uu+KjCRW4QSydTyRpmhvk6sMzHT9i9/vIMdaDHaYHBXlXF+Tp/mvS9CG4Zw==", - "license": "Apache-2.0", - "dependencies": { - "@swc/helpers": "^0.5.0", - "react-aria": "^3.48.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@react-aria/i18n": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/@react-aria/i18n/-/i18n-3.13.1.tgz", - "integrity": "sha512-z56ZYcbfpNmMyiGLhyEjytpmEfoTlBaksk84q4kds3HvNkf7QWKj+DJVfVDrJX+c1LyuBsszLSX7yxJRiHsYKQ==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@internationalized/date": "^3.12.1", - "@internationalized/message": "^3.1.9", - "@internationalized/string": "^3.2.8", - "@swc/helpers": "^0.5.0", - "react-aria": "^3.48.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@react-aria/ssr": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.10.1.tgz", - "integrity": "sha512-jn038/ZYmu6DpfXJ6r2U9zFFppjbc9wnApPJSCxao2RZVEqep4YyoniHSy8qv6V21/xyS4IV7W9a+X2jOjSuag==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@swc/helpers": "^0.5.0", - "react-aria": "^3.48.0" - }, - "engines": { - "node": ">= 12" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@react-aria/utils": { - "version": "3.34.1", - "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.34.1.tgz", - "integrity": "sha512-H6+rGZL+0f58bBNaUMfctEnT+NogqwAk+nHiB8sR3K+YlQ37GTuCijy2U/pPvQtFMS5mURrjZeBH5JNNXsx14A==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@swc/helpers": "^0.5.0", - "react-aria": "^3.48.0", - "react-stately": "^3.46.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@react-spectrum/color": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@react-spectrum/color/-/color-3.2.1.tgz", - "integrity": "sha512-FbB+GaVufsN+obNJmUAe8XGZTeJjEWvEf/k/7nxDxoDCBdg/o6eEPONAZyID5Dj1f8BZKNym8VURxDZx7IIyuQ==", - "license": "Apache-2.0", - "dependencies": { - "@adobe/react-spectrum": "^3.47.0", - "@swc/helpers": "^0.5.0", - "react-stately": "^3.46.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@react-spectrum/provider": { - "version": "3.11.1", - "resolved": "https://registry.npmjs.org/@react-spectrum/provider/-/provider-3.11.1.tgz", - "integrity": "sha512-TsoNdVdmlQ7L+75ILq5Yb3+wp/I1AtIeat0o+Y+ZBxP+TtWpwT1ZtCB5l3cplFVzHzOpZlzO0VaDrDP9ElGYDw==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@adobe/react-spectrum": "^3.47.0", - "@swc/helpers": "^0.5.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@react-stately/color": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@react-stately/color/-/color-3.10.1.tgz", - "integrity": "sha512-v4YlLa/oRQpPa/M9Yq1e0Bl8Z7xzGZdNDAIbfIMZuiJtacaOKriPW+AOgjjT/Ab0fNX2YDbwIYvkiMOJBnTi5A==", - "license": "Apache-2.0", - "dependencies": { - "@swc/helpers": "^0.5.0", - "react-stately": "^3.46.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@react-stately/utils": { - "version": "3.12.1", - "resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.12.1.tgz", - "integrity": "sha512-NqKfzrknpfwiewx7R2vk1P+CneClInPDsIhw15+jOcUYSEfej0nta4cJywuKQJ2gsPwqX/ojDNixedCve9FWGw==", - "license": "Apache-2.0", - "dependencies": { - "@swc/helpers": "^0.5.0", - "react-stately": "^3.46.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@react-types/color": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@react-types/color/-/color-3.2.0.tgz", - "integrity": "sha512-beV3vz80nzZ1EuYUM7296Kyi3AHcMrbQw0qub/9yzHWVTKKc5sy/e4dCMKcWL/ArkeAyc7jDOiui190RQ4l0Fw==", - "license": "Apache-2.0", - "dependencies": { - "@react-aria/color": "^3.2.0", - "@react-spectrum/color": "^3.2.0", - "@react-stately/color": "^3.10.0" - }, - "peerDependencies": { - "@react-spectrum/provider": "^3.0.0", - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@react-types/shared": { - "version": "3.36.1", - "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.36.1.tgz", - "integrity": "sha512-AzsuD9OfxTOZMMvTRhlN3oHBwOmFN7tDh27LzqmHt4+uOgPhJT7ZM7/kVs/8/o0WxayMUIk3hBmCFRHv1FUoag==", - "license": "Apache-2.0", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, "node_modules/@rolldown/binding-android-arm64": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.4.tgz", @@ -1266,46 +1198,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@spectrum-icons/ui": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/@spectrum-icons/ui/-/ui-3.7.1.tgz", - "integrity": "sha512-veQymocUYo5OciXQajSailOdbWe+k6+2ehfF8D4d0V923D4xOUadtT253xXZ5vEQjPat6Kyp2WDKeQNjd7kL1w==", - "license": "Apache-2.0", - "dependencies": { - "@adobe/react-spectrum-ui": "1.2.1", - "@babel/runtime": "^7.24.4", - "@swc/helpers": "^0.5.0" - }, - "peerDependencies": { - "@adobe/react-spectrum": "^3.47.0", - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@spectrum-icons/workflow": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@spectrum-icons/workflow/-/workflow-4.3.1.tgz", - "integrity": "sha512-kDF+/EbFVyLGytotqqdYt4uSij4j/PQmDQO5km/C6DyzKjyuic3FnSBFinR+mA6oFv1OjMcLvrrDBqK3wbqRlA==", - "license": "Apache-2.0", - "dependencies": { - "@adobe/react-spectrum-workflow": "2.3.5", - "@swc/helpers": "^0.5.0" - }, - "peerDependencies": { - "@adobe/react-spectrum": "^3.47.0", - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@swc/helpers": { - "version": "0.5.23", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.23.tgz", - "integrity": "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.8.0" - } - }, "node_modules/@tailwindcss/node": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.3.tgz", @@ -1863,6 +1755,17 @@ "vite": "^5.2.0 || ^6 || ^7 || ^8" } }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@types/esrecurse": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", @@ -1970,18 +1873,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/aria-hidden": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", - "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/balanced-match": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", @@ -2073,11 +1964,17 @@ ], "license": "CC-BY-4.0" }, - "node_modules/client-only": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", - "license": "MIT" + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } }, "node_modules/clsx": { "version": "2.1.1", @@ -2127,6 +2024,7 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "devOptional": true, "license": "MIT" }, "node_modules/debug": { @@ -2147,13 +2045,6 @@ } } }, - "node_modules/decimal.js": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", - "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", - "license": "MIT", - "peer": true - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -2171,16 +2062,6 @@ "node": ">=8" } }, - "node_modules/dom-helpers": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" - } - }, "node_modules/electron-to-chromium": { "version": "1.5.405", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.405.tgz", @@ -2595,29 +2476,6 @@ "node": ">=0.8.19" } }, - "node_modules/input-otp": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/input-otp/-/input-otp-1.4.2.tgz", - "integrity": "sha512-l3jWwYNvrEa6NTCt7BECfCm48GvwuZzkoeG3gBL2w4CHeOXW3eKFmf9UNYkNfYc3mxMrthMnxjIE07MT0zLBQA==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" - } - }, - "node_modules/intl-messageformat": { - "version": "10.7.18", - "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.18.tgz", - "integrity": "sha512-m3Ofv/X/tV8Y3tHXLohcuVuhWKo7BBq62cqY15etqmLxg2DZ34AGGgQDeR+SCta2+zICb1NX83af0GJmbQ1++g==", - "license": "BSD-3-Clause", - "peer": true, - "dependencies": { - "@formatjs/ecma402-abstract": "2.3.6", - "@formatjs/fast-memoize": "2.2.7", - "@formatjs/icu-messageformat-parser": "2.11.4", - "tslib": "^2.8.0" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -2662,6 +2520,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, "license": "MIT" }, "node_modules/jsesc": { @@ -3024,18 +2883,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "license": "MIT", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -3115,15 +2962,6 @@ "node": ">=18" } }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -3253,17 +3091,6 @@ "node": ">= 0.8.0" } }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -3283,46 +3110,6 @@ "node": ">=0.10.0" } }, - "node_modules/react-aria": { - "version": "3.51.0", - "resolved": "https://registry.npmjs.org/react-aria/-/react-aria-3.51.0.tgz", - "integrity": "sha512-AyWLw0XR38cFPwBu/ErgGaVrc5dupLEKmRlMXTGvFKOtbaGRQ2+yQJkjVhpdHhoRhU4+G+tJDFeHDTS8tK3bfQ==", - "license": "Apache-2.0", - "dependencies": { - "@internationalized/date": "^3.12.3", - "@internationalized/number": "^3.6.7", - "@internationalized/string": "^3.2.10", - "@react-types/shared": "^3.36.1", - "@swc/helpers": "^0.5.0", - "aria-hidden": "^1.2.3", - "clsx": "^2.0.0", - "react-stately": "3.49.0", - "use-sync-external-store": "^1.6.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/react-aria-components": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/react-aria-components/-/react-aria-components-1.20.0.tgz", - "integrity": "sha512-BMbpIgoV9aELeBrB0Y120NgoigHb5OdcJwc+4e7uSnbTbamea6lo+gqcc4LAxzMaK3Jf+7LI1oCDE6yANsmxIQ==", - "license": "Apache-2.0", - "dependencies": { - "@internationalized/date": "^3.12.3", - "@internationalized/string": "^3.2.10", - "@react-types/shared": "^3.36.1", - "@swc/helpers": "^0.5.0", - "client-only": "^0.0.1", - "react-aria": "3.51.0", - "react-stately": "3.49.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, "node_modules/react-dom": { "version": "19.2.8", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz", @@ -3335,11 +3122,14 @@ "react": "^19.2.8" } }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" + "node_modules/react-icons": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.7.0.tgz", + "integrity": "sha512-LBLy340Rzqy6+/yVhZKT3B/QpP1BZaesGqasf09HPOBzRarcDIFH0WwXlXQfE7q7ipxK4MSiC5DIBWURCny6fw==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } }, "node_modules/react-router": { "version": "7.18.2", @@ -3379,39 +3169,6 @@ "react-dom": ">=18" } }, - "node_modules/react-stately": { - "version": "3.49.0", - "resolved": "https://registry.npmjs.org/react-stately/-/react-stately-3.49.0.tgz", - "integrity": "sha512-13iNq2KzBrRAzxRc+n53hgROfIistiYY/sPtIhCw1qUB7/kmo+X1xEU2uiS5zcCIrc55AUPwoHqOIIpKWSwB9A==", - "license": "Apache-2.0", - "dependencies": { - "@internationalized/date": "^3.12.3", - "@internationalized/number": "^3.6.7", - "@internationalized/string": "^3.2.10", - "@react-types/shared": "^3.36.1", - "@swc/helpers": "^0.5.0", - "use-sync-external-store": "^1.6.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", - "license": "BSD-3-Clause", - "dependencies": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": ">=16.6.0", - "react-dom": ">=16.6.0" - } - }, "node_modules/rolldown": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.4.tgz", @@ -3501,37 +3258,15 @@ } }, "node_modules/tailwind-merge": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz", - "integrity": "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.6.0.tgz", + "integrity": "sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/dcastil" } }, - "node_modules/tailwind-variants": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/tailwind-variants/-/tailwind-variants-3.3.1.tgz", - "integrity": "sha512-4pAvwUtM4HKBiRZftncAbpn6V9Hhwoa5Fl7O2u5zbp7Z5Cvu+/o/6+176WY3WCEES209543quG8zFIcXCsc5Jw==", - "license": "MIT", - "engines": { - "node": ">=16.9.x", - "pnpm": ">=7.x" - }, - "peerDependencies": { - "tailwind-merge": ">=3.0.0", - "tailwindcss": "*" - }, - "peerDependenciesMeta": { - "tailwind-merge": { - "optional": true - }, - "tailwindcss": { - "optional": true - } - } - }, "node_modules/tailwindcss": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.3.tgz", @@ -3573,16 +3308,9 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/tw-animate-css": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.4.0.tgz", - "integrity": "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Wombosvideo" - } + "dev": true, + "license": "0BSD", + "optional": true }, "node_modules/type-check": { "version": "0.4.0", @@ -3638,15 +3366,6 @@ "punycode": "^2.1.0" } }, - "node_modules/use-sync-external-store": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", - "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, "node_modules/vite": { "version": "8.2.1", "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.1.tgz", diff --git a/package.json b/package.json index 110778c..34ed76b 100644 --- a/package.json +++ b/package.json @@ -10,11 +10,17 @@ "preview": "vite preview" }, "dependencies": { - "@heroui/react": "^3.2.4", - "@heroui/styles": "^3.2.4", + "@radix-ui/react-label": "^2.1.15", + "@radix-ui/react-separator": "^1.1.15", + "@radix-ui/react-slot": "^1.3.3", + "@radix-ui/react-tabs": "^1.1.21", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", "react": "^19.2.8", "react-dom": "^19.2.8", + "react-icons": "^5.7.0", "react-router-dom": "^7.18.2", + "tailwind-merge": "^3.6.0", "tailwindcss": "^4.3.3" }, "devDependencies": { diff --git a/src/App.css b/src/App.css index 7106dbf..14047a1 100644 --- a/src/App.css +++ b/src/App.css @@ -1,23 +1,45 @@ @import 'tailwindcss'; -@import '@heroui/styles'; @theme { - --color-app-bg: #f8fafc; + --color-background: var(--color-app-bg); + --color-foreground: var(--color-app-text); + --color-card: var(--color-app-surface); + --color-card-foreground: var(--color-app-text); + --color-primary: var(--color-app-primary); + --color-primary-foreground: var(--color-app-primary-contrast); + --color-secondary: var(--color-app-surface-muted); + --color-secondary-foreground: var(--color-app-text); + --color-muted: var(--color-app-surface-muted); + --color-muted-foreground: var(--color-app-text-muted); + --color-accent: var(--color-app-interactive-hover); + --color-accent-foreground: var(--color-app-text); + --color-destructive: #ef4444; + --color-destructive-foreground: #ffffff; + --color-border: var(--color-app-border); + --color-input: var(--color-app-border-input); + --color-ring: var(--color-app-focus); + --radius: 0.5rem; + --color-app-bg: #ffffff; --color-app-surface: #ffffff; - --color-app-surface-muted: #f1f5f9; - --color-app-border: #e2e8f0; - --color-app-border-input: #cbd5e1; - --color-app-text: #1e293b; - --color-app-text-strong: #0f172a; - --color-app-text-muted: #64748b; - --color-app-primary: #1a73e8; - --color-app-primary-dark: #1558b0; - --color-app-primary-soft: #e8f1fd; + --color-app-surface-muted: rgba(148,151,169,0.08); + --color-app-border: #dedee5; + --color-app-border-input: #dedee5; + --color-app-text: #101114; + --color-app-text-strong: #101114; + --color-app-text-muted: #9497a9; + --color-app-primary: #7132f5; + --color-app-primary-dark: #5741d8; + --color-app-primary-soft: rgba(133,91,251,0.16); --color-app-primary-contrast: #ffffff; - --color-app-interactive-hover: #f1f5f9; - --color-app-interactive-active: #e8f1fd; - --color-app-focus: #1a73e8; - --shadow-app-panel: 0 16px 42px rgb(0 0 0 / 5%); + --color-app-interactive-hover: rgba(148,151,169,0.08); + --color-app-interactive-active: rgba(133,91,251,0.16); + --color-app-focus: #7132f5; + --color-app-success: rgba(20,158,97,0.16); + --color-app-success-text: #026b3f; + --color-app-neutral-badge: rgba(104,107,130,0.12); + --color-app-neutral-badge-text: #484b5e; + --shadow-app-panel: rgba(0,0,0,0.03) 0px 4px 24px; + --shadow-app-micro: rgba(16,24,40,0.04) 0px 1px 4px; } .app-shell { @@ -36,8 +58,8 @@ top: 0; height: 100vh; padding: 24px 16px; - border-right: 1px solid var(--color-app-border); - background: var(--color-app-surface); + border-right: none; + background: var(--color-app-primary); overflow: hidden; } @@ -45,18 +67,27 @@ display: flex; align-items: center; gap: 12px; - padding: 0 8px 28px; + padding: 12px; + border: none; + border-radius: 10px; + margin-bottom: 20px; } .sidebar__logo { - display: inline-grid; + position: relative; width: 40px; height: 40px; - place-items: center; - border-radius: 8px; - color: #ffffff; - background: var(--color-app-primary); - font-weight: 800; + border-radius: 10px; + color: var(--color-app-primary) !important; + background: #ffffff; +} + +.sidebar__logo svg { + display: block; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); } .sidebar__brand-text, @@ -76,36 +107,39 @@ .sidebar__brand strong { line-height: 1.2; + color: #ffffff; } .sidebar__brand span { margin-top: 3px; - color: var(--color-app-text-muted); + color: rgba(255,255,255,0.7); font-size: 13px; } .sidebar__toggle { - display: grid; + display: flex; + align-items: center; + justify-content: center; width: 100%; - height: 36px; - margin-bottom: 14px; - place-items: center; - border: 1px solid var(--color-app-border); - border-radius: 8px; + height: 32px; + margin-bottom: 12px; + border: none; + border-radius: 6px; color: var(--color-app-text-muted); - background: var(--color-app-surface); + background: transparent; cursor: pointer; - font-weight: 800; transition: - border-color 160ms ease, color 160ms ease, background-color 160ms ease; } .sidebar__toggle:hover { - color: var(--color-app-primary); - border-color: var(--color-app-primary); - background: var(--color-app-primary-soft); + color: var(--color-app-text); + background: var(--color-app-interactive-hover); +} + +.sidebar__toggle:active { + background: var(--color-app-interactive-active); } .sidebar__toggle:focus-visible, @@ -119,74 +153,194 @@ gap: 6px; } +.sidebar__divider { + height: 1px; + background: rgba(255,255,255,0.2); + margin: 4px 0 16px; +} + .sidebar__link { display: flex; align-items: center; min-height: 42px; padding: 10px 12px; border-radius: 8px; - color: var(--color-app-text-muted); + color: rgba(255,255,255,0.75); font-size: 15px; font-weight: 600; gap: 10px; text-decoration: none; transition: - background-color 160ms ease, - color 160ms ease; + color 160ms ease, background 160ms ease; +} + +.sidebar__link:hover { + color: #ffffff; + background: rgba(255,255,255,0.1); } .sidebar__link-icon { display: inline-grid; - flex: 0 0 28px; - width: 28px; - height: 28px; + flex: 0 0 32px; + width: 32px; + height: 32px; place-items: center; - border-radius: 7px; - color: var(--color-app-primary-dark); - background: var(--color-app-primary-soft); + border-radius: 8px; + color: #ffffff; + background: rgba(255,255,255,0.15); font-size: 11px; font-weight: 800; + transition: + background-color 160ms ease, + color 160ms ease; } -.sidebar__link:hover { - color: var(--color-app-text); - background: var(--color-app-interactive-hover); +.sidebar__link:hover .sidebar__link-icon { + background: rgba(255,255,255,0.25); } .sidebar__link--active { + color: #ffffff; + background: rgba(255,255,255,0.15); +} + +.sidebar__link--active:hover { + background: rgba(255,255,255,0.2); +} + +.sidebar__link--active:hover .sidebar__link-icon { + background: #ffffff; color: var(--color-app-primary); - background: var(--color-app-interactive-active); +} + +.sidebar__link--active .sidebar__link-icon { + background: #ffffff; + color: var(--color-app-primary); +} + +/* Submenu */ +.sidebar__group { + display: grid; +} + +.sidebar__link--parent { + cursor: pointer; +} + +.sidebar__link--parent.sidebar__link { + font: inherit; + border: none; + width: 100%; +} + +.sidebar__chevron { + margin-left: auto; + width: 18px; + height: 18px; + color: rgba(255,255,255,0.6); + transition: transform 160ms ease; +} + +.sidebar__chevron--open { + transform: rotate(90deg); +} + +.sidebar__submenu { + display: grid; + gap: 2px; + padding-left: 50px; +} + +.sidebar__sublink { + display: flex; + align-items: center; + min-height: 36px; + padding: 8px 12px; + border-left: 2px solid transparent; + color: rgba(255,255,255,0.65); + font-size: 14px; + font-weight: 500; + text-decoration: none; + transition: + color 160ms ease, + border-color 160ms ease; +} + +.sidebar__sublink:hover { + color: #ffffff; + border-left-color: #ffffff; +} + +.sidebar__sublink--active { + color: #ffffff; + border-left-color: #ffffff; } .app-shell--collapsed .sidebar { - padding-inline: 14px; + padding: 24px 0; } .app-shell--collapsed .sidebar__brand { + display: flex; justify-content: center; - padding-inline: 0; + padding: 0; + border: none; + margin-bottom: 12px; +} + +.app-shell--collapsed .sidebar__logo { + display: grid; + width: 38px; + height: 38px; + place-items: center; + border-radius: 10px; + background: #ffffff; + color: var(--color-app-primary) !important; } .app-shell--collapsed .sidebar__brand-text, .app-shell--collapsed .sidebar__link-text { width: 0; + overflow: hidden; opacity: 0; pointer-events: none; } -.app-shell--collapsed .sidebar__link { +.app-shell--collapsed .sidebar__link, +.app-shell--collapsed .sidebar__link--parent { justify-content: center; - padding-inline: 8px; + padding: 8px 0; +} + +.app-shell--collapsed .sidebar__link--active { + background: transparent; +} + +.app-shell--collapsed .sidebar__link--active .sidebar__link-icon { + border-radius: 8px; +} + +.app-shell--collapsed .sidebar__chevron, +.app-shell--collapsed .sidebar__submenu { + display: none; } .app-shell--collapsed .sidebar__link-icon { - flex-basis: 30px; + flex: none; width: 30px; height: 30px; } +.app-shell--collapsed .sidebar__toggle { + width: 28px; + height: 28px; + margin-inline: auto; + margin-bottom: 12px; +} + .main-content { min-width: 0; + min-height: 100vh; padding: 16px; } @@ -334,3 +488,4 @@ grid-template-columns: 1fr; } } + diff --git a/src/App.jsx b/src/App.jsx index af3bfe5..eb20339 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -2,25 +2,35 @@ import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom'; import MainLayout from './layouts/MainLayout.jsx'; import CoursesPage from './pages/Course/CoursesPage.jsx'; import CourseDetail from './pages/Course/CourseDetails.jsx'; -import StudentsPage from './pages/StudentsPage.jsx'; -import ClassesPage from './pages/ClassesPage.jsx'; -import ReportsPage from './pages/ReportsPage.jsx'; +import LessonPage from './pages/lesson/LessonPage.jsx'; +import MCQPage from './pages/question-bank/MCQPage.jsx'; +import ParagraphPage from './pages/question-bank/ParagraphPage.jsx'; +import UsersPage from './pages/user/UsersPage.jsx'; +import MasterdataPage from './pages/settings/MasterdataPage.jsx'; +import RolePage from './pages/settings/RolePage.jsx'; +import PermissionPage from './pages/settings/PermissionPage.jsx'; import './App.css'; function App() { return ( - + <> + }> } /> } /> } /> - } /> - } /> - } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> - + + ); } diff --git a/src/components/CourseCard.jsx b/src/components/CourseCard.jsx index 4b37992..3d1c075 100644 --- a/src/components/CourseCard.jsx +++ b/src/components/CourseCard.jsx @@ -1,9 +1,10 @@ -import { Card, Button } from '@heroui/react'; -import { PencilIcon, EllipsisVerticalIcon } from '@heroicons/react/24/outline'; +import { Card } from '../components/ui/card'; +import { Button } from '../components/ui/button'; +import { HiOutlinePencil, HiOutlineEllipsisVertical } from 'react-icons/hi2'; const statusStyles = { - Active: 'bg-green-100 text-green-700', - Inactive: 'bg-gray-100 text-gray-600' + Active: 'bg-app-success text-app-success-text', + Inactive: 'bg-app-neutral-badge text-app-neutral-badge-text' }; function CourseCard({ course, image, handleClick }) { @@ -17,78 +18,69 @@ function CourseCard({ course, image, handleClick }) { return ( {/* Image */} -
+
{title} -
- {code} +
+ {code}
{/* Content */} - -

+
+

{title}

-

+

{description}

- +
{/* Footer */} - { - e.stopPropagation(); - // handle edit - }} +
e.stopPropagation()} >
{status} - Updated {formattedDate} + Updated {formattedDate}
- +
); } diff --git a/src/components/PageHeader.jsx b/src/components/PageHeader.jsx index 3c89534..0fe9508 100644 --- a/src/components/PageHeader.jsx +++ b/src/components/PageHeader.jsx @@ -1,33 +1,33 @@ -import { SearchField, Separator } from '@heroui/react'; +import { Separator } from '../components/ui/separator'; import { - BellIcon, - QuestionMarkCircleIcon, - UserCircleIcon -} from '@heroicons/react/16/solid'; + HiOutlineBell, + HiOutlineQuestionMarkCircle, + HiOutlineUserCircle, + HiOutlineBars3, +} from 'react-icons/hi2'; +import { useSidebar } from '../layouts/MainLayout'; function PageHeader() { + const { toggleSidebar } = useSidebar(); + return ( <> -
- - - - - - - - - -
- - - +
+ +
+ + +
- + ); } diff --git a/src/components/ui/badge.jsx b/src/components/ui/badge.jsx new file mode 100644 index 0000000..503fb43 --- /dev/null +++ b/src/components/ui/badge.jsx @@ -0,0 +1,26 @@ +import * as React from 'react'; +import { cva } from 'class-variance-authority'; +import { cn } from '../../lib/utils'; + +const badgeVariants = cva( + 'inline-flex items-center rounded-md 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 shadow hover:bg-primary/80', + secondary: 'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80', + destructive: 'border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80', + outline: 'text-foreground', + }, + }, + defaultVariants: { + variant: 'default', + }, + } +); + +function Badge({ className, variant, ...props }) { + return
; +} + +export { Badge, badgeVariants }; diff --git a/src/components/ui/button.jsx b/src/components/ui/button.jsx new file mode 100644 index 0000000..01eab2b --- /dev/null +++ b/src/components/ui/button.jsx @@ -0,0 +1,50 @@ +import * as React from 'react'; +import { Slot } from '@radix-ui/react-slot'; +import { cva } from 'class-variance-authority'; +import { cn } from '../../lib/utils'; + +const buttonVariants = cva( + 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50', + { + variants: { + variant: { + default: + 'bg-primary text-primary-foreground shadow hover:bg-primary/90', + destructive: + 'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90', + outline: + 'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground', + secondary: + 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80', + ghost: 'hover:bg-accent hover:text-accent-foreground', + link: 'text-primary underline-offset-4 hover:underline', + }, + 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', + }, + } +); + +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/src/components/ui/card.jsx b/src/components/ui/card.jsx new file mode 100644 index 0000000..3ec4382 --- /dev/null +++ b/src/components/ui/card.jsx @@ -0,0 +1,54 @@ +import * as React from 'react'; +import { cn } from '../../lib/utils'; + +const Card = React.forwardRef(({ className, ...props }, ref) => ( +
+)); +Card.displayName = 'Card'; + +const CardHeader = React.forwardRef(({ className, ...props }, ref) => ( +
+)); +CardHeader.displayName = 'CardHeader'; + +const CardTitle = React.forwardRef(({ className, ...props }, ref) => ( +
+)); +CardTitle.displayName = 'CardTitle'; + +const CardDescription = React.forwardRef(({ className, ...props }, ref) => ( +
+)); +CardDescription.displayName = 'CardDescription'; + +const CardContent = React.forwardRef(({ className, ...props }, ref) => ( +
+)); +CardContent.displayName = 'CardContent'; + +const CardFooter = React.forwardRef(({ className, ...props }, ref) => ( +
+)); +CardFooter.displayName = 'CardFooter'; + +export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }; diff --git a/src/components/ui/data-table.jsx b/src/components/ui/data-table.jsx new file mode 100644 index 0000000..cf744ad --- /dev/null +++ b/src/components/ui/data-table.jsx @@ -0,0 +1,196 @@ +import { useState } from 'react'; +import { HiOutlineMagnifyingGlass } from 'react-icons/hi2'; +import { Input } from './input'; + +const styles = { + wrapper: { + overflow: 'auto', + borderRadius: 12, + border: '1px solid #dedee5', + boxShadow: 'rgba(0,0,0,0.03) 0px 4px 24px', + background: '#ffffff', + maxHeight: 'calc(100vh - 325px)', + }, + table: { + width: '100%', + borderCollapse: 'collapse', + fontFamily: "'Helvetica Neue', Helvetica, Arial, sans-serif", + }, + thead: { + position: 'sticky', + top: 0, + zIndex: 1, + background: '#f8f9fa', + }, + th: { + padding: '14px 16px', + textAlign: 'left', + borderBottom: '1px solid #dedee5', + fontWeight: 600, + fontSize: 12, + color: '#9497a9', + textTransform: 'uppercase', + letterSpacing: '0.06em', + background: 'rgba(148,151,169,0.08)', + }, + td: { + padding: '14px 16px', + borderBottom: '1px solid #dedee5', + fontSize: 14, + color: '#101114', + lineHeight: 1.43, + }, +}; + +function DataTable({ + columns, + data, + searchKeys, + searchPlaceholder = 'Search...', + perPage = 10, + emptyText = 'No results found.', +}) { + const [search, setSearch] = useState(''); + const [page, setPage] = useState(1); + + const handleSearch = (e) => { + setSearch(e.target.value); + setPage(1); + }; + + const filtered = data.filter((row) => { + if (!search) return true; + const q = search.toLowerCase(); + return searchKeys.some((key) => { + const val = row[key]; + return val != null && String(val).toLowerCase().includes(q); + }); + }); + + const totalPages = Math.max(1, Math.ceil(filtered.length / perPage)); + const paged = filtered.slice((page - 1) * perPage, page * perPage); + const startIdx = filtered.length === 0 ? 0 : (page - 1) * perPage + 1; + const endIdx = Math.min(page * perPage, filtered.length); + + return ( + <> + {searchKeys && searchKeys.length > 0 && ( +
+ + +
+ )} +
+ + + + {columns.map((col) => ( + + ))} + + + + {paged.map((row, idx) => ( + { e.currentTarget.style.background = 'rgba(113,50,245,0.06)'; }} + onMouseLeave={(e) => { e.currentTarget.style.background = idx % 2 === 1 ? 'rgba(148,151,169,0.04)' : '#ffffff'; }} + > + {columns.map((col) => ( + + ))} + + ))} + {paged.length === 0 && ( + + + + )} + +
+ {col.label} +
+ {col.render + ? col.render(row, (page - 1) * perPage + idx + 1) + : row[col.key]} +
+ {emptyText} +
+
+ + {/* Pagination */} +
+ + {startIdx}–{endIdx} of {filtered.length} + +
+ + {Array.from({ length: totalPages }, (_, i) => ( + + ))} + +
+
+ + ); +} + +export { DataTable }; diff --git a/src/components/ui/input.jsx b/src/components/ui/input.jsx new file mode 100644 index 0000000..b5be936 --- /dev/null +++ b/src/components/ui/input.jsx @@ -0,0 +1,17 @@ +import * as React from 'react'; +import { cn } from '../../lib/utils'; + +const Input = React.forwardRef(({ className, type, ...props }, ref) => ( + +)); +Input.displayName = 'Input'; + +export { Input }; diff --git a/src/components/ui/label.jsx b/src/components/ui/label.jsx new file mode 100644 index 0000000..81e5782 --- /dev/null +++ b/src/components/ui/label.jsx @@ -0,0 +1,17 @@ +import * as React from 'react'; +import * as LabelPrimitive from '@radix-ui/react-label'; +import { cn } from '../../lib/utils'; + +const Label = React.forwardRef(({ className, ...props }, ref) => ( + +)); +Label.displayName = LabelPrimitive.Root.displayName; + +export { Label }; diff --git a/src/components/ui/separator.jsx b/src/components/ui/separator.jsx new file mode 100644 index 0000000..44ce5f9 --- /dev/null +++ b/src/components/ui/separator.jsx @@ -0,0 +1,22 @@ +import * as React from 'react'; +import * as SeparatorPrimitive from '@radix-ui/react-separator'; +import { cn } from '../../lib/utils'; + +const Separator = React.forwardRef( + ({ className, orientation = 'horizontal', decorative = true, ...props }, ref) => ( + + ) +); +Separator.displayName = SeparatorPrimitive.Root.displayName; + +export { Separator }; diff --git a/src/components/ui/tabs.jsx b/src/components/ui/tabs.jsx new file mode 100644 index 0000000..8fd994e --- /dev/null +++ b/src/components/ui/tabs.jsx @@ -0,0 +1,43 @@ +import * as React from 'react'; +import * as TabsPrimitive from '@radix-ui/react-tabs'; +import { cn } from '../../lib/utils'; + +const Tabs = TabsPrimitive.Root; + +const TabsList = React.forwardRef(({ className, ...props }, ref) => ( + +)); +TabsList.displayName = TabsPrimitive.List.displayName; + +const TabsTrigger = React.forwardRef(({ className, ...props }, ref) => ( + +)); +TabsTrigger.displayName = TabsPrimitive.Trigger.displayName; + +const TabsContent = React.forwardRef(({ className, ...props }, ref) => ( + +)); +TabsContent.displayName = TabsPrimitive.Content.displayName; + +export { Tabs, TabsList, TabsTrigger, TabsContent }; diff --git a/src/components/ui/textarea.jsx b/src/components/ui/textarea.jsx new file mode 100644 index 0000000..1faa088 --- /dev/null +++ b/src/components/ui/textarea.jsx @@ -0,0 +1,16 @@ +import * as React from 'react'; +import { cn } from '../../lib/utils'; + +const Textarea = React.forwardRef(({ className, ...props }, ref) => ( +