From 1dc582ba07774dc0673ca8f23f0190bff9e85133 Mon Sep 17 00:00:00 2001 From: abhishek Date: Mon, 24 Aug 2026 20:35:18 +0530 Subject: [PATCH] Initial commit --- .env.example | 5 + .gitignore | 7 + .npmrc | 1 + README.md | 101 + index.html | 17 + package-lock.json | 3998 +++++++++++++++++ package.json | 35 + public/favicon.ico | Bin 0 -> 11108 bytes public/favicon.png | Bin 0 -> 2008 bytes public/icon-16.png | Bin 0 -> 803 bytes public/icon-180.png | Bin 0 -> 17329 bytes public/icon-192.png | Bin 0 -> 18637 bytes public/icon-32.png | Bin 0 -> 2008 bytes public/icon-48.png | Bin 0 -> 3327 bytes public/icon-512.png | Bin 0 -> 31096 bytes public/logo-512.png | Bin 0 -> 31096 bytes public/logo-wordmark-light.png | Bin 0 -> 22027 bytes public/logo-wordmark.png | Bin 0 -> 22191 bytes replace_required.js | 41 + src/App.tsx | 152 + src/api/catalogue.ts | 53 + src/api/client.ts | 186 + src/api/insights.ts | 159 + src/api/people.ts | 160 + src/api/products.ts | 213 + src/api/stock.ts | 98 + src/api/tenants.ts | 71 + src/api/types.ts | 697 +++ src/auth/AuthContext.tsx | 73 + src/auth/roles.ts | 88 + src/auth/session.ts | 81 + src/components/ConsoleSearchBar.tsx | 67 + src/components/DataState.tsx | 65 + src/components/Freshness.tsx | 53 + src/components/KpiCard.tsx | 86 + src/components/PageBody.tsx | 34 + src/components/PageHeader.tsx | 109 + src/components/SectionHeader.tsx | 34 + src/components/shell/AppShell.tsx | 787 ++++ src/components/shell/AssistantPanel.tsx | 414 ++ src/demo/DemoPanel.tsx | 137 + src/demo/accounts.ts | 69 + src/demo/fixtures.ts | 961 ++++ src/demo/index.ts | 217 + src/env.d.ts | 9 + src/features/auth/LoginPage.tsx | 539 +++ .../nearle-admin/NearleAdminShell.tsx | 18 + .../nearle-admin/import/ImportDrawer.tsx | 181 + .../nearle-admin/import/SheetImportPanel.tsx | 320 ++ .../nearle-admin/import/parseProductSheet.ts | 224 + .../pages/GlobalCataloguePage.tsx | 295 ++ .../nearle-admin/pages/OnboardTenantPage.tsx | 279 ++ .../nearle-admin/pages/StoreDetailPage.tsx | 273 ++ .../nearle-admin/pages/StoresPage.tsx | 252 ++ src/features/store-admin/BranchScope.tsx | 95 + src/features/store-admin/DateRangePicker.tsx | 97 + .../store-admin/OrderDetailDrawer.tsx | 517 +++ src/features/store-admin/PeopleDrawers.tsx | 622 +++ src/features/store-admin/StoreAdminShell.tsx | 247 + src/features/store-admin/counterLabels.ts | 125 + src/features/store-admin/format.ts | 77 + src/features/store-admin/orderStatus.ts | 143 + .../store-admin/pages/ConsolePage.tsx | 785 ++++ .../store-admin/pages/InventoryPage.tsx | 727 +++ .../store-admin/pages/OnboardBranchPage.tsx | 321 ++ .../store-admin/pages/ReportsPage.tsx | 576 +++ src/features/store-admin/pages/SalesPage.tsx | 1176 +++++ .../store-admin/pages/TerminalsPage.tsx | 566 +++ src/features/store-admin/pages/UsersPage.tsx | 608 +++ src/features/store-admin/posStatus.test.ts | 294 ++ src/features/store-admin/posStatus.ts | 345 ++ src/features/store-admin/terminalProblems.ts | 318 ++ src/features/store-admin/useTerminalBoard.ts | 272 ++ src/hooks/useIsMobile.ts | 45 + src/index.css | 613 +++ src/main.tsx | 49 + src/queries/hooks.ts | 312 ++ src/queries/interval.ts | 9 + src/queries/keys.ts | 77 + src/theme/nearle.css | 141 + src/theme/nearle.d.ts | 10 + src/theme/nearle.js | 72 + src/theme/nearle.ts | 121 + tsconfig.json | 25 + vite.config.ts | 37 + 85 files changed, 21081 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 .npmrc create mode 100644 README.md create mode 100644 index.html create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 public/favicon.ico create mode 100644 public/favicon.png create mode 100644 public/icon-16.png create mode 100644 public/icon-180.png create mode 100644 public/icon-192.png create mode 100644 public/icon-32.png create mode 100644 public/icon-48.png create mode 100644 public/icon-512.png create mode 100644 public/logo-512.png create mode 100644 public/logo-wordmark-light.png create mode 100644 public/logo-wordmark.png create mode 100644 replace_required.js create mode 100644 src/App.tsx create mode 100644 src/api/catalogue.ts create mode 100644 src/api/client.ts create mode 100644 src/api/insights.ts create mode 100644 src/api/people.ts create mode 100644 src/api/products.ts create mode 100644 src/api/stock.ts create mode 100644 src/api/tenants.ts create mode 100644 src/api/types.ts create mode 100644 src/auth/AuthContext.tsx create mode 100644 src/auth/roles.ts create mode 100644 src/auth/session.ts create mode 100644 src/components/ConsoleSearchBar.tsx create mode 100644 src/components/DataState.tsx create mode 100644 src/components/Freshness.tsx create mode 100644 src/components/KpiCard.tsx create mode 100644 src/components/PageBody.tsx create mode 100644 src/components/PageHeader.tsx create mode 100644 src/components/SectionHeader.tsx create mode 100644 src/components/shell/AppShell.tsx create mode 100644 src/components/shell/AssistantPanel.tsx create mode 100644 src/demo/DemoPanel.tsx create mode 100644 src/demo/accounts.ts create mode 100644 src/demo/fixtures.ts create mode 100644 src/demo/index.ts create mode 100644 src/env.d.ts create mode 100644 src/features/auth/LoginPage.tsx create mode 100644 src/features/nearle-admin/NearleAdminShell.tsx create mode 100644 src/features/nearle-admin/import/ImportDrawer.tsx create mode 100644 src/features/nearle-admin/import/SheetImportPanel.tsx create mode 100644 src/features/nearle-admin/import/parseProductSheet.ts create mode 100644 src/features/nearle-admin/pages/GlobalCataloguePage.tsx create mode 100644 src/features/nearle-admin/pages/OnboardTenantPage.tsx create mode 100644 src/features/nearle-admin/pages/StoreDetailPage.tsx create mode 100644 src/features/nearle-admin/pages/StoresPage.tsx create mode 100644 src/features/store-admin/BranchScope.tsx create mode 100644 src/features/store-admin/DateRangePicker.tsx create mode 100644 src/features/store-admin/OrderDetailDrawer.tsx create mode 100644 src/features/store-admin/PeopleDrawers.tsx create mode 100644 src/features/store-admin/StoreAdminShell.tsx create mode 100644 src/features/store-admin/counterLabels.ts create mode 100644 src/features/store-admin/format.ts create mode 100644 src/features/store-admin/orderStatus.ts create mode 100644 src/features/store-admin/pages/ConsolePage.tsx create mode 100644 src/features/store-admin/pages/InventoryPage.tsx create mode 100644 src/features/store-admin/pages/OnboardBranchPage.tsx create mode 100644 src/features/store-admin/pages/ReportsPage.tsx create mode 100644 src/features/store-admin/pages/SalesPage.tsx create mode 100644 src/features/store-admin/pages/TerminalsPage.tsx create mode 100644 src/features/store-admin/pages/UsersPage.tsx create mode 100644 src/features/store-admin/posStatus.test.ts create mode 100644 src/features/store-admin/posStatus.ts create mode 100644 src/features/store-admin/terminalProblems.ts create mode 100644 src/features/store-admin/useTerminalBoard.ts create mode 100644 src/hooks/useIsMobile.ts create mode 100644 src/index.css create mode 100644 src/main.tsx create mode 100644 src/queries/hooks.ts create mode 100644 src/queries/interval.ts create mode 100644 src/queries/keys.ts create mode 100644 src/theme/nearle.css create mode 100644 src/theme/nearle.d.ts create mode 100644 src/theme/nearle.js create mode 100644 src/theme/nearle.ts create mode 100644 tsconfig.json create mode 100644 vite.config.ts diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..dd616c6 --- /dev/null +++ b/.env.example @@ -0,0 +1,5 @@ +# The Fiesta API host used by the production build. +# In development this is left unset and Vite proxies /fiesta -> fiesta.nearle.app +# (see vite.config.ts), which keeps the network tab consistent and avoids +# preflight surprises. +VITE_API_BASE="https://fiesta.nearle.app" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..11c21a4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +node_modules +dist +.env +.env.local +*.local +.DS_Store +.vscode diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..c0c80ba --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +engine-strict=false diff --git a/README.md b/README.md new file mode 100644 index 0000000..0d5222e --- /dev/null +++ b/README.md @@ -0,0 +1,101 @@ +# Nearle Console + +The new Nearle Daily retail operations console. Built against the existing Fiesta +backend — the API is a fixed constraint, not something this repo changes. + +## Running it + +```bash +npm install +npm run dev # http://localhost:3100 +npm run typecheck # tsc --noEmit +npm run build # typecheck + production build +``` + +> **Port 3100, not 3000.** The old console (`daily_merchant_web`) runs its dev +> server on 3000. `strictPort` is on, so if 3100 is taken this fails loudly +> rather than silently moving — which is the failure that makes you think your +> changes did not land when you are actually looking at a different app. + +In development Vite proxies `/fiesta` → `https://fiesta.nearle.app`. For a +deployed build set `VITE_API_BASE` (see `.env.example`). + +## Stack + +| Layer | Choice | +|---|---| +| Build | Vite 8 | +| Language | **TypeScript 7**, strict, `noUncheckedIndexedAccess`. No `.js`/`.jsx` anywhere. | +| UI | React 19 + **@astryxdesign/core** | +| Theme | `src/theme/nearle.ts`, compiled to `nearle.css` | +| Routing | React Router 7, split per page | +| Server state | TanStack Query 5 | +| Icons | lucide-react | +| Spreadsheets | `xlsx`, loaded on demand | + +## What's built + +Only the **Nearle Admin** workspace so far: + +- `/nearle/stores` — every tenant, branch counts, per-tenant performance +- `/nearle/stores/:tenantId` — one tenant's branches, orders and revenue +- `/nearle/onboard/tenant` — provision a merchant group +- `/nearle/onboard/branch` — commission an outlet with its delivery thresholds +- `/nearle/catalogue` — the global catalogue, plus both product-import paths + +`/admin/*` (Store Admin) and `/store/*` (Store Manager) render a named +placeholder rather than a 404, so those roles land somewhere that explains +itself. + +## Things about the backend that shape this code + +These are not bugs in this repo. They are the API's behaviour, and each one is +worked around deliberately. + +1. **No web authentication.** The login endpoints return the user record and no + token; only `/v1/pos/*` has middleware. So the session *is* that record, held + in `sessionStorage`. `src/auth/session.ts` is the only file that changes when + tokens arrive. +2. **Every list call needs a scoping id or it 400s.** The IDOR pass added + controller-level guards. Query hooks are therefore `enabled`-gated on the id + and the id is part of the query key. +3. **Roles are derived, not asserted.** `issuperadmin` is checked before + `roleid`, because the flag is server-derived and a roleid is not. Roleids 7 + and 8 are till roles and never reach an admin workspace. +4. **`POST /products/create` takes one product and returns no id.** The + controller passes the struct to the service by value, so GORM writes the + generated id into a copy that is then discarded. The sheet importer therefore + creates, then re-queries by SKU to resolve ids, then batches the location and + stock writes. See `importSheetProducts` in `src/api/products.ts`. +5. **The two import paths are not symmetric.** Catalogue import is one + idempotent batch call. Sheet import is N creates with no dedupe on SKU, so + re-uploading a file duplicates its products — the UI says so before the + button. +6. **The global catalogue has a price *range*, not a price**, and no mapping to + a tenant's own categories. Category, subcategory, retail price, cost and tax + are collected before an import can be enabled. +7. **POS endpoints take one required `locationid`.** There is no tenant-wide + counter-sales call, so a multi-branch POS view fans out per branch. + +## The 30-second cadence + +Online orders land when an order is placed; counter sales reach the console on a +30-second refetch; anything still sitting on an offline till has not arrived at +all. So **any figure blending the two is eventually consistent**, and the +`` component exists to say when a number was last true and how many +bills are still stranded. Polling is applied per query in `src/queries/hooks.ts` +rather than globally — a provisioning form has no business re-polling. + +## Design + +The visual system is ported from KROW (which is built on Astryx too, so the +tokens are role-for-role comparable), with Nearle purple `#662582` as the accent. +The ambient canvas in `index.css` — a fixed, viewport-wide horizontal gradient at +`z-index: -1` — is the one piece of KROW that carries most of the character. + +Radius, type scale and control heights are matched to KROW in +`src/theme/nearle.ts`, with the reasoning kept in comments. + +**Open:** the dark-mode accent `#B57FD0` was chosen for 5.41:1 contrast on +Astryx's dark surface and still needs brand-owner sign-off before dark mode +ships. diff --git a/index.html b/index.html new file mode 100644 index 0000000..5c6b8b7 --- /dev/null +++ b/index.html @@ -0,0 +1,17 @@ + + + + + + + + + + + Nearle Console + + +
+ + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..3a7e6a9 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3998 @@ +{ + "name": "nearle-console", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "nearle-console", + "version": "0.1.0", + "dependencies": { + "@astryxdesign/core": "^0.4.5", + "@stylexjs/stylex": "^0.19.0", + "@tanstack/react-query": "^5.101.4", + "lucide-react": "^1.33.0", + "react": "^19.2.8", + "react-dom": "^19.2.8", + "react-router-dom": "^7.18.2", + "recharts": "^3.10.1", + "xlsx": "^0.18.5" + }, + "devDependencies": { + "@astryxdesign/cli": "^0.4.5", + "@tailwindcss/vite": "^4.3.3", + "@types/node": "^26.2.0", + "@types/react": "^19.2.18", + "@types/react-dom": "^19.2.4", + "@vitejs/plugin-react": "^5.0.4", + "tailwindcss": "^4.3.3", + "typescript": "^7.0.2", + "vite": "^8.2.2" + } + }, + "node_modules/@astryxdesign/cli": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/@astryxdesign/cli/-/cli-0.4.5.tgz", + "integrity": "sha512-3kUYK5K5eO77O4trXmzG/GfwMBiFI2Y7MOfsVK3euHZk98zQX/Z04MXag+S89v5qeObS3S2LI27QitkIq7hoOQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "commander": "^12.1.0", + "jiti": "^2.7.0", + "jscodeshift": "^17.3.0", + "zod": "^4.4.3" + }, + "bin": { + "astryx": "clients/cli/bin/astryx.mjs", + "cli": "clients/cli/bin/astryx.mjs" + }, + "engines": { + "node": ">=22.13.0" + }, + "peerDependencies": { + "@astryxdesign/charts": "*", + "@astryxdesign/core": "*", + "@astryxdesign/lab": "*", + "@astryxdesign/theme-neutral": "*", + "gpt-tokenizer": "^3.4.0" + }, + "peerDependenciesMeta": { + "@astryxdesign/charts": { + "optional": true + }, + "@astryxdesign/core": { + "optional": true + }, + "@astryxdesign/lab": { + "optional": true + }, + "@astryxdesign/theme-neutral": { + "optional": true + } + } + }, + "node_modules/@astryxdesign/core": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/@astryxdesign/core/-/core-0.4.5.tgz", + "integrity": "sha512-rbGaoAGZq1QImY2VWeWQNAh1ZqQa/KLWnmoOdy13lSjzMcAPDS6WrVv9fZfsNDx+DIG2oySLtMikwZk5WFl1Uw==", + "hasInstallScript": true, + "license": "MIT", + "peer": true, + "dependencies": { + "intl-messageformat": "^11.2.9" + }, + "peerDependencies": { + "@stylexjs/stylex": "^0.19.0", + "react": ">=19.0.0", + "react-dom": ">=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", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz", + "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.7.tgz", + "integrity": "sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/traverse": "^7.29.7", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.29.7.tgz", + "integrity": "sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.29.7.tgz", + "integrity": "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.29.7.tgz", + "integrity": "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz", + "integrity": "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-flow": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.29.7.tgz", + "integrity": "sha512-ajMX6QPcyomotqwpzhkYGxcK2i/us0rs1Qo9QvUpa+Fca0FTmqrzKrctoIYLMxcOhGZldGT/BAVkRGTWBiR8gQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", + "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz", + "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.29.7.tgz", + "integrity": "sha512-GtcpjFvanPfzNQi3eTitsCqtRRmmqzpy/A+yhTR1HaZo1Ly3EA8ZXxlPyHdR8/IuRMYc3E4wdGBewB2QKQjAaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-flow-strip-types": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.29.7.tgz", + "integrity": "sha512-wRHeUjUjCZnMHmiO5bRgjFLcoEh7JyTdByOW11ahhwNa4V0bmeGEaIvt51yq0zQp2yWIpqfxXXPyUP6GFJZHOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/plugin-syntax-flow": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.29.7.tgz", + "integrity": "sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.29.7.tgz", + "integrity": "sha512-idmp1dFaekP9GbcMvG24Kvw2BfhFZjHnNJCkV4WuIY4PskJzwI3f1N5OdgYke38T7rftO6ERulFRn2cFeZwRkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.29.7.tgz", + "integrity": "sha512-6GM1dhvK3gNODkXcEcMCOLEDCLSoZ/sBbro2Ax8HURyasQ4NshagQixkRFdh5niI6E4gmA/jYI/4aT7rRos3ZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.29.7.tgz", + "integrity": "sha512-/6Rz4DK1ETDEM/bWHsPHcaEe7ZaT1EqSXjtSP/L0DijOYuaUhiRiOKcwpZ8P7zR4xXEHc2ITdiCgBm9Tpyv9ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz", + "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz", + "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.29.7.tgz", + "integrity": "sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/plugin-syntax-typescript": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-flow": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.29.7.tgz", + "integrity": "sha512-KYIRV0BuaN68CDdsqFkAD7MU7yipUqQNuNElwATdxaIdpTjhvtY82QvkBJs7zV3Evxj2jFAAZ1iO8nyy0nhjqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "@babel/plugin-transform-flow-strip-types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.29.7.tgz", + "integrity": "sha512-/Foi8vKY2EVbed/1eZx0gJEEwHAIxogrySI7rULcRIvhZzbvoE/b5qG5Ghc0WKAFKOHA9SD1x7RsFlOYdutIiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "@babel/plugin-syntax-jsx": "^7.29.7", + "@babel/plugin-transform-modules-commonjs": "^7.29.7", + "@babel/plugin-transform-typescript": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/register": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.29.7.tgz", + "integrity": "sha512-AMGJoWuES861riy6pcB0fphE1YXybtQnBYQMuIyPv6mKLiosfa79BKTnAOyx215c/3RJPJpdQwoHZ3earVH7AA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-deep": "^4.0.1", + "find-cache-dir": "^2.0.0", + "make-dir": "^2.1.0", + "pirates": "^4.0.6", + "source-map-support": "^0.5.16" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz", + "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.8", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.8", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@formatjs/fast-memoize": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-3.1.7.tgz", + "integrity": "sha512-zXfhLpvA6T7+efdt9JLbBwZ00tT7NsBMDVnDu8rpHeNNv8KfRZAMo2gkG0k9lK/Nzc//3kJ9pImsfuJxk3KhUA==", + "license": "MIT" + }, + "node_modules/@formatjs/icu-messageformat-parser": { + "version": "3.5.17", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-3.5.17.tgz", + "integrity": "sha512-cN9jhVqT7u0K9tix43fhjoUwL0nazyW6zsNIXs2QdPADr+nurPfYyssUiMqcSCGlPcCiqnYVxgSn7zBSuI+5Bg==", + "license": "MIT", + "dependencies": { + "@formatjs/icu-skeleton-parser": "2.1.11" + } + }, + "node_modules/@formatjs/icu-skeleton-parser": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-2.1.11.tgz", + "integrity": "sha512-j8cUmOJzVgkHuS0QiQ6ga76UIoLOFSAMWhs7aZJztH3aAdCOAE6vpC8KVvFB4cU10ON0y2/5oOVmPJ43s2lTwA==", + "license": "MIT" + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.146.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.146.0.tgz", + "integrity": "sha512-XC0QsnnhVe7sLIWmYmdPw7x5P0h4W8vUU3Nv1ySgWXtvCz8NizoAEpGXA0sOYoJQV2Rl13LgURAHQ5cI5ILCSA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@reduxjs/toolkit": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.12.0.tgz", + "integrity": "sha512-KiT+RzZbp6mQET+Mg+h2c97+9j1sNflUxQkIHI7Yuzf6Peu+OYpmkn6nbHWmLLWj+1ZODUJFwGZ7gx3L9R9EOw==", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@standard-schema/utils": "^0.3.0", + "immer": "^11.0.0", + "redux": "^5.0.1", + "redux-thunk": "^3.1.0", + "reselect": "^5.1.0" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", + "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.5.tgz", + "integrity": "sha512-DLe/i+l8ynIBY7XEQ191TeZvCoowIGa18R+dIV30GW7DiOtp74i/xX8hs8GUjW5ARV7VZuie3d6AumSmCwbeRA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.5.tgz", + "integrity": "sha512-zXcwKlQApYAOELHd8PwKDFkagYF9Wy4e0RJ+0qnzl9Pjnpj75TEG8ufv40p2J7kCEfwZAsNiuzRIyNNMWT38ig==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.5.tgz", + "integrity": "sha512-dK4QakI42nzWgJT5sm4y4y/O//D4OxM75/cH28RLV+nzIN9AY+YsbuUVrUTjlLjXR6vpyxFbSsbmNuJ6BP9sww==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.5.tgz", + "integrity": "sha512-fqSALaUu1Wjd1nK2uW2kJDWdLCc8lx1IcY+MTY26Aurfdx19anlzhqXOgCFbBFQnlFDTn4TC1/7Nz4Bl2mLP3A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.5.tgz", + "integrity": "sha512-/vCnNxlkxs9tKxNDcyWUePpJ/PgTzxIaVhoM5SmG8UV+GR/IcPam4VYxi7GIMo7PSDuNqlJqvprqii9NqqVCMw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.5.tgz", + "integrity": "sha512-abk0NLA519LxRCszmbE0jYKuQ9YPocOXTiOXOo6Yr+YAT95VH+PtqYAjOJvGKt3viEd/x4qzabAlwd5bHOOARg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.5.tgz", + "integrity": "sha512-Y7eALiJ8lr0M2HH103Js+g7V34wf6snlpZLAsHI90uLhr3PVlNsbFVAXJC9d/V6BnPyKtpSwI+NcB/RLxsQxuA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.5.tgz", + "integrity": "sha512-xMvZgnbZg4YVnR/AX2b3oOPDTFYJvUVaJg5FedA/LuvexAtXibZQej4cnTkw3rjsJ/ggUROB64TdtETiim+FYA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.5.tgz", + "integrity": "sha512-GRjeqTUDHTo5GwntsLaAMcBahG3nlpjftXWZLN73HiYQlhwEowvarFgQnRnQZtIp4keXX7quXFbG38uPZBa2EA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.5.tgz", + "integrity": "sha512-vLNTR45F2Uwc8AufkNXPmB4VliaXs+FvcheEogIzOXzO4l+LzieXF5A/TWxLy5HtqpsRCHUfd0lPVrrdgXdLHQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.5.tgz", + "integrity": "sha512-Mgj59/HTuYeK9Gz2MA+mBWKnHsAgkBSec15ZMb1st3oIfFbX7gCjOae7GydHhzcyQi9Z/7M1QuN9bR3oFqF0jQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.5.tgz", + "integrity": "sha512-mY8AP0/ichsbhAxGnLa3d3+MwV0EfgrPND2bplI3Ym8T6R2pJ0N87bvrKVwNXmdy3jnr6eQBecdqx/HMknBmpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.5.tgz", + "integrity": "sha512-8SLssA2oweAxyRgDp789ACfRb/3P+zNRJpzZxSizxF9m8NUDQ4+3xjo8ttjhVGGw6Qxb70oZiEtIjaKikCO7Yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.5.tgz", + "integrity": "sha512-vGbruD5zquhoc8D9SViXgN2FBJtNdTyQ4DtG+SWiEGlJiAzoKcZ2xp+xuXCffhubVdt0NJlTZqkeRuERy7g8Cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.5.tgz", + "integrity": "sha512-e/SXpgISz+IoqVcSSI0rx/d/he8zqLex+/rCWpnHpmVfmPIUjag9H6P7zotf0gJHwPUhQxZ/mF8tr6acebT9yw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", + "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "license": "MIT" + }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, + "node_modules/@stylexjs/stylex": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@stylexjs/stylex/-/stylex-0.19.0.tgz", + "integrity": "sha512-CnUFp7YMaDLDeemsWOfJgoC/gKM5P/yBNMcpJaE6ChJmXr7s0DJwSeGTTlHJcqqwN9OW1qGtmARWLFhGZN1pTA==", + "license": "MIT", + "peer": true, + "dependencies": { + "css-mediaquery": "^0.1.2", + "invariant": "^2.2.4", + "styleq": "0.2.1" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.3.tgz", + "integrity": "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.24.1", + "jiti": "^2.7.0", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.3.3" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.3.tgz", + "integrity": "sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-x64": "4.3.3", + "@tailwindcss/oxide-freebsd-x64": "4.3.3", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.3", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-x64-musl": "4.3.3", + "@tailwindcss/oxide-wasm32-wasi": "4.3.3", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.3" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.3.tgz", + "integrity": "sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.3.tgz", + "integrity": "sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.3.tgz", + "integrity": "sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.3.tgz", + "integrity": "sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.3.tgz", + "integrity": "sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.3.tgz", + "integrity": "sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.3.tgz", + "integrity": "sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.3.tgz", + "integrity": "sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.3.tgz", + "integrity": "sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.3.tgz", + "integrity": "sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.11.1", + "@emnapi/runtime": "^1.11.1", + "@emnapi/wasi-threads": "^1.2.2", + "@napi-rs/wasm-runtime": "^1.1.4", + "@tybys/wasm-util": "^0.10.2", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.3.tgz", + "integrity": "sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.3.tgz", + "integrity": "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.3.tgz", + "integrity": "sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.3.3", + "@tailwindcss/oxide": "4.3.3", + "tailwindcss": "4.3.3" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, + "node_modules/@tanstack/query-core": { + "version": "5.101.4", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.101.4.tgz", + "integrity": "sha512-gNwcvOJcRbLWPOLG/2OBm+zM+Yv+MKsXKEOWC57USuZDEsI71hEErQsiEGx5wX9rzWWkfwM0fVSPoiIFSsxfiw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.101.4", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.101.4.tgz", + "integrity": "sha512-yRg2pfOCxIs4ZJW3XYYHU/WgtD04FHSnfHlpRT7h7pR77hwkdRG4wxbKe4aq6P0RvXUTBSQpQeadS1SUYUe+KA==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.101.4" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", + "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.2.0.tgz", + "integrity": "sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "undici-types": "~8.3.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.18", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz", + "integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==", + "devOptional": true, + "license": "MIT", + "peer": true, + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-Bsc+QHgp+P/F02XDzNCY9jnZNCUuLki36KT7VKrTXXLdHf+vHMNZnW1rVu5DNW/rCK+fya3DATySbLM4yhtKUw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", + "license": "MIT" + }, + "node_modules/@typescript/typescript-aix-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.0.2.tgz", + "integrity": "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-darwin-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-arm64/-/typescript-darwin-arm64-7.0.2.tgz", + "integrity": "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-darwin-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-x64/-/typescript-darwin-x64-7.0.2.tgz", + "integrity": "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-freebsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-arm64/-/typescript-freebsd-arm64-7.0.2.tgz", + "integrity": "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-freebsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-x64/-/typescript-freebsd-x64-7.0.2.tgz", + "integrity": "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-arm": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm/-/typescript-linux-arm-7.0.2.tgz", + "integrity": "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm64/-/typescript-linux-arm64-7.0.2.tgz", + "integrity": "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-loong64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-loong64/-/typescript-linux-loong64-7.0.2.tgz", + "integrity": "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-mips64el": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-mips64el/-/typescript-linux-mips64el-7.0.2.tgz", + "integrity": "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-ppc64/-/typescript-linux-ppc64-7.0.2.tgz", + "integrity": "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-riscv64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-riscv64/-/typescript-linux-riscv64-7.0.2.tgz", + "integrity": "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-s390x": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-s390x/-/typescript-linux-s390x-7.0.2.tgz", + "integrity": "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-x64/-/typescript-linux-x64-7.0.2.tgz", + "integrity": "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-netbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-arm64/-/typescript-netbsd-arm64-7.0.2.tgz", + "integrity": "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-netbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-x64/-/typescript-netbsd-x64-7.0.2.tgz", + "integrity": "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-openbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-arm64/-/typescript-openbsd-arm64-7.0.2.tgz", + "integrity": "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-openbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-x64/-/typescript-openbsd-x64-7.0.2.tgz", + "integrity": "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-sunos-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-sunos-x64/-/typescript-sunos-x64-7.0.2.tgz", + "integrity": "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-win32-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-arm64/-/typescript-win32-arm64-7.0.2.tgz", + "integrity": "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-win32-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-x64/-/typescript-win32-x64-7.0.2.tgz", + "integrity": "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.2.0.tgz", + "integrity": "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.29.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-rc.3", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.18.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/adler-32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz", + "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/ast-types": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.1.tgz", + "integrity": "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.15", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.15.tgz", + "integrity": "sha512-FwMjJJ7HnyZpWe+oWxegG0fezZyBZUagI5LZEoO3GCbtbKNwRfMH9Ue5d5v01PNePBy1QSfPSDTTeVL0Hb9EzA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/browserslist": { + "version": "4.28.8", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.8.tgz", + "integrity": "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "baseline-browser-mapping": "^2.11.12", + "caniuse-lite": "^1.0.30001809", + "electron-to-chromium": "^1.5.402", + "node-releases": "^2.0.53", + "update-browserslist-db": "^1.3.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001809", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001809.tgz", + "integrity": "sha512-xxWVywk6a6Arlk+hymeycyn/VgqEfLDxupvhH/xiY5SJ/18kmi9o6MiO320DCUzypORHLtvh0I4i04tUhCNHNQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/cfb": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz", + "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "crc-32": "~1.2.0" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/codepage": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz", + "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/css-mediaquery": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/css-mediaquery/-/css-mediaquery-0.1.2.tgz", + "integrity": "sha512-COtn4EROW5dBGlE/4PiKnh6rZpAPxDeFLaEEwt4i10jpDMFt2EhQGS79QmmrO+iKCHv0PU/HrOWEhijFd1x99Q==", + "license": "BSD" + }, + "node_modules/csstype": { + "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/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", + "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.411", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.411.tgz", + "integrity": "sha512-gglkxzokjHfawpGxq75XdBV2/l3BAPzrsMs70qgaZdTW5rpV1tC4MdgJVP9fN126bODA4ZJQkn1wryEzJyQXIg==", + "dev": true, + "license": "ISC" + }, + "node_modules/enhanced-resolve": { + "version": "5.24.5", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.5.tgz", + "integrity": "sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/es-toolkit": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.51.0.tgz", + "integrity": "sha512-zC2lQGkM7QX+Gm6iM3+WIdZJzthsEd14LvRNJneSO2hzyz/zNBENR8+YXWo1cKxgPBtV6ksPYHELbcwBRzmdCw==", + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks", + "tests/types", + "tests/browser-compat" + ] + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/flow-estree": { + "version": "0.328.0", + "resolved": "https://registry.npmjs.org/flow-estree/-/flow-estree-0.328.0.tgz", + "integrity": "sha512-uMB3dC4nfZYn+dd7/PYkyAK1mqBR4TJ4TWRdOjubpb/ObrLvPFdFSmMTq55fqttCCTygQ4NtD1Zdd+wcjC5t0g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/flow-parser": { + "version": "0.328.0", + "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.328.0.tgz", + "integrity": "sha512-F+Ik2Of7ndl2FyPmZ77NrhbZp9lYjIsreDXeq4fo0sjxa3SIFv48QAszBqvPeMMzmw7JzqySJUxGR8CSD7EdXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flow-estree": "0.328.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/frac": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz", + "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/gpt-tokenizer": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/gpt-tokenizer/-/gpt-tokenizer-3.4.0.tgz", + "integrity": "sha512-wxFLnhIXTDjYebd9A9pGl3e31ZpSypbpIJSOswbgop5jLte/AsZVDvjlbEuVFlsqZixVKqbcoNmRlFDf6pz/UQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/immer": { + "version": "11.1.18", + "resolved": "https://registry.npmjs.org/immer/-/immer-11.1.18.tgz", + "integrity": "sha512-EQyQtLiYW029lyoczMl/Hh4Xu7cDecSc58JRYpHyL4tIAu3eqd1yJzQX04d2BZHDkzFFvm6qJEJWOtfDSWAXbQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/intl-messageformat": { + "version": "11.2.14", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-11.2.14.tgz", + "integrity": "sha512-9f2VD1HFuxUvMw0RxsaP8WmMns6JRTnsNB/zghTFrp11ZktiXWwVDeZBPQchBKEmo+Gx/ZhxI7Qht7YglFD4PA==", + "license": "BSD-3-Clause", + "dependencies": { + "@formatjs/fast-memoize": "3.1.7", + "@formatjs/icu-messageformat-parser": "3.5.17" + } + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/jscodeshift": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/jscodeshift/-/jscodeshift-17.4.0.tgz", + "integrity": "sha512-i3ESKiiTsGynxzTg5BhsZViD0ai72/6SsI1efDZxG6/5KCoElsmquxtyhXK5lpEgoO7MTNpYkjFEdEL97SkBNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.7", + "@babel/parser": "^7.24.7", + "@babel/plugin-transform-class-properties": "^7.24.7", + "@babel/plugin-transform-modules-commonjs": "^7.24.7", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", + "@babel/plugin-transform-optional-chaining": "^7.24.7", + "@babel/plugin-transform-private-methods": "^7.24.7", + "@babel/preset-flow": "^7.24.7", + "@babel/preset-typescript": "^7.24.7", + "@babel/register": "^7.24.6", + "flow-parser": "0.*", + "graceful-fs": "^4.2.4", + "neo-async": "^2.5.0", + "picocolors": "^1.0.1", + "picomatch": "^4.0.2", + "recast": "^0.23.11", + "tmp": "^0.2.3", + "write-file-atomic": "^5.0.1" + }, + "bin": { + "jscodeshift": "bin/jscodeshift.js" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@babel/preset-env": "^7.1.6" + }, + "peerDependenciesMeta": { + "@babel/preset-env": { + "optional": true + } + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "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", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.33.0.tgz", + "integrity": "sha512-MTRwMy0ZlL8Ur/vOAiJ9XGHE+kFPC7brq6MxAm0GiGXEBj0qy0jA/pG4N675oSzciO/UCdX8T+5yUQdmDeTLxg==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.53", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.53.tgz", + "integrity": "sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss": { + "version": "8.5.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.17", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz", + "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz", + "integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.8" + } + }, + "node_modules/react-is": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.8.tgz", + "integrity": "sha512-s5un28nYxKJw5gvUHyW5PCC28CvBqLu9r3cWgzHT4Vo/5fqqkFcdRYsGcKf50WMPpjjFZS5d76fn3YCo2njKwQ==", + "license": "MIT", + "peer": true + }, + "node_modules/react-redux": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.3.0.tgz", + "integrity": "sha512-KQopgqFo/p/fgmAs5qz6p5RWaNAzq40WAu7fJIXnQpYxFPbJYtsJPWvGeF2rOBaY/kEuV77AVsX8TsQzKm+A/g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/use-sync-external-store": "^0.0.6", + "use-sync-external-store": "^1.4.0" + }, + "peerDependencies": { + "@types/react": "^18.2.25 || ^19", + "react": "^18.0 || ^19", + "redux": "^5.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, + "node_modules/react-refresh": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.2.tgz", + "integrity": "sha512-aUVMjFm3GAPTTZL7oYr5E7ETiqfQCHRLH+B+5afnICvf0r7kkK4eR6SMuwbSTJw/7t+12khT/Kahij49fqOCIg==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.18.2.tgz", + "integrity": "sha512-AIKJ/jgGlFb3EbfCXk5Gzshiwt+l3mqbCrNjmEWMMjqQxNJ3svBa6bgzFyCC2Sw3RA0VWF1kg3uQf2OFhxb8hw==", + "license": "MIT", + "dependencies": { + "react-router": "7.18.2" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/recast": { + "version": "0.23.21", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.21.tgz", + "integrity": "sha512-mFAyJq9vUbSTARLZUvAEf1z3YxlvAwswbmxMx2mPA/MSm4KmpwvwvhsH/NIrZhyOuwD60Lzyw2qh83uCbgTPYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ast-types": "^0.16.1", + "esprima": "~4.0.0", + "source-map": "~0.6.1", + "tiny-invariant": "^1.3.3", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/recharts": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.10.1.tgz", + "integrity": "sha512-QXFrvt6IVcw7eeZCoyXTwkIJAX3Dv1nyVhMicXJ47GsGDDpcN8z6o644DibE9XjpBTThtsomLKnTV6lc+cVFUA==", + "license": "MIT", + "workspaces": [ + "www" + ], + "dependencies": { + "@reduxjs/toolkit": "^1.9.0 || 2.x.x", + "clsx": "^2.1.1", + "decimal.js-light": "^2.5.1", + "es-toolkit": "^1.39.3", + "eventemitter3": "^5.0.1", + "immer": "^11.1.8", + "react-redux": "8.x.x || 9.x.x", + "reselect": "5.2.0", + "tiny-invariant": "^1.3.3", + "use-sync-external-store": "^1.2.2", + "victory-vendor": "^37.0.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/redux": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", + "license": "MIT", + "peer": true + }, + "node_modules/redux-thunk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", + "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", + "license": "MIT", + "peerDependencies": { + "redux": "^5.0.0" + } + }, + "node_modules/reselect": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.2.0.tgz", + "integrity": "sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw==", + "license": "MIT" + }, + "node_modules/rolldown": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.5.tgz", + "integrity": "sha512-VD2IE5PUG4Oj8zz2VGykiYd5wbnjdIiSsNQb8Qu5B+noEp+A78mu2iVvpp27g8es14Tk9rofNs5Tku9iQCS4fA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.146.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm-eabi": "1.2.5", + "@rolldown/binding-android-arm64": "1.2.5", + "@rolldown/binding-darwin-arm64": "1.2.5", + "@rolldown/binding-darwin-x64": "1.2.5", + "@rolldown/binding-freebsd-x64": "1.2.5", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.5", + "@rolldown/binding-linux-arm64-gnu": "1.2.5", + "@rolldown/binding-linux-arm64-musl": "1.2.5", + "@rolldown/binding-linux-ppc64-gnu": "1.2.5", + "@rolldown/binding-linux-s390x-gnu": "1.2.5", + "@rolldown/binding-linux-x64-gnu": "1.2.5", + "@rolldown/binding-linux-x64-musl": "1.2.5", + "@rolldown/binding-openharmony-arm64": "1.2.5", + "@rolldown/binding-win32-arm64-msvc": "1.2.5", + "@rolldown/binding-win32-x64-msvc": "1.2.5" + } + }, + "node_modules/rolldown/node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/ssf": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz", + "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", + "license": "Apache-2.0", + "dependencies": { + "frac": "~1.1.2" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/styleq": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/styleq/-/styleq-0.2.1.tgz", + "integrity": "sha512-L0TR0NQb+X4/ktDEKmjWyp27gla+LUYi/by5k5SjKXf6/pvZP7wbwEB5J+tqxdFVPgzbsuz+d4RTScO/QZquBw==", + "license": "MIT" + }, + "node_modules/tailwindcss": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.3.tgz", + "integrity": "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tmp": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz", + "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/typescript": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz", + "integrity": "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc" + }, + "engines": { + "node": ">=16.20.0" + }, + "optionalDependencies": { + "@typescript/typescript-aix-ppc64": "7.0.2", + "@typescript/typescript-darwin-arm64": "7.0.2", + "@typescript/typescript-darwin-x64": "7.0.2", + "@typescript/typescript-freebsd-arm64": "7.0.2", + "@typescript/typescript-freebsd-x64": "7.0.2", + "@typescript/typescript-linux-arm": "7.0.2", + "@typescript/typescript-linux-arm64": "7.0.2", + "@typescript/typescript-linux-loong64": "7.0.2", + "@typescript/typescript-linux-mips64el": "7.0.2", + "@typescript/typescript-linux-ppc64": "7.0.2", + "@typescript/typescript-linux-riscv64": "7.0.2", + "@typescript/typescript-linux-s390x": "7.0.2", + "@typescript/typescript-linux-x64": "7.0.2", + "@typescript/typescript-netbsd-arm64": "7.0.2", + "@typescript/typescript-netbsd-x64": "7.0.2", + "@typescript/typescript-openbsd-arm64": "7.0.2", + "@typescript/typescript-openbsd-x64": "7.0.2", + "@typescript/typescript-sunos-x64": "7.0.2", + "@typescript/typescript-win32-arm64": "7.0.2", + "@typescript/typescript-win32-x64": "7.0.2" + } + }, + "node_modules/undici-types": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", + "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.1.tgz", + "integrity": "sha512-ZZ61DsRsOnakl74HAmp3oSN4aXUmEWXf+i/yv0h7tIBfICc3VdrFErQKUUKPgu3AMsTUMbcongALEN4l6GSUrQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.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/victory-vendor": { + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz", + "integrity": "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==", + "license": "MIT AND ISC", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, + "node_modules/vite": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.2.tgz", + "integrity": "sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.26", + "rolldown": "~1.2.4", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.4.0 || ^0.5.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/vite/node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/wmf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz", + "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/word": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz", + "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/xlsx": { + "version": "0.18.5", + "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz", + "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "cfb": "~1.2.1", + "codepage": "~1.15.0", + "crc-32": "~1.2.1", + "ssf": "~0.11.2", + "wmf": "~1.0.1", + "word": "~0.3.0" + }, + "bin": { + "xlsx": "bin/xlsx.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..7899743 --- /dev/null +++ b/package.json @@ -0,0 +1,35 @@ +{ + "name": "nearle-console", + "private": true, + "version": "0.1.0", + "type": "module", + "scripts": { + "dev": "vite --port 3100 --host 0.0.0.0", + "build": "tsc --noEmit && vite build", + "preview": "vite preview --port 3100", + "typecheck": "tsc --noEmit", + "test": "tsx --test \"src/**/*.test.ts\"" + }, + "dependencies": { + "@astryxdesign/core": "^0.4.5", + "@stylexjs/stylex": "^0.19.0", + "@tanstack/react-query": "^5.101.4", + "lucide-react": "^1.33.0", + "react": "^19.2.8", + "react-dom": "^19.2.8", + "react-router-dom": "^7.18.2", + "recharts": "^3.10.1", + "xlsx": "^0.18.5" + }, + "devDependencies": { + "@astryxdesign/cli": "^0.4.5", + "@tailwindcss/vite": "^4.3.3", + "@types/node": "^26.2.0", + "@types/react": "^19.2.18", + "@types/react-dom": "^19.2.4", + "@vitejs/plugin-react": "^5.0.4", + "tailwindcss": "^4.3.3", + "typescript": "^7.0.2", + "vite": "^8.2.2" + } +} diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..0239e38dab08b195a081c095bcd6ee26bc4934d0 GIT binary patch literal 11108 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 Ht6l#GVNE)- literal 0 HcmV?d00001 diff --git a/public/favicon.png b/public/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..5060a965b0867a8be08be697f20a9b9b13eb1181 GIT binary patch literal 2008 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 E0JtmymH+?% literal 0 HcmV?d00001 diff --git a/public/icon-192.png b/public/icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..b04d5fa2c94d0cf403ac9bb3bf7f554f5764a50f GIT binary patch literal 18637 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 F{R3r!maG5( literal 0 HcmV?d00001 diff --git a/public/icon-32.png b/public/icon-32.png new file mode 100644 index 0000000000000000000000000000000000000000..5060a965b0867a8be08be697f20a9b9b13eb1181 GIT binary patch literal 2008 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 literal 0 HcmV?d00001 diff --git a/public/logo-512.png b/public/logo-512.png new file mode 100644 index 0000000000000000000000000000000000000000..bd0d622194232a3b16860625c2be6104f50657cf GIT binary patch literal 31096 zcmeFY_^Rq@`nq?hphF+CT~El5Q0wq&ua%bGSF( z-}C$j_sjbQpJC?gv-jC+ueh$Y)``;5QX#^p!-pV-7I`A3@M&XuBvK#hChaJ~j7;F3 zN~vUx(q# zF-C8~{PB&?7oniz3y?yt7I54x%+LbTi-7iVww|#Z~ zVWElH*$@i{;=zyd8srA{)Zk2Tyy>kC_Y=BKbfyPwMU@R*8Mc^4ytcKa6P~cXmy6%J zaF50xE8F$>V;gen&_owG7$UL{jO!n0Af9fZcex(nYg?@oskh9jt1ejH;p)nBHKE2Z zQvPmjDUftgWZ5DzaS5`@Bv3`!F~GiOWH9SUWFYId73Gjtj?6z z86~G={bK8CeW`ECR2%TROeZM`GRurl}F~&j?15CV-uO!oPny9}2!WgD>sv%PnAhf5;yU`cfYk z>;7)mTVO0Bb4Fe4wWw4O{bCk&1A{ypMgjCNm(2ANEp1YAaB@MX=c!U}b6}xc_($W< z!tY&=*vy95f$}sAGg{X9a3&~8{K=%ys^3dtCf_pV>8x4AM`r6#QX0PtAqA|^H$YzG zusE68n|0FZKGLu#M!-?+x8`SNPyR$aKUBs0Xve?U`b#hz?_n-5rBdsIIpxm7C{D%R zW}LiE7n@UG@R*-+bf84zexMJT3L&erbyz_1!Dhr!EW-Nb;sHwmrnH&OJwqAUX$p}$ zVC2Jc9~buHfCJ0aO)~9T7q?h57`Y!e^l-`EvD>0+=VTrkP?k^^u)uqWpD3;XL$KZG z*RhoJ&oP^#GMl}c_~gWM9zDbN3fV|d41S=vJ4%R0GbM$CTw+uL6e_{=9VqnEQW|(L(g%RlBCYAu1 z(Dedk5kJl78knA>I@Dw9sQuU`?!I)@j57`VSKidejDg~-Hj3c{TwP;2k)d#WXo21e(f2U|d#tt(#+=U3hV)%4YJQ3~ zT;1MkEiY!y7Hh_~i?2^`SBYTXJ4^Bg59DvTf|dcxxY7kiq|5Bf^fLJHuiA_S*TRl1 z10g(eFz1~c`t6kSJ{*-o?PtCW!_swUxvT0rAb~K ztda6(q}Ga??>x=REP`d@x?mJwMb3gJQ`wZbADPou_jYOJKS`9X%pNWo19xK8Hy9GH z^p3esI~rQ)r$<}@4>6VtbTPsVjh}oeW#x9MUw=N#{KK;ithkHAFSY#`yKyu;udbSa zAHAe4C1FUOBZfMKVL_#YURN_h!X7xqfkzSFKU_*xG?5_Z^nd*Wswuwrw@*q?N`vqf zP6Mpn0hrWNVEf+7M;KE_em&-a|70!P{3rGn=?#2J1=6~rb{hUg&09ug`>hhOn1Nlt zC>2n2nT`?_+t`&m^>}w3n&cR6OWn}EU8GbIV-E@ih0joB223o+Pgg&tqZk!|l zU2qvv`fIM@lpzz!wK1K0S4cvpsPDE*Nvd)vKuWT$Om8ThL$g)>P3t^v5MV@Li1QuJh-k%3HP#CW$z>WOJQ!3;}4(aJZD>!Q2&|0=%7^62e=_xe9o7 zCWaa;Zdj=q?+c=C9P59Hlmzy8a|H3svce+WI;gJ5#AYqSCy5VqAda^I5nl6aX<6Cb z{dRo7bi(v|e^(we&=EG+*#bw)aN4^V&y3t26Sys%DVO-_$)vFO;XcJ?E#iedED>>Y zw%a>dt)%?Kjf=N8n%Jv*Q!E`h-s`w#hI(Fp&hn|DV*{Rmpszj<#R}6S!$*a(E?jM{ zkJ9uqUiIsaAVI9_yIQ@oL%qa~W9H3p;ygXKS_)#Lq*fwF<=Sjrhezw}C@!!ws~fL> zLI3kc5R#$`tECFFzT!z)gTk|3Ga~!#>du0!{alJ>kct2as6DJ)n*8q*z&nF9ui#gS00wz?JyG)xGG*?8>Jf)>`TzwruDjbV0lmt#Ma^4>;oG zvCjdk|LnDUCf)5xKj5T?y`7N|mFGwGxv)@>=|JsoM?~*Yy!{mz*Y$j1wy~{_ZS9s~~%pnlTk%qza0YV-UNvYe+yKS3#nci5$wTdt+cQLW4+u!bG?B*=uhJqi{)Eso`oWZE8<*Om zA99A`x=eHhDN^90-u6$|wH#U8T8Jkx-9#_mogi*lD9oR4RkhnnjuHk6d`YW>$jyc# zVI*5SZ85-x;*cm15?r5^JaF5vs=J^DiWQ%vTO4b;QZt7=IFZ@nHG7H8Q^sxtHsZLHe)V&vCi1uv-R;rR2yGA#R9`Fb?VMLVmIHvq)Xz zovjMr1~>KG@1GvGF|aX}5SL)f-1pJ*Ezg&)`{(VCFTA1$qoGtxY#b#-J-EgKuC=ah zU+08wqOr7i;WMt{I#J#jN(g^&k>=*&pKaMF@imVUfiycIi)J0ncCdL|xkY9fAj1ph z$@R7ut$pAW;`0q;9<019A>Sj8MxDyRNXdTqiS?gG#CErhGTNc5t17*lvT^&tHhZs{ ztS?-dGa0OfEO5QgQ@F;sSd?`jMQC6tB9tJ{<68CCWwG+L*~JXWJwk!`E2~JyvAf(d zI#6gj2yaps=P=PTJlY%hIR7R9R^n5_rTgwWWlXAOV8G91d$r^whPCQB@LPT2k}S8t zT{OhUc>HN}`K;-O!{p^1{K7FptspGK!<;=5F(c(oyU$$>D@`|G z#o|Jcfs~R@TuI;@Op!QJMVJ|I1;r^nbbWH(HH~~dava;%i=vp}4;TN!o@Ns%)Os@v zgI_9sd**v~nq2-I$E`op*Dul!F=-_I!YomVAkv3{>r972kr>>Nd?(O)@+?j!Tc=oL zG<}*(<(2q|c5&yg*|SztrsOP*ixRqp@QjNKhHBs2EkK-X$BZ*6W5lkyEx)#bt04Y> zvsG8O7(XZgeSn8019K@^S!)3czeK=D$pN+jJCY!9_leD@boM{3HSE%*nRCQPmAC=d z$Qbp2oRAV?y+KpjO|5)LOS{cY8xT(&Wt|ATt_{|ZJ&h2+_a1LTRq5JWy-OVkvj_OihZtlq+b`SV`B!%P z<^kYgs;=JZ0)c}&V(iRhNe_h{GIB%kX8@qZTV;RR6JNXRT9S1Te@E2(9W`$Uyp``DJA+t*;n{Km z*tev?kn)N_THvyQqG`!6lQZ<-;8IgUM5sT3xlt4+NU$#~ltTIvZ0fWf{Y;tsF1SuY z3CWbZBbgV`WF5bjn*M}H&+v1wivPlxGey~#t@N`*V245Y5cAs4qBa?x>4!HkLyT{H z_G#H#lw|rLMvb5rKZ4Q+1T#V`OhPPF0wJUAFY`yV0T)SIT_s1wem7%!q)-q4PGrv4 zqfj}}ZWPeKF+9FjQ-Z4;D-$`lUjfGLk>!aNnsRuNGrYZ=>(Z@ zLZHe$O?(a$AQPAnpdQ+)9Zn*=LgI` zt{WBs7nn?_73@uI*3GMw4#D8}7hJXBz{iIYz;iMAFWDbD-^?zc)jpImZ+==r{?{I8 zq-97pwmfVO27u%bv~?@;N=K0zE+dx_owFqAkKm@ZvZ%3=mHzCHYl-blln|fK z@ll8ze~@=*^5%7;bo%8AjusE>mZPup!x~HZ>}wsU#sZ+}S0{R(JA>YRlZ-e0spUJG zd@ha$Dsw-$msy7tD zoCCT11SG|KFM*6%Jo5Tv=kvuZ{}0APxba()eM?S`hu|c<4@o64&z1E12_o4TYdvTl zT)OmkvoI|@J9&80gsiBl9t(_3ChhH6DnyS-Dr4b>J*^o#ID`PW>G+QUl1p+!42VT$ z{`5E39!n!Reg#)gc}hE_iKiQl!iyI(j|l;tE)}m14XahU0qDXKF&$@|0WSTFcYFWw z1}+1LkPG3`<|@pcWJ!}9>RI7np)^4}9NXi!1+Gf=Dq;HCUpWbI(b9W8W><^M4+7Qx zOxL&VEk+?DiG#FwJRW**LoZv)noj4RB{6tK9lw2--qCtVe(Q*_#>b8rm%9G+>LrWA z=42gKk>jxf$tS^0$;%SnAQeLem<2}aRYyWtZT-imSkll4pss`}SxDhko1w5=z9}H; zhy19n68vWDyB9Icy|LHI+mwza0@NBInK;Z_9a-ot*2}e7nYd}34Z9>G3XiU)lUrt2 z>m=hZ#4#EC=l8PHNO`%TyJg8X$kKMztEOb~N)BtW_#G?~nTeR$i)FKCl9P#BFT2=r zaQe&d86BaA6{mr(-(g@}{w~b%>cq2DA~8Y-G7hb~_IB%#yFSGkM~e-;a(t4&AFxGG z;IuPt_7r+V;i0v!5ZSc*WRtN6Rswll&(E(`BlV8FPAX=t>jE!3IWMSB=@$o;HQiLc zkCkVJ1O?|g4Ep+S@gK97dmhcUZ2mdRPnguR!EQ*#CtKhLoC>g8_xtrRZ{%SXFrHo) z5b3+Z^shNrZRXlfX7ar+f~c9gGV{hO3)7WFTzqy1%>36J%)i=S^2eEWte)-VClrbL z|J;PVeOKQWhj=*VT}4DpCeMq0CvZP|&x*f5Vvaul9xaH3-Zn@?FgLkj zpVbYoU{0BTu1M>Yo!Xt)WWq<$xtuWbwQj?p`yXAm$ACiPQss^(i~@1*^%NjO85L5z zg3HBLc004O3TZEJQ1RO@J0`n%6@$OpvJB@?H%n7OBH68k)G@H(k4!hnj>f4+e zO|Ub&QULNd-#Zx`by~dWyHa?%NCHip@cKc&d739s{?escWmNrx}{=3j?ktj;!2QBZU3ZSRq3 zn5MUWahzVAoi-Uf&61UgMLuvFDci$Ajo>Ha@Efg?kcE*xA|LNsD~!*<$%X?W>o(+o zi|0pfA#ua#aZa2xFE4QmQpm=8JvUFMWlZU6qrDOmSjwxguIj3Dz2>T4i0+Z4KX9wE z{bnxmL$>MMG>ak2%!6J5nhzC*rz?;kusmgcwI>OXcQB68lhFh(ioL6J;7MJ1ccUen zH0y1juXpkXdRDG4FP!JP&1VfpPtCz1P3XlY3@ckd+e@nrCtL-p&Xy3lbGcg_>R zu0t>4BNLh9%|8<0d2dlr=RwfNm9A3juOxLO#0!_M4kV^I<( zMG#~!T*gA-*|DrVzxo%Bm$t_<6^#1M^XmpQ90S5)1j7d+rua2j-9ac`#NF%>}aqaW0vhLDh z=6HCzddZdg`)8rcA<(O7v8LBw1zW?E#s?RNg)VJFxw7p{dUuH*wMli$0kbVWt?pz% zx!hm`kdw)}qI=oQ08xCv@gw&LiYM2yEwvk|no~EwhG)Zfn3zi|%Cmq`ffJwmc(4ex zXoLHUY1F%KkJEkbJvM-8zN^@>tkpSpFA$G z%a=5%V=8_+90=(UYAd8z{HS)&j@ASK48*jfVDA>*gPyzI}f1UBJ)1 z(U#jHAk4k-No5oTKjrhgAmv!f(T{xyt(F|}PhI|A=rC&Eot2hZ7VabDTga_vSt}7$9eVI8~As z7&yw4kp>!*+cV-rU~YWlBWP1bVD8%*dkrxAQq%VoLXm|Wi zZGJ73)jeIeUiIW&-p8>Rw&x&t8JE_vKPJM0Dwbr)oeG~FAMb2;oKdu8?yzCywilXN zy`zekxwd7a>wZH}{nD<^IZInaMoKswt-@<`GDg&)SVV85=X2O^(XSqfNeNl(n^S%r z#bB8npGnO4SI4ECPe&oiAwt|F|Ny^SftZpAE7eJJenEB}za82o8*-2@W$z z;EbQ?s}B7wqsIIGe<=7N(rX>m@RBCit5I(Twr-w8qux-}nc2kBCWQ&Tl!8*iBLod7X7l;=hy8cG~p4 zK2m{SR=4zH*1NT+$<_7R;-xCzgm0KDFvSP}cHa^jM<^1YfN26mOZKAHtWFe*)ad)1 zo~If(UafHu+=%^G_L2xL{+D^so<|oAck%Vjq$^aF4`IwLO%>l~M};Pj9BVA7xQ?<9 zz){=R_N1Nd1?p|1B_91BL|YL43b0DG_08N$-y;8B&!w`{a%Sm&_D0S)b&PjZOG>y9 zj^w-$0Bf;iN}*3GN8eT6BjV@9;Mw7xZq`8rtaENwffU#3zhYyVObQYx8TzQ?wSyfF zdy!}i>sMK1rhPGN=5^$qY%nwNg@}9{B&Xr@lifN9M;W^o3umS&dx?uWJ^HyP)dbw| zTm^v00}}S0n8&sE!o4H6uYc-vW89#W_6}8}E~Gt@89f*Fw`RX*=JS2nIQZm0vWp=hws*6}A(zEz zjLdcaQO?VD9x+S61$WR(^dc19mn=>>sQr3uv0urCUNcuzp8hHC6+K=_c2l2raA{!1 zk`rP);(Gm`Aa!|P-yNI?z6?IdnAVBo$Qd~%%sfK}g_}#=WB@@9l_;QBSuo(JN|3Cd z&nO&exfYmWbuoT1A?WAueW}WCV#l?D*cZyhYJ-MVKiz0~#n*o@7nArCS!CleaT9x5 z2S7Vs{{DFkG0zQ}4@2EyAbGwau9B;V)B1un%(oW1nx5en+qUJ;Y9UasI2=-g_gwqZ zf(mt!tEfVa+O+Or~4?hk7Bm=_K`PsZyXce@ph-AeZHz z7p3mfNN-!h1O^5dk62Lht9O4%v%^4dMs=|RkkpZIel^+GGy%^Lg&;Onot*lTBrq?_ z;`Qsc-@7SMG5TW<=KRHchJs_JH}8C1Hat!SgyOSDc-+Jpy*mJ@2{>MoeK=p0tI8Q6 zo^5o{dEKS21l*EAM`^u0j0r_f&JK-T@xM22=>M^X<6^V)K#Td?On|fPy4J7lviBzA z&fjBA6HK|Y%}#9(FU3bZs(g6hWf@yzf&$iqM?`djnJ91fUUuCI!LNkSYoaIhG!I09h5{d32b z8^rs4bZ{U8OF7>tvRyNjGwNOly`U#w;FU;vA`Ps0zscSz4{iZ=-FR5ZZ*e~1*mVHX zNVcx?LoYjKw`9a454eK8T4HqmR~Asy+}uv$;?uG`9+9ddmO=SF5&jP>g(CWn({EqO zWR~4UrxU};dxloER9lHCU@;u>H$LfZr9bC<+qTxmGzI(Ve$y3Pi*e60t_u!^16UaROzd7dPP*!1u*8vmDbH7iDx?wD&UwwTz&>{ z)!*IWAAHwt>qPMUlSUy-(x=;_lkkhjW(-v1uQ8fWk*`W8Jw<>gSV>5z;o}z`1UB%} zPEtmSBJ=p``^-*>^OB} z*;sxEkRaG;;V5R$-2T`u&dY&=rj2|}f$WZyI^U7SHsOymcY76D&8T!7B9j?!fDP~g zY0BTN01~;y<1!yr`ggTUPncz!g`Gg5l`e`Dx-9t^Gx)3k1ke8gQq>cq)-SmwcV z4g>_Kph5kN<)4{!f=bC*BFjSjl8N?j14DF}93?{c?-mv(7%v9mqfS?#k`wn=dpuEh z@!A`Bn$-)nFpIoo>!^4*Oa(|-VDN1Jc*!W@*wP91O#_%^%M09Wv=oFt#H}z%>J9^0 z?9#$x)zhycs_~C*YZ$<)71r6ReKo%t;oXquR{xr0MOBXnZsYAF*wK}c1>B+nuKczTnbvG+4=`b8odJB39Q97=|T(AK$D1Cs_tR~NS@F+Jf#jQ`A z!U5(Y%L}m)@qh1?*!yepmVu;7SJN0PeV7!{@VW(>gsEhF;(~WRfjFK{D>2@N{aB1! znI$|tYolSV9>i!?YhyWvM~bRyW5yOi7C6nT6eEn!9=&DA>Re;r!D@)rvCPwk`@!0? zZYD8=1YiQ^p`0J2f2T|A2U_Hy!X)P@Tns&rf=7jbB+<^mevXZ=UCmyVZg{ZJ}OYyp?&jVI{vzUcfwUIXF-JXAE z)pV}gv+qrvU0-5B^pFz0A>`d%Yu#2Ol)l-xqV;u@h%}p;WgVcu5Hop;c0;HHj!M+K zL9hzK2aAdA&pV_q36^1s?>UU%|6OI72op(vTkRBVO@H6gaCd2QXl+XazH&OOXfH05?6d)l&T)*3qoi-n> z90p$?g_n>s6aQFbbJg|=RcfGLxC60me&M9E+=7+Jq;=f%d>kAC(Cn)5Q6A>Xy;xBu zZ2FeK{A8r>}3lC&V&t`)wx$YV*JS zajY`9v$~yb+DNbgvL`?|-1h(o53`=m>;4ZOmgxWdR`fsPIeTlHU44eA&(Op52wJXI zI=Fycm@uWz#h7c$@9o6TsDWj7yh_?qN~l#+Jy)HE=pkp{-<#!_ZnpyLSV)5-iV-cU zx(>2bG+WnPLX%QWQNC@l;mo3LC>iaRf%XvKQ_RJ&|kyO+pG0ULsC{GKryJY|}l5 z257$&xwD`}E|ySA0TIF!v-*#ruLsqYAj_=)dtgcBs#ULUNQ9vd`>08t2k+E^rmQW! zaqz?}w^;0KxV(46x~|M46TGSLBNgZ^f`(58)M2Vakr|K$MfNI%;i6>+E=agpuI{!G z8OI?xSSc;2dCgGtX&@L9DnNoLRur%kLbn2iFQ67gx)u{f4YqsR?ZM}ZgCq4A>Xlpc zCFBd#x>^e!YMYn{y$y*)MdQUT!v=L%dkTM0@Kx?J^SnGQ33pAmQtE$7i&nakC0o>o zuK$1Iv8+%X(n!~tf)MJW!51j`YbI6Lnv(dUT^s7hAoXcA*57LX%zP9T^#4K0U^8Xz z)$Jl?kj(9lC<_xQRVH770dXg;QYfF4CZow+aj`(>&?Yw}sWRD0_=u;&u#(sqS)yX( z7#>6nj~2C@mXjd5=Lqu20JBS(XDr%<_NJCdYwyXh8ya0u!N?@a8zb z-DZLWl?V4bbWq_`{G<`^*W6^1pf`)T;)lHeRfK5E|8SdS_lbE8MWaBx$&YHMuYSzL zSZun%poJ#dJZvazy{hHW&?F{C^E>t(llZMJLH3#G7;&ds5{?=0;3X6m6}60dFpJNG zg-p6xG&R6+>XPB>gglMWpIQ={>Qh4*Co4|=Z&h(MJ_Ge8nG;!?z12YsrsBd<~)>2 z_vlmx`=SfwUsGK=_XW1--IeG8o=(Q;dO`Rn7M7Y^P+6}09c~>0e9Ffc_!}#$;TL{I z0Zw9kfjV7L%vt+^*m{B0a95-3-xFziIytxQTXPrNn@6KLi+}2*QvK}4 zKfWHH$u^JYy|r>6ew;Cwcz}kqn zy^86jEmsN7f?09#eQ5WStTy8b)F+JG-6@W*cM`C!{+gDaZg8f1|FcUx5PJ5Mkm_)h z)~N6>#DWuw!Uwb75A_JYY&fZ;mz+$gk8QPrg(+F?9!~DRS3$IQ{Nq#-KK?O^5M05a ztSD*eoVmsW6o>w>+r6Xm`a#t7%8<&rgI;BOGZjc>8`!w^K2^8o{fvU_;Qy0`0?qU_ zRN@ryq12s!>7UiYq@Lh_HXnDQ4V9Z*n4ZqjOYf~Zo)5gRM_%`W!11vnOeq#Y^> z1KVmG-l3y2&zxyIk}Y+q-n_vx6uu9b{aAoYNl&`QL=*x{=Vq!gm;fKqV z!5V^?@1BVH46J+G^*s*E*z<6;EiU})!THsa-i+4=L zh3O83)^mU6-}?SI=_pBQzU8$CYxbM_;yl-}CX>RcYA^{37GI75&;MKp`3MvAt`c3h znk7{)w*Nnt0MPV4LAMEURxdU``M4K)!&>Qj+1d$# zf!%5d$;=NG!%S@GyLM}MY3C;;4=ZknyPVz)hbWbX)o)dP>b23WWo<`2+&2B2FSbNw zuoQsoD$f&3Dd~jLb&(>(!%Yq*OYmCFdn!1zYIp&EpR1Fbe^Ki0_$4uSu{%ndSq? zkqKBu$e`6e7XI${5r|hl_vs|5bc_>AbMo5$4qm=+6FnRxQ!R_ifN(dY&N#B;!VKav zWGE|*dGb#BsE-bE`2$Pa%wSFD%<~5`j}D-r$*oaejfSpmn)Px9PZp+}W)9qJKimpd zEoFVKY03U7r=rgaGc;KvM%8J;@83|N427#q#squ7whGpw$e0TMPQKbf?VS|mIt&J+W;!n{n#-I0%~Of~ zq8q(lL&3U7vJAD~KnNlgk=lzRf{%B<{JVGO33-YjXR-M0s`l05RaN8jMOlKiLvcwa z)RXU5PwM`>kaBZj*>8YZh0Wj~3?4n(DZfqV>@QXHk2LZzS?-yRoOUEtTm&~JoU7+v z;T0ROw#sUB&U^I}%Hf}%Zdtqi2{$kJHsW>OtycPFV#q@2)kp|FcwmzAe+C>5EyKKD zZTbtgL>*oO-p18bIl6z4+&KVpo}Vu?&Co-*9<(dr-}ZD$Qz}}ef>5ef2VcHqQzKdv zQvWzdTk`aK@?q)QHld&1qern5*5~^XwRdPHaUg_f<#y^_Wa({{Wx+oxDg!l$Z6|8?b@q>(iZT9b>&jwQo9q$@QM&=_Ln0dLo~%DxZJn^K@!z zq8dyO|GY{gID(ARDkzN9ko8QHP#XHB?;2>id8$Nf-twq1%}B!ujqiFzrNsPJKi2QP zm*~IJ5x@sw^{wa6o{WOxaEcBp!#{|NM0O<6vt5<7sbuN*DMkqc{Pf?jNsXT!8J2XA zi_Nz&SC@{%&py8^Olf>N?wdxdh7hqwomuZuKrbFZ_bGC%#=FKR#^ zQ}DB+(5_&+=W8gmNw>Ry@%SFr-KPrlei+^l(WAY#RBvU=K{2iS^ZP8#*`WNiw%@$? z_`LXrsYmX4QYyELxjOi|I}cTF6N+LR!hl}nix&&f2M#bOy;7styK0pW6JL&&p))ef zH!EDH?GZ49poy@V`eR>g)1*()Ctlt0Ijhxp#Jopn(SobSg03etb7`YaX}*K__;09& zxnHNGEgAU0dDT0j!djEROi&KDGpQajb!kC>$dn2&Ygv;U^3VqQD5+2J-acG2S3low ze`U!tzGYM+ZV+9aYW}E=8*1O4jVCL9q-+?^c+zy%sLeD3nh8mUF6#5IW?lsT%%iB* z#0k+=V&AtVB>7-=N5^<5)N`Qii#E^QuS&1~Jd^9Qq(W#IrxUE`yzAHP+Ks6}tIa$+ z)f80g$aVOuhJ9(r3d+HBE0r;4m7^3G%NMqilc4(5mnOo=$R*8CXb3mG@OC?jTNWMv z!tg=fM@iGHhqb>?PP*qdkNsQbIU=#nWQl6lR5;29@*tHY{cL|qU=u#;598@PF>X z8``aN3{E!3&BAHcv*FSIBAiq!t;zWnz>z&0xe&rMkA?jvu59l_7TgyD%r)P zq;Ndym-}^w>x*fcID{4iq$C|jhw&qSEzR$b^yuL6-3@96pI?;7!tzCz#p6^CJatP# zm~r~~Z2>Oa2XR*>szUzz0+X0^IZ0!W42C^}^9)FI(c9!-il@ILRrKvN?>*NM48u8; z3c`9V@O9Myqf9r@f%8(4=b#{TBGwom62-_zsMByg$hKPzcueXqJYKmYV@ zr~y=IRiC1K@!;2Pv2v01;Y;fe7P2K5XXD>6Ndp+}GET0Ff30LmwMiy59^u8=cl#!0 zp~Qh792HFbaQ(|`ILr1bb+T3K>--K{O>A>hjnH``qBat9tJr_kEdsY8_%Ig?USJ+# zB*|aU<+C&gDZpTsw~!;eFeeFLK0#A;TrWq@aC`~#2j7j&cqEaJMeSoafL01pK{17J z?zP6ebxvPpiY%qaJDT6|U^d@|c6o2N?R<)8c-lINfeTv-*ff-znEvfJyAg^a0vAuGis1;}c&c#E>)ICmkp z_cAtnzU=qFg3vNJ^jQ_9mBcWO&&k{6zrSmyMOYEFo+DC02ZvB=cmz=>1LG92k1IIb zWtlxDLQ(qE7v1@W)B0bh;y+NT=KqQO3_;|lLGL}UmPZ#Z%$c8ZvU%d5g1gE88BXCD@+p?q_yULXmUw}X|H68PYT zx0EPYACv5T(|MFin3_Dq1R-Lv5J@tR*hkDsso|COm)$srKY!;>BZ{i3DxIqJw+*{% z!UQqrRJx?g37QOlr9RcIv}T^foNVcCss>JK%0U#+*pxE zvB#{AC#=Fn*K6W$HQhfO`y(k#+f1oT8U@&`Z=6uNcZH}iA;u8>+RM@1VtE*)5-qQs6si%FVLT-FS9yb*QV=Lus%>UinZurY_p7pU$#meV z7R4eDnHmWS%X(cA8vYQavvFFP$v-(BK8^gTgEGi(*wKnD9u`2LxAp$G7>TqvZb<3p z;K3dT>Gew^J1gZ{)EcwB$c7q%@IT-@Wol$PB!{zC!7YS(S)qZUyzED>&bod3T*x-4 zIfE))ta`T0#Pg+9Xt0{4zuZ9`$+HeSm6vav1T!iGtge@iWXY_Jky-tFGduc%6M{_Z zjHL;6?kt;cKQA{;Er3TC;=z2BhTSkyqp1*KocaeWv{7258vB^zWB4fJRF6nPWMy#o z>`;%2ndmpan2$b1@zoxcmps(dlGXAw@UecgYGKm`a@ArlL&MIIz_X1yseoQ_z30^1 z%jIPcD4?&?B-u$uf1;_sj!YP>VE^o(#qg1q>zA5k4{G-wuKj8f{4XYbxxXM*{t9uk(wObcsG(OlnuZH>Yzr2^W>1ADbt z%!9i7f$n0JPue{_ye z!jEY9yLglLV>7KVf?$(0Pa1Uy#!~JX_xZx-Ds#-ozsOra0XF=u@~ykoivRk=GwHK3 zGYJdnP0@@eN8SO#mKxY15tyO4_RZmJgr=%4SP;m|lCHgjZdW%6p!dsOK0Dy){=uuQ zd$?^jqTVU>^b5(BQyZeHlY7oB{nc(km{Ha=$Wo8dXOkPaS-+6^)N7~b@amOBB^_L% znt`lX%walaym zNiAwG?chl{bFI{PUrUsIZOmJYbS5t5Rwl?w-0E$t_r3Lo6HqS!PG7DlleThMcx9_bOy zb&%>-SSKbV zG*;`QZ&!09FY1#QMA>czL1b@_pM7;wGMdicJ{GGy7NJG*PyC)}>Fbx136+5E-SUHK z_5gzZTckAl<;Z(!4z;lohY_E*HQc<-@?}2|@+?sGm%P@D3W}cbG&M7Ma@5?KEi z6npIeYFegvu58)dr32v?_D*7LTjn}(PfW}AiiZtHektY5Sl1IWzL7PeDS!$+VXuBT zI4F*Nrjv{~AHPk5=6E^r9FNf69k!W@A-Z`fmV*16*Ov*E8S{xUS{Ngj2QJ@q=OyVc zQi%1cL`k%XjoRAjNgMP03w^_*qiP}2PM+l;*+89QO1@ZO2x2*)zn9AYsqzc=NZTLW zCwU3og^n%B5y0c|&p*%O6o6t$sFPtuu^CRQXT|WZEbuqMRvM(SFF59!!o2mz9+I;{ z@*i-r^HGKJDO8R$U1e*S`cr9B;^X6fstt>Nx(Qo2ppO5vNz9PR*$a_uv@urEKrcCV zdyIPX6(*)y9>xT%q$3u8FVY}PR8z;WQnZCPdLY>J^~Z6J$h~VeP~SqFn#$yUG1MQ; zoln>{OEzf6@OWAr`iswO1%4O(Af3yS^68SNY{Yar4Q}(rre9qY%Cj4`H8*_b%z^dW zSEM=m4kZikYhI#fmrW0?FcEL}A+-f`S>{m_n*$y)>^%R+Oq6Mn;dqo^$*buC>zT^< z$JzpS!%E=IS_BrdE<9zMz7*SMaT>V_Ft*D_yrGL6gwLvn%IX>V@#WI5pL`~-2yLeN zV}0j?JWMwqA9vDL;9(Lw)OwgxFu`Y@LbVvG*TX3qT_>JJ9TGHE!|w3ZrKdW~mE3~H ztHV<%b=dm&Ww#~hfYC~$dcMv%`<&^)#foNC`LyS#k~>W^eo*?g?^e@o_~99cFHJKk zj4TRE0Bz182)D50VyiSV8O?}itp62tUW`d$$YkQPX=LEh+#3V{6b3JR7E}%d7LQNB&MIeUv?3lVTN-LD`pR>zTFzGXd|uU> zh}TKIu5Uii!kEyK*5YjYk5*A-Monq5^_hq3wBVb1(H2izov||w1B1#8TwHHq#uC<* zrd^BQPsp=R^{B9->Cl#Pa+9c%*LPt8Zj1y6Fgivug~3Hzx16`C8@A2bq>eTXEU^Jl zVY~Am>VbVa$70h{E$e=*@#}k9goWYLeJ#BjP|)-mDwqW*;XpmKKxe3djoB*2b@P&( zlcK$-2K&P|5QHM&QVH>~s`S>S-bO0z92D0?Uim%erW0(N>B%seCxA^&*7lBEwQcWL zOo)0;2RM$E78jGL<6~t_*Dp?%ZdGAor>9|Hd!l0$ZKp9X;=}U^-e0n9cva_Hw`KUI~7KM(6!~BeHFw1hwmWsWrM% zcOq*nD0|p7ZBYfM<3-cKMMLq4*Wa705uQCo{gOU9${Q&r{t)xkdmM{EGyakp=-cJ{ zFXnyn;@tL!cjJ(pIup3_Qr536dqZzLL7Gd<^SwUA8=eV^WtrQa%FChkS#E72@0{(G~7rk@thx%3J7 z5orrL27Wnr)@hd1681ZKb*qX0`CN0~s)<3M|G@kNj+^jBl!e-fq-nsz$2LmZPzFYpHX3=5FW>N)3b#{Kl zS9N~$Rq@K!Gtd15!>bv%?ghc@EK!I)WF-y0j-sEtyQ&4Q#YxlpeXIsl_UYUZH|UE! zJd)_ayC01U<5q&PhCS{T zRN%EE9TjYO<3m1TxZp$Z@$pme7We8w#JhHURB5?U33%;!MD*G-D~aQ|2s&Dow%d1# zPzmF4pYanPrh){o3sb{yOC#S!7`V$li9mN}@wk%X`TN_Lw$9JlS}qSjQ|G9|_cxGn zoN_}#pry^#|1$2RvH^=%*hTrJAbTfPFJqP-f>@hvNRB2KWbD_n>ul~rew57qRrhTL z_EsTXPVNNh0UGiYMNnC0(3T?ak8=a1ol`C$8})5Fl7lFQPxP||D@7ibX3ShQFyf6uNae+DfW7K)@o8!9)59!vehT`s3OGH2Fagczr%%1v_j1 zNFuh^Sgn*AwH~w~Ymc!cVs=RT)3b_J@`swWQ=Cg{aT-rJ?UNB-fEb-GznT}{QlGgl z>e@Zex)Og=^kF*D++`**KM6W`;T5p8s0z}f*~iiaaseGCB-?1Ib1&yg z4+hDVycdl#YvIg1q>m_XJm-h&A=cLhP%!ATDlUgTQlR^whzcy29Y>m_ESzQ`D04G3 zu!6NL^2290cw_?^xUiRZ_{Px}Jf2|={ZuwFtQ+FWOa)bDzfYXA<}BSLVgZi^JsJuRb)OVP_y zCR6_Sq+uYc{F1N+kv-0SB5)81MCH68ZEAw=^TeA{bpGQEBORoV9IS=<0+H~TJf z;tc(c?)_o!6u7D59m}G6Gbwx!aT>9$q5R!+@t0%D3CiF5j$2YV*#ba!3Gj?DRd9L1 zlh-r5C9QRrI9%uZs`6rLg%OG3lH#cL!5Rw2G)Spaz`o7s?%*+_k z+Q+rMS-u2DPFb^&p-7lbzR6F=H&9C1e5R?Je3EXPA>&>1klIv3D9- z_(7Rt{mtPl%MzCdvc#4sQ*e0T!Q+GaHu$_JQL^zpzVV&~r{&AL)hr8J3gxR>1@X(X zp@~m{xbfxmo+<|oB`@|&*{`VvS($X`ax!?d4<=c!2~{O{UbgOx6->LXce8<%z#jE9d8aV%WA6NA$V2hz4zI0@hfVGMH~hw`nTm`eNdM5m26@BI`76=2GNHf4oas=u)|LsnA`=F zs;2CUSZgwt?aAsVR~u&C1&A|rV6arZk^1%sX{k#6(Qn+2m9Dub=m3Eiv$uF-mKbo0 zyk4UGpGmHnzDua$K=UBoBa9f+2CAyf2;-Gb|f0ZQL>C0TZwecE-ZtV z9cLFC;O}Sj51T*V&CyOH)dN$t{?ESulYs0j=3naccGR(TS@Ui@3BBy7fC zEh=V5{(kB9dCA#OD&gaH0c=OVydR7lUdHb`GssHf0v2>qze-l3eyCh*pk zITWA_4aTTPbKTw*p@wrIr2)nQfeHK^-QtBv-rVU+O_K}f+@BXto4XIY*7VoB>Nj)v zYxKgw6eE}`C2yzuHNE;n!5g1oqnrs^U_`|mQFSJ_nJMGqqM0N7yK3jCfq>Z@_>Q(` zm#&EdP_=0cL@<}2wDmI?M&VN%AjYds*JKH)B|0&qZF}d>`e8cQF2eI2gV-Yj;`p+PI z&TFTzVzO((13#BdSejOda?IqH(t%;w2}xg27xEybL2Tv_D_l_$UO*$xp!^Rxe1z=P z!c|Z2k4Q~_hn{fGzF}Xd8na%&JkiPSzF>aquA~Cq5w&v$9>}dn1sv!vgccd5>Pa6t z(2g6|!2y+%jn9;Yzi1{=kahBDL_`79{$}t2;5k+O5!NS;7aTcDFj|<=_p#zI0DTB# zlcehvpk^NhAexn(qI*+P(75pli710kYd9jPwep<%l(OlKZ_#VYCp2j)Gq>)RyH|oO zp=19*3<62HnBua5(XpPNhkbaG*|T5&Rr_U7p#fFyIM%9t8buu4ygIiA=Pmww0O;_kH{?mQcsMS)t!ae*_##ETDxTeZ9SgAAhy z5@()r4EG?134|!InloK3_0_rIq)qZ|mfLt2N^%4}aT2-1`QCas8)6Sq=njR<+EA#M zXWxr7Fs@9Mt99*il4)C4{k`N@=}Ay)i#^!Akgw8EOAlV}MDDb1FgzOewsWna<>6=S zlMg;Q1AJ5E_{~rEeRhh_epbjZOxDPAFm;SOPDV8($C>DWov#CT@B}FC>Now`k6T&) zXd%%Mgl1@x4o|X>|NEQ~hNd%526Due8E>dclt{8BPqGeARtk3sf2m+mWuTfqnOFT; zRhUIscP5>QpoDl-SHX-jV4ZP&IbkV1_S?Bq@lo;8DrSnDoXG$Yx=D5v_G zHo2pN1ucuSprYlnLvst6n?zw=ljb3@rZR=M#w@nc>Yl10LTP&65lY;TP(lJcMYK?i z8|42k2*M54lclMw^X2E4c(5i2(t!KaR#Q!G=wEh#>!ml)#-9RJ;~2-;r75#Y`959U z$SZe$B>$jdJ!W!p(E>MTn0rCFKT(ku zjx!h5E!{R`74a`N`bcw=ve<^9{B^+=R8oa+1!7oyNqsBzER+4et-PT)p-kzQB+~ZS!&q?E`^{ ztSP+wc~XKyZ~xiEaspxQ?_@Sa2-Ltno;Fw5?h3MQ_{B5IXbMJ;g-1Gin>%J<-=oE= zpZ;~cXMFNB%Ud_ItucZBk2F^k!;_3jPWrdk zh+|7)nz;C8Kt8H)dboAF?JVB`SHgip8pMxBqs+X(J&6T+_KQ1Rw3ZhktHrRtK_tkB zc|tU~n%)-!N?D_%{W(k(o~-KB7>56Tou5AyD~x(k)kcj`k_yV+kOjI=#Z%=C37~0w zWq&LLvWgTDMR6a;t4}gED~?-xTYJ?ML0D*g#C740{}xfwm4ol}!<-T|%UgbK=(Poz zyzj@tapOV;+CLAXY6vTeP|JAQlD_JrQEtHKknwJl5i}@+jj!G^0RQYcG&KWxSA9^( zOqmG^3ZK|iHhZ~a)2@}`{SmYj^3*e}daduG3DZpvc~a>=S=ldT3oa<2JUPCxG=k+h z%w8pnlv1Mh+|O#6K9MuE0R38j+^#~a$4SQ2AJztW&h8U5V(clQcNS&V`k@GS04BD# zL|~BHGWhY4?U~qYSDcp>Zm}-P-SARbF|06%4xTQDM8e>4aY05fNcvUrR}Q1)8{aQ6 z=^k}cJ$h|r9P#L^9xwimzQpA9Q#mGM#kT7}gZh>1lfjs>-ehpDxo99~!CLZwh49`P z$yML|Jx~Y>+c9Q^C(yl{McoN?H#BR_UY(NO#21i~2hjIK1rRwC3J<-&{L@W>Fjf4;oNDu|Uk0>MVQ2{yWQ{Ur%qqyo z_$NSa%r$+2Rtn7*(&OfQPbjMkaj#v%>z3SN2*?2-yQNL+Qy_qM1)^Xtr_uLpou5h7 zi$=eHCo@fnchKY$5Y}UT@Yspcz9&9{;~;-C3tuVaKUx+Zjn_u3*VjO9{tk~tps znpFL2qggcaehf|sFg$Nd73452fwzt(%A^;J@jnuE-X^!u-M+*cVTEJW2unA(HH9*y zS#?ir$g|(Yo)gY(35jC;+CTzzyBQo`U>dXlwRQ1VEbDc(J6X5yLbbK+;U7s%*`Rt* z)nq6iPqix&!Kl_`aOhc(Bj=#aCPf$}hPb<3(jKzEB_(y@gcHlW+zmn@v3?z}h6{k4^=SI; zbnxVDTLGB_ex59yg63w9hcFw1YpZ=dxF&zVN&$`+e2N_v{R3nCl9E>MYaTg&R#^>=v z+S+#G7@X+2oL`UM0T~co29OeUDw`{Br#+9Ye#J_GE&@{3(2xaY8aRFf@YRquaEBUp z@uG(pp&sy&81!q5_u3KPPDOm!(7>#r37zZo&b;5QpGTSdN!e4=-;`` z=W;p$U0R>*KyBi$kjf!9@&hit z4^&>qs|<5uf{S0laR#Dn5XrB2X)D&@56-41suM%(O7i)r7IOZNv|3{1CsT$Neen8I ztJ`?;&w6?RykFG1bBc@WYR;sm@MpcNQ*+&2z8&~0;e2q&<5z#avtRv*U2eL;hyano znL*xXAs8~Og;zP==v`o-eHpc$p+M{rP07KhWGbz@7~d;8*+(GR0pjLo6B{S3T12&t z+K5Hh6y8Y}G%(F^cHm&8y^}gUL-0?D7)>UfVtlsQrz!oxZJzLNR93Tj6Xz!%NA#kNK*& zC;0QqOts9M%)h-@$D1Cq`2pR@7of2x3Mhd1*tQaW_E`X-&jo_}-y7h7?AlbgT=|dwRnSyer?!;( z^d9GWPg9kAAA=_f#8Mtymn+{dV%6yu&|g~?EZolcY$WlH>GkxQhGh^&a}l(xr+ZpB ziavm(lN89;xq#5%PWnZ78>GDaQc>VfRgKVmxE_-A<6H=7Lx4vHH_20^pKGbrD|J}WRT;xtT zgII_eDnF>L0!7bTl^Ml z6<=y4;^9N?2mY6Y#tkkdqz4I|D5#F2pBUFKoX#>}F20|7;CM8A6+sUAoY46Ao0;q3&Js2WXhX-g(S+IV4oH24_#Rh`#2x}t>n68}&)RIG3 zkDuH@F|-m^hh6y-lq5<5+4AsA4qH05k!gd|x}RQtKFBuyL3{1VlviXmg0NXtp-D0O z`&OCw3mH~%b4d_uEfcJ!YrXw%E^hdqGmk#(dfBA~_XaaUbfzMRln4nzUG@gHu9(Bo zY?6IWz4jLt-9!M`I}en*_SGJu%opd?U4$-=v*8#R7%(w4{j@v& z@o;OA9o_@!%WmETT8elK*U7uWwgMm)4(HI1iOv5Ia4SM%yd7MK0|+ z+uJJ|8Yc2UdZc>Mjss)Pm@l)aV1Qf-T*l)C`|}{VrAs!ZVK;~i8&m20c#Z5~it#Nb z?uvi}B~6W0-dt)y!KzR)ZpDPm)_<3>b~X;y12pGouY*mDY&q8FS9p|Ct_#W{IolTG zhQnN3hcgolCL`O8-_}2iSTCbXKX&Yit-Mh=wZqmq;WDHau;b%FOV72*(lXAEo6DO1 z%f|WExRCf&4|0qEs4pQH1V*2l59Z@JLY1K4iiKUbG<_EkL<0{fw7VK`O@k^h>7fu@ zBk!`{dPwfq(f_<>gq`J>J!pA65R`)&2S8lxJ)5b(o-iw>LR;S8ab3)!nTHLT=u8mj z-aQ%WFZWLgbhiwlL5jreB~Jv^>=_HN8W`5JZ(Z_R7GuWuq?)wmm=cKS%o}I40Ckn~ zijxMSc@LaB?7W?qnc;Rj}N*Fg9uD7_`2DvAV(Bu&)f|{&x zFsj6Q3%@?miCq=&4Q28HLbPO4j-&d7FMzS;ihzR35mlTZQQ{b3nVdW!>r4zbVR zYy=DoKv8kK*V+G#I=PrxK{Yv&1QMD*pdez;=QnlBDq8TH#j`Wo1+mE`HurN%TEO%I zO0n(n6X#|ca;gvrBTt~UdBDYsCZ>Y9)x63gr2)J=5_e zuF^AtRJ7)`C5y+V&!{vyz~$9~oPlojQzUGf6HjT4l|G@@wl5ImDo?rK z-hPs$=ZwpIo@v*FDnA&&anOLV9ryZwVr|MJ3>J`1H5L7PENd-GZf z5s_XmjO-6I6H*0S8<3`x%4~Nne6an`g^`8M4^UCwaO^^e+VMc)%m(F~^l|BRvLpsy z8A$^$dt`0J@lPYGh=&^x>{`}+73W5;{zy?K6wHM~Z#rSM(897pHFecd7Ur#?T71Sk zV{!}h5~l%v-B>E4c7=Ci-}_&ktjVD?JWomjj1pg+NP~#9?Aa9d`*$PB=@OKNRp~>P$G;kPAg%XMIfG~W82go8Isz(&+s~@wW_J#b^%m&$m zuy;b{@7{&SmJl@~PIhNlu;Gk>?%dob>~8(Q<5AGp>IZ$Ld2kkj!i!Vd7-dBACG$a# z?UqVix*dI^BK9>lLuCPS2TfHW@SHB~#mMl8|8VJu{MDL$liLV++9{{S*=oR2VB&|H!9?#X=+ zJ)JCYQAkn(Ld^wY`4-fUpWIt}a=rLXU}DHP=o6+Ldwrc=l^*|1+a`%Kzy9Ll{lE%U zPmJIfUCzG+|3v)2qI5u{^6Mb-CtkA;qaWRUaVd4%SQmF?0nZaik{jryf61M~Spno9k`_@7SR1{CSR(peE%^b)78mspG z>rd)CiRM?ZBSA=5pOU>&g5w~)Do)p7)D8g*~D}`!Nz@&~zEG5bIIN$G*nxoTcC$Sljhe?1C zhAWG}ug7C~4V1va#RFo3d!T0B)L453^f5orO@#rD%f`ZlPwXzO)@On@HORqtehkTi zyZY2umB#gs%d-l_Q5dYk(!WMwnrmWZ$I$ws zDCh$I4?u8z^2%c17GEX@$94!AO=!?26Wr1U?D@$g4<9~*f26C=?L9XvU!ouFg0@I^ zt}|70lXazJvRxx8m_U7EIEQMI&oUHbzF;?nUHK%iB{<_C`g!Qu{VYpqR7EK|nj*j3?15&>3ZE0y%Ov!jz z0<%JHjT`}Vw2mvT<71xsD^rjoAjAbTM!OG22R{rY-ycQfaVv|$azBq=qAD|6v((tS znkzt89IsiVO&VrUqXDBUe?Pn;w-C=r;?g%jA#cu?evCjy%K0P5JkH81dyjb>4Hdz$ zL!&NTH1<7Hbn{gE$2o5|c4nH=u5!nAMgbrr$$EwX>SQJPy02`5tfHPf1I!u`Lh_JC zxDTWHCAS=*1q*37n+ib~j`8KbrBLZbnDhj2Dql$|Ar@*yGq3i76o?+RS@@qB2IX^b zF489d5o_2GS(`aH7KY)#Cd}@i+U*LgP45JC4rKI#C)L$s|y1($9mF!^_E3k#RvJ zs#0&sZ}H@wTsc^#<-TPpuT!*O>UUK-_O&vgHh@urD0vc~+h1kf4y@if9Qxu{&a}W5 z>{GYykMumKNQf}Hq+VhOE=`=1gXGt-3Tm%e}Nz z7L;60J}659!Ub2wu*Y$ELh1K&O(f!k!YF#;@)F9S@dBf1Ujx;+YMJ^wZn?59bQ^O{ z8o@sRnnEtB@wRT>-3G3(nWX#-t|AG4zY;OcGg!MHdg)(koTiq#6u3D_lb*-gtn{ci z_7-}5D9|4#vd{zXO4op=qVqTzW}@>Z!Ln#HfQs6Vl`KCzj*7l8j@cTaX}=f@_J%O} zP~p3bVYKarpfha+Zj8LQ?aY+bZ5Lkfx;>)2+8NyHGgiL}dO#nbj2|$h`rUy2)O$@{ zv*snbe1+xA2obwOYm7OZc;>#VRq{>EH0RwxM(tU!Bo1MuD6n14k4>bTLLiBi7y@vd4Np?fC+(m z&vuMYId9BvIEwo7^)cLQ)+KyU`n6FJ_D9!#$k%;A%;R)P2J`A5QmiJ1+iKjx9Uxk% z%`ecGzx}~!d66`~;P>vXtAvW>E>-DA4{SbT9QW$gz=_3R`$BF-O;%ybphKFFEbk|K z7g1ULpft|T)tvr0GZ4JEU{`czKNn|EGi(24tTlrJ!~70Q7Hf0Y?VoxZ3R0I9C#C#O zby6~PGt{uQcoT(vJi=86sB>EiJ|v(JMA=Sf4FzBR&+8I|aGD$GS2r@DH)J%f5~g~{ zy2Gx7@*x%z0g~J;9ZRPh`~u#a0i7{_%5LjBfyz5zUWM+&SL8WXE|7yAPR(ZlyWTu9 zA&OxthzhE?*}kKbU%L%jzV5!(x*=2}kl+3_F5&aPsVArQU||2&>C&frU5jFq ztP}uB&IKrUYyh{bO2q8~5qyUcg;95=hDA0XcRKZ#h|1_olkFtD`arJ>YE?*b%Ou8s zRuEV>32(T#KzQM&@g*gzW-rw%V-Jsh_I}Z;o2U<-p7lJidl~8Do91CcYc;(wEDA^e zGS2HiWc@Qg<#7xyloHgSCBtHT{;aI7BT-u5`27>y5}5)-K*YD(eRK5qK0^vLEDAUa z+z0rhw}(eke@_PGs9aoF!c`*j?TUgzNz%u)L4-|w+o|B6(~S}FU~vvfj7rJ5rOf9y9 zuM03ViW~Vl7yyPy9_-7bfJ7Kx!!Tc4g*0gSCx5K~-Fv|L?Py-qq6wF0yIUbRw4xV3g*Xg#-pPYRA@MZPR!WxeMZ2tQwQ9rk@IuI6`&H{UcPMs8M1=9#RSNz zSmGP=!DDVp%Y=xBsE>AKq>t(HR9&Z`k;v8M{oc=<5gPF8ONc(Z4h#k=EomLaZ>Z#N z)^9y&X^k3j8n!w!3oUCp8ufm4bm5J|7DoWzT(c!&`VWU4MyaUG{V9 zsgC&=3v!4Cl%G2}$oy-nl2F#|$LIEmkW!;=XQ#h>U_w6UG66CNgl_SnHN&nWlxXY)=`|!{`6rdB6GXW$AF^*J zR=*2Tt11{nORpSFeV&m{I7v#_`lvo<80q9caXj9)qZGSQA|R5XkgQ{`;a5D{(d>a; zUt%!Sun;lXWCH<|(gSuCh+d`y_q7Wt%hXA!%<1HTla7eMa=4ls4>B}crZkkAP9oQb zjDKM{pHW>ykjgnA{=v%4htYbYeL__8hw1fy?-}yu#P=8v{$-Q!+fz43Vz(TfL=k9z zsf|O!iiFdz3#W^6=hZd`#=(6GL}+!U+96fx{rZqku1wq^fu@X~Kc>i(!h3z8=XV~# zNQ=po#&mN{9>V>_R=$Iz;=k+qR*ur%mCL?r~+MYJ6!Do}fK zu$Fx{QKt&pZRif8(u)WW=2hY5)DEkQl!76Sk*Xz7YO`P-ZAelW^l4 zB{iBt=7&hx-h7j_Et;Sg^+#*TpxvOcqgg*zX#Vu{0~fP$Cdf0W^*h08zj$Isc#4007nH^TUjeXhx6VrY6A%0|O;^8!4}6?qkNZ zZgM3U{NoaGpi5r`!9XcHgfZ6?`3A#ufsGFbu+^6g5FtKn2m0c%y+tDpM7HTSZQgJs$~ZZb77k zvg<|ZL1DFxieuU86JP1_&M0qLS$<_LG)A) zu{p=~fl%rO@Ru%s`0t{+=kW(6R*euPD!AEo zrK-zX!Z*KvrH>Seh_njI_e8vd@}(l!APJj$$%O{aP)lYqrV9cgrgcfzmN*(yHjkl( z8s?_E*{dEP!)`^D9m}bo>hH_Q8;GYH6b(w8yVIScwV(X98UhZ2|K@DaXkqFA5dey!>~XB)wED@Q8+mZ+FR*7!iaV@l)np zJO7~k8nJ6EDYT?%?c~J{q{`eY3dY`5KO1VtwWS`@wZFa#@nYQ<)6by7Qj!O~D8vK69ehAP*5tzU^_mN~4AuKid5y zm#ldxgzQ(DU7-bwq$fv>t(M_y1B&>zsKbo~)VI+}vCU9dih_NY~Qar&&Rgd#54v zRb11Ya$zg2T9CMUBWI*Vj$lY`hVN`Hn-3F=g*LQ57tT7^XCaw!kkH>Lo;}}D?xJgj z5N2nh_I@3rA>LFYwZwmC&k)^_XbC$|5D(_V#H-REw;Ou7vDa^8B7g z@q$)M8l(MngEGZQ$ph{8M@w1$KSZ&<8BM4+1V5LI(zOyDq7+S)3w_+ne_AwVyF_wES|j9+h<{?cJ#K+KdC7_etNLqGk*Cr zfm2%{g3b8qbGXFpHK?A3WExhCDN&m1m-}Unvkz;08?0$_`AIvC+^qC|LgX6bntO#w z&wRz4%N~{SkG%LhBm>_L_L-S}RH_$lr=It}T~@)H5O zLjHvY1GWy2xTG(?ja|nwFc}09m8V56EuH@g@3}lOpz{;3!zF^jltHQi2oFZWqIBr> z#^TkW-CqOkWCCHBt|QmL+<&Z;YQem-J&jsnZQ{|uSqw5DbU-g|A*Bo3>05K9dTGCL_N8) zpXHDIO4qbhrFQ@c6y(=`@Em^a!9O`~7dnZ~FPG6`BjsqDX)S9U>(xj2+LHz(efC#> zojbSiUqXRz3(+~}@PHJAh>ZINBt^cBaaRspdd1R{q1Y!nNKen2Pq zPi`uU%*1@hqMVwlepEBLb+vx8dbM?%<9X}kQ%67aVXJGmvE!8E?X-FSWRI!(Ik^hC zT?e(c@mp;U{lX}bWOFup%)S4c$f$P$&!GU2JWxU+0%(jRy`FozHwVH@s6|Xp+GiKO zpJj2DPc&sbIb8ZB6i!yrm`vzMw=CQDnvY=-JEvQYi6(2OV|{(6sU@$dx7La6yxSxKVv6FyN2an3o!qTMsZ^Xp?A4`hY(1)t8f zT@}uw*ppz-V4xh_zn%>2g1G++T9W~04AkC&g(S}xOr7E!IXo)DFe&0?o^+RXS6qPc z0{-v^%Y#F{3h)VW011;23y5Ryex_hRy9E4~z_sWf{Cnl{@h01QHiUW<(dR8Z)xR%L zzVI5+(FyE!02DRc22rvmSvcl*n37nZ$A-+)_+P67V%#%DH)X z0;2ui8H-Z}m^@!j^xLOkWM+J@&u6a808GN#p&?}5i+|So?3|U_g04d5sk}+4cDOcA z!*fPS5tkL6Aw2l5Cki{9-jH{W!2YX84*0G721Wo~bf|eb7^H^<5Z#F8QJ~dD=Pekb zk^=nOTmaZ|aQixaAW481F&DDMpa9HXnyekB#7q2RGCS>!;<7*}ZlE3nmfF!-4dFR3 zC0uk{(Ijkq-e z&{{tfgEiMvUz-tj073QI~QUZ)sySf0j)yY>f8a&m&Z?a@&Be6$`0ir<$JWBRA zW`jVvz-sSzAFyF0`3!2m_YUrkJ8%=FM5J~8pVwmnf~i*N&T3kV^~FA=>-6Vsj~ua zGGM60ltFphde4%LQyU!}{y2k@OLu(_JSwgv{}ej5GFG7tP| zLSzc-MS_8c7STO04?_KnFq@TxzEiQ-FLBi5XN~PCD@DGWQr-- zK|*QK=ji_$abEbzQy576-(9Eg^Hx9VYgfmlgjgCARp6>&$kdIr!EwqPSZFPw| zA|o?#CjlNJb?D6-lgpc`fa(ui0#%f_K?Gs7Gt3L#jQYuHI zEtN=~J$Gm*j(_oT;5RS~CHhMO0~w#6W}{8gi-3(VgGtWRph!HXnG#UsJbx=#XpZW+hD m{=e@nxLg0vc>JCapf8M#`1*D=nt-D+L_<|a<%5!C(EkF(DGks7 literal 0 HcmV?d00001 diff --git a/public/logo-wordmark-light.png b/public/logo-wordmark-light.png new file mode 100644 index 0000000000000000000000000000000000000000..3da485900daab21aad2ffae3ce2b3844b04cdf9e GIT binary patch literal 22027 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 literal 0 HcmV?d00001 diff --git a/public/logo-wordmark.png b/public/logo-wordmark.png new file mode 100644 index 0000000000000000000000000000000000000000..8c470b02960ac24880ccf021c2bdda087fb2cd2e GIT binary patch literal 22191 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 `label={${label} *}`; + +function processFile(file) { + let content = fs.readFileSync(file, 'utf8'); + + // Find all components with isRequired + // We need to match: label="Something" ... isRequired + + // First, find all lines with label="..." + // If a block has isRequired, replace the label and remove isRequired. + + const tags = ['TextInput', 'Selector', 'NumberInput', 'TimeInput']; + + // A simple regex to find JSX tags and their props + const regex = /<(TextInput|Selector|NumberInput|TimeInput)([^>]+)isRequired([^>]*)>/g; + + content = content.replace(regex, (match, tag, before, after) => { + // extract label + const labelMatch = before.match(/label="([^"]+)"/); + if (!labelMatch) { + // maybe label is after isRequired + const labelMatchAfter = after.match(/label="([^"]+)"/); + if (!labelMatchAfter) return match; // fallback + + const newLabel = req(labelMatchAfter[1]); + return `<${tag}${before}${after.replace(labelMatchAfter[0], newLabel)}>`; + } + + const newLabel = req(labelMatch[1]); + return `<${tag}${before.replace(labelMatch[0], newLabel)}${after}>`; + }); + + fs.writeFileSync(file, content); +} + +processFile('src/features/nearle-admin/pages/OnboardTenantPage.tsx'); +processFile('src/features/nearle-admin/pages/OnboardBranchPage.tsx'); + +console.log("Done"); diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..40ffc3f --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,152 @@ +import { lazy, Suspense, type ComponentType } from 'react'; +import { Navigate, Route, Routes } from 'react-router-dom'; +import { Spinner } from '@astryxdesign/core/Spinner'; +import { RequireRole, useAuth } from '@/auth/AuthContext'; +import { HOME_ROUTE } from '@/auth/roles'; +import { LoginPage } from '@/features/auth/LoginPage'; +import { NearleAdminShell } from '@/features/nearle-admin/NearleAdminShell'; +import { StoreAdminShell } from '@/features/store-admin/StoreAdminShell'; + +/** + * Routes are split per page. + * + * The reference console ships as one 1.8MB chunk with no route splitting, which + * means every visitor downloads the spreadsheet importer to look at a dashboard. + * That is the one thing from it worth deliberately not copying. + */ +const named = (key: T, loader: () => Promise>) => + lazy(() => loader().then((module) => ({ default: module[key] }))); + +const StoresPage = named('StoresPage', () => import('@/features/nearle-admin/pages/StoresPage')); +const StoreDetailPage = named('StoreDetailPage', () => import('@/features/nearle-admin/pages/StoreDetailPage')); +const OnboardTenantPage = named('OnboardTenantPage', () => import('@/features/nearle-admin/pages/OnboardTenantPage')); +const GlobalCataloguePage = named('GlobalCataloguePage', () => import('@/features/nearle-admin/pages/GlobalCataloguePage')); + +const ConsolePage = named('ConsolePage', () => import('@/features/store-admin/pages/ConsolePage')); +const SalesPage = named('SalesPage', () => import('@/features/store-admin/pages/SalesPage')); +const InventoryPage = named('InventoryPage', () => import('@/features/store-admin/pages/InventoryPage')); +const ReportsPage = named('ReportsPage', () => import('@/features/store-admin/pages/ReportsPage')); +const OnboardBranchPage = named('OnboardBranchPage', () => import('@/features/store-admin/pages/OnboardBranchPage')); +const UsersPage = named('UsersPage', () => import('@/features/store-admin/pages/UsersPage')); +const TerminalsPage = named('TerminalsPage', () => import('@/features/store-admin/pages/TerminalsPage')); + +function RouteFallback() { + return ( +

+ +
+ ); +} + +/** + * Routes. + * + * Three workspaces, hard-partitioned by role and by URL prefix, the same way the + * three logins imply. Only the Nearle Admin workspace is built so far; the other + * two resolve to a placeholder rather than a 404, so a Store Admin signing in + * today lands somewhere that explains itself. + */ +export function App() { + const { user } = useAuth(); + + return ( + + } /> + + {/* Nearle Admin — the platform workspace */} + + }> + + + + } + > + } /> + } /> + } /> + } /> + {/* Branch onboarding moved to the Store Admin workspace — the merchant + opens their own outlets. Kept as a redirect rather than deleted so a + bookmark or a stale link lands on the directory instead of a 404. */} + } /> + } /> + {/* Absorbed here rather than by the global `*`, so a wrong sub-path can + never bounce out to a HOME_ROUTE that points back into this + workspace and loop. */} + } /> + + + {/* Store Admin — the merchant workspace, scoped to one tenant's branches */} + + }> + + + + } + > + } /> + } /> + } /> + } /> + } /> + } /> + {/* Reached from the account menu, not the nav — setup is not a place + anyone works from day to day. See `AppShellProps.manageItems`. */} + } /> + } /> + {/* Catches `/admin/dashboard` and anything else that does not resolve. + Without this, an unknown sub-path escapes to the global `*`, which + redirects to this role's HOME_ROUTE — and if that is itself an + unknown path, the two bounce forever and the page renders blank. */} + } /> + + + {/* Not built yet. Named rather than missing, so the redirect has somewhere to land. */} + } /> + + } + /> + + ); +} + +function NotBuiltYet({ workspace }: { workspace: string }) { + const { signOut } = useAuth(); + return ( +
+
+

+ The {workspace} workspace is not built yet +

+

+ This console currently covers the Nearle Admin workspace. Your account signs in + correctly — there is simply nothing here for it to open. +

+ +
+
+ ); +} diff --git a/src/api/catalogue.ts b/src/api/catalogue.ts new file mode 100644 index 0000000..5826383 --- /dev/null +++ b/src/api/catalogue.ts @@ -0,0 +1,53 @@ +/** + * The global FMCG catalogue — a separate pgvector database, bridged to tenant + * data by the composite key `(brand, catalogueid)`. + * + * A catalogue row's bare `id` is only unique WITHIN its own brand table: + * `brand_dabur.id = 1` and `brand_nestle.id = 1` are different products. Every + * call that references a catalogue product sends both. + */ + +import { api, WEB } from './client'; +import type { CatalogueBrand, CatalogueProduct, CatalogueRef } from './types'; + +export interface CatalogueQuery { + /** Omit to search every brand merged — that is the "show everything" entry point. */ + brand?: string; + category?: string; + keyword?: string; + pageno?: number; + pagesize?: number; +} + +export const catalogueApi = { + /** + * Browse the global catalogue. Called with no `brand` this returns the full + * merged, paginated list — the list is never gated behind a brand selector. + */ + products: (query: CatalogueQuery = {}) => + api.get(`${WEB}/catalogue/getproducts`, { + brand: query.brand, + category: query.category, + keyword: query.keyword, + pageno: query.pageno ?? 0, + pagesize: query.pagesize ?? 48, + }), + + /** Brands with product counts, for the filter chip row. Never hardcode this list. */ + brands: () => api.get(`${WEB}/catalogue/getbrands`), + + categories: () => api.get(`${WEB}/catalogue/getcategories`), + + /** + * The `(brand, catalogueid)` pairs this tenant has already imported, for + * badging "Imported" in the browser. Called without `brand` because the list + * mixes brands. + */ + importedRefs: (tenantid: number) => + api.get(`${WEB}/products/getimportedcatalogueproducts`, { tenantid }), +}; + +/** Key for the imported-refs lookup. Both halves, always. */ +export function catalogueKey(ref: CatalogueRef | CatalogueProduct): string { + return 'catalogueid' in ref ? `${ref.brand}:${ref.catalogueid}` : `${ref.brand}:${ref.id}`; +} diff --git a/src/api/client.ts b/src/api/client.ts new file mode 100644 index 0000000..3da4d6c --- /dev/null +++ b/src/api/client.ts @@ -0,0 +1,186 @@ +/** + * The Fiesta HTTP client. + * + * Everything the console knows about talking to the backend lives here, so the + * day the backend starts issuing a session token, this is the only file that + * changes. Nothing else in the app calls `fetch`. + */ + +import { demoResolve, isDemoActive, MISS } from '@/demo'; +import type { FiestaEnvelope } from './types'; + +/** + * In dev, Vite proxies `/fiesta` -> https://fiesta.nearle.app (see + * vite.config.ts), which keeps the network tab honest and sidesteps preflight + * surprises. In production the deployed host is set by VITE_API_BASE. + */ +const API_BASE = import.meta.env['VITE_API_BASE'] ?? '/fiesta'; + +/** Every console route lives under this prefix. `/mob/*` and `/pos/*` differ. */ +export const WEB = '/live/api/v1/web'; +export const POS = '/live/api/v1/pos'; + +/** + * A failed call, carrying the backend's own message. + * + * Fiesta answers HTTP 200 with `status: false` in several places, so the HTTP + * status alone is not enough to tell success from failure — both are checked. + */ +export class FiestaError extends Error { + readonly code: number; + readonly endpoint: string; + + constructor(message: string, code: number, endpoint: string) { + super(message); + this.name = 'FiestaError'; + this.code = code; + this.endpoint = endpoint; + } + + /** + * True when the backend rejected the call for want of a scoping id. + * + * The IDOR pass added controller-level guards: an unscoped list call 400s + * rather than returning every tenant's rows. That is a bug in the caller, + * not a server fault, and it should surface as one. + */ + get isMissingScope(): boolean { + return this.code === 400 && /required/i.test(this.message); + } +} + +export type QueryValue = string | number | boolean | null | undefined; + +/** Drops empty params rather than sending `?tenantid=` and getting a 400 back. */ +function toQueryString(params: Record | undefined): string { + if (!params) return ''; + const search = new URLSearchParams(); + for (const [key, value] of Object.entries(params)) { + if (value === undefined || value === null || value === '') continue; + search.set(key, String(value)); + } + const qs = search.toString(); + return qs ? `?${qs}` : ''; +} + +interface RequestOptions { + method?: 'GET' | 'POST' | 'PUT' | 'DELETE'; + params?: Record; + body?: unknown; + signal?: AbortSignal; +} + +async function request(path: string, options: RequestOptions = {}): Promise { + const { method = 'GET', params, body, signal } = options; + + // Demo mode short-circuits before any network call. In a production build + // `isDemoActive` is a constant `false`, so the bundler removes this branch + // and the fixtures with it. + if (import.meta.env.DEV && isDemoActive()) { + const fixture = await demoResolve(path, params as Record | undefined); + if (fixture !== MISS) { + // A beat of latency, so loading states are visible while working on them. + await new Promise((resolve) => setTimeout(resolve, 180)); + return fixture as T; + } + } + + const url = `${API_BASE}${path}${toQueryString(params)}`; + + const init: RequestInit = { + method, + headers: { Accept: 'application/json' }, + signal: signal ?? null, + }; + + if (body !== undefined) { + init.headers = { ...init.headers, 'Content-Type': 'application/json' }; + init.body = JSON.stringify(body); + } + + let response: Response; + try { + response = await fetch(url, init); + } catch (cause) { + // A network failure and a 500 read very differently to a user; keep them + // distinguishable rather than collapsing both into "something went wrong". + throw new FiestaError( + cause instanceof DOMException && cause.name === 'AbortError' + ? 'Request cancelled' + : 'Could not reach the server', + 0, + path, + ); + } + + let envelope: FiestaEnvelope; + try { + envelope = (await response.json()) as FiestaEnvelope; + } catch { + throw new FiestaError(`Malformed response (HTTP ${response.status})`, response.status, path); + } + + if (!response.ok || envelope.status === false) { + throw new FiestaError( + envelope.message ?? `Request failed (HTTP ${response.status})`, + envelope.code ?? response.status, + path, + ); + } + + return envelope.details as T; +} + +/** + * The whole envelope, for the handful of callers that need `message` or + * `tenantform` on success — login being the one that matters. + */ +async function requestEnvelope( + path: string, + options: RequestOptions = {}, +): Promise> { + const { method = 'GET', params, body } = options; + const url = `${API_BASE}${path}${toQueryString(params)}`; + + const init: RequestInit = { method, headers: { Accept: 'application/json' } }; + if (body !== undefined) { + init.headers = { ...init.headers, 'Content-Type': 'application/json' }; + init.body = JSON.stringify(body); + } + + let response: Response; + try { + response = await fetch(url, init); + } catch { + throw new FiestaError('Could not reach the server', 0, path); + } + + try { + return (await response.json()) as FiestaEnvelope; + } catch { + throw new FiestaError(`Malformed response (HTTP ${response.status})`, response.status, path); + } +} + +export const api = { + get: (path: string, params?: Record, signal?: AbortSignal) => + request(path, { method: 'GET', params, signal }), + + post: (path: string, body?: unknown, params?: Record) => + request(path, { method: 'POST', body, params }), + + put: (path: string, body?: unknown, params?: Record) => + request(path, { method: 'PUT', body, params }), + + del: (path: string, body?: unknown, params?: Record) => + request(path, { method: 'DELETE', body, params }), + + envelope: requestEnvelope, +}; + +/** Normalises anything thrown into a message worth showing a person. */ +export function errorMessage(error: unknown): string { + if (error instanceof FiestaError) return error.message; + if (error instanceof Error) return error.message; + return 'Something went wrong'; +} diff --git a/src/api/insights.ts b/src/api/insights.ts new file mode 100644 index 0000000..0b45473 --- /dev/null +++ b/src/api/insights.ts @@ -0,0 +1,159 @@ +/** + * Order, delivery and POS reads — the numbers behind store performance. + * + * Note the split: online orders come from `/web/orders/*` and counter sales + * from `/pos/*`, which is the ONLY part of the API carrying auth middleware. + * Any figure that blends the two is eventually consistent by construction, so + * screens that show one must also show its freshness. + */ + +import { api, POS, WEB } from './client'; +import type { + DeliveryRow, + DeliverySummary, + LocationOrderSummary, + OrderRow, + OrderSummary, + PosLocationHealth, + PosSalesPage, + PosSalesSummary, +} from './types'; + +export interface DateRange { + fromdate?: string; + todate?: string; +} + +export interface OrderQuery extends DateRange { + tenantid: number; + /** Omit for every branch of the tenant. */ + locationid?: number; + status?: string; + keyword?: string; + pageno?: number; + pagesize?: number; +} + +export const insightsApi = { + /** + * The order rows themselves. + * + * `/orders/tenant/getorders` rather than the bare `/orders/getorders`: the + * controller routes on which ids are present, and passing a tenant with no + * partner, customer or app-user reaches `GetTenantOrders`. Passing a + * `locationid` as well reaches `GetTenantLocationOrders`, which is the + * branch-scoped read — so one call covers both "all branches" and "one + * branch" by presence alone. + * + * `pageno` is 1-based here. The controller floors anything <= 0 to 1, so + * sending 0 silently gives page one rather than an error. + */ + orders: (query: OrderQuery) => + api.get(`${WEB}/orders/tenant/getorders`, { + tenantid: query.tenantid, + locationid: query.locationid, + status: query.status, + keyword: query.keyword, + fromdate: query.fromdate, + todate: query.todate, + pageno: query.pageno ?? 1, + pagesize: query.pagesize ?? 50, + }), + + /** + * The delivery jobs. + * + * A separate read from the orders list, NOT a filter over it. The rows are a + * different struct with different fields — rider name, planned vs actual + * distance, rider charge vs job value, notes — and a different status ladder. + * Deriving deliveries from orders, which is what this page did first, loses + * every one of those. + * + * The controller 400s unless one of tenantid/partnerid/customerid/ + * applocationid/userid/appuserid is present, so `tenantid` is required here. + */ + deliveries: (query: OrderQuery) => + api.get(`${WEB}/deliveries/getdeliveries`, { + tenantid: query.tenantid, + locationid: query.locationid, + status: query.status, + keyword: query.keyword, + fromdate: query.fromdate, + todate: query.todate, + pageno: query.pageno ?? 1, + pagesize: query.pagesize ?? 50, + }), + + orderSummary: (tenantid: number, range: DateRange = {}) => + api.get(`${WEB}/orders/getordersummary`, { tenantid, ...range }), + + /** Per-branch order totals for one tenant. `tenantid` is required. */ + locationSummary: (tenantid: number, range: DateRange = {}) => + api.get(`${WEB}/orders/getlocationsummary`, { tenantid, ...range }), + + revenueSummary: (tenantid: number, range: DateRange = {}) => + api.get(`${WEB}/orders/getrevenuesummary`, { tenantid, ...range }), + + timeSeries: (tenantid: number, range: DateRange = {}) => + api.get[]>(`${WEB}/orders/gettimeseries`, { tenantid, ...range }), + + deliverySummary: (tenantid: number, range: DateRange = {}) => + api.get(`${WEB}/deliveries/deliverysummary`, { tenantid, ...range }), + + /** + * Counter sales for ONE outlet. + * + * `locationid` is required and singular — there is no tenant-wide POS call, + * so a multi-branch view fans out one request per branch. + */ + posSales: ( + locationid: number, + params: DateRange & { + pageno?: number; + pagesize?: number; + /** The three server-side filters the old console's bills tab offers. */ + terminalid?: string; + cashiername?: string; + paymentmode?: string; + } = {}, + ) => + api.get(`${POS}/sales`, { + locationid, + pageno: params.pageno ?? 0, + pagesize: params.pagesize ?? 50, + fromdate: params.fromdate, + todate: params.todate, + terminalid: params.terminalid, + cashiername: params.cashiername, + paymentmode: params.paymentmode, + }), + + /** + * One counter bill in full, with its lines. + * + * `reference` accepts the till's order UUID, the invoice number, or this + * backend's posorderid — a support call starts from whichever the person is + * looking at, so the endpoint takes all three. + */ + posSaleDetail: (locationid: number, reference: string) => + api.get(`${POS}/sales/detail`, { locationid, reference }), + + /** + * Counter-sales totals for ONE outlet. + * + * The richest read in the API: bill count, gross, tax, discount, roundoff and + * average bill, already broken down by payment mode, by day and by terminal. + * Reports gets its offline half from this and nothing else. + * + * Kept apart from the order summaries above on purpose. These are `posorders` + * rows; those are `orders` rows. The two are never added together — see + * `store-admin-backend-gap.md` §3.1 for why that would double-count a branch + * that both runs a till and uploads a spreadsheet. + */ + posSalesSummary: (locationid: number, range: DateRange = {}) => + api.get(`${POS}/sales/summary`, { locationid, ...range }), + + /** Till presence for one outlet — how many are online, how many bills are stranded. */ + posHealth: (locationid: number) => + api.get(`${POS}/health/location`, { location_id: locationid }), +}; diff --git a/src/api/people.ts b/src/api/people.ts new file mode 100644 index 0000000..c28ec01 --- /dev/null +++ b/src/api/people.ts @@ -0,0 +1,160 @@ +/** + * People — back-office staff and till accounts. + * + * Two account systems that happen to share one table. A till account is NOT a + * Nearle Daily user: the backend excludes roles 7 and 8 from every application + * lookup inside the query itself, deliberately, so a cashier is "not found" + * rather than "refused". They are read and written through different endpoints + * with different conventions, and this file keeps them apart. + * + * Three things this module will not do, each for a reason recorded in + * `store-admin-user-menu-plan.md`: + * + * - **No delete.** `DELETE /users/delete` and `DELETE /deleteposuser` are hard + * deletes with no cascade. Deactivating via `status` is the safe equivalent + * and is what both list screens offer. + * - **No password management.** `PUT /users/update` doubles as the + * password-reset call and passwords are stored in clear. Creating an account + * is in scope; issuing its password is not, until hashing exists. + * - **Never send `roleid: -1`.** The old console clears a role that way; + * `UpdateStaff` does not special-case it, so `-1` lands in the column and the + * account ends up holding a role that matches nothing. + */ + +import { api, POS, WEB } from './client'; +import type { PosRole, PosUser, StaffInfo, StaffShift } from './types'; + +/* ── Back-office staff ───────────────────────────────────────────────────── */ + +export interface CreateStaffRequest { + tenantid: number; + locationid: number; + firstname: string; + lastname?: string; + email: string; + contactno: string; + roleid: number; + status?: string; +} + +export interface UpdateStaffRequest { + userid: number; + firstname?: string; + lastname?: string; + email?: string; + contactno?: string; + roleid?: number; + locationid?: number; + status?: string; +} + +export const staffApi = { + /** + * The tenant's back-office directory. + * + * `getstaffs`, NOT `getallusers`. This one resolves `rolename` server-side, + * and the backend says why that matters: "`app_roles` holds six rows for four + * back-office roles and most accounts carry an id absent from it, so any + * mapping written client-side is wrong." + */ + list: (tenantid: number) => + api.get(`${WEB}/tenants/getstaffs`, { tenantid }), + + create: (body: CreateStaffRequest) => api.post(`${WEB}/users/create`, body), + + /** + * Update a person. + * + * GORM's `Updates` with a struct skips zero values, so an omitted field is + * left alone rather than blanked — which is why every field here is optional + * and why clearing something is not possible through this call. + */ + update: (body: UpdateStaffRequest) => api.put(`${WEB}/users/update`, body), +}; + +/* ── Till accounts ───────────────────────────────────────────────────────── */ + +export interface CreatePosUserRequest { + tenantid: number; + locationid: number; + full_name: string; + /** + * The role NAME, lowercase — "supervisor" or "cashier". + * + * Not the id. `PosRoleFromName` reads the name off the request and returns 0 + * for anything it does not recognise, which every caller treats as a refusal + * rather than as a default. Sending 7 or 8 here does nothing. + */ + role: string; + /** Ten digits. The till matches on it exactly — see the note in `normaliseMobile`. */ + contactno: string; + pin?: string; + /** A `staffshifts.staff_shift_id`. Zero leaves it unset. */ + shift_id?: number; + status?: string; +} + +export interface UpdatePosUserRequest { + tenantid: number; + locationid: number; + user_id: number; + full_name?: string; + role?: string; + contactno?: string; + shift_id?: number; + status?: string; +} + +export const posUsersApi = { + /** Till accounts at one outlet. `locationid` is required and singular. */ + list: (tenantid: number, locationid: number) => + api.get(`${POS}/getposusers`, { tenantid, locationid }), + + /** + * The role picker's source. + * + * Read rather than hardcoded. Supervisor is 7 and Cashier is 8 today, but the + * endpoint also carries the label and the description a person needs to + * choose between them — and a third role would appear here first. + */ + roles: () => api.get(`${POS}/posroles`), + + /** + * Create a till account. + * + * The response carries the PIN or password ONCE. The backend is explicit that + * a listing never returns it: "An admin who loses it reissues rather than + * looks it up." So it is shown at creation and never read back. + */ + create: (body: CreatePosUserRequest) => api.post(`${POS}/createposuser`, body), + + update: (body: UpdatePosUserRequest) => api.put(`${POS}/updateposuser`, body), + + /** Shift windows a till account can be put on. */ + shifts: (tenantid: number, locationid: number) => + api.get(`${POS}/getstaffshifts`, { tenantid, locationid }), +}; + +/** + * Ten digits, or nothing. + * + * The till matches the mobile number EXACTLY, so `+91 98765 43210` typed back + * as `9876543210` would not find the row. Stripping to the last ten digits at + * the edge means an admin can paste whatever their contact list gave them. + */ +export function normaliseMobile(input: string): string { + const digits = input.replace(/\D/g, ''); + return digits.length > 10 ? digits.slice(-10) : digits; +} + +/** Mon-first mask → "Mon–Fri", "Every day". `weekdays` empty means every day. */ +export function weekdayLabel(mask: string | undefined): string { + if (!mask || !/^[01]{7}$/.test(mask)) return 'Every day'; + const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']; + const on = [...mask].map((bit, index) => (bit === '1' ? days[index] : null)).filter(Boolean); + if (on.length === 7) return 'Every day'; + if (on.length === 0) return '—'; + if (mask === '1111100') return 'Mon–Fri'; + if (mask === '0000011') return 'Weekends'; + return on.join(', '); +} diff --git a/src/api/products.ts b/src/api/products.ts new file mode 100644 index 0000000..2563660 --- /dev/null +++ b/src/api/products.ts @@ -0,0 +1,213 @@ +/** + * Product, stock and import endpoints. + * + * Two import paths exist and they are NOT symmetric — the asymmetry is the + * backend's, not a choice made here: + * + * Catalogue path : one batch call, idempotent. Re-importing the same + * (tenantid, brand, catalogueid) tops up stock and + * overwrites price instead of duplicating. + * + * Sheet path : `create` accepts ONE product, not an array, and does not + * return the generated productid — the controller passes the + * struct to the service by value, so GORM writes the id into + * a copy that is then discarded, and the response echoes what + * was sent. So a sheet import is N creates, then a lookup by + * SKU to resolve ids, then one batched location call and one + * batched stock call. + * + * `importSheetProducts` below encapsulates that whole dance so no screen has to + * know about it. + */ + +import { api, WEB } from './client'; +import type { + ImportCatalogueProductRequest, + Product, + ProductCategory, + ProductLocationRequest, + ProductStockRequest, + ProductSubCategory, +} from './types'; + +export interface LocationProductQuery { + tenantid: number; + locationid: number; + pageno?: number; + pagesize?: number; +} + +export const productsApi = { + /** A store's own catalogue — what is actually imported, with live stock. */ + locationProducts: (query: LocationProductQuery) => + api.get(`${WEB}/products/getlocationproducts`, { + tenantid: query.tenantid, + locationid: query.locationid, + pageno: query.pageno ?? 0, + pagesize: query.pagesize ?? 50, + }), + + allProducts: (tenantid: number) => + api.get(`${WEB}/products/getallproducts`, { tenantid }), + + count: (tenantid: number) => + api.get<{ count?: number }>(`${WEB}/products/getproductscount`, { tenantid }), + + categories: (tenantid: number) => + api.get(`${WEB}/products/getproductcategories`, { tenantid }), + + subCategories: (tenantid: number, categoryid: number) => + api.get(`${WEB}/products/getproductsubcategories`, { + tenantid, + categoryid, + }), + + /** Batch, idempotent. Send the whole selection in one call. */ + importFromCatalogue: (rows: ImportCatalogueProductRequest[]) => + api.post(`${WEB}/products/importcatalogueproduct`, rows), + + /** Single product only — the backend parses one object, not an array. */ + createProduct: (product: Partial) => + api.post(`${WEB}/products/create`, product), + + /** Array. Upserts on (tenantid, locationid, productid). */ + createProductLocations: (rows: ProductLocationRequest[]) => + api.post(`${WEB}/products/createproductlocation`, rows), + + /** Array. Appends to the stock ledger. */ + createProductStock: (rows: ProductStockRequest[]) => + api.post(`${WEB}/products/createproductstock`, rows), + + publish: (body: { tenantid: number; locationid: number; productid: number }) => + api.post(`${WEB}/products/publishproduct`, body), + + /** Unlinks from the store. The product row and its order history survive. */ + removeFromStore: (body: { tenantid: number; locationid: number; productid: number }) => + api.del(`${WEB}/products/deleteproductlocation`, body), +}; + +/* ──────────────────────────────────────────────────────────────────────────── + The sheet-import dance + ──────────────────────────────────────────────────────────────────────────── */ + +/** One validated row from the uploaded workbook. */ +export interface SheetProductRow { + productname: string; + productsku: string; + categoryid: number; + subcategoryid: number; + retailprice: number; + productcost: number; + taxpercent: number; + quantity: number; + productunit?: string; + unitvalue?: string; + productbrand?: string; + productdesc?: string; +} + +export interface SheetImportResult { + created: number; + linked: number; + stocked: number; + /** Rows the backend rejected, paired with the reason, so they can be retried. */ + failures: { row: SheetProductRow; reason: string }[]; +} + +export interface SheetImportOptions { + tenantid: number; + locationid: number; + rows: SheetProductRow[]; + onProgress?: (done: number, total: number) => void; +} + +/** + * Imports a parsed sheet. + * + * NOT idempotent, and it cannot be made so from this side: nothing in the API + * dedupes on SKU, so uploading the same workbook twice creates the products + * twice. The importer UI is responsible for warning before a re-upload. + * + * Creates run sequentially rather than in parallel on purpose. There is no + * batch create, and firing 500 concurrent writes at a single-instance Go + * service to save a few seconds is a poor trade against a half-imported tenant. + */ +export async function importSheetProducts( + options: SheetImportOptions, +): Promise { + const { tenantid, locationid, rows, onProgress } = options; + const failures: SheetImportResult['failures'] = []; + const createdSkus: string[] = []; + + for (const [index, row] of rows.entries()) { + try { + await productsApi.createProduct({ + tenantid, + productname: row.productname, + productsku: row.productsku, + categoryid: row.categoryid, + subcategoryid: row.subcategoryid, + retailprice: row.retailprice, + productcost: row.productcost, + taxpercent: row.taxpercent, + productunit: row.productunit, + unitvalue: row.unitvalue, + productbrand: row.productbrand, + productdesc: row.productdesc, + productstatus: 'Active', + }); + createdSkus.push(row.productsku); + } catch (error) { + failures.push({ row, reason: error instanceof Error ? error.message : 'Create failed' }); + } + onProgress?.(index + 1, rows.length); + } + + if (createdSkus.length === 0) { + return { created: 0, linked: 0, stocked: 0, failures }; + } + + // Resolve the ids the create endpoint refused to hand back. + const all = await productsApi.allProducts(tenantid); + const bySku = new Map(); + for (const product of all) { + if (product.productsku) bySku.set(product.productsku, product); + } + + const locationRows: ProductLocationRequest[] = []; + const stockRows: ProductStockRequest[] = []; + + for (const row of rows) { + if (!createdSkus.includes(row.productsku)) continue; + const product = bySku.get(row.productsku); + if (!product) { + failures.push({ row, reason: 'Created, but could not be found again by SKU' }); + continue; + } + locationRows.push({ + tenantid, + locationid, + productid: product.productid, + price: row.retailprice, + status: 'available', + }); + stockRows.push({ + tenantid, + locationid, + productid: product.productid, + quantity: row.quantity, + stocktype: 'in', + status: 'Active', + }); + } + + if (locationRows.length > 0) await productsApi.createProductLocations(locationRows); + if (stockRows.length > 0) await productsApi.createProductStock(stockRows); + + return { + created: createdSkus.length, + linked: locationRows.length, + stocked: stockRows.length, + failures, + }; +} diff --git a/src/api/stock.ts b/src/api/stock.ts new file mode 100644 index 0000000..5dc0aef --- /dev/null +++ b/src/api/stock.ts @@ -0,0 +1,98 @@ +/** + * Stock requests and stock movement — the Store Admin's approval surface. + * + * Read `store-admin-backend-gap.md` §2.2 before extending this file. The + * approval workflow the spec describes does not exist in Fiesta: the request + * table has no reason, requester, approved quantity, approver or remarks, and + * `UpdateStockRequest(requestID, status)` takes a bare string. Exactly one + * value does anything — "Received" — and it posts a stock movement for the FULL + * requested quantity, guarded against double-receiving. + * + * So "approve for a different quantity" is not implemented here because it + * cannot be implemented here. It is a backend change, not a frontend one. + */ + +import { api, WEB } from './client'; +import type { StockRequest, StockStatementRow } from './types'; + +/** + * The status values the backend actually distinguishes. + * + * `Received` is the only one with behaviour. The others are stored verbatim and + * read back, which is enough to drive a queue but is not a state machine — the + * backend will accept any string at all. + */ +export const STOCK_REQUEST_STATUS = { + pending: 'Pending', + received: 'Received', + rejected: 'Rejected', +} as const; + +export type StockRequestStatus = (typeof STOCK_REQUEST_STATUS)[keyof typeof STOCK_REQUEST_STATUS]; + +export interface StockRequestQuery { + tenantid: number; + /** Omit for every branch. */ + locationid?: number; + status?: string; + date?: string; + pageno?: number; + pagesize?: number; +} + +export const stockApi = { + requests: (query: StockRequestQuery) => + api.get(`${WEB}/products/getstockrequests`, { + tenantid: query.tenantid, + locationid: query.locationid, + status: query.status, + date: query.date, + pageno: query.pageno ?? 0, + pagesize: query.pagesize ?? 50, + }), + + /** + * Approve a request — which means marking it Received. + * + * This adds `request.qty` to the branch's stock. There is no way to approve a + * different amount: the service reads the quantity off the request row, not + * off this call. + */ + approve: (requestid: number) => + api.put(`${WEB}/products/updatestockrequest`, { + requestid, + status: STOCK_REQUEST_STATUS.received, + }), + + /** Reject — a status write and nothing else. No stock moves, no reason stored. */ + reject: (requestid: number) => + api.put(`${WEB}/products/updatestockrequest`, { + requestid, + status: STOCK_REQUEST_STATUS.rejected, + }), + + /** + * Stock movement for one branch. + * + * Opening, credit, debit, closing — and that is the whole vocabulary. + * `productstocks.stocktype` is `in`/`out`, so a sale, a transfer and a + * correction are indistinguishable once written. The spec's seven movement + * types (§3.8) cannot be sourced; see gap §2.5. + */ + statement: (params: { + tenantid: number; + locationid: number; + subcategoryid?: number; + keyword?: string; + pageno?: number; + pagesize?: number; + }) => + api.get(`${WEB}/products/getstockstatement`, { + tenantid: params.tenantid, + locationid: params.locationid, + subcategoryid: params.subcategoryid, + keyword: params.keyword, + pageno: params.pageno ?? 0, + pagesize: params.pagesize ?? 100, + }), +}; diff --git a/src/api/tenants.ts b/src/api/tenants.ts new file mode 100644 index 0000000..095e132 --- /dev/null +++ b/src/api/tenants.ts @@ -0,0 +1,71 @@ +/** Tenant and branch endpoints — the Nearle Admin's provisioning surface. */ + +import { api, WEB } from './client'; +import type { TenantInfo, TenantLocation } from './types'; + +/** Everything the tenant-onboarding form collects. */ +export interface CreateTenantRequest { + tenantname: string; + companyname: string; + primarycontact: string; + primaryemail: string; + locationname: string; + categoryid: number; + subcategoryid?: number; + address: string; + suburb?: string; + city: string; + state: string; + postcode: string; + latitude?: string; + longitude?: string; + moduleid?: number; + status?: string; +} + +/** Everything the branch-onboarding form collects. */ +export interface CreateBranchRequest { + tenantid: number; + locationname: string; + email?: string; + contactno?: string; + address: string; + suburb?: string; + city: string; + state: string; + postcode: string; + latitude?: string; + longitude?: string; + opentime?: string; + closetime?: string; + deliveryradius?: number; + deliverymins?: number; + status?: string; +} + +export const tenantsApi = { + /** + * Every tenant on the platform. Deliberately unscoped — this is the + * Nearle Admin's list, and the backend treats it as the platform-operator + * endpoint rather than a tenant-scoped one. + */ + listAll: () => api.get(`${WEB}/tenants/getalltenants`), + + /** Branches under one tenant. `tenantid` is required — omit it and it 400s. */ + locations: (tenantid: number) => + api.get(`${WEB}/tenants/gettenantlocations`, { tenantid }), + + search: (keyword: string) => + api.get(`${WEB}/tenants/searchbykeyword`, { keyword }), + + /** Provisions the enterprise and spawns its primary Administrator account. */ + createTenant: (body: CreateTenantRequest) => + api.post(`${WEB}/tenants/createtenantlocation`, body), + + /** Commissions a branch and spawns a placeholder branch-manager account. */ + createBranch: (body: CreateBranchRequest) => + api.post(`${WEB}/tenants/createlocation`, body), + + updateBranch: (body: Partial & { locationid: number }) => + api.put(`${WEB}/tenants/updatelocation`, body), +}; diff --git a/src/api/types.ts b/src/api/types.ts new file mode 100644 index 0000000..7399c66 --- /dev/null +++ b/src/api/types.ts @@ -0,0 +1,697 @@ +/** + * Types for the Fiesta REST API. + * + * Hand-written from the Go structs in `backend_fiesta/models` — there is no + * OpenAPI document to generate from. Field names mirror the `json:` tags + * EXACTLY, including the ones that are misspelled on the wire + * (`applolcationid`, `Subcategoryname`, `Catlougeid`, `Accountname`). Fixing + * them here would only mean the client silently reads undefined. + */ + +/* ──────────────────────────────────────────────────────────────────────────── + Envelope + ──────────────────────────────────────────────────────────────────────────── */ + +/** + * Every Fiesta handler answers in this shape. `details` is the payload and its + * type varies per endpoint — sometimes an object, sometimes an array, and on + * the create endpoints an echo of what was sent. + * + * `status` is the success flag and `code` repeats the HTTP status in the body. + * Both are checked: several handlers return HTTP 200 with `status: false`. + */ +export interface FiestaEnvelope { + status: boolean; + code: number; + message?: string; + details?: T; + /** Present on the tenant login endpoints. */ + tenantform?: boolean; +} + +/* ──────────────────────────────────────────────────────────────────────────── + Users — models/users.go + ──────────────────────────────────────────────────────────────────────────── */ + +export interface FiestaUser { + userid: number; + authname: string; + configid: number; + authmode: number; + roleid: number; + firstname: string; + lastname: string; + fullname?: string; + email: string; + contactno: string; + address?: string; + suburb?: string; + city?: string; + state?: string; + postcode?: string; + shiftid?: number; + shiftname?: string; + partnerid?: number; + tenantid: number; + locationid: number; + applocationid?: number; + applocation?: string; + status: string; + /** Server-derived platform-operator flag. Never inferred client-side. */ + issuperadmin?: boolean; +} + +/* ──────────────────────────────────────────────────────────────────────────── + Tenants & locations — models/tenant.go + ──────────────────────────────────────────────────────────────────────────── */ + +export interface TenantInfo { + tenantid: number; + locationid: number; + tenantname: string; + locationname: string; + tenanttype?: string; + registrationno?: string; + companyname?: string; + primaryemail?: string; + primarycontact?: string; + locationcontact?: string; + categoryid?: number; + subcategoryid?: number; + address?: string; + suburb?: string; + city?: string; + state?: string; + postcode?: string; + latitude?: string; + longitude?: string; + tenantimage?: string; + tenantinfo?: string; + partnerid?: number; + minorder?: number; + /** Misspelled on the wire — `applolcationid`, not `applocationid`. */ + applolcationid?: number; + applocation?: string; + approved?: number; + moduleid?: number; + subcategoryname?: string; + firstname?: string; + lastname?: string; + /** Capitalised on the wire. */ + Accountname?: string; + status: string; +} + +export interface TenantLocation { + locationid: number; + tenantid: number; + applocationid?: number; + moduleid?: number; + roleid?: number; + locationname: string; + email?: string; + contactno?: string; + latitude?: string; + longitude?: string; + address?: string; + suburb?: string; + city?: string; + state?: string; + postcode?: string; + opentime?: string; + closetime?: string; + partnerid?: number; + deliveryradius?: number; + deliverymins?: number; + cancelsecs?: number; + status: string; +} + +/* ──────────────────────────────────────────────────────────────────────────── + Global catalogue — models/catalogue.go (the separate pgvector database) + ──────────────────────────────────────────────────────────────────────────── */ + +export interface CatalogueProduct { + id: number; + brand: string; + product_name: string; + title?: string; + description?: string; + category?: string; + image_id?: string; + images?: string[]; + size?: string; + variant_key?: string; + product_sku?: string; + sku_source?: string; + /** + * A DISPLAY STRING, not a number — e.g. "₹33-37". The catalogue has no exact + * price; `retailprice` always comes from the store owner at import time. + */ + price_range?: string; + providers?: string[]; + fssai_license?: string; + highlights?: string[]; + nutrients?: string[]; +} + +export interface CatalogueBrand { + brand: string; + product_count: number; +} + +/** The composite key. A bare `id` repeats across brand tables — never use it alone. */ +export interface CatalogueRef { + brand: string; + catalogueid: number; +} + +/* ──────────────────────────────────────────────────────────────────────────── + Products & stock — models/product.go + ──────────────────────────────────────────────────────────────────────────── */ + +export interface Product { + productid: number; + applocationid?: number; + productlocationid?: number; + tenantid?: number; + categoryid?: number; + categoryname?: string; + subcategoryid?: number; + /** Capitalised on the wire. */ + Subcategoryname?: string; + catalogueid?: number; + productname?: string; + productimage?: string; + /** A JSON-encoded array of URLs, held as a string. Parse before use. */ + productimages?: string; + productdesc?: string; + productsku?: string; + brandid?: number; + productbrand?: string; + productunit?: string; + unitvalue?: string; + productcost?: number; + taxamount?: number; + taxpercent?: number; + productstock?: number; + quantity?: number; + /** Effective selling price at the scoped location. Read-only, computed. */ + price?: number; + retailprice?: number; + productstatus?: string; + locationstatus?: string; +} + +export interface ProductCategory { + categoryid: number; + categoryname: string; +} + +export interface ProductSubCategory { + subcategoryid: number; + subcategoryname: string; + categoryid?: number; +} + +/** Body of POST /web/products/importcatalogueproduct — send an ARRAY of these. */ +export interface ImportCatalogueProductRequest { + tenantid: number; + locationid: number; + brand: string; + catalogueid: number; + categoryid: number; + subcategoryid: number; + quantity: number; + stocktype: string; + status: string; + retailprice: number; + productcost: number; + taxpercent: number; +} + +/** Body of POST /web/products/createproductlocation — send an ARRAY. Upserts. */ +export interface ProductLocationRequest { + tenantid: number; + locationid: number; + productid: number; + /** Misspelled on the wire — `catlougeid`. */ + catlougeid?: number; + minquantity?: number; + maxquantity?: number; + price?: number; + status?: string; +} + +/** Body of POST /web/products/createproductstock — send an ARRAY. */ +export interface ProductStockRequest { + tenantid: number; + locationid: number; + productid: number; + quantity: number; + stocktype: string; + status: string; +} + +/* ──────────────────────────────────────────────────────────────────────────── + Orders & deliveries — the summary shapes the console reads + ──────────────────────────────────────────────────────────────────────────── */ + +export interface OrderSummary { + totalorders?: number; + delivered?: number; + pending?: number; + cancelled?: number; + revenue?: number; + [key: string]: unknown; +} + +export interface LocationOrderSummary { + locationid?: number; + locationname?: string; + totalorders?: number; + delivered?: number; + cancelled?: number; + revenue?: number; + [key: string]: unknown; +} + +export interface DeliverySummary { + totaldeliveries?: number; + dispatched?: number; + delivered?: number; + [key: string]: unknown; +} + +/* ──────────────────────────────────────────────────────────────────────────── + POS — /v1/pos/* + ──────────────────────────────────────────────────────────────────────────── */ + +/** `GET /pos/sales` — verified against `models/pos.go` `PosSalesPage`. */ +export interface PosSalesPage { + total?: number; + pageno?: number; + pagesize?: number; + bills?: PosSale[]; +} + +/** + * One counter bill, from `models/posorder.go` `PosOrders`. + * + * Two identifiers, and they mean different things. `terminalorderid` is a UUID + * minted at the till, globally unique, and the only thing that safely keys a + * row — delivery is at-least-once, so the same bill legitimately arrives more + * than once. `invoicenumber` is the human-facing one and is unique only per + * terminal: a replaced till restarts its series, so duplicates across terminals + * are expected and gaps are normal. + * + * `billedat` is when the sale was rung, NOT when it reached us. A till that was + * offline all day uploads bills dated yesterday, so every daily figure must use + * this rather than `receivedat` — otherwise a sync outage looks like a bumper + * trading day the moment it clears. + */ +export interface PosSale { + posorderid?: number; + terminalorderid?: string; + invoicenumber?: string; + tenantid?: number; + locationid?: number; + terminalid?: string; + cashiername?: string; + customerid?: number; + customername?: string; + customermobile?: string; + billedat?: string; + businessdate?: string; + subtotal?: number; + discount?: number; + taxamount?: number; + roundoff?: number; + /** What the shopper actually paid — the figure every revenue report sums. */ + total?: number; + itemcount?: number; + /** The largest tender. `paymentsjson` carries the full split. */ + paymentmode?: string; + paymentsjson?: string; + /** Which upload batch carried this bill, for tracing a till's complaint. */ + batchid?: string; + receivedat?: string; +} + +/** + * One order row, from `models/order.go` `OrderInfo`. + * + * A subset — the struct has ~70 fields including pickup/drop geometry and push + * tokens that no list needs. Note that the delivery half lives here too: rider, + * KMS and every timestamp of the journey are columns on the order, which is why + * the old console served orders and deliveries from one read. + * + * `orderheaderid` is the key, NOT `orderid`. The old console's own comment + * records why: "orderid is not unique". + */ +export interface OrderRow { + orderheaderid: number; + orderid?: string; + tenantid?: number; + locationid?: number; + locationname?: string; + orderdate?: string; + deliverydate?: string; + orderstatus?: string; + deliverystatus?: string; + itemcount?: number; + /** The old console reads `quantity` first; not every writer sets itemcount. */ + quantity?: number; + /** + * The order's money, and it moves. + * + * The old console reads `ordervalue || orderamount || deliveryamt` — a + * fallback chain, not indecision. `ordervalue` is on the fuller Orders struct + * (amount + tax + charges − promo), `orderamount` is the goods alone, and + * `deliveryamt` is what a delivery job carries. Which one is populated + * depends on the endpoint the row came through, so all three are typed. + */ + ordervalue?: number; + orderamount?: number; + /** Cash to collect on delivery. Shown only when > 0. */ + collectionamt?: number; + deliverycharge?: number; + deliveryamt?: number; + paymenttype?: number; + paymentstatus?: number; + taxamount?: number; + ordernotes?: string; + /** The delivery REGION — a coarser grouping than the branch. */ + applocation?: string; + /** Set when the shop finished packing. Sits between confirm and pickup. */ + packtime?: string; + starttime?: string; + arrivaltime?: string; + /** Who placed it, and where it is going. */ + deliverycustomer?: string; + deliverycontactno?: string; + deliveryaddress?: string; + deliverysuburb?: string; + pickupcustomer?: string; + pickupcontactno?: string; + pickupaddress?: string; + pickupsuburb?: string; + /** The delivery half. Blank on an order nobody has been assigned to. */ + deliveryid?: number; + rider?: string; + ridercontactno?: string; + riderkms?: string | number; + assigntime?: string; + pickuptime?: string; + deliverytime?: string; + canceltime?: string; +} + +/** + * One till, as it last reported itself. + * + * Every value is a STRING. This is a Redis hash under a TTL, not a table: + * `services/posService.go` types it `[]map[string]string` and hands the hash + * straight back, so numbers arrive as `"14"` and booleans as `"true"`. Typing + * them as numbers here would compile and then silently produce `NaN`, so the + * shape stays honest and the parsing is explicit at the call site. + * + * A terminal that stops refreshing simply disappears from the array — there is + * no reaper job and no stale row claiming "online" after closing time. + */ +export interface PosTerminalHealth { + terminal_id?: string; + location_id?: string; + store_name?: string; + app_version?: string; + /** "online" while refreshing. The MQTT Last Will writes "offline" on power loss. */ + status?: string; + /** Queue depth. The number that matters most — see `models/poshealth.go`. */ + pending_bills?: string; + pending_registrations?: string; + oldest_pending_at?: string; + today_bills?: string; + today_amount?: string; + last_bill_at?: string; + battery_level?: string; + battery_charging?: string; + storage_free_mb?: string; + printer_reachable?: string; + drawer_status?: string; + /** When the till says it wrote this hash — the till's own clock. */ + reported_at?: string; + /** + * When the server received it — the server's clock. + * + * The two disagreeing is itself the signal: a till whose clock is wrong + * writes bills under the wrong business date. See `models/poshealth.go`. + */ + received_at?: string; + /** + * Why the till went offline, present only on an offline record. + * + * On the expired-key stub this is the ONLY field carrying information — + * `{terminal_id, location_id, status, reason}` and nothing else. That + * shape is how a vanished till is told apart from one that declared itself + * offline on its way out; see `isExpiredStub` in `posStatus.ts`. + */ + reason?: string; + [key: string]: string | undefined; +} + +/** `GET /pos/health/location` returns the tills at one outlet, newest state first. */ +export type PosLocationHealth = PosTerminalHealth[]; + +/** `GET /pos/sales/summary` — verified against `models/pos.go` `PosSalesSummary`. */ +export interface PosSalesSummary { + locationid?: number; + fromdate?: string; + todate?: string; + billcount?: number; + itemcount?: number; + grosssales?: number; + taxcollected?: number; + discountgiven?: number; + roundoff?: number; + averagebill?: number; + bypaymentmode?: { paymentmode?: string; amount?: number; billcount?: number }[]; + byday?: { day?: string; amount?: number; billcount?: number }[]; + /** + * Which tills contributed. Note the spelling. + * + * `models/posorder.go` declares `PosTerminalTotal.Terminalid` with the tag + * `json:"terminalid"` — no underscore — while the presence payload uses + * `terminal_id`. The two surfaces disagree, and reading this one with the + * presence spelling silently yields `undefined` for every row, which is + * exactly the bug that made the billed-but-absent reconciliation a no-op. + * + * `terminalid` is also `COALESCE`d to `''` in the GROUP BY, so bills with no + * code collapse into a single empty-id row — see `readTerminalId`. + */ + byterminal?: { terminalid?: string; amount?: number; billcount?: number }[]; +} + +/** + * A store's request for stock, from `models/stockrequest.go`. + * + * Note what is NOT here: reason, requestedby, approvedqty, approvedby, + * approvaldate, remarks. The spec asks for all six; the table has none of them. + * `status` is a free string whose only meaningful value is "Received", which + * posts a stock movement for the FULL `qty` and cannot be overridden. + */ +export interface StockRequest { + requestid: number; + tenantid: number; + tenantname?: string; + locationid: number; + locationname?: string; + productid: number; + productname?: string; + productimage?: string; + qty: number; + status: string; + created?: string; + updated?: string; +} + +/** From `models/product.go` `Productstockstatement` — opening/in/out/closing, nothing finer. */ +export interface StockStatementRow { + productid: number; + productname?: string; + productimage?: string; + productunit?: string; + unitvalue?: string; + retailprice?: number; + locationid?: number; + opening?: number; + credit?: number; + debit?: number; + closing?: number; +} + +/** + * One delivery job, from `models/deliveries.go` `Deliveryinfo`. + * + * A SEPARATE read from the orders list, not a projection of it. The two agree + * on the order id and diverge everywhere else: a delivery carries `ridername` + * (the orders list calls it `rider`), a planned `kms` alongside an `actualkms`, + * and `deliverycharges` — what the rider is paid — alongside `deliveryamt`, + * what the job is worth. None of that exists on the order. + * + * `orderstatus` here walks the DELIVERY ladder, not the order one: + * pending → accepted → arrived → picked → active → delivered, with skipped and + * cancelled off to the side. Colouring it with the order map would be wrong. + * + * Note `Pickupaddress` is capitalised on the wire. That is the Go tag, not a + * typo here — the old console falls back through four fields for the same + * reason, because app-created jobs leave the customer blank but fill the + * address. + */ +export interface DeliveryRow { + deliveryid: number; + orderheaderid?: number; + orderid?: string; + tenantid?: number; + locationid?: number; + locationname?: string; + locationsuburb?: string; + tenantname?: string; + tenantcity?: string; + orderstatus?: string; + deliverydate?: string; + assigntime?: string; + starttime?: string; + arrivaltime?: string; + pickuptime?: string; + deliverytime?: string; + canceltime?: string; + expecteddeliverytime?: string; + itemcount?: number; + orderamount?: number; + pickupcustomer?: string; + pickupcontactno?: string; + /** Capitalised on the wire — see the note above. */ + Pickupaddress?: string; + pickupaddress?: string; + pickuplocation?: string; + pickupsuburb?: string; + deliverycustomer?: string; + deliverycontactno?: string; + deliveryaddress?: string; + deliverylocation?: string; + deliverysuburb?: string; + /** What the rider is paid. Distinct from `deliveryamt`, the job's value. */ + deliverycharges?: number; + deliveryamt?: number; + deliverytype?: string; + notes?: string; + ordernotes?: string; + ridername?: string; + ridercontact?: string; + /** Planned distance. `actualkms`/`riderkms` is what was really ridden. */ + kms?: string; + actualkms?: string; + riderkms?: string; + transitminutes?: number; +} + + +/* ──────────────────────────────────────────────────────────────────────────── + People — back-office staff and till accounts + ──────────────────────────────────────────────────────────────────────────── */ + +/** + * One back-office person, from `GET /tenants/getstaffs` (`models.StaffInfo`). + * + * `password` is DELIBERATELY ABSENT from this type. The endpoint returns it — + * passwords are stored and compared in clear — so declaring it here is all it + * would take for a careless cell to render one. Leaving it off the type means + * the compiler refuses. + * + * `rolename` is resolved server-side and is the only trustworthy label. The + * backend's own comment: "`app_roles` holds six rows for four back-office roles + * and most accounts carry an id absent from it, so any mapping written + * client-side is wrong." + */ +export interface StaffInfo { + userid: number; + rolename?: string; + roleid?: number; + authname?: string; + firstname?: string; + lastname?: string; + fullname?: string; + email?: string; + contactno?: string; + address?: string; + suburb?: string; + city?: string; + state?: string; + postcode?: string; + tenantid?: number; + locationid?: number; + locationname?: string; + status?: string; +} + +/** + * One till account, from `GET /getposusers` (`models.PosUser`). + * + * Note the wire names are snake_case here and camel-ish everywhere else in this + * API — the POS half was written later and to its own convention. + * + * `password` is returned ONLY in the answer to a creation or a reset, never by + * a listing, which is the backend's deliberate shape: "An admin who loses it + * reissues rather than looks it up." We show it once at creation and never + * store or re-read it. `pin` is treated the same way. + */ +export interface PosUser { + user_id: number; + full_name?: string; + first_name?: string; + last_name?: string; + authname?: string; + contactno?: string; + role_id?: number; + role?: string; + has_password?: boolean; + shift_id?: number; + shift_name?: string; + shift_start?: string; + shift_end?: string; + location_id?: number; + status?: string; + /** Present only on a create/reset response. Never on a listing. */ + password?: string; + pin?: string; +} + +/** `GET /posroles` — the source of the role picker. Never hardcode 7 and 8. */ +export interface PosRole { + role_id: number; + /** The lowercase name the create endpoint expects — "supervisor", "cashier". */ + role: string; + label: string; + description?: string; +} + +/** + * A shift window, from `models.StaffShifts`. + * + * `start_time` / `end_time` are wall-clock `HH:MM` text, not instants — they + * repeat, and a date component on them invites exactly the timezone confusion + * that once filed a day of POS bills under the wrong business date. + * + * `weekdays` is a 7-character mask starting Monday: "1111100" is Mon–Fri. + * Empty means every day. + */ +export interface StaffShift { + staff_shift_id: number; + tenantid?: number; + locationid?: number; + name?: string; + start_time?: string; + end_time?: string; + weekdays?: string; + status?: string; +} diff --git a/src/auth/AuthContext.tsx b/src/auth/AuthContext.tsx new file mode 100644 index 0000000..3e6b650 --- /dev/null +++ b/src/auth/AuthContext.tsx @@ -0,0 +1,73 @@ +import { createContext, use, useCallback, useMemo, useState, type ReactNode } from 'react'; +import { Navigate, useLocation } from 'react-router-dom'; +import { HOME_ROUTE, type ConsoleRole, type SessionUser } from './roles'; +import { clear, login as loginRequest, persist, restore } from './session'; +import { disableDemo, enableDemo } from '@/demo'; + +interface AuthContextValue { + user: SessionUser | null; + signIn: (email: string, password: string) => Promise; + /** Development only — see `src/demo`. Absent from a production build. */ + signInAsDemo: (session: SessionUser) => void; + signOut: () => void; +} + +const AuthContext = createContext(null); + +export function AuthProvider({ children }: { children: ReactNode }) { + const [user, setUser] = useState(() => restore()); + + const signIn = useCallback(async (email: string, password: string) => { + const session = await loginRequest(email, password); + setUser(session); + return session; + }, []); + + /** + * Seeds a session without touching the API. Demo mode is switched on at the + * same moment, so the fixture backend and the fixture session can never be + * out of step — a demo user paired with live data would be the worst of both. + */ + const signInAsDemo = useCallback((session: SessionUser) => { + if (!import.meta.env.DEV) return; + enableDemo(); + persist(session); + setUser(session); + }, []); + + const signOut = useCallback(() => { + if (import.meta.env.DEV) disableDemo(); + clear(); + setUser(null); + }, []); + + const value = useMemo( + () => ({ user, signIn, signInAsDemo, signOut }), + [user, signIn, signInAsDemo, signOut], + ); + + return {children}; +} + +export function useAuth(): AuthContextValue { + const context = use(AuthContext); + if (!context) throw new Error('useAuth must be used inside '); + return context; +} + +/** + * Route guard. + * + * A role that reaches a workspace it does not own is redirected to its own home + * rather than shown an error — the same partition the old console enforces, and + * the same one the three logins imply. + */ +export function RequireRole({ role, children }: { role: ConsoleRole; children: ReactNode }) { + const { user } = useAuth(); + const location = useLocation(); + + if (!user) return ; + if (user.role !== role) return ; + + return <>{children}; +} diff --git a/src/auth/roles.ts b/src/auth/roles.ts new file mode 100644 index 0000000..11dcd4c --- /dev/null +++ b/src/auth/roles.ts @@ -0,0 +1,88 @@ +/** + * Role resolution. + * + * The backend issues no session token: the login endpoints look the user up and + * return the record. So "signed in" here means "we hold a verified user object", + * and the role is DERIVED from that record rather than asserted by the client. + * + * When the backend does start issuing tokens, this file and `session.ts` are + * the only two that should need to change. + */ + +import type { FiestaUser } from '@/api/types'; + +export type ConsoleRole = 'nearle-admin' | 'store-admin' | 'store-manager'; + +/** + * Roleids that reach the Store Admin workspace. + * + * 7 (Supervisor) and 8 (Cashier) must NEVER appear here: they are till roles, + * and a cashier landing in the tenant console is a privilege escalation, not a + * cosmetic bug. + */ +const STORE_ADMIN_ROLE_IDS: ReadonlySet = new Set([1, 3]); + +/** Till-only roles, listed so the exclusion is explicit rather than implied. */ +export const TILL_ROLE_IDS: ReadonlySet = new Set([7, 8]); + +export interface SessionUser { + userid: number; + role: ConsoleRole; + name: string; + email: string; + roleid: number; + tenantid: number; + locationid: number; + issuperadmin: boolean; +} + +/** + * `issuperadmin` is checked FIRST because it is server-derived. A roleid cannot + * be trusted to imply platform access, so the flag wins over the numeric split. + */ +export function resolveRole(user: Pick): ConsoleRole { + if (user.issuperadmin === true) return 'nearle-admin'; + if (STORE_ADMIN_ROLE_IDS.has(user.roleid)) return 'store-admin'; + return 'store-manager'; +} + +export function toSessionUser(user: FiestaUser): SessionUser { + const name = [user.firstname, user.lastname].filter(Boolean).join(' ').trim(); + return { + userid: user.userid, + role: resolveRole(user), + name: name || user.fullname || user.authname || user.email, + email: user.email, + roleid: user.roleid, + tenantid: user.tenantid, + locationid: user.locationid, + issuperadmin: user.issuperadmin === true, + }; +} + +/** Where each role lands when it has nowhere more specific to go. */ +/** + * Where each role lands. + * + * These MUST be paths that actually resolve. The global `*` route redirects + * here, so a HOME_ROUTE pointing at a path with no matching route sends the + * router straight back to `*`, which sends it here again: an infinite redirect + * that React Router resolves by rendering nothing at all. It fails as a blank + * page with no console error, which is the worst way for a routing bug to + * present. `/admin/dashboard` did exactly that — it was the old console's name + * for the page this one calls Console. + * + * Each workspace also carries its own catch-all in `App.tsx`, so a wrong + * sub-path is absorbed there and never reaches the global one. + */ +export const HOME_ROUTE: Record = { + 'nearle-admin': '/nearle/stores', + 'store-admin': '/admin/console', + 'store-manager': '/store/console', +}; + +export const ROLE_LABEL: Record = { + 'nearle-admin': 'Nearle Admin', + 'store-admin': 'Store Admin', + 'store-manager': 'Store Manager', +}; diff --git a/src/auth/session.ts b/src/auth/session.ts new file mode 100644 index 0000000..4c7e033 --- /dev/null +++ b/src/auth/session.ts @@ -0,0 +1,81 @@ +/** + * Sign-in and session persistence. + * + * There is no token to hold. `TenantWebLogin` returns the user record and + * nothing else, so the session IS that record. It is kept in sessionStorage + * rather than localStorage: a shared back-office machine should not stay signed + * in after the browser closes, and there is no server-side session to revoke. + */ + +import { api, WEB } from '@/api/client'; +import type { FiestaUser } from '@/api/types'; +import { toSessionUser, type SessionUser } from './roles'; + +const STORAGE_KEY = 'nearle.session.v1'; + +/** Thrown when the account exists but has never had a password set. */ +export class PasswordSetupRequiredError extends Error { + readonly userid: number; + constructor(userid: number) { + super('This account needs a password before it can sign in.'); + this.name = 'PasswordSetupRequiredError'; + this.userid = userid; + } +} + +interface LoginBody { + authname: string; + password: string; + roleid?: number; + configid?: number; +} + +/** + * Signs in against the web login endpoint. + * + * The handler answers HTTP 200 with `status: false` for a wrong password, and + * with `code: 409` plus `details.setup` when no password has been set, so the + * envelope is inspected rather than trusting the HTTP status. + */ +export async function login(email: string, password: string): Promise { + const body: LoginBody = { authname: email.trim(), password }; + + const envelope = await api.envelope( + `${WEB}/users/tenant/weblogin`, + { method: 'POST', body }, + ); + + if (envelope.code === 409 && envelope.details?.setup === true) { + throw new PasswordSetupRequiredError(envelope.details.userid ?? 0); + } + + if (envelope.status !== true || !envelope.details) { + throw new Error(envelope.message ?? 'Sign-in failed'); + } + + const session = toSessionUser(envelope.details); + persist(session); + return session; +} + +export function persist(session: SessionUser): void { + sessionStorage.setItem(STORAGE_KEY, JSON.stringify(session)); +} + +export function restore(): SessionUser | null { + const raw = sessionStorage.getItem(STORAGE_KEY); + if (!raw) return null; + try { + const parsed = JSON.parse(raw) as SessionUser; + // A stored blob is only as trustworthy as the tab it came from; a shape + // check keeps a corrupted value from crashing the shell on boot. + if (typeof parsed?.userid !== 'number' || typeof parsed?.role !== 'string') return null; + return parsed; + } catch { + return null; + } +} + +export function clear(): void { + sessionStorage.removeItem(STORAGE_KEY); +} diff --git a/src/components/ConsoleSearchBar.tsx b/src/components/ConsoleSearchBar.tsx new file mode 100644 index 0000000..f63a939 --- /dev/null +++ b/src/components/ConsoleSearchBar.tsx @@ -0,0 +1,67 @@ +import { useState } from 'react'; +import { Search } from 'lucide-react'; + +export interface ConsoleSearchBarProps { + value: string; + onChange: (value: string) => void; + placeholder?: string; + className?: string; + style?: React.CSSProperties; +} + +export function ConsoleSearchBar({ + value, + onChange, + placeholder = 'Search', + className, + style, +}: ConsoleSearchBarProps) { + const [isFocused, setIsFocused] = useState(false); + + return ( +
event.preventDefault()} + style={{ + position: 'relative', + display: 'flex', + alignItems: 'center', + width: '100%', + ...style, + }} + > + + onChange(event.target.value)} + onFocus={() => setIsFocused(true)} + onBlur={() => setIsFocused(false)} + placeholder={placeholder} + aria-label={placeholder} + style={{ + height: 32, + width: '100%', + borderRadius: 12, + border: `1px solid ${isFocused ? 'var(--color-brand)' : 'var(--color-line)'}`, + background: isFocused ? 'var(--color-surface)' : 'var(--color-surface-subtle)', + paddingLeft: 32, + paddingRight: 12, // Removed space for kbd since it's not present here + fontSize: 13, + color: 'var(--color-ink-1)', + outline: 'none', + transition: 'border-color .2s, background .2s', + boxShadow: 'none', + }} + /> + + ); +} diff --git a/src/components/DataState.tsx b/src/components/DataState.tsx new file mode 100644 index 0000000..3cd7dd9 --- /dev/null +++ b/src/components/DataState.tsx @@ -0,0 +1,65 @@ +import type { ReactNode } from 'react'; +import { EmptyState } from '@astryxdesign/core/EmptyState'; +import { Spinner } from '@astryxdesign/core/Spinner'; +import { HStack } from '@astryxdesign/core/HStack'; +import { AlertTriangle, Inbox } from 'lucide-react'; +import { errorMessage } from '@/api/client'; + +export interface DataStateProps { + isLoading: boolean; + error: unknown; + isEmpty: boolean; + /** What is missing, in the user's words — "No tenants yet". */ + emptyTitle: string; + emptyDescription?: string; + emptyAction?: ReactNode; + children: ReactNode; +} + +/** + * The four states every list has, in one place. + * + * Written as a wrapper rather than repeated per screen because the failure mode + * it prevents is a real one: pages that handle loading and success, and render + * a blank rectangle for empty and error. + */ +export function DataState({ + isLoading, + error, + isEmpty, + emptyTitle, + emptyDescription, + emptyAction, + children, +}: DataStateProps) { + if (isLoading) { + return ( + + + + ); + } + + if (error) { + return ( + } + /> + ); + } + + if (isEmpty) { + return ( + } + actions={emptyAction} + /> + ); + } + + return <>{children}; +} diff --git a/src/components/Freshness.tsx b/src/components/Freshness.tsx new file mode 100644 index 0000000..f6c7ef6 --- /dev/null +++ b/src/components/Freshness.tsx @@ -0,0 +1,53 @@ +import { Text } from '@astryxdesign/core/Text'; +import { HStack } from '@astryxdesign/core/HStack'; +import { RefreshCw } from 'lucide-react'; + +export interface FreshnessProps { + /** `dataUpdatedAt` from the query that fed the figure. */ + updatedAt: number | undefined; + isFetching?: boolean; + /** Bills sitting on a till that has not synced. Hidden when zero. */ + unsynced?: number; +} + +function clock(ms: number): string { + return new Date(ms).toLocaleTimeString([], { + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + }); +} + +/** + * How old the number above this line is. + * + * Online orders land as they are placed; counter sales arrive on the console's + * 30-second refetch, and whatever is still sitting on an offline till has not + * arrived at all. So any figure blending the two is eventually consistent, and + * printing one confident number without saying when it was true is how a screen + * loses the room's trust. + */ +export function Freshness({ updatedAt, isFetching, unsynced }: FreshnessProps) { + return ( + + + + + {updatedAt ? `as of ${clock(updatedAt)}` : 'not loaded yet'} + + + + {typeof unsynced === 'number' && unsynced > 0 ? ( + + · {unsynced} bill{unsynced === 1 ? '' : 's'} not yet synced + + ) : null} + + ); +} diff --git a/src/components/KpiCard.tsx b/src/components/KpiCard.tsx new file mode 100644 index 0000000..f3b29c5 --- /dev/null +++ b/src/components/KpiCard.tsx @@ -0,0 +1,86 @@ +import type { ReactNode } from 'react'; +import { Card } from '@astryxdesign/core/Card'; +import { HStack } from '@astryxdesign/core/HStack'; +import { Text } from '@astryxdesign/core/Text'; +import { VStack } from '@astryxdesign/core/VStack'; + +export type KpiTone = 'accent' | 'success' | 'warning' | 'error' | 'neutral'; + +const TONE_COLOR: Record = { + accent: 'var(--color-brand)', + success: 'var(--color-brand)', + warning: 'var(--color-brand)', + error: 'var(--color-brand)', + neutral: 'var(--color-brand)', +}; + +export interface KpiCardProps { + /** Small-caps label. Say what it is, not what it means. */ + label: string; + value: string; + /** + * The sub-pill. This is where the number gets its meaning — "1 of 84 orders" + * says something "1.2%" does not. + */ + note?: string; + tone?: KpiTone; + icon?: ReactNode; +} + +/** + * A KPI tile. + * + * Label and icon, a large tabular value, a muted sub-pill carrying the + * interpretation, and a 2px underline in the tile's tone. Sized to sit 4–6 + * across rather than 4 — a console is glanced at all day, not read once. + */ +export function KpiCard({ label, value, note, tone = 'neutral', icon }: KpiCardProps) { + const color = TONE_COLOR[tone]; + + return ( + + + + + {label} + + {icon ? {icon} : null} + + + + + {value} + + + + {note ? ( + + {note} + + ) : null} + + + + ); +} diff --git a/src/components/PageBody.tsx b/src/components/PageBody.tsx new file mode 100644 index 0000000..f5f65e5 --- /dev/null +++ b/src/components/PageBody.tsx @@ -0,0 +1,34 @@ +import type { ReactNode } from 'react'; +import { VStack } from '@astryxdesign/core/VStack'; + +/** + * Two page measures, and the rule for choosing between them. + * + * KROW declares `--container-max: 80rem` for a page and a wider shell measure + * for the app frame, and the distinction matters: a form stretched across + * 1600px puts five inputs in a row and makes the eye travel further than the + * reading task deserves, while a table at 80rem wastes half the screen. + * + * 'reading' — forms, single flows, anything read top to bottom + * 'data' — tables, dashboards, catalogue grids + */ +export type PageMeasure = 'reading' | 'data'; + +const MAX_WIDTH: Record = { + reading: 'var(--container-page, 80rem)', + data: 'var(--container-admin, 102rem)', +}; + +export function PageBody({ + measure = 'data', + children, +}: { + measure?: PageMeasure; + children: ReactNode; +}) { + return ( + + {children} + + ); +} diff --git a/src/components/PageHeader.tsx b/src/components/PageHeader.tsx new file mode 100644 index 0000000..86332c8 --- /dev/null +++ b/src/components/PageHeader.tsx @@ -0,0 +1,109 @@ +import type { ReactNode } from 'react'; + +export interface PageHeaderProps { + title: string; + /** A real count beside the name — "8 on record". Tabular, meta-coloured. */ + count?: string; + /** One line of context. */ + description?: string; + /** Right-aligned actions, wrapping. */ + actions?: ReactNode; + /** An optional tabs row directly under the header rule. */ + tabs?: ReactNode; + isLive?: boolean; +} + +/** + * The page frame header, built to KROW's `AdminPage` spec. + * + * Title + count + a live pill on one line, a one-line subtitle beneath, actions + * right-aligned and wrapping, and — the part that makes pages feel like one + * product — a hairline rule beneath the whole thing at half-opacity, with 16px + * of padding above it. Everything the page stacks below sits on a 24px rhythm. + */ +export function PageHeader({ title, count, description, actions, tabs, isLive }: PageHeaderProps) { + return ( + <> +
+
+
+

+ {title} +

+ + {count ? ( + + {count} + + ) : null} + + {isLive ? ( + + + Live + + ) : null} +
+ + {description ? ( +

+ {description} +

+ ) : null} +
+ + {actions ? ( +
+ {actions} +
+ ) : null} +
+ {tabs} + + ); +} diff --git a/src/components/SectionHeader.tsx b/src/components/SectionHeader.tsx new file mode 100644 index 0000000..b44ea05 --- /dev/null +++ b/src/components/SectionHeader.tsx @@ -0,0 +1,34 @@ +import type { ReactNode } from 'react'; +import { Heading } from '@astryxdesign/core/Heading'; +import { Text } from '@astryxdesign/core/Text'; +import { HStack } from '@astryxdesign/core/HStack'; + +export interface SectionHeaderProps { + title: string; + /** + * Sits inline beside the title, not beneath it. + * + * Required rather than optional on purpose: a bare heading tells the reader + * what a block is called but not what it is showing them, and every section + * in this console has something worth saying there. + */ + note: string; + /** A control or a "View all" link, right-aligned. */ + action?: ReactNode; +} + +export function SectionHeader({ title, note, action }: SectionHeaderProps) { + return ( + + + + {title} + + + {note} + + + {action} + + ); +} diff --git a/src/components/shell/AppShell.tsx b/src/components/shell/AppShell.tsx new file mode 100644 index 0000000..ae83b74 --- /dev/null +++ b/src/components/shell/AppShell.tsx @@ -0,0 +1,787 @@ +import { useEffect, useRef, useState, type ReactNode } from 'react'; +import { Link, NavLink, Outlet, useLocation } from 'react-router-dom'; +import { Bell, ChevronDown, ChevronLeft, LogOut, Menu, Search, Sparkles, X } from 'lucide-react'; +import { useIsMobile } from '@/hooks/useIsMobile'; +import { useAuth } from '@/auth/AuthContext'; +import { ROLE_LABEL } from '@/auth/roles'; +import { AssistantPanel } from './AssistantPanel'; + +export interface NavEntry { + to: string; + label: string; +} + +/** One entry in the account menu's MANAGE group. */ +export interface MenuEntry { + to: string; + label: string; + icon: ReactNode; + /** One line under the label, for an entry whose scope is not obvious. */ + note?: string; +} + +export interface AppShellProps { + /** Destinations for the header tabs and the mobile sheet. */ + nav: readonly NavEntry[]; + /** Where the logo links to — the workspace's own landing page. */ + home: string; + /** Accessible name for the tab list, e.g. "Store Admin". */ + navLabel: string; + /** + * An optional control between the logo and the tabs — the Store Admin's + * branch selector lives here. It sits inside the header rather than on each + * page because it scopes every page, and a control that moves between pages + * reads as a different control each time. + */ + scopeControl?: ReactNode; + /** + * Setup destinations, listed inside the account menu rather than in the nav. + * + * This follows the old console's judgement, and its reasoning holds: setup is + * configuration, not a place anyone works from day to day, and a nav slot + * spent on it is a slot taken from a section that IS worked from. The account + * menu is where "my workspace's setup" belongs, beside the account and + * sign-out. + * + * Listed as individual destinations rather than one "Settings" entry for the + * old console's other reason: a single entry lands everyone on the first + * section and makes them click again, and the sections are what people come + * here for. + */ + manageItems?: readonly MenuEntry[]; +} + +/** + * The application chrome, shared by every workspace. + * + * Built to KROW's `AdminLayout` spec. Deliberately NOT a coloured slab: the + * header is a 56px white-at-85% bar with a backdrop blur and a hairline bottom + * border, carrying text tabs whose active state is accent-coloured text plus a + * 2px accent bar sitting on that border. The repo's own reasoning for text tabs + * over pills: several destinations means several competing shapes if each one + * is a pill, and a console header should recede rather than compete with the + * page. + * + * One component rather than one per workspace. The workspaces differ in exactly + * three things — their destinations, their home, and whether they have a scope + * control — so those are props. Copying four hundred lines of chrome per role + * is how two headers drift apart and stop looking like one product. + */ +export function AppShell({ nav, home, navLabel, scopeControl, manageItems }: AppShellProps) { + const { user, signOut } = useAuth(); + const { pathname } = useLocation(); + + const [query, setQuery] = useState(''); + const [isSearchFocused, setIsSearchFocused] = useState(false); + const [isMenuOpen, setIsMenuOpen] = useState(false); + const [isAssistantOpen, setIsAssistantOpen] = useState(true); + const [isNavOpen, setIsNavOpen] = useState(false); + const isMobile = useIsMobile(); + const searchRef = useRef(null); + const menuRef = useRef(null); + + // ⌘K / Ctrl-K focuses search, the same global shortcut the reference uses. + useEffect(() => { + function onKeyDown(event: KeyboardEvent) { + if (event.key.toLowerCase() === 'k' && (event.metaKey || event.ctrlKey)) { + event.preventDefault(); + searchRef.current?.focus(); + } + } + window.addEventListener('keydown', onKeyDown); + return () => window.removeEventListener('keydown', onKeyDown); + }, []); + + const isSearchWide = isSearchFocused || query !== ''; + + // Escape closes the account menu. A menu that can only be dismissed by + // finding the trigger again is a trap for anyone on a keyboard, and this one + // sits over the page rather than beside it. + useEffect(() => { + if (!isMenuOpen) return; + function onKeyDown(event: KeyboardEvent) { + if (event.key === 'Escape') setIsMenuOpen(false); + } + function onMouseDown(event: MouseEvent) { + if (menuRef.current && !menuRef.current.contains(event.target as Node)) { + setIsMenuOpen(false); + } + } + window.addEventListener('keydown', onKeyDown); + document.addEventListener('mousedown', onMouseDown); + return () => { + window.removeEventListener('keydown', onKeyDown); + document.removeEventListener('mousedown', onMouseDown); + }; + }, [isMenuOpen]); + + return ( +
+ {/* Two elements, on purpose. The bar is full-bleed — the glass, the blur + and the hairline run the whole width of the screen, because a header + that stops short of the edge reads as a floating card, not as chrome. + The ROW inside it is capped by `.app-gutter`, so the logo and nav sit + on exactly the same left edge as the page title below them at every + width, including a 2560px monitor where the body is centred. */} +
+
+ + {/* 24px tall, width auto — the reference pins the logo's height and + lets the wordmark set its own width. */} + Nearle + + + {/* Below md the scope control moves into the navigation sheet. On a + 390px phone the logo, the selector and the right-hand cluster add + up to 503px and push the header 113px past the viewport, so the + page scrolls sideways. The sheet gives the branch names room to be + read in full, and every page states its own scope in the header + line beneath, so the phone never leaves the operator guessing. */} + {scopeControl ? ( +
+ {scopeControl} +
+ ) : null} + + {/* Below lg the tabs are gone, so a flexible spacer keeps the right + cluster against the edge instead of bunched beside the logo. */} +
+ + {/* Text tabs. Active = accent text + a 2px accent bar on the header rule. */} + + +
+ {/* Search: 176px at rest, 256px once focused or filled. */} +
event.preventDefault()} + style={{ + position: 'relative', + alignItems: 'center', + width: isSearchWide ? 256 : 176, + transition: 'width .2s cubic-bezier(.16,1,.3,1)', + }} + > + + setQuery(event.target.value)} + onFocus={() => setIsSearchFocused(true)} + onBlur={() => setIsSearchFocused(false)} + placeholder="Search" + aria-label="Search the console" + style={{ + height: 32, + width: '100%', + borderRadius: 12, + border: `1px solid ${isSearchFocused ? 'var(--color-brand)' : 'var(--color-line)'}`, + background: isSearchFocused + ? 'var(--color-surface)' + : 'var(--color-surface-subtle)', + paddingLeft: 32, + paddingRight: 40, + fontSize: 13, + color: 'var(--color-ink-1)', + outline: 'none', + transition: 'border-color .2s, background .2s', + boxShadow: isSearchFocused ? '0 0 0 2px rgba(102,37,130,.15)' : 'none', + }} + /> + + ⌘K + + + + + + + + {/* Assistant button moved to floating pill */} + + {/* Avatar trigger + chevron, with the account menu below it. */} +
+ + + {isMenuOpen ? ( +
+
+
+ {user?.name} +
+
{user?.email}
+
+ + {user ? ROLE_LABEL[user.role] : ''} + +
+
+ {manageItems && manageItems.length > 0 ? ( + <> + +

+ Manage +

+ {manageItems.map((entry) => ( + setIsMenuOpen(false)} + /> + ))} + + ) : null} + + + + + {/* When this code was built. If it does not match what you + were told was delivered, you are looking at an old copy — + which is not something any screen otherwise reveals. */} +

+ Build {__BUILD_STAMP__} +

+
+ ) : null} +
+ + {/* Wrapped rather than styled inline: an inline `display` would win + over the media query that hides this above the tabs breakpoint. */} + + setIsNavOpen(true)}> + + + +
+
+
+ + {/* Body: a column on a phone so the assistant stacks under the page, a + row from md where it becomes a side column. */} +
+
+ +
+ + {isAssistantOpen || isMobile ? ( + setIsAssistantOpen(false)} isStacked={isMobile} /> + ) : null} +
+ + {/* Floating Assistant Button */} + {!isMobile && !isAssistantOpen ? ( + + ) : null} + + {isNavOpen ? ( + setIsNavOpen(false)} + pathname={pathname} + /> + ) : null} +
+ ); +} + +/** + * The navigation sheet, below lg. + * + * 288px from the right, opaque rather than glass — a translucent sheet over a + * page of tables is unreadable — with the active item carried by a tinted fill + * instead of the 2px underline, which has nothing to sit on here. + */ +function MobileNav({ + nav, + scopeControl, + onClose, + pathname, +}: { + nav: readonly NavEntry[]; + scopeControl?: ReactNode; + onClose: () => void; + pathname: string; +}) { + return ( +
+
+ ); +} + +/** + * One destination in the account menu. + * + * A `NavLink` rather than a button with `navigate()`: middle-click, ⌘-click and + * "open in new tab" all work on a real anchor and none of them work on a + * button, and a setup screen is exactly the kind of thing someone parks in a + * second tab. + */ +function MenuLink({ entry, onNavigate }: { entry: MenuEntry; onNavigate: () => void }) { + const [isHovered, setIsHovered] = useState(false); + return ( + setIsHovered(true)} + onMouseLeave={() => setIsHovered(false)} + style={{ + display: 'flex', + alignItems: 'center', + gap: 10, + padding: '8px 12px', + borderRadius: 12, + textDecoration: 'none', + background: isHovered ? 'var(--color-surface-sunken)' : 'transparent', + color: 'var(--color-ink-1)', + transition: 'background .15s', + }} + > + + {entry.icon} + + + {entry.label} + {entry.note ? ( + + {entry.note} + + ) : null} + + + ); +} + +const Rule = () => ( +
+); + +function initials(name: string): string { + const parts = name.trim().split(/\s+/).filter(Boolean); + if (parts.length === 0) return '—'; + const first = parts[0]?.[0] ?? ''; + const last = parts.length > 1 ? (parts[parts.length - 1]?.[0] ?? '') : ''; + return (first + last).toUpperCase(); +} + +/** + * A 32px icon-only control. + * + * `label` is required, not optional — an icon-only control with no accessible + * name is a bug, so the API makes it impossible to omit. + */ +function IconButton({ + label, + children, + onClick, + hasUnread, +}: { + label: string; + children: ReactNode; + onClick?: () => void; + hasUnread?: boolean; +}) { + const [isHovered, setIsHovered] = useState(false); + return ( + + ); +} diff --git a/src/components/shell/AssistantPanel.tsx b/src/components/shell/AssistantPanel.tsx new file mode 100644 index 0000000..20decc1 --- /dev/null +++ b/src/components/shell/AssistantPanel.tsx @@ -0,0 +1,414 @@ +import { useRef, useState } from 'react'; +import { useLocation } from 'react-router-dom'; +import { ArrowUp, History, Maximize2, Minimize2, PanelRightClose } from 'lucide-react'; + +/** Per-route context, so the panel knows which page it is sitting beside. */ +const CONTEXT: Record< + string, + { page: string; title: string; greeting: string; reading: string; prompts: string[] } +> = { + '/nearle/stores': { + page: 'Stores', + title: 'Good afternoon', + greeting: 'Every tenant on the platform, and which of them have branches sitting idle.', + reading: 'Reading the tenant directory — branch counts, status and per-tenant performance.', + prompts: [ + 'Which tenants have no branches?', + 'Who onboarded most recently?', + 'What needs attention?', + 'Summarise platform growth', + ], + }, + '/nearle/onboard/tenant': { + page: 'Onboard tenant', + title: 'Provisioning a tenant', + greeting: 'Registering the enterprise also creates its primary Administrator account.', + reading: 'Reading the onboarding form — required fields, category and head-office address.', + prompts: ['What does provisioning create?', 'Which fields are required?', 'What happens next?'], + }, + '/nearle/catalogue': { + page: 'Global catalogue', + title: 'Stocking a store', + greeting: 'The catalogue carries a price range, not a price — the store sets the real one.', + reading: 'Reading the global catalogue and what this tenant has already imported.', + prompts: [ + 'Which products are already imported?', + 'Catalogue or spreadsheet?', + 'What does re-importing do?', + 'Which columns does the sheet need?', + ], + }, + + /* ── Store Admin ──────────────────────────────────────────────────────── */ + + '/admin/console': { + page: 'Console', + title: 'Across your branches', + greeting: 'App sales, counter sales and imported bills are counted separately — they live in different ledgers.', + reading: 'Reading every branch — revenue by channel, stock health, till status and what is waiting on you.', + prompts: [ + 'Which branch is underperforming?', + 'Any tills not syncing?', + 'What needs my approval?', + 'Where is stock running out?', + ], + }, + '/admin/sales': { + page: 'Sales', + title: 'Orders and deliveries', + greeting: 'An app order and a counter bill are both sales, but only one of them has a delivery.', + reading: 'Reading orders, counter bills and delivery progress across your branches.', + prompts: [ + 'Why is the cancel rate high?', + 'Which orders are stuck?', + 'Compare online and counter sales', + 'What is out for delivery?', + ], + }, + '/admin/inventory': { + page: 'Inventory', + title: 'Catalogue and stock', + greeting: 'A product in the store catalogue does not mean stock on the shelf — that takes an approved request.', + reading: 'Reading your catalogue, per-branch stock levels and the stock requests waiting on you.', + prompts: [ + 'What is waiting for approval?', + 'Which products are unpublished?', + 'What is low or out of stock?', + 'How do I upload a product sheet?', + ], + }, + '/admin/users': { + page: 'Users & access', + title: 'Who can sign in', + greeting: 'A till account and a console login are two different things — a cashier has no console access at all.', + reading: 'Reading your back-office directory and the till accounts at each branch.', + prompts: [ + 'What is the difference between the two?', + 'How do I add a cashier?', + 'Why can I not delete someone?', + 'Who is inactive?', + ], + }, + '/admin/reports': { + page: 'Reports', + title: 'Revenue, sales and stock', + greeting: 'Fast and slow movers are the two lists that change what you order next.', + reading: 'Reading revenue by channel and branch, product performance and stock movement.', + prompts: [ + 'Which products are slow moving?', + 'Compare my three branches', + 'Online versus counter revenue', + 'What is my inventory worth?', + ], + }, +}; + +const FALLBACK = { + page: 'Console', + title: 'Good afternoon', + greeting: 'Ask about anything on this page.', + reading: 'Reading this page.', + prompts: ['What needs attention?', 'Summarise this page'], +}; + +/** + * Nearle Buddy, as a layout column. + * + * Not an overlay and not a floating bubble: the panel is part of the page on + * every supported route, so the page reflows beside it rather than being + * covered. It opens by stating the most useful thing about the page it is next + * to, says what it read, and offers prompts scoped to that page. + * + * 380px by default, sticky under the 56px header, and its own height is the + * viewport minus 104px so it never pushes the page taller than the screen. + */ +export function AssistantPanel({ + onClose, + isStacked = false, +}: { + onClose: () => void; + /** Below md the panel sits under the page rather than beside it. */ + isStacked?: boolean; +}) { + const { pathname } = useLocation(); + const [draft, setDraft] = useState(''); + const [isFocused, setIsFocused] = useState(false); + const [isExpanded, setIsExpanded] = useState(false); + const textareaRef = useRef(null); + + const key = Object.keys(CONTEXT).find((entry) => pathname.startsWith(entry)); + const context = (key ? CONTEXT[key] : undefined) ?? FALLBACK; + + const expandedStyle: React.CSSProperties = isExpanded + ? { + width: '50vw', + maxWidth: 'none', + } + : {}; + + return ( +