From 5b7d58813ec0068b9291e68896210833ec8fd41e Mon Sep 17 00:00:00 2001 From: amianthus <49116958+SirTenzin@users.noreply.github.com> Date: Mon, 22 Sep 2025 13:11:31 +0000 Subject: [PATCH] wip --- package.json | 3 +- pnpm-lock.yaml | 173 ++++++++++---- vite/src/App.tsx | 146 ++++++------ vite/src/components/general/CopyablePre.tsx | 44 ++-- .../general/buttons/ClickableIcon.tsx | 29 +++ vite/src/components/v2/badge.tsx | 52 +++++ vite/src/components/v2/breadcrumb.tsx | 42 ++++ vite/src/components/v2/button.tsx | 0 .../products/components/PlanTypeBadge.tsx | 41 ++++ .../views/products/plan/PlanEditorView.tsx | 92 ++++++++ .../views/products/plan/components/Editor.tsx | 28 +++ .../plan/components/EditorTopSection.tsx | 55 +++++ .../products/plan/components/PlanCard.tsx | 25 ++ .../products/plan/components/RightPanel.tsx | 115 ++++++++++ .../views/products/product/ManageProduct.tsx | 90 ++++---- .../views/products/product/ProductView.tsx | 215 +++++++++--------- 16 files changed, 865 insertions(+), 285 deletions(-) create mode 100644 vite/src/components/general/buttons/ClickableIcon.tsx create mode 100644 vite/src/components/v2/badge.tsx create mode 100644 vite/src/components/v2/breadcrumb.tsx create mode 100644 vite/src/components/v2/button.tsx create mode 100644 vite/src/views/products/components/PlanTypeBadge.tsx create mode 100644 vite/src/views/products/plan/PlanEditorView.tsx create mode 100644 vite/src/views/products/plan/components/Editor.tsx create mode 100644 vite/src/views/products/plan/components/EditorTopSection.tsx create mode 100644 vite/src/views/products/plan/components/PlanCard.tsx create mode 100644 vite/src/views/products/plan/components/RightPanel.tsx diff --git a/package.json b/package.json index 7be32b2d5..d46e044fb 100644 --- a/package.json +++ b/package.json @@ -21,11 +21,9 @@ "dev:wsl": "concurrently \"cd server && npm run dev\" \"cd vite && npm run dev\" \"cd shared && npm run dev\"", "setup": "node setup.js", "setupci": "node setupci", - "db:push": " bun -F @autumn/shared db:push", "db:generate": "bun -F @autumn/shared db:generate", "db:migrate": " bun -F @autumn/shared db:migrate", - "docker:up": "docker compose -f docker-compose.dev.yml up --build", "docker:up:unix": "docker compose -f docker-compose.unix.yml up --build", "docker:up:ci": "docker compose -f docker-compose.ci.yml up --build", @@ -43,6 +41,7 @@ "tailwind-scrollbar-hide": "^4.0.0" }, "devDependencies": { + "@biomejs/biome": "^2.2.4", "@types/node": "^24.0.3", "concurrently": "^9.1.2", "dotenv": "^16.5.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fb7b64c60..d5f6936e9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -24,6 +24,9 @@ importers: specifier: ^4.0.0 version: 4.0.0(tailwindcss@4.1.12) devDependencies: + '@biomejs/biome': + specifier: ^2.2.4 + version: 2.2.4 '@types/node': specifier: ^24.0.3 version: 24.3.0 @@ -80,7 +83,7 @@ importers: version: 1.9.0 '@opentelemetry/auto-instrumentations-node': specifier: ^0.60.1 - version: 0.60.1(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0)) + version: 0.60.1(@opentelemetry/api@1.9.0)(@opentelemetry/core@2.0.1(@opentelemetry/api@1.9.0)) '@opentelemetry/exporter-trace-otlp-proto': specifier: ^0.202.0 version: 0.202.0(@opentelemetry/api@1.9.0) @@ -173,7 +176,7 @@ importers: version: 16.6.1 drizzle-orm: specifier: ^0.43.1 - version: 0.43.1(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(bun-types@1.2.21(@types/react@18.3.24))(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7) + version: 0.43.1(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(bun-types@1.2.22(@types/react@18.3.24))(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7) express: specifier: ^4.21.1 version: 4.21.2 @@ -324,17 +327,17 @@ importers: version: 0.31.4 drizzle-orm: specifier: ^0.43.1 - version: 0.43.1(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(bun-types@1.2.21(@types/react@18.3.24))(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7) + version: 0.43.1(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(bun-types@1.2.22(@types/react@18.3.24))(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7) drizzle-zod: specifier: ^0.8.2 - version: 0.8.3(drizzle-orm@0.43.1(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(bun-types@1.2.21(@types/react@18.3.24))(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7))(zod@3.25.76) + version: 0.8.3(drizzle-orm@0.43.1(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(bun-types@1.2.22(@types/react@18.3.24))(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7))(zod@3.25.76) zod: specifier: ^3.25.23 version: 3.25.76 devDependencies: '@types/bun': specifier: latest - version: 1.2.21(@types/react@18.3.24) + version: 1.2.22(@types/react@18.3.24) '@types/node': specifier: ^24.0.3 version: 24.3.0 @@ -789,6 +792,59 @@ packages: '@better-fetch/fetch@1.1.18': resolution: {integrity: sha512-rEFOE1MYIsBmoMJtQbl32PGHHXuG2hDxvEd7rUHE0vCBoFQVSDqaVs9hkZEtHCxRoY+CljXKFCOuJ8uxqw1LcA==} + '@biomejs/biome@2.2.4': + resolution: {integrity: sha512-TBHU5bUy/Ok6m8c0y3pZiuO/BZoY/OcGxoLlrfQof5s8ISVwbVBdFINPQZyFfKwil8XibYWb7JMwnT8wT4WVPg==} + engines: {node: '>=14.21.3'} + hasBin: true + + '@biomejs/cli-darwin-arm64@2.2.4': + resolution: {integrity: sha512-RJe2uiyaloN4hne4d2+qVj3d3gFJFbmrr5PYtkkjei1O9c+BjGXgpUPVbi8Pl8syumhzJjFsSIYkcLt2VlVLMA==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [darwin] + + '@biomejs/cli-darwin-x64@2.2.4': + resolution: {integrity: sha512-cFsdB4ePanVWfTnPVaUX+yr8qV8ifxjBKMkZwN7gKb20qXPxd/PmwqUH8mY5wnM9+U0QwM76CxFyBRJhC9tQwg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [darwin] + + '@biomejs/cli-linux-arm64-musl@2.2.4': + resolution: {integrity: sha512-7TNPkMQEWfjvJDaZRSkDCPT/2r5ESFPKx+TEev+I2BXDGIjfCZk2+b88FOhnJNHtksbOZv8ZWnxrA5gyTYhSsQ==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-arm64@2.2.4': + resolution: {integrity: sha512-M/Iz48p4NAzMXOuH+tsn5BvG/Jb07KOMTdSVwJpicmhN309BeEyRyQX+n1XDF0JVSlu28+hiTQ2L4rZPvu7nMw==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-x64-musl@2.2.4': + resolution: {integrity: sha512-m41nFDS0ksXK2gwXL6W6yZTYPMH0LughqbsxInSKetoH6morVj43szqKx79Iudkp8WRT5SxSh7qVb8KCUiewGg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-linux-x64@2.2.4': + resolution: {integrity: sha512-orr3nnf2Dpb2ssl6aihQtvcKtLySLta4E2UcXdp7+RTa7mfJjBgIsbS0B9GC8gVu0hjOu021aU8b3/I1tn+pVQ==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-win32-arm64@2.2.4': + resolution: {integrity: sha512-NXnfTeKHDFUWfxAefa57DiGmu9VyKi0cDqFpdI+1hJWQjGJhJutHPX0b5m+eXvTKOaf+brU+P0JrQAZMb5yYaQ==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [win32] + + '@biomejs/cli-win32-x64@2.2.4': + resolution: {integrity: sha512-3Y4V4zVRarVh/B/eSHczR4LYoSVyv3Dfuvm3cWs5w/HScccS0+Wt/lHOcDTRYeHjQmMYVC3rIRWqyN2EI52+zg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [win32] + '@browserbasehq/sdk@2.6.0': resolution: {integrity: sha512-83iXP5D7xMm8Wyn66TUaUrgoByCmAJuoMoZQI3sGg3JAiMlTfnCIMqyVBoNSaItaPIkaCnrsj6LiusmXV2X9YA==} @@ -3775,8 +3831,8 @@ packages: '@types/body-parser@1.19.6': resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} - '@types/bun@1.2.21': - resolution: {integrity: sha512-NiDnvEqmbfQ6dmZ3EeUO577s4P5bf4HCTXtI6trMc6f6RzirY5IrF3aIookuSpyslFzrnvv2lmEWv5HyC1X79A==} + '@types/bun@1.2.22': + resolution: {integrity: sha512-5A/KrKos2ZcN0c6ljRSOa1fYIyCKhZfIVYeuyb4snnvomnpFqC0tTsEkdqNxbAgExV384OETQ//WAjl3XbYqQA==} '@types/bunyan@1.8.11': resolution: {integrity: sha512-758fRH7umIMk5qt5ELmRMff4mLDlN+xyYzC+dkPTdKwbSkJFvz6xwyScrytPU0QIBbRRwbiE8/BIg8bpajerNQ==} @@ -4372,8 +4428,8 @@ packages: bullmq@5.58.4: resolution: {integrity: sha512-1EE7pbEw2YNc64nVW+RYVvA6I1ijkhCacsZ/zWbjhDtN8sxZ+Wy3WQ/rUSMOOk/PGAGEQRTgngbTpYbvGYijHQ==} - bun-types@1.2.21: - resolution: {integrity: sha512-sa2Tj77Ijc/NTLS0/Odjq/qngmEPZfbfnOERi0KRUYhT9R8M4VBioWVmMWE5GrYbKMc+5lVybXygLdibHaqVqw==} + bun-types@1.2.22: + resolution: {integrity: sha512-hwaAu8tct/Zn6Zft4U9BsZcXkYomzpHJX28ofvx7k0Zz2HNz54n1n+tDgxoWFGB4PcFvJXJQloPhaV2eP3Q6EA==} peerDependencies: '@types/react': ^19 @@ -7584,7 +7640,7 @@ snapshots: '@babel/traverse': 7.28.3 '@babel/types': 7.28.2 convert-source-map: 2.0.0 - debug: 4.4.1(supports-color@5.5.0) + debug: 4.4.1(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -7727,7 +7783,7 @@ snapshots: '@babel/parser': 7.28.3 '@babel/template': 7.27.2 '@babel/types': 7.28.2 - debug: 4.4.1(supports-color@5.5.0) + debug: 4.4.1(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -7750,6 +7806,41 @@ snapshots: '@better-fetch/fetch@1.1.18': {} + '@biomejs/biome@2.2.4': + optionalDependencies: + '@biomejs/cli-darwin-arm64': 2.2.4 + '@biomejs/cli-darwin-x64': 2.2.4 + '@biomejs/cli-linux-arm64': 2.2.4 + '@biomejs/cli-linux-arm64-musl': 2.2.4 + '@biomejs/cli-linux-x64': 2.2.4 + '@biomejs/cli-linux-x64-musl': 2.2.4 + '@biomejs/cli-win32-arm64': 2.2.4 + '@biomejs/cli-win32-x64': 2.2.4 + + '@biomejs/cli-darwin-arm64@2.2.4': + optional: true + + '@biomejs/cli-darwin-x64@2.2.4': + optional: true + + '@biomejs/cli-linux-arm64-musl@2.2.4': + optional: true + + '@biomejs/cli-linux-arm64@2.2.4': + optional: true + + '@biomejs/cli-linux-x64-musl@2.2.4': + optional: true + + '@biomejs/cli-linux-x64@2.2.4': + optional: true + + '@biomejs/cli-win32-arm64@2.2.4': + optional: true + + '@biomejs/cli-win32-x64@2.2.4': + optional: true + '@browserbasehq/sdk@2.6.0': dependencies: '@types/node': 18.19.123 @@ -8076,7 +8167,7 @@ snapshots: '@eslint/config-array@0.21.0': dependencies: '@eslint/object-schema': 2.1.6 - debug: 4.4.1(supports-color@5.5.0) + debug: 4.4.1(supports-color@8.1.1) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -8090,7 +8181,7 @@ snapshots: '@eslint/eslintrc@3.3.1': dependencies: ajv: 6.12.6 - debug: 4.4.1(supports-color@5.5.0) + debug: 4.4.1(supports-color@8.1.1) espree: 10.4.0 globals: 14.0.0 ignore: 5.3.2 @@ -8780,10 +8871,10 @@ snapshots: - encoding - supports-color - '@opentelemetry/auto-instrumentations-node@0.60.1(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))': + '@opentelemetry/auto-instrumentations-node@0.60.1(@opentelemetry/api@1.9.0)(@opentelemetry/core@2.0.1(@opentelemetry/api@1.9.0))': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation-amqplib': 0.49.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation-aws-lambda': 0.53.1(@opentelemetry/api@1.9.0) @@ -10163,7 +10254,7 @@ snapshots: '@puppeteer/browsers@2.10.8': dependencies: - debug: 4.4.1(supports-color@5.5.0) + debug: 4.4.1(supports-color@8.1.1) extract-zip: 2.0.1 progress: 2.0.3 proxy-agent: 6.5.0 @@ -11119,9 +11210,9 @@ snapshots: '@types/connect': 3.4.38 '@types/node': 22.18.0 - '@types/bun@1.2.21(@types/react@18.3.24)': + '@types/bun@1.2.22(@types/react@18.3.24)': dependencies: - bun-types: 1.2.21(@types/react@18.3.24) + bun-types: 1.2.22(@types/react@18.3.24) transitivePeerDependencies: - '@types/react' @@ -11381,7 +11472,7 @@ snapshots: '@typescript-eslint/types': 8.41.0 '@typescript-eslint/typescript-estree': 8.41.0(typescript@5.7.3) '@typescript-eslint/visitor-keys': 8.41.0 - debug: 4.4.1(supports-color@5.5.0) + debug: 4.4.1(supports-color@8.1.1) eslint: 9.34.0(jiti@2.5.1) typescript: 5.7.3 transitivePeerDependencies: @@ -11391,7 +11482,7 @@ snapshots: dependencies: '@typescript-eslint/tsconfig-utils': 8.41.0(typescript@5.7.3) '@typescript-eslint/types': 8.41.0 - debug: 4.4.1(supports-color@5.5.0) + debug: 4.4.1(supports-color@8.1.1) typescript: 5.7.3 transitivePeerDependencies: - supports-color @@ -11410,7 +11501,7 @@ snapshots: '@typescript-eslint/types': 8.41.0 '@typescript-eslint/typescript-estree': 8.41.0(typescript@5.7.3) '@typescript-eslint/utils': 8.41.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.7.3) - debug: 4.4.1(supports-color@5.5.0) + debug: 4.4.1(supports-color@8.1.1) eslint: 9.34.0(jiti@2.5.1) ts-api-utils: 2.1.0(typescript@5.7.3) typescript: 5.7.3 @@ -11425,7 +11516,7 @@ snapshots: '@typescript-eslint/tsconfig-utils': 8.41.0(typescript@5.7.3) '@typescript-eslint/types': 8.41.0 '@typescript-eslint/visitor-keys': 8.41.0 - debug: 4.4.1(supports-color@5.5.0) + debug: 4.4.1(supports-color@8.1.1) fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.5 @@ -11795,9 +11886,9 @@ snapshots: transitivePeerDependencies: - supports-color - bun-types@1.2.21(@types/react@18.3.24): + bun-types@1.2.22(@types/react@18.3.24): dependencies: - '@types/node': 24.3.0 + '@types/node': 22.18.0 '@types/react': 18.3.24 bytes@3.1.2: {} @@ -12224,18 +12315,18 @@ snapshots: transitivePeerDependencies: - supports-color - drizzle-orm@0.43.1(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(bun-types@1.2.21(@types/react@18.3.24))(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7): + drizzle-orm@0.43.1(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(bun-types@1.2.22(@types/react@18.3.24))(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7): optionalDependencies: '@opentelemetry/api': 1.9.0 '@types/pg': 8.15.5 - bun-types: 1.2.21(@types/react@18.3.24) + bun-types: 1.2.22(@types/react@18.3.24) kysely: 0.28.5 pg: 8.16.3 postgres: 3.4.7 - drizzle-zod@0.8.3(drizzle-orm@0.43.1(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(bun-types@1.2.21(@types/react@18.3.24))(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7))(zod@3.25.76): + drizzle-zod@0.8.3(drizzle-orm@0.43.1(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(bun-types@1.2.22(@types/react@18.3.24))(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7))(zod@3.25.76): dependencies: - drizzle-orm: 0.43.1(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(bun-types@1.2.21(@types/react@18.3.24))(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7) + drizzle-orm: 0.43.1(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(bun-types@1.2.22(@types/react@18.3.24))(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7) zod: 3.25.76 dunder-proto@1.0.1: @@ -12310,7 +12401,7 @@ snapshots: esbuild-register@3.6.0(esbuild@0.25.9): dependencies: - debug: 4.4.1(supports-color@5.5.0) + debug: 4.4.1(supports-color@8.1.1) esbuild: 0.25.9 transitivePeerDependencies: - supports-color @@ -12446,7 +12537,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.1(supports-color@5.5.0) + debug: 4.4.1(supports-color@8.1.1) escape-string-regexp: 4.0.0 eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 @@ -12542,7 +12633,7 @@ snapshots: extract-zip@2.0.1: dependencies: - debug: 4.4.1(supports-color@5.5.0) + debug: 4.4.1(supports-color@8.1.1) get-stream: 5.2.0 yauzl: 2.10.0 optionalDependencies: @@ -12747,7 +12838,7 @@ snapshots: dependencies: basic-ftp: 5.0.5 data-uri-to-buffer: 6.0.2 - debug: 4.4.1(supports-color@5.5.0) + debug: 4.4.1(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -12872,7 +12963,7 @@ snapshots: http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.4 - debug: 4.4.1(supports-color@5.5.0) + debug: 4.4.1(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -12881,7 +12972,7 @@ snapshots: https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.4 - debug: 4.4.1(supports-color@5.5.0) + debug: 4.4.1(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -12983,7 +13074,7 @@ snapshots: dependencies: '@ioredis/commands': 1.3.1 cluster-key-slot: 1.1.2 - debug: 4.4.1(supports-color@5.5.0) + debug: 4.4.1(supports-color@8.1.1) denque: 2.1.0 lodash.defaults: 4.2.0 lodash.isarguments: 3.1.0 @@ -13666,7 +13757,7 @@ snapshots: dependencies: '@tootallnate/quickjs-emscripten': 0.23.0 agent-base: 7.1.4 - debug: 4.4.1(supports-color@5.5.0) + debug: 4.4.1(supports-color@8.1.1) get-uri: 6.0.5 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 @@ -13890,7 +13981,7 @@ snapshots: proxy-agent@6.5.0: dependencies: agent-base: 7.1.4 - debug: 4.4.1(supports-color@5.5.0) + debug: 4.4.1(supports-color@8.1.1) http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 lru-cache: 7.18.3 @@ -13915,7 +14006,7 @@ snapshots: dependencies: '@puppeteer/browsers': 2.10.8 chromium-bidi: 8.0.0(devtools-protocol@0.0.1475386) - debug: 4.4.1(supports-color@5.5.0) + debug: 4.4.1(supports-color@8.1.1) devtools-protocol: 0.0.1475386 typed-query-selector: 2.12.0 ws: 8.18.3 @@ -14201,7 +14292,7 @@ snapshots: require-in-the-middle@7.5.2: dependencies: - debug: 4.4.1(supports-color@5.5.0) + debug: 4.4.1(supports-color@8.1.1) module-details-from-path: 1.0.4 resolve: 1.22.10 transitivePeerDependencies: @@ -14462,7 +14553,7 @@ snapshots: socks-proxy-agent@8.0.5: dependencies: agent-base: 7.1.4 - debug: 4.4.1(supports-color@5.5.0) + debug: 4.4.1(supports-color@8.1.1) socks: 2.8.7 transitivePeerDependencies: - supports-color @@ -14582,7 +14673,7 @@ snapshots: dependencies: component-emitter: 1.3.1 cookiejar: 2.1.4 - debug: 4.4.1(supports-color@5.5.0) + debug: 4.4.1(supports-color@8.1.1) fast-safe-stringify: 2.1.1 form-data: 4.0.4 formidable: 3.5.4 diff --git a/vite/src/App.tsx b/vite/src/App.tsx index fb8f4043d..eaaacbbe8 100644 --- a/vite/src/App.tsx +++ b/vite/src/App.tsx @@ -1,81 +1,81 @@ -import OnboardingView2 from "./views/onboarding2/OnboardingView2"; +import { AppEnv } from "@autumn/shared"; +import { BrowserRouter, Route, Routes } from "react-router"; +import { MainLayout } from "./app/layout"; +import { AdminView } from "./views/admin/AdminView"; +import { AcceptInvitation } from "./views/auth/AcceptInvitation"; +import { PasswordSignIn } from "./views/auth/components/PasswordSignIn"; +import { SignIn } from "./views/auth/SignIn"; +import CliAuth from "./views/CliAuth"; +import { Otp } from "./views/cli/Otp"; +import CustomersPage from "./views/customers/CustomersPage"; +import { AnalyticsView } from "./views/customers/customer/analytics/AnalyticsView"; import CustomerView from "./views/customers/customer/CustomerView"; import CustomerProductView from "./views/customers/customer/product/CustomerProductView"; -import DevScreen from "./views/developer/DevView"; -import ProductView from "./views/products/product/ProductView"; -import ProductsView from "./views/products/ProductsView"; -import CliAuth from "./views/CliAuth"; - -import { AppEnv } from "@autumn/shared"; -import { Otp } from "./views/cli/Otp"; -import { SignIn } from "./views/auth/SignIn"; -import { AcceptInvitation } from "./views/auth/AcceptInvitation"; -import { BrowserRouter, Routes, Route } from "react-router"; -import { AdminView } from "./views/admin/AdminView"; -import { PasswordSignIn } from "./views/auth/components/PasswordSignIn"; -import { AnalyticsView } from "./views/customers/customer/analytics/AnalyticsView"; -import { TerminalView } from "./views/TerminalView"; import { DefaultView } from "./views/DefaultView"; -import { MainLayout } from "./app/layout"; -import CustomersPage from "./views/customers/CustomersPage"; +import DevScreen from "./views/developer/DevView"; +import OnboardingView2 from "./views/onboarding2/OnboardingView2"; +import ProductsView from "./views/products/ProductsView"; +import PlanEditorView from "./views/products/plan/PlanEditorView"; +import ProductView from "./views/products/product/ProductView"; +import { TerminalView } from "./views/TerminalView"; export default function App() { - return ( - - - } /> - } /> - } /> - }> - } /> - } /> - } /> - } /> - } /> - } /> + return ( + + + } /> + } /> + } /> + }> + } /> + } /> + } /> + } /> + } /> + } /> - } - /> - } - /> - } /> - } - /> + } + /> + } + /> + } /> + } + /> - } /> - } /> - } /> - } - /> - } - /> - } - /> - } /> - } /> - } - /> - } - /> - } /> - - - - ); + } /> + } /> + } /> + } + /> + } + /> + } + /> + } /> + } /> + } + /> + } + /> + } /> + + + + ); } diff --git a/vite/src/components/general/CopyablePre.tsx b/vite/src/components/general/CopyablePre.tsx index e6f377e56..68aa99a69 100644 --- a/vite/src/components/general/CopyablePre.tsx +++ b/vite/src/components/general/CopyablePre.tsx @@ -1,19 +1,33 @@ -import { Button } from "../ui/button"; -import { Copy } from "lucide-react"; import CopyButton from "./CopyButton"; export function CopyablePre({ text }: { text: string }) { - return ( -
-
-        
-        
-          {text}
-        
-      
-
- ); + return ( +
+
+				
+				
+					{text}
+				
+			
+
+ ); +} + +// A compact, inline version for use in places like badges or small UI elements. +export function CopyableSpan({ text, className = "" }: { text: string; className?: string }) { + return ( + + {text} + + + ); } diff --git a/vite/src/components/general/buttons/ClickableIcon.tsx b/vite/src/components/general/buttons/ClickableIcon.tsx new file mode 100644 index 000000000..9ff0658cf --- /dev/null +++ b/vite/src/components/general/buttons/ClickableIcon.tsx @@ -0,0 +1,29 @@ +import { Slot } from "@radix-ui/react-slot"; +import type { ButtonHTMLAttributes } from "react"; +import { cn } from "@/lib/utils"; + +interface ClickableIconProps extends ButtonHTMLAttributes { + icon: React.ReactElement; + asChild?: boolean; +} + +export const ClickableIcon = ({ + icon, + onClick, + asChild, + ...props +}: ClickableIconProps) => { + const Comp = asChild ? Slot : "button"; + + return ( + + {icon} + + ); +}; diff --git a/vite/src/components/v2/badge.tsx b/vite/src/components/v2/badge.tsx new file mode 100644 index 000000000..a42c6ad1e --- /dev/null +++ b/vite/src/components/v2/badge.tsx @@ -0,0 +1,52 @@ +import { Slot } from "@radix-ui/react-slot"; +import { cva, type VariantProps } from "class-variance-authority"; +import type * as React from "react"; + +import { cn } from "@/lib/utils"; + +const badgeVariants = cva( + "inline-flex items-center rounded-full border border-zinc-200 px-2.5 py-0.5 text-xs transition-colors focus:outline-none focus:ring-2 focus:ring-zinc-950 focus:ring-offset-2 dark:border-zinc-800 dark:focus:ring-zinc-300", + { + variants: { + variant: { + default: + "border-transparent bg-zinc-900 text-zinc-50 shadow hover:bg-zinc-900/80 dark:bg-zinc-50 dark:text-zinc-900 dark:hover:bg-zinc-50/80", + secondary: + "border-transparent bg-zinc-100 text-zinc-900 hover:bg-zinc-100/80 dark:bg-zinc-800 dark:text-zinc-50 dark:hover:bg-zinc-800/80", + destructive: + "border-transparent bg-red-500 text-zinc-50 shadow hover:bg-red-500/80 dark:bg-red-900 dark:text-zinc-50 dark:hover:bg-red-900/80", + outline: "text-zinc-950 dark:text-zinc-50", + purple: "bg-purple-50 text-purple-500 border-purple-200", + blue: "bg-blue-50 text-blue-500 border-blue-200", + green: + "bg-green-50 text-green-500 border-green-200 !hover:bg-green-100", + yellow: "bg-yellow-50 text-yellow-500 border-yellow-200", + red: "bg-red-50 text-red-500 border-red-200", + status: "px-1.5 py-0 text-white border-none", + }, + }, + defaultVariants: { + variant: "default", + }, + }, +); + +function Badge({ + className, + variant, + asChild = false, + ...props +}: React.ComponentProps<"span"> & + VariantProps & { asChild?: boolean }) { + const Comp = asChild ? Slot : "span"; + + return ( + + ); +} + +export { Badge, badgeVariants }; diff --git a/vite/src/components/v2/breadcrumb.tsx b/vite/src/components/v2/breadcrumb.tsx new file mode 100644 index 000000000..5dd5a679c --- /dev/null +++ b/vite/src/components/v2/breadcrumb.tsx @@ -0,0 +1,42 @@ +import { useNavigate } from "react-router"; +import { + Breadcrumb, + BreadcrumbItem, + BreadcrumbList, + BreadcrumbSeparator +} from "@/components/ui/breadcrumb"; +import { useEnv } from "@/utils/envUtils"; +import { navigateTo } from "@/utils/genUtils"; + +interface BreadcrumbItemType { + name: string; + href: string; +} + +export default function V2Breadcrumb({ + items, +}: { + items: BreadcrumbItemType[]; +}) { + const env = useEnv(); + const navigate = useNavigate(); + + return ( + + + {items.map((item, index) => ( + <> + navigateTo(item.href, navigate, env)} + className="cursor-pointer" + > + {item.name} + + {index < items.length - 1 && } + + ))} + + + ); +} diff --git a/vite/src/components/v2/button.tsx b/vite/src/components/v2/button.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/vite/src/views/products/components/PlanTypeBadge.tsx b/vite/src/views/products/components/PlanTypeBadge.tsx new file mode 100644 index 000000000..640e06ad0 --- /dev/null +++ b/vite/src/views/products/components/PlanTypeBadge.tsx @@ -0,0 +1,41 @@ +import { Badge } from "@/components/ui/badge"; +import { cn } from "@/lib/utils"; + +export const PlanTypeBadge = ({ product }: { product: any }) => { + const badgeType = + product.is_default && + product.free_trial && + !product.free_trial.card_required + ? "default trial" + : product.is_default + ? "default" + : product.is_add_on + ? "add-on" + : null; + + if (!badgeType) return null; + + return ( + // <> + // {product.is_default ? ( + // Default + // ) : product.is_add_on ? ( + // Add-On + // ) : ( + // <> + // )} + // + + {badgeType} + + ); +}; diff --git a/vite/src/views/products/plan/PlanEditorView.tsx b/vite/src/views/products/plan/PlanEditorView.tsx new file mode 100644 index 000000000..ac0c2955b --- /dev/null +++ b/vite/src/views/products/plan/PlanEditorView.tsx @@ -0,0 +1,92 @@ +import { useState } from "react"; +import { useParams } from "react-router"; +import V2Breadcrumb from "@/components/v2/breadcrumb"; +import ErrorScreen from "@/views/general/ErrorScreen"; +import LoadingScreen from "@/views/general/LoadingScreen"; +import { UpdateProductButton } from "../product/components/UpdateProductButton"; +import { useProductChangedAlert } from "../product/hooks/useProductChangedAlert"; +import { useProductData } from "../product/hooks/useProductData"; +import { useProductQuery } from "../product/hooks/useProductQuery"; +import { ProductContext } from "../product/ProductContext"; +import ProductSidebar from "../product/ProductSidebar"; +import ConfirmNewVersionDialog from "../product/versioning/ConfirmNewVersionDialog"; +import { ManagePlan } from "./components/Editor"; + +export default function PlanEditorView() { + const { plan_id } = useParams(); + + const { product: originalProduct, isLoading, error } = useProductQuery(); + + const { + product, + setProduct, + hasChanges, + entityFeatureIds, + setEntityFeatureIds, + actionState, + } = useProductData({ originalProduct }); + + const { modal } = useProductChangedAlert({ hasChanges }); + const [showNewVersionDialog, setShowNewVersionDialog] = useState(false); + + if (isLoading) return ; + if (error) { + return ( + + {error ? error.message : `Plan ${plan_id} not found`} + + ); + } + + if (!product) return; + + return ( + + +
+
+ + +
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+ {modal} +
+ ); +} diff --git a/vite/src/views/products/plan/components/Editor.tsx b/vite/src/views/products/plan/components/Editor.tsx new file mode 100644 index 000000000..ab02d7faf --- /dev/null +++ b/vite/src/views/products/plan/components/Editor.tsx @@ -0,0 +1,28 @@ +import { useCusQuery } from "@/views/customers/customer/hooks/useCusQuery"; +import { useProductContext } from "@/views/products/product/ProductContext"; +import EditorTopSection from "./EditorTopSection"; +import PlanCard from "./PlanCard"; + +export const ManagePlan = ({ + hideAdminHover = false, +}: { + hideAdminHover?: boolean; +}) => { + const { customer } = useCusQuery(); + const { product, entityId } = useProductContext(); + + return ( +
+ + +
+ +
+
+ ); +}; diff --git a/vite/src/views/products/plan/components/EditorTopSection.tsx b/vite/src/views/products/plan/components/EditorTopSection.tsx new file mode 100644 index 000000000..68d88fe80 --- /dev/null +++ b/vite/src/views/products/plan/components/EditorTopSection.tsx @@ -0,0 +1,55 @@ +import type { FullCustomer } from "@autumn/shared"; +import { AdminHover } from "@/components/general/AdminHover"; +import { Badge } from "@/components/ui/badge"; +import { SelectEntity } from "@/views/customers/customer/components/customer-header/SelectEntity"; +import { PlanTypeBadge } from "../../components/PlanTypeBadge"; + +export default function EditorTopSection({ + product, + hideAdminHover, + customer, + entityId, +}: { + product: any; + hideAdminHover: boolean; + customer: FullCustomer; + entityId: string; +}) { + return ( +
+
+
+ + + {product.name} + + + v{product.version} +
+
+
+ Add-on + +
+ {customer && ( + + )} +
+ ); +} diff --git a/vite/src/views/products/plan/components/PlanCard.tsx b/vite/src/views/products/plan/components/PlanCard.tsx new file mode 100644 index 000000000..a786bf2d9 --- /dev/null +++ b/vite/src/views/products/plan/components/PlanCard.tsx @@ -0,0 +1,25 @@ +import type { ProductV2 } from "@autumn/shared"; +import { PencilIcon, Trash2 } from "lucide-react"; +import { CopyableSpan } from "@/components/general/CopyablePre"; +import { Card, CardHeader } from "@/components/ui/card"; + +export default function PlanCard({ product }: { product: ProductV2 }) { + return ( + + +
+
+ + {product.name} + + +
+
+ + +
+
+
+
+ ); +} diff --git a/vite/src/views/products/plan/components/RightPanel.tsx b/vite/src/views/products/plan/components/RightPanel.tsx new file mode 100644 index 000000000..811d4f9f1 --- /dev/null +++ b/vite/src/views/products/plan/components/RightPanel.tsx @@ -0,0 +1,115 @@ +import { Minus, Plus } from "lucide-react"; +import { useState } from "react"; +import { SideAccordion } from "@/components/general/SideAccordion"; +import { Accordion } from "@/components/ui/accordion"; +import { isOneOffProduct } from "@/utils/product/priceUtils"; +import { AttachButton } from "@/views/customers/customer/product/components/AttachButton"; +import { CustomerProductBadge } from "@/views/customers/customer/product/components/CustomerProductBadge"; +import { UpdateProductButton } from "../../product/components/UpdateProductButton"; +import { CreateFreeTrial } from "../../product/free-trial/CreateFreeTrial"; +import { FreeTrialView } from "../../product/free-trial/FreeTrialView"; +import { useProductContext } from "../../product/ProductContext"; +import { ProductProps } from "../../product/ProductProps"; +import { ProductVersions } from "../../product/ProductVersions"; +import { EntitiesSidebar } from "../../product/product-item/EntitiesSidebar"; + +export default function ProductSidebar() { + const { product, setProduct, isCusProductView } = useProductContext(); + const [freeTrialModalOpen, setFreeTrialModalOpen] = useState(false); + const [entitiesOpen, setEntitiesOpen] = useState(false); + const [accordionValues, setAccordionValues] = useState([ + "properties", + "versions", + "free-trial", + "entities", + ]); + + const handleFreeTrialModalOpen = () => { + setFreeTrialModalOpen(!freeTrialModalOpen); + }; + const handleDeleteFreeTrial = async () => { + setProduct({ ...product, free_trial: null }); + }; + + const handleAccordionToggle = (value: string) => { + setAccordionValues((prev) => + prev.includes(value) ? prev.filter((v) => v !== value) : [...prev, value], + ); + }; + + return ( +
+
+ {isCusProductView && } +
+ + +
+ + + +
+
+ + + +
+
+ setEntitiesOpen(!entitiesOpen)} + buttonIcon={} + > + + +
+ + + +
+ : + } + disabledReason={ + isOneOffProduct(product.items, product.is_add_on) + ? "Can't add a free trial to an a one time product" + : undefined + } + > +
+ {product.free_trial ? ( + + ) : ( + + Add a free trial to this product. + + )} +
+
+
+
+ {isCusProductView ? : } +
+
+
+ ); +} diff --git a/vite/src/views/products/product/ManageProduct.tsx b/vite/src/views/products/product/ManageProduct.tsx index 312802006..1eda62366 100644 --- a/vite/src/views/products/product/ManageProduct.tsx +++ b/vite/src/views/products/product/ManageProduct.tsx @@ -1,56 +1,54 @@ import { AdminHover } from "@/components/general/AdminHover"; -import { useProductContext } from "./ProductContext"; -import { useNavigate } from "react-router"; -import { useEnv } from "@/utils/envUtils"; -import { ProductItemTable } from "./product-item/ProductItemTable"; import { SelectEntity } from "@/views/customers/customer/components/customer-header/SelectEntity"; import { useCusQuery } from "@/views/customers/customer/hooks/useCusQuery"; +import { useProductContext } from "./ProductContext"; +import { ProductItemTable } from "./product-item/ProductItemTable"; export const ManageProduct = ({ - hideAdminHover = false, + hideAdminHover = false, }: { - hideAdminHover?: boolean; + hideAdminHover?: boolean; }) => { - const { customer } = useCusQuery(); - const { product, entityId } = useProductContext(); + const { customer } = useCusQuery(); + const { product, entityId } = useProductContext(); - return ( -
-
-
-
- -

- {product.name} -

-
-
-
- {/* */} - {customer && ( - - )} -
+ return ( +
+
+
+
+ +

+ {product.name} +

+
+
+
+ {/* */} + {customer && ( + + )} +
-
- -
-
- ); +
+ +
+
+ ); }; diff --git a/vite/src/views/products/product/ProductView.tsx b/vite/src/views/products/product/ProductView.tsx index 9415fc35d..52379ac97 100644 --- a/vite/src/views/products/product/ProductView.tsx +++ b/vite/src/views/products/product/ProductView.tsx @@ -1,140 +1,139 @@ "use client"; +import { useState } from "react"; +import { useParams } from "react-router"; import ErrorScreen from "@/views/general/ErrorScreen"; import LoadingScreen from "@/views/general/LoadingScreen"; -import ProductSidebar from "./ProductSidebar"; import ProductViewBreadcrumbs from "./components/ProductViewBreadcrumbs"; - -import { useState } from "react"; -import { ProductContext } from "./ProductContext"; -import { useParams } from "react-router"; -import { ManageProduct } from "./ManageProduct"; +import { UpdateProductButton } from "./components/UpdateProductButton"; import { useProductChangedAlert } from "./hooks/useProductChangedAlert"; import { useProductData } from "./hooks/useProductData"; -import { UpdateProductButton } from "./components/UpdateProductButton"; import { useProductQuery } from "./hooks/useProductQuery"; +import { ManageProduct } from "./ManageProduct"; +import { ProductContext } from "./ProductContext"; +import ProductSidebar from "./ProductSidebar"; import ConfirmNewVersionDialog from "./versioning/ConfirmNewVersionDialog"; function ProductView() { - const { product_id } = useParams(); + const { product_id } = useParams(); - const { product: originalProduct, isLoading, error } = useProductQuery(); + const { product: originalProduct, isLoading, error } = useProductQuery(); - const { - product, - setProduct, - hasChanges, - entityFeatureIds, - setEntityFeatureIds, - actionState, - } = useProductData({ originalProduct }); + const { + product, + setProduct, + hasChanges, + entityFeatureIds, + setEntityFeatureIds, + actionState, + } = useProductData({ originalProduct }); - const { modal } = useProductChangedAlert({ hasChanges }); - const [showNewVersionDialog, setShowNewVersionDialog] = useState(false); + const { modal } = useProductChangedAlert({ hasChanges }); + const [showNewVersionDialog, setShowNewVersionDialog] = useState(false); - if (isLoading) return ; - if (error) { - return ( - - {error ? error.message : `Product ${product_id} not found`} - - ); - } + if (isLoading) return ; + if (error) { + return ( + + {error ? error.message : `Product ${product_id} not found`} + + ); + } - if (!product) return <>; + if (!product) return; - // const updateProduct = async () => { - // try { - // await ProductService.updateProduct(axiosInstance, product.id, { - // ...UpdateProductSchema.parse(product), - // items: product.items, - // free_trial: product.free_trial, - // }); + // const updateProduct = async () => { + // try { + // await ProductService.updateProduct(axiosInstance, product.id, { + // ...UpdateProductSchema.parse(product), + // items: product.items, + // free_trial: product.free_trial, + // }); - // if (isNewProduct) { - // toast.success("Product created successfully"); - // } else { - // toast.success("Product updated successfully"); - // } + // if (isNewProduct) { + // toast.success("Product created successfully"); + // } else { + // toast.success("Product updated successfully"); + // } - // await refetch(); - // await mutateCount(); - // } catch (error) { - // toast.error(getBackendErr(error, "Failed to update product")); - // } - // }; + // await refetch(); + // await mutateCount(); + // } catch (error) { + // toast.error(getBackendErr(error, "Failed to update product")); + // } + // }; - // const updateProductClicked = async () => { - // if (!counts) { - // toast.error("Something went wrong, please try again..."); - // return; - // } + // const updateProductClicked = async () => { + // if (!counts) { + // toast.error("Something went wrong, please try again..."); + // return; + // } - // if (version && version < data?.numVersions) { - // toast.error("You can only update the latest version of a product"); - // return; - // } + // if (version && version < data?.numVersions) { + // toast.error("You can only update the latest version of a product"); + // return; + // } - // if (counts?.all > 0) { - // setShowNewVersionDialog(true); - // return; - // } + // if (counts?.all > 0) { + // setShowNewVersionDialog(true); + // return; + // } - // await updateProduct(); - // }; + // await updateProduct(); + // }; - return ( - - -
-
- + // mutateCount, + actionState, + // handleCreateProduct: updateProductClicked, + entityFeatureIds, + setEntityFeatureIds, + hasChanges, + }} + > + +
+
+ -
-
- -
-
-
-
- -
-
-
-
- -
-
- {modal} - - ); +
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+ {modal} +
+ ); } export default ProductView;