commit a7f9c66514bd20a2f025d823e2644d93e9119b31 Author: safavimohadeseh78 Date: Wed Sep 9 17:08:45 2026 +0330 Initialize the user app and load home banners from the API. Co-authored-by: Cursor diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..3b510aa --- /dev/null +++ b/.editorconfig @@ -0,0 +1,8 @@ +[*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue,css,scss,sass,less,styl}] +charset = utf-8 +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true +end_of_line = lf +max_line_length = 100 diff --git a/.env.development b/.env.development new file mode 100644 index 0000000..c1df275 --- /dev/null +++ b/.env.development @@ -0,0 +1,2 @@ +VITE_API_BASE_URL=https://parche.montest.ir/server/api +VITE_USE_MOCK_AUTH=false diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..29b0b73 --- /dev/null +++ b/.env.example @@ -0,0 +1,10 @@ +VITE_API_BASE_URL=https://parche.montest.ir/server/api + +# سرور محلی (بعد از migrate + seed): +# VITE_API_BASE_URL=http://127.0.0.1:3000/api +# +# یا با پروکسی Vite: +# VITE_API_BASE_URL=/server/api + +VITE_USE_MOCK_AUTH=false + diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6313b56 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..79977f6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,44 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +.DS_Store +dist +dist-ssr +coverage +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +*.tsbuildinfo + +.eslintcache + +# Cypress +/cypress/videos/ +/cypress/screenshots/ + +# Vitest +__screenshots__/ + +# Vite +*.timestamp-*-*.mjs + +# env +.env +.env.local +.env.*.local diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 0000000..d5648b9 --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,10 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "plugins": ["eslint", "typescript", "unicorn", "oxc", "vue"], + "env": { + "browser": true + }, + "categories": { + "correctness": "error" + } +} diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..29a2402 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/prettierrc", + "semi": false, + "singleQuote": true, + "printWidth": 100 +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..3f84126 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "Vue.volar", + "dbaeumer.vscode-eslint", + "EditorConfig.EditorConfig", + "oxc.oxc-vscode", + "esbenp.prettier-vscode" + ] +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..c2c0f87 --- /dev/null +++ b/README.md @@ -0,0 +1,48 @@ +# fabric-trade + +This template should help get you started developing with Vue 3 in Vite. + +## Recommended IDE Setup + +[VS Code](https://code.visualstudio.com/) + [Vue (Official)](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur). + +## Recommended Browser Setup + +- Chromium-based browsers (Chrome, Edge, Brave, etc.): + - [Vue.js devtools](https://chromewebstore.google.com/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd) + - [Turn on Custom Object Formatter in Chrome DevTools](http://bit.ly/object-formatters) +- Firefox: + - [Vue.js devtools](https://addons.mozilla.org/en-US/firefox/addon/vue-js-devtools/) + - [Turn on Custom Object Formatter in Firefox DevTools](https://fxdx.dev/firefox-devtools-custom-object-formatters/) + +## Type Support for `.vue` Imports in TS + +TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types. + +## Customize configuration + +See [Vite Configuration Reference](https://vite.dev/config/). + +## Project Setup + +```sh +npm install +``` + +### Compile and Hot-Reload for Development + +```sh +npm run dev +``` + +### Type-Check, Compile and Minify for Production + +```sh +npm run build +``` + +### Lint with [ESLint](https://eslint.org/) + +```sh +npm run lint +``` diff --git a/env.d.ts b/env.d.ts new file mode 100644 index 0000000..a1b07af --- /dev/null +++ b/env.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/eslint.config.ts b/eslint.config.ts new file mode 100644 index 0000000..89fdc89 --- /dev/null +++ b/eslint.config.ts @@ -0,0 +1,26 @@ +import { globalIgnores } from 'eslint/config' +import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript' +import pluginVue from 'eslint-plugin-vue' +import pluginOxlint from 'eslint-plugin-oxlint' +import skipFormatting from 'eslint-config-prettier/flat' + +// To allow more languages other than `ts` in `.vue` files, uncomment the following lines: +// import { configureVueProject } from '@vue/eslint-config-typescript' +// configureVueProject({ scriptLangs: ['ts', 'tsx'] }) +// More info at https://github.com/vuejs/eslint-config-typescript/#advanced-setup + +export default defineConfigWithVueTs( + { + name: 'app/files-to-lint', + files: ['**/*.{vue,ts,mts,tsx}'], + }, + + globalIgnores(['**/dist/**', '**/dist-ssr/**', '**/coverage/**']), + + ...pluginVue.configs['flat/essential'], + vueTsConfigs.recommended, + + ...pluginOxlint.buildFromOxlintConfigFile('.oxlintrc.json'), + + skipFormatting, +) diff --git a/index.html b/index.html new file mode 100644 index 0000000..f7b2849 --- /dev/null +++ b/index.html @@ -0,0 +1,14 @@ + + + + + + + + سامانه سفارش عمده پارچه + + +
+ + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..6367dd5 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6307 @@ +{ + "name": "fabric-trade", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "fabric-trade", + "version": "0.0.0", + "dependencies": { + "@unocss/vite": "^66.7.5", + "@vueuse/core": "^14.4.0", + "jalaali-js": "^2.0.0", + "lodash-es": "^4.18.1", + "pinia": "^4.0.2", + "unocss": "^66.7.5", + "vue": "^3.5.40", + "vue-router": "^5.2.0" + }, + "devDependencies": { + "@tsconfig/node24": "^24.0.4", + "@types/lodash-es": "^4.17.12", + "@types/node": "^24.13.3", + "@vitejs/plugin-vue": "^6.0.8", + "@vitejs/plugin-vue-jsx": "^5.1.6", + "@vue/eslint-config-typescript": "^14.9.0", + "@vue/tsconfig": "^0.9.1", + "eslint": "^10.7.0", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-oxlint": "~1.73.0", + "eslint-plugin-vue": "~10.9.2", + "jiti": "^2.7.0", + "npm-run-all2": "^9.0.2", + "oxlint": "~1.74.0", + "prettier": "3.9.5", + "typescript": "~6.0.0", + "vite": "^8.1.5", + "vite-plugin-vue-devtools": "^8.1.5", + "vue-eslint-parser": "^10.4.1", + "vue-tsc": "^3.3.7" + }, + "engines": { + "node": "^22.18.0 || >=24.12.0" + } + }, + "node_modules/@antfu/install-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/@antfu/install-pkg/-/install-pkg-1.1.0.tgz", + "integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==", + "license": "MIT", + "dependencies": { + "package-manager-detector": "^1.3.0", + "tinyexec": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "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", + "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==", + "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==", + "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==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.29.7.tgz", + "integrity": "sha512-EtU0Hi3GvrTqD56xKmZvV/uCXK2ZbwVNPNLAquVItcAZpUhkXwWlo3Fmj0c2LxgSf2I8IDULeAepwNP1OefLXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/plugin-syntax-decorators": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.29.7.tgz", + "integrity": "sha512-9MTTLbF39X6sqM92JPEsoI7++26hjZvzkxKZy64aMhWLH2mPkJ/Q3AV4QLmls3R14FpSpkOwQQfUh962JGQxxg==", + "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-import-attributes": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.29.7.tgz", + "integrity": "sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==", + "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-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "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-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/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==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emnapi/core": { + "version": "2.0.0-alpha.3", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-2.0.0-alpha.3.tgz", + "integrity": "sha512-AZypUeJ/yByuxyS7BlSNRDOMLMlROYtjYdIAuBmJssVz1UJDSeYxLrdizhXCFYhedC5bqd/ASy8EuNXbVVXp9g==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "2.0.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "2.0.0-alpha.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-2.0.0-alpha.3.tgz", + "integrity": "sha512-hFPAhMUjJD9BSyCANEISPOogeXC9Zo9ZQl7L6vKnaVsMkCtzznaW/naYypeyl0Gv5rYfWYsZbpixTMpjDJzQeA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-2.0.1.tgz", + "integrity": "sha512-9DsSk+o5NBX0CCJT8s0EROGSGxjR/tKu6aBTaVyq+SjAEQH4XcdcRxPBRzsBLizTTJ49MJjF+jgu3qnO9GLQcQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.7.0.tgz", + "integrity": "sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz", + "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "license": "MIT" + }, + "node_modules/@iconify/utils": { + "version": "3.1.4", + "resolved": "https://registry.npmmirror.com/@iconify/utils/-/utils-3.1.4.tgz", + "integrity": "sha512-b1S7B1k9ohZ+iNTi2ATxbRYG9fTrJmUT0rc46bvVnNxqNRGW7dyo/vRREwyniI5IRN2RSJHDcm+s3BjWrSAjHw==", + "license": "MIT", + "dependencies": { + "@antfu/install-pkg": "^1.1.0", + "@iconify/types": "^2.0.0", + "import-meta-resolve": "^4.2.0" + } + }, + "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==", + "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==", + "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==", + "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==", + "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==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.2.tgz", + "integrity": "sha512-JfB4kuJQjaoHuCTseIINHtHWeJnvgEcxjwA5t/Y00ZgaOO1Crz3fjT/p8kT28zA/Caz7oiUMn3d6H2yOVCVwuw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.3", + "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.3" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@oxc-parser/binding-android-arm-eabi": { + "version": "0.131.0", + "resolved": "https://registry.npmmirror.com/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.131.0.tgz", + "integrity": "sha512-t2xicr9pfzkSRYx5aPqZqlLaayIwJTqgQ81Jor31Xep2nGyL2Aq3d0K5wOfeR7VevaSdxaS9dzSQP9xDwn8fDg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-android-arm64": { + "version": "0.131.0", + "resolved": "https://registry.npmmirror.com/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.131.0.tgz", + "integrity": "sha512-nlGIod6gw75x1aEDgLS+srj+JRGY0HHm9MI9YgzE/B64l6d6+H3MSP9NOgp0+HTg8tp4vV9rVfgQGgd+TfVZcA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-darwin-arm64": { + "version": "0.131.0", + "resolved": "https://registry.npmmirror.com/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.131.0.tgz", + "integrity": "sha512-jukuV6xe5RbQKFo7QD34NDCLDZp4PSOm8rmckhNdH/60ymG5zXbDzGBEyc+nTkuLQNama2aSGCt+CPfpjNTqyw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-darwin-x64": { + "version": "0.131.0", + "resolved": "https://registry.npmmirror.com/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.131.0.tgz", + "integrity": "sha512-g3JOo4khe9rslHm5WYaVDWb0HS/M1MLR3I9S8560MkKIcC96VQY00QjOlsuRyfSj/JDXj8i9T7ryPO2RidiXVg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-freebsd-x64": { + "version": "0.131.0", + "resolved": "https://registry.npmmirror.com/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.131.0.tgz", + "integrity": "sha512-1hziITDTxjMePnX+dR9ocVT+EuZkQ8wm4FPAbmbEiKG+Phbo73J1ZnPAA6Y/aGsWF3McOFnQuZIktAFwalkfJQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-arm-gnueabihf": { + "version": "0.131.0", + "resolved": "https://registry.npmmirror.com/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.131.0.tgz", + "integrity": "sha512-9uRxfXwyKG9+MwmGQBo2ncPNwZH5HTmCETFM2WiuDBNDCW4NC5ttSQkwCAMrTAWgwMzVBH1CP8pM0v7nebCWXQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-arm-musleabihf": { + "version": "0.131.0", + "resolved": "https://registry.npmmirror.com/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.131.0.tgz", + "integrity": "sha512-mgbLvzRShXOLBdWGInf08Af4q+pfj1xD8hSgLClDZ9of/BXkB6+LIhTH7fihiDUipqB3yoSkKBWaZ3Ejlf5Yag==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-arm64-gnu": { + "version": "0.131.0", + "resolved": "https://registry.npmmirror.com/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.131.0.tgz", + "integrity": "sha512-OPT8++4aN6j2GJ8+3IZHS/byXoZP4aSBn+FoG6rgBJ2fKwPKXWF3MqrFMNW7NKHM28FLY579xYLxJSfgobEqPA==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-arm64-musl": { + "version": "0.131.0", + "resolved": "https://registry.npmmirror.com/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.131.0.tgz", + "integrity": "sha512-vtPiwmfVTAXzaxDKsOXG+LwgRAA7WEnaeHzhS5z0GE89gAK18KSXnly7Z6saXXq6L3dVMyK44uoTI03zKxrpmw==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-ppc64-gnu": { + "version": "0.131.0", + "resolved": "https://registry.npmmirror.com/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.131.0.tgz", + "integrity": "sha512-8AW8L7w5cGHSdZPcyZX2yR0+GUODsT15rbRjfdD54rv6DMbtuEB19ysLOpKJlRGfH6UNYNpCHaU1uJWgTWf1/w==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-riscv64-gnu": { + "version": "0.131.0", + "resolved": "https://registry.npmmirror.com/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.131.0.tgz", + "integrity": "sha512-vvpjkjEOUsPcsYf8evE4MO3aGx9+3wodXEBOicGNnOwTuAik8eBONNkgSdhkGsAblQmfVHJyanRnpxglddTXIA==", + "cpu": [ + "riscv64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-riscv64-musl": { + "version": "0.131.0", + "resolved": "https://registry.npmmirror.com/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.131.0.tgz", + "integrity": "sha512-AqmcNC3fClXX+fxQ6VGEN1667xVFiRBkY0CZmDMSiaeFUsv1+UkBPYYi48IUKcA9/ivvoKNRzQl2I4//kT9F/w==", + "cpu": [ + "riscv64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-s390x-gnu": { + "version": "0.131.0", + "resolved": "https://registry.npmmirror.com/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.131.0.tgz", + "integrity": "sha512-7d3jOMKy7RSQCcDLIci+ySll2FgsOMl/GiRux4q2JNv0zg4EdhFISa9idvrdN/HEUIQQJNg6dmveUeJl2YErGA==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-x64-gnu": { + "version": "0.131.0", + "resolved": "https://registry.npmmirror.com/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.131.0.tgz", + "integrity": "sha512-JHK/h95qVqVQ+ITER837kcTdwBDFpFaNnOTYGCP0zdUSX/mLKC7tXOoyrTb6vG7iRPwGlcgBil3v2IjYw1FqJA==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-x64-musl": { + "version": "0.131.0", + "resolved": "https://registry.npmmirror.com/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.131.0.tgz", + "integrity": "sha512-b2BO82O8azXAyf7EUgOPKu145nWypbNyk07HbU09fkzhm9lEA5oPvaN/M8Nlo7tOErVTa2WOgS4QbOnxAPXdDQ==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-openharmony-arm64": { + "version": "0.131.0", + "resolved": "https://registry.npmmirror.com/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.131.0.tgz", + "integrity": "sha512-GHO9glZaX7LkX/OGfluEPf1yjg+ehiFbUdowbX6uNWOQhmwKWU4m4+nZ9FJkrHNKuxyI1KKertMdGjVKCApKWA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-wasm32-wasi": { + "version": "0.131.0", + "resolved": "https://registry.npmmirror.com/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.131.0.tgz", + "integrity": "sha512-3SkikPaEFoih1N83qLVEDLRLeY4nYsf6JT9SnWiMCQ5lGQdKup6bEuKCqkRiG9dD1IIaFeYz9RjlciPmYoFIWA==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmmirror.com/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@oxc-parser/binding-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmmirror.com/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@oxc-parser/binding-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@oxc-parser/binding-win32-arm64-msvc": { + "version": "0.131.0", + "resolved": "https://registry.npmmirror.com/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.131.0.tgz", + "integrity": "sha512-Os5bEhryeA2jkH+ZrnZyAC1EP5gs+X4YB1Fjqml7UPD5kU7ecsK1MPEVMfCrdt/GDNpDbavYXiOXOdyJ5b3OPw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-win32-ia32-msvc": { + "version": "0.131.0", + "resolved": "https://registry.npmmirror.com/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.131.0.tgz", + "integrity": "sha512-m+jNz9EuF0NXoiptc6B9h5yompZQVW/a5MJeOu5zojfH5yWk82tvF2ccrHkfhgtrS9h9DD5l1Qv8dWlfY7Nz8g==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-win32-x64-msvc": { + "version": "0.131.0", + "resolved": "https://registry.npmmirror.com/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.131.0.tgz", + "integrity": "sha512-o14Hk8dAyiEUMFEWEgmAwFZvBt1RzAYLM3xeQ+5315JXgVYhoemivgYcbYVRbsFkS71ShMGlAFE0kPnr460rww==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.142.0.tgz", + "integrity": "sha512-7W+2q5AKQVU36fkaryontrHn3YDt1RyUYXatw9i5H8ocYe2sPKSFB6eS8WNPeRKiN1qAWWZUPm7gwFzJGrccqQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@oxlint/binding-android-arm-eabi": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm-eabi/-/binding-android-arm-eabi-1.74.0.tgz", + "integrity": "sha512-+gHd12muVI9ZLBaWLPkHt3Fj7jihFjgQ1MGtBaRL8vWrWrI0P7dLUty/cHrHS0oqPYIRgQUJsPu2CExQuMcwNw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-android-arm64": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm64/-/binding-android-arm64-1.74.0.tgz", + "integrity": "sha512-xjKdoMB+H+RCOByv/7l7nfIGW9mlOisqYdcyC75UqYuQecLpReAeEYUf2CNeDEI3KtmUgxpRw/+c63y4AeF/Bw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-darwin-arm64": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-arm64/-/binding-darwin-arm64-1.74.0.tgz", + "integrity": "sha512-iUK7wvc6sejMKsC+Pt67mntoF5weFcyEunhZfLJceU6gL419mexz5wBkSx/EnkFBExMLNtOi9fnDSc5xfK0IzQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-darwin-x64": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-x64/-/binding-darwin-x64-1.74.0.tgz", + "integrity": "sha512-ggKc/tn5SJ1u2yG2izC6VKODfYKV8MQ2AicJlNzOjuyrC29udvOef6/JzK2r32xqCnBDLFouR1VCkjzEI0/N9Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-freebsd-x64": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-freebsd-x64/-/binding-freebsd-x64-1.74.0.tgz", + "integrity": "sha512-u++dH/43jy9hTLbneaWlS0gla/Bp1JdwJ2zgevCl8nDFUh6qRCGMxcL0f0lb7By3A9p/LfFr+7cG4HU1hG856g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm-gnueabihf": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.74.0.tgz", + "integrity": "sha512-Sj1zmtFDVTPeIbIz4ZfcXAbFHqCmKCXdCUlAJzvTF7I20NTH1RDpoF2PhkqNODutJzVhJYmm3oz0GwgY+tvE2g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm-musleabihf": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-1.74.0.tgz", + "integrity": "sha512-//PKyQb/tQXcHArx2f7z+oVI/eMS2Jpv+edNuAtOrgIhWdGcpHxogveAxzmF2rpH1AIHp4Hq04RF/rgJdiICnQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm64-gnu": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.74.0.tgz", + "integrity": "sha512-/k1Me+aX2tjuH10K62mLS0y8cLkJBHX6Ce0xPK+eWeel4bSdEGZ8dv4+hYMzg0GrSmjwy4yAYsDPeEeKBft/2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm64-musl": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.74.0.tgz", + "integrity": "sha512-3tFSjBxc5D8/zvjEuLvOqcA8ZXKD0+6NuaVO/edeamNc49MoAsbfaC9s1UiwODwgF6slGaF8yJA2TPkukd77tg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-ppc64-gnu": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.74.0.tgz", + "integrity": "sha512-9QggtPkSPXOCTu8Szis7auOK/sC7KdQaN+/TujP7YVVhzCAOhgdRfgv8uEz0r2tk5xdgus5rLYUrCDoZNtiRUw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-riscv64-gnu": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-1.74.0.tgz", + "integrity": "sha512-VM5VPUJ4DJIWiK+AZn8FScUqMr6OFrCAYybMYjEEi7W13ParI64MByiXTkKMqZpBmvQ9zxl9Ebq2VUOiZRJYUg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-riscv64-musl": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-1.74.0.tgz", + "integrity": "sha512-SaDY1gh9rOA592J54g+gu5hkOFFQBZsMmIYHs+NRHG+Uq0OxtuuCXMWQ3vu1830Eugv5uMXyjG+bv2Z9y4IXjw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-s390x-gnu": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.74.0.tgz", + "integrity": "sha512-ZATQeHZCyr6MbDveg0obD5sxLHFOghtOdC5jwVwYlvFWqtFOxctgFEG6Ef/64hYvZrWyhyCckB10AelqLopeDA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-x64-gnu": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.74.0.tgz", + "integrity": "sha512-+aIvJyrdeD7LwCQ2WYLMUWNmnbeDRSPb40aBYtPjD9+PTqUwgJnk+HK5yLfSMeqXrMrDhE9uTmtt2y50tvjhHw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-x64-musl": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-musl/-/binding-linux-x64-musl-1.74.0.tgz", + "integrity": "sha512-XyktaR8lhK2qWiCK0Tk8oYD+/cgn+oHA6ddRnxSSXUKkkojkV78CmShZUxQF+yrBFs0SuW+JBOPG6hecyc/iZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-openharmony-arm64": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-openharmony-arm64/-/binding-openharmony-arm64-1.74.0.tgz", + "integrity": "sha512-mzbjrPl4neaVUiJ1fUiEUxTGaSZBoiKtaoB6jmIpz9S+VOA2vDYmJpihQ82w6178V5jxziclTg8Cgj5yF6tTDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-win32-arm64-msvc": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.74.0.tgz", + "integrity": "sha512-vUAe9okpS2Oa5+lX67lqHMuNUvfkleRKwrUDJ/WJBsgmddvZ1mrsh2HVmuFDRzqFELhaJhFaCNOuR6a7L3rtIA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-win32-ia32-msvc": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.74.0.tgz", + "integrity": "sha512-yyXXJyYYSXL4I8K8jAWjJs+J3fa9gH2JmEbo4f5adm+1tNC9itseicBNuwK7BDHvqQ5J534s+yDULu89vYL2ZQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-win32-x64-msvc": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.74.0.tgz", + "integrity": "sha512-VTC9IYTIMrVUk/i6Ms1ohzzDKZFkWn0KU2OBbPBzgmVZ2V30165T/zK4LztTr0Xgp9fZ1qQZ1rsZAu/rEmySlA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "license": "MIT" + }, + "node_modules/@quansync/fs": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/@quansync/fs/-/fs-1.0.0.tgz", + "integrity": "sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==", + "license": "MIT", + "dependencies": { + "quansync": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/@quansync/fs/node_modules/quansync": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/quansync/-/quansync-1.0.0.tgz", + "integrity": "sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.1.tgz", + "integrity": "sha512-02hOeOSryYxVrOIphmLAsqnCJWxwlzFk+pEt/N/i6OgT3lShHO7xGCU5cpgchRDHboAEbSjzgGh+O/u1GswQmA==", + "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.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.1.tgz", + "integrity": "sha512-fMsTOnN0OjFm3CyppWPitKnc8UlliVARUULW6cfU6AIqjdtgmSFWSk9vecHzZduv/yMWIHDlRhM1e8Iff9uAfA==", + "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.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.1.tgz", + "integrity": "sha512-1wjKdz/XLGKHaTNHjQveQ/B23TKx4ItAqm1JbyVuvNPc4Ze0Fb48s49TAd/2zcplPl8okE/UbTgmlVfwT7eFeQ==", + "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.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.1.tgz", + "integrity": "sha512-Fa0jHR07E7YBN4vOEsbVf2briYNsuOowfLJaXULZM0ldMlaCaj2LJgLMbMe4iacRyZmvR8efFhgR9wKuGclQUg==", + "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.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.1.tgz", + "integrity": "sha512-pzkgu1SSHGgRRyRZ4fbmSgmajbVt+epaLP99NDjFft69v/ypfTi6swBMiVdh2EkQ0OSnHE1lZDM7DRGkyAzUpA==", + "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.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.1.tgz", + "integrity": "sha512-QI5SEDY8cbiYWHx0VO4vIc3UlS6a32vXHjU8Qy/17adEmZIPuByJg13UEvo9c/UCiUkdcVWY83C+b+JrwnNyUg==", + "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.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.1.tgz", + "integrity": "sha512-Sm41FyCeXqmYcERoYOCbGIL5hNfd8w9LQ7Y61Bev48HkcjaJqV/iiVOaiDxjVTRMS+QKrZmD8cfPt4uMVnvM+A==", + "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.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.1.tgz", + "integrity": "sha512-2x+WhXTGl9yJYPbltW/BSEPTVz9OIWQyER4N+gJEDWkkn904eRcBzELqh/Hf7K0w/ubGbKNMv0ZC+94QK/IFEg==", + "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.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.1.tgz", + "integrity": "sha512-eEjmQpuRQayHPWWnywaWHkFT3ToPbP3RYy42VVd/B9aBGDA+Ol25EIWHxKQST3IiWJjikCWUF7KtbfqwZrzVwQ==", + "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.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.1.tgz", + "integrity": "sha512-/Orga1fZYkLc/56jBICcHrKchl8Z2UKdDSr3LG9ToWO1lQ6a4Livk9Xz+9WN91zsz5QR3XQz2NNoSDEvP6qadw==", + "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.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.1.tgz", + "integrity": "sha512-xxBJRL+0q0Kce7orznGWLuylHDY65vuARXZRpX+hPdv+DqK2c3NlCsVA98tlWzWNEE7yPqA/1NQ5nnCrj49Y5A==", + "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.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.1.tgz", + "integrity": "sha512-M6AdXIXw3s+/8XpKMzdGDEXGS1S7kwUsy+rcTIUIOx5Ge4nXKCtAFHFV9YKkXvGcC5WMoTjAteLzlsQROVI0Yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.2.1.tgz", + "integrity": "sha512-/TX0SoRGojHzSAHpfVBbavRVSazg5U3h3Y3VXfcc0cdugq6kxdqw8LPGFiPr+/7gE/60zRcsOY2Vi9b9eT0jww==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "2.0.0-alpha.3", + "@emnapi/runtime": "2.0.0-alpha.3", + "@napi-rs/wasm-runtime": "^1.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.1.tgz", + "integrity": "sha512-EvRrivJieyHG+AO9lleZWgq+g0+S7oV2C51yuqlcyU/R9net+sI4Pj0F+lUoP2bEr6TWX3SqFaaS0SzfLxSzkw==", + "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.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.1.tgz", + "integrity": "sha512-Z4eCmn5QJ/5+azF9knpLWKfVd9aidn0mAe9TpJgvBLId9Ax3t0+JVxBmT25Bv7NBbVW1TZyKjQjQReouMeH5UQ==", + "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.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node24": { + "version": "24.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node24/-/node24-24.0.4.tgz", + "integrity": "sha512-2A933l5P5oCbv6qSxHs7ckKwobs8BDAe9SJ/Xr2Hy+nDlwmLE1GhFh/g/vXGRZWgxBg9nX/5piDtHR9Dkw/XuA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "license": "MIT" + }, + "node_modules/@types/jsesc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@types/jsesc/-/jsesc-2.5.1.tgz", + "integrity": "sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==", + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/lodash": { + "version": "4.17.25", + "resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.17.25.tgz", + "integrity": "sha512-+K1NIO8I+F9/wNulfVvu23QYd0Pe9/OCqRrim4NoYIf1VoEDL90Ve4ClzpyqBLc7NpGGWRvYNCKZ1BE/Jpf8dQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/lodash-es": { + "version": "4.17.12", + "resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz", + "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@types/node": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz", + "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.21", + "resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", + "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==", + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.65.0.tgz", + "integrity": "sha512-IEgob78X12rHpUmtcwFsXhZdVGJtwTVP8FiCLZkR6GlYVrl2PcuB+KhCE5BlVC/eQpQnu8WXRtkHZuPar+gCRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.65.0", + "@typescript-eslint/type-utils": "8.65.0", + "@typescript-eslint/utils": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.65.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz", + "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.65.0.tgz", + "integrity": "sha512-CZ4nMxWwgu1HEEFNkeaCptra9QCtkmKdgf3sWh1rl1trIhmxLilgTV4cwcbQ4wemnT4sWQN8CaKOmdYx+g2gMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.65.0.tgz", + "integrity": "sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.65.0", + "@typescript-eslint/types": "^8.65.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz", + "integrity": "sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.65.0.tgz", + "integrity": "sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.65.0.tgz", + "integrity": "sha512-YjaZ7PRI5qY7ax2L3PbvX0rRyGtipAReCWs0mhhDBHjH/vl0g0BonaGXrKdKpMbIIsMIwDgbk/xzkBTyAltS5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0", + "@typescript-eslint/utils": "8.65.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz", + "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz", + "integrity": "sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.65.0", + "@typescript-eslint/tsconfig-utils": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.65.0.tgz", + "integrity": "sha512-gXiwIHsYreboxeJucHKPvgwl7dXt50mF8s1/c00cP/WoVTyWKFdtfhRWwZiXYFU5H2O8vVoSLNrexFZjYS/SGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz", + "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.65.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@unocss/cli": { + "version": "66.7.5", + "resolved": "https://registry.npmmirror.com/@unocss/cli/-/cli-66.7.5.tgz", + "integrity": "sha512-fgWkECRn2LGVo9sEpmjk/KJ3NSKUDitaZCNrJcEYM93DG+ELriTHcL+VWBlF4rcZf9fdGlq1nKbbRHUZirFCHQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "@unocss/config": "66.7.5", + "@unocss/core": "66.7.5", + "@unocss/preset-wind3": "66.7.5", + "@unocss/preset-wind4": "66.7.5", + "@unocss/transformer-directives": "66.7.5", + "cac": "^7.0.0", + "chokidar": "^5.0.0", + "colorette": "^2.0.20", + "consola": "^3.4.2", + "magic-string": "^0.30.21", + "pathe": "^2.0.3", + "perfect-debounce": "^2.1.0", + "tinyglobby": "^0.2.17", + "unplugin-utils": "^0.3.1" + }, + "bin": { + "unocss": "bin/unocss.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/config": { + "version": "66.7.5", + "resolved": "https://registry.npmmirror.com/@unocss/config/-/config-66.7.5.tgz", + "integrity": "sha512-dkPl9glhEahJ+Xoja5ZseKKnH+vZaeaKQzg8b0otcKcPPNrHQgu1nu3QgfeOjnXOGrjZIotHwUeVtt4ZA2Skgg==", + "license": "MIT", + "dependencies": { + "@unocss/core": "66.7.5", + "colorette": "^2.0.20", + "consola": "^3.4.2", + "unconfig": "^7.5.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/core": { + "version": "66.7.5", + "resolved": "https://registry.npmmirror.com/@unocss/core/-/core-66.7.5.tgz", + "integrity": "sha512-UdJb8MiMywcau8QrWEVgUAz0kvoFHyR+sACwYCgmBh/BpKJGyR/zw/Ys3wvysbm0f+i/20VGBex/QQxYVlzdyQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/extractor-arbitrary-variants": { + "version": "66.7.5", + "resolved": "https://registry.npmmirror.com/@unocss/extractor-arbitrary-variants/-/extractor-arbitrary-variants-66.7.5.tgz", + "integrity": "sha512-5zOkbnLIJc8E749qNjLXfPHl3FdHloop12h706/ojr6idK4ix0KLm43R//uLnphixCehdHJRuHnavnM4C/kQbA==", + "license": "MIT", + "dependencies": { + "@unocss/core": "66.7.5" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/inspector": { + "version": "66.7.5", + "resolved": "https://registry.npmmirror.com/@unocss/inspector/-/inspector-66.7.5.tgz", + "integrity": "sha512-WmTJMnj8bmRxvw/wGeShmL2eEHr2/L0BdGTXSxhfzwcO8y5XZEbBmVciLcM7pqaTXQ6JY4+KnITrDUf1urjZxQ==", + "license": "MIT", + "dependencies": { + "@unocss/core": "66.7.5", + "@unocss/rule-utils": "66.7.5", + "colorette": "^2.0.20", + "gzip-size": "^6.0.0", + "sirv": "^3.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/preset-attributify": { + "version": "66.7.5", + "resolved": "https://registry.npmmirror.com/@unocss/preset-attributify/-/preset-attributify-66.7.5.tgz", + "integrity": "sha512-1Oi1Cp81pqYJwG3h4+OlP5A0FKyaa07MFBXaXc4Yr3faiTbsI8SKj2TqnZCb+NQvBsEqslEd+jKXGZ3lKzCVOQ==", + "license": "MIT", + "dependencies": { + "@unocss/core": "66.7.5" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/preset-icons": { + "version": "66.7.5", + "resolved": "https://registry.npmmirror.com/@unocss/preset-icons/-/preset-icons-66.7.5.tgz", + "integrity": "sha512-ZsxadWnGGtHdANTikuMnjkQaT1qwUFJHZBF4fzVsPMnUiiKGs8rzXukwM9w3Gi9ontM7nbG2FYi9clWETSlpFg==", + "license": "MIT", + "dependencies": { + "@iconify/utils": "^3.1.3", + "@unocss/core": "66.7.5", + "ofetch": "^1.5.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/preset-mini": { + "version": "66.7.5", + "resolved": "https://registry.npmmirror.com/@unocss/preset-mini/-/preset-mini-66.7.5.tgz", + "integrity": "sha512-o37TSl4ecT0dKu+3/TYuTYht75h82SEwDNl476m9ve0KW4Pv1O2tT/9TWd7N5cutEpySr8/YtjMwtWBD+drxBg==", + "license": "MIT", + "dependencies": { + "@unocss/core": "66.7.5", + "@unocss/extractor-arbitrary-variants": "66.7.5", + "@unocss/rule-utils": "66.7.5" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/preset-tagify": { + "version": "66.7.5", + "resolved": "https://registry.npmmirror.com/@unocss/preset-tagify/-/preset-tagify-66.7.5.tgz", + "integrity": "sha512-/8YB1tXVi+WmNKPhsCdtO1xnQKEkshSnWDp6AbvVP3f6qOF7adlMYpAt3kpWCoVUBsfOQ06ZQlnfricMjObyoQ==", + "license": "MIT", + "dependencies": { + "@unocss/core": "66.7.5" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/preset-typography": { + "version": "66.7.5", + "resolved": "https://registry.npmmirror.com/@unocss/preset-typography/-/preset-typography-66.7.5.tgz", + "integrity": "sha512-2dxC8LT9KYa29UZT4muDFl7DbIXvKktTfeSMbUGMdZKbHcuMtKSP2U52wti1PL5I9duqp4v+8G33EeVutGTUaQ==", + "license": "MIT", + "dependencies": { + "@unocss/core": "66.7.5", + "@unocss/rule-utils": "66.7.5" + } + }, + "node_modules/@unocss/preset-uno": { + "version": "66.7.5", + "resolved": "https://registry.npmmirror.com/@unocss/preset-uno/-/preset-uno-66.7.5.tgz", + "integrity": "sha512-zaUlYgNngbt50fZA/LbtsEnmPKojPqeiXCyUUiKQMv2uJQhncR32xhLZXVQ+TJD7hlfZ+6FAqlco1NIiAcub9w==", + "license": "MIT", + "dependencies": { + "@unocss/core": "66.7.5", + "@unocss/preset-wind3": "66.7.5" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/preset-web-fonts": { + "version": "66.7.5", + "resolved": "https://registry.npmmirror.com/@unocss/preset-web-fonts/-/preset-web-fonts-66.7.5.tgz", + "integrity": "sha512-OLLTK7kswdSu51qxEm6O+AehecgCfS08Ivqo+280lKzFW7V1jXr5okeJ1ty+85oHCprJqzcD9iG1khxNRLomcA==", + "license": "MIT", + "dependencies": { + "@unocss/core": "66.7.5", + "ofetch": "^1.5.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/preset-wind": { + "version": "66.7.5", + "resolved": "https://registry.npmmirror.com/@unocss/preset-wind/-/preset-wind-66.7.5.tgz", + "integrity": "sha512-LVKgGr0A9Lc7F85xGXrrb71DDXMlHGA8bcj/Kht8BCsuRdBZadNbLlnv5qnSJiHYhi3/blzcgVoaeRor6L9yNA==", + "license": "MIT", + "dependencies": { + "@unocss/core": "66.7.5", + "@unocss/preset-wind3": "66.7.5" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/preset-wind3": { + "version": "66.7.5", + "resolved": "https://registry.npmmirror.com/@unocss/preset-wind3/-/preset-wind3-66.7.5.tgz", + "integrity": "sha512-atFe/7Qein+oMdyZs0hEo+3EjV99Av2LVxDbzpCungxIfbS3TnQt70EIuHXMPNCVWLYwrMV+/P1n9Ntb0E3mow==", + "license": "MIT", + "dependencies": { + "@unocss/core": "66.7.5", + "@unocss/preset-mini": "66.7.5", + "@unocss/rule-utils": "66.7.5" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/preset-wind4": { + "version": "66.7.5", + "resolved": "https://registry.npmmirror.com/@unocss/preset-wind4/-/preset-wind4-66.7.5.tgz", + "integrity": "sha512-n3jIvQv8x1jXpmWfvNFBIqHNARki4mKZXfxAA31gekpXsRRTg16u1+yC1+PBBJgoEDFEnnmKnG7EZP88S8LVXA==", + "license": "MIT", + "dependencies": { + "@unocss/core": "66.7.5", + "@unocss/extractor-arbitrary-variants": "66.7.5", + "@unocss/rule-utils": "66.7.5" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/rule-utils": { + "version": "66.7.5", + "resolved": "https://registry.npmmirror.com/@unocss/rule-utils/-/rule-utils-66.7.5.tgz", + "integrity": "sha512-/AKHBRF6ZOexE3EDDv8ZEYh40P5e2Eha41IYUbE1wjigW7++ssmvimSTdufJzZvU5DGP++E3B3WEsFPprZMjAg==", + "license": "MIT", + "dependencies": { + "@unocss/core": "66.7.5", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/transformer-attributify-jsx": { + "version": "66.7.5", + "resolved": "https://registry.npmmirror.com/@unocss/transformer-attributify-jsx/-/transformer-attributify-jsx-66.7.5.tgz", + "integrity": "sha512-r8qDwNSt0eGSwWws3xsuIHb3PZYR2embFdYoE67hD/xlYgLjX1uPy/HUlGCSSh4uLc4vVYjurr0Oq5xF/B8YiA==", + "license": "MIT", + "dependencies": { + "@unocss/core": "66.7.5", + "oxc-parser": "^0.131.0", + "oxc-walker": "^0.7.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/transformer-compile-class": { + "version": "66.7.5", + "resolved": "https://registry.npmmirror.com/@unocss/transformer-compile-class/-/transformer-compile-class-66.7.5.tgz", + "integrity": "sha512-KuECgsGF7tGe808vIvKViEjI0UCUgYgneJfK+/TWBkjC7s8dLVaUtJzzcP9/r6OfGlgyn/x1YTdRqTaCENa7Xg==", + "license": "MIT", + "dependencies": { + "@unocss/core": "66.7.5" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/transformer-directives": { + "version": "66.7.5", + "resolved": "https://registry.npmmirror.com/@unocss/transformer-directives/-/transformer-directives-66.7.5.tgz", + "integrity": "sha512-VMJApXXOwlDubkW+cNMmOkfxLefFupJr+yU23gGYUB2NPsuVltc8H5CDM0gfVebpeL5CUW05evxzEAk2wsju+Q==", + "license": "MIT", + "dependencies": { + "@unocss/core": "66.7.5", + "@unocss/rule-utils": "66.7.5", + "css-tree": "^3.2.1" + } + }, + "node_modules/@unocss/transformer-variant-group": { + "version": "66.7.5", + "resolved": "https://registry.npmmirror.com/@unocss/transformer-variant-group/-/transformer-variant-group-66.7.5.tgz", + "integrity": "sha512-iDmP3mM8J+IxuQf5Uh33zsi528xnGxTpKRJ0c+LCrqBTTkR2tZr4aCzNmJ0g29Js2yEOsyNXRRc8BNTuvgn0AA==", + "license": "MIT", + "dependencies": { + "@unocss/core": "66.7.5" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@unocss/vite": { + "version": "66.7.5", + "resolved": "https://registry.npmmirror.com/@unocss/vite/-/vite-66.7.5.tgz", + "integrity": "sha512-1z1TBCNJCR2WzjPtjzmCHr8rtzXHosMjLdsCNe6Mzsfwiw044gzzLVuiEZO9vIjkI50lvQ+gIOxOiVQG0hGAeA==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "@unocss/config": "66.7.5", + "@unocss/core": "66.7.5", + "@unocss/inspector": "66.7.5", + "chokidar": "^5.0.0", + "magic-string": "^0.30.21", + "pathe": "^2.0.3", + "tinyglobby": "^0.2.17", + "unplugin-utils": "^0.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 || ^8.0.0-0" + } + }, + "node_modules/@vitejs/plugin-vue": { + "version": "6.0.8", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.8.tgz", + "integrity": "sha512-0ZjgOg7oO6farnNGup7yvoM/YXZV84OZxHAwtflItNa/6zzQyVb5LNxyea3FEKEX2XlagIKzrlH7wwxkKgtiew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vitejs/plugin-vue-jsx": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue-jsx/-/plugin-vue-jsx-5.1.6.tgz", + "integrity": "sha512-YXvi4as2clxt6DFw5+a0tTA97ntiQXm/raR8ofNj3aNwwdlVGTiG2gp7EvfZW17P50acL/9bP0ccF4XnqNmlgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.29.0", + "@babel/plugin-syntax-typescript": "^7.29.7", + "@babel/plugin-transform-typescript": "^7.29.7", + "@rolldown/pluginutils": "^1.0.1", + "@vue/babel-plugin-jsx": "^2.0.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "vue": "^3.0.0" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.28.tgz", + "integrity": "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.28" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.28.tgz", + "integrity": "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.28.tgz", + "integrity": "sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vue-macros/common": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vue-macros/common/-/common-3.1.4.tgz", + "integrity": "sha512-/5Fv+6DgIcM9ajY05ZmKBv+LMX1M9A0X+IUwDRVdt67ciw8OV9bvG2r34p3RiEadlsQybjhKPRKNXDC8Bp23cw==", + "license": "MIT", + "dependencies": { + "@vue/compiler-sfc": "^3.5.22", + "ast-kit": "^2.1.2", + "local-pkg": "^1.1.2", + "magic-string-ast": "^1.0.2", + "unplugin-utils": "^0.3.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/vue-macros" + }, + "peerDependencies": { + "vue": "^2.7.0 || ^3.2.25" + }, + "peerDependenciesMeta": { + "vue": { + "optional": true + } + } + }, + "node_modules/@vue/babel-helper-vue-transform-on": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-2.0.1.tgz", + "integrity": "sha512-uZ66EaFbnnZSYqYEyplWvn46GhZ1KuYSThdT68p+am7MgBNbQ3hphTL9L+xSIsWkdktwhPYLwPgVWqo96jDdRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/babel-plugin-jsx": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@vue/babel-plugin-jsx/-/babel-plugin-jsx-2.0.1.tgz", + "integrity": "sha512-a8CaLQjD/s4PVdhrLD/zT574ZNPnZBOY+IhdtKWRB4HRZ0I2tXBi5ne7d9eCfaYwp5gU5+4KIyFTV1W1YL9xZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.4", + "@babel/types": "^7.28.4", + "@vue/babel-helper-vue-transform-on": "2.0.1", + "@vue/babel-plugin-resolve-type": "2.0.1", + "@vue/shared": "^3.5.22" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + } + } + }, + "node_modules/@vue/babel-plugin-resolve-type": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@vue/babel-plugin-resolve-type/-/babel-plugin-resolve-type-2.0.1.tgz", + "integrity": "sha512-ybwgIuRGRRBhOU37GImDoWQoz+TlSqap65qVI6iwg/J7FfLTLmMf97TS7xQH9I7Qtr/gp161kYVdhr1ZMraSYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/parser": "^7.28.4", + "@vue/compiler-sfc": "^3.5.22" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.40.tgz", + "integrity": "sha512-39E8IgOhTbVDnoJFMKc2DvYnypcZwUqgUhQkccva/0m6FUwtIKSGV7n1hpVmYcFaoRAwf9pBcwnKlCEsN63ZEQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@vue/shared": "3.5.40", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.40.tgz", + "integrity": "sha512-pwkx4vqlqOspFstrcmzwkKLePVMD3PT65imRzLhanU2V1Fj4K13g6OXjanOyzw3aTAuRk84BOmY8f3rEHqPaVA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.40", + "@vue/shared": "3.5.40" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.40.tgz", + "integrity": "sha512-gIf497P4kpuALcvs5n3AEg1Vdn0pSY4XbjASIfHNYF1/MP3T2Mf2STERTubysBxCRxzJGJYtF/O7vwJrxFB3Vw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@vue/compiler-core": "3.5.40", + "@vue/compiler-dom": "3.5.40", + "@vue/compiler-ssr": "3.5.40", + "@vue/shared": "3.5.40", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.19", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.40.tgz", + "integrity": "sha512-rrE5xiXG663+vHCHa3J9p2z5OcBRjXmoqenprJxAFQxg5pSshzeBiCE6pu46axapRJ2Adk0YDA2BRZVjiHXnhg==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.40", + "@vue/shared": "3.5.40" + } + }, + "node_modules/@vue/devtools-api": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-8.2.1.tgz", + "integrity": "sha512-6u4vXBlIBAC1wMplIZgpyPn7uh/s4Bf6F5bMzvLv+EdJ0aHs/+4B7Ygv864EStQSjRbsRzTko/kUG1A1IejQ3A==", + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^8.2.1" + } + }, + "node_modules/@vue/devtools-core": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@vue/devtools-core/-/devtools-core-8.2.1.tgz", + "integrity": "sha512-s/VfAY9oDTb/kFEWmy461jaFde2MIV1RO/gi1vwM+PAZBZ/Pc2Ndu3BNBdZUze8QDUuyYvElbEEGA83syjJfzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^8.2.1", + "@vue/devtools-shared": "^8.2.1" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/@vue/devtools-kit": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-8.2.1.tgz", + "integrity": "sha512-FIGIuq3AWReEpbAHY/cRGeHDfI0qOb8OCQ3YjbEAX04uaxIDbGc9rhkbVcG7rnfHPXE3RsU5KrWOu9V/okd8AQ==", + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^8.2.1", + "birpc": "^2.6.1", + "hookable": "^5.5.3", + "perfect-debounce": "^2.0.0" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-8.2.1.tgz", + "integrity": "sha512-Fkac7lUdGReh6pVOi3AYPRGe82LQqRmAfThW7RRligOAP0ZA/Z1z9XLHDM9dv34pV2HRc79DK8uKPeG2fLnA/g==", + "license": "MIT" + }, + "node_modules/@vue/eslint-config-typescript": { + "version": "14.9.0", + "resolved": "https://registry.npmjs.org/@vue/eslint-config-typescript/-/eslint-config-typescript-14.9.0.tgz", + "integrity": "sha512-E3j9hDlfVf10F30MRcLTPY2IIhWIx1nsvkVukk14kTcuA+oBVot9zsP1hzsO+PAMDxV3Fd9FimBJtUBNBL5KFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^8.60.0", + "fast-glob": "^3.3.3", + "typescript-eslint": "^8.60.0", + "vue-eslint-parser": "^10.4.0" + }, + "bin": { + "vue-eslint-config-typescript": "dist/bin.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": "^9.10.0 || ^10.0.0", + "eslint-plugin-vue": "^9.28.0 || ^10.0.0", + "typescript": ">=4.8.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/language-core": { + "version": "3.3.9", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.3.9.tgz", + "integrity": "sha512-in/68oAa4BCtVY6n/nkuhLIkV8DHYd2UivedJ6cMZ6UYtlq9jaoaSNUBHYCVO44z3nKg7MdE5OBoHKt5SxeBKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "@vue/compiler-dom": "^3.5.0", + "@vue/shared": "^3.5.0", + "alien-signals": "^3.2.1", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1", + "picomatch": "^4.0.4" + } + }, + "node_modules/@vue/language-core/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/@vue/reactivity": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.40.tgz", + "integrity": "sha512-B7ot9UlUZOi1zbq61/LvE88ZLTV8IlajTdiZTAEiDQgrnIMIZoPr9kGw0Zw46ObW62O9+H/Be3kMbfb7kYPQZA==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.40" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.40.tgz", + "integrity": "sha512-KAZLweuZ6uUJPK1PMSQPgBU5gCjgrrfjUhSglmU9NhH+Zjepa8cnwSydPWDWHDwOgY4g3VcZ+PljbiHlURNCbw==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.40", + "@vue/shared": "3.5.40" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.40.tgz", + "integrity": "sha512-ZfrX8ssZQds900L9pr8AuK05ddnMsR4MPMZr8cPN9GoqoPWcXLhjvvbIA2SMv+7a97sJ1vv9pj/zxK0Cq/eEFQ==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.40", + "@vue/runtime-core": "3.5.40", + "@vue/shared": "3.5.40", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.40.tgz", + "integrity": "sha512-XNJym9WpevhTVt1HuwOrCRJ5Q+9z4BjTMrDtjTrvx74SmUll8spNTw6whWJa9mEkO4PKn5TihI/bm/8ds2QVJw==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.40", + "@vue/runtime-dom": "3.5.40", + "@vue/shared": "3.5.40" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.40.tgz", + "integrity": "sha512-WxnBtruIqOoV3rA4jeKDWzrYI5h7Cp4+pjwDi8kWGHz+IslhiN+wguLVVhtv2l8VoU02rzDCVfDjgCl1lNpZVg==", + "license": "MIT" + }, + "node_modules/@vue/tsconfig": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.9.1.tgz", + "integrity": "sha512-buvjm+9NzLCJL29KY1j1991YYJ5e6275OiK+G4jtmfIb+z4POywbdm0wXusT9adVWqe0xqg70TbI7+mRx4uU9w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "typescript": ">= 5.8", + "vue": "^3.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/@vueuse/core": { + "version": "14.4.0", + "resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-14.4.0.tgz", + "integrity": "sha512-X4WHz1HlCzCBoYXesUkifzzWBAcZgXG8Fi5iNPQg/epdzOB3gu8Fawj3hvuwYR1nGcXGnvxwYYcUC/71++svtQ==", + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.21", + "@vueuse/metadata": "14.4.0", + "@vueuse/shared": "14.4.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/@vueuse/metadata": { + "version": "14.4.0", + "resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-14.4.0.tgz", + "integrity": "sha512-swx/255R6JyHZFJhx845iz5CRWDZdCfvkZOpACWc5+c5WHcG24mv8gUT1WIdFQaHt6dq79rvILd9QnCWiyVm9g==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared": { + "version": "14.4.0", + "resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-14.4.0.tgz", + "integrity": "sha512-JRgY90Sz8DDtPMsaDflvPMp9xYk69JZAmbuDvAquUVXKr2gEjqtzGNTTthLfckH0BzBqvnu31gb4a8TGLRe79g==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/acorn": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/alien-signals": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-3.2.1.tgz", + "integrity": "sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g==", + "dev": true, + "license": "MIT" + }, + "node_modules/ansi-styles": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-7.0.0.tgz", + "integrity": "sha512-kKvt3m4uwzqL0wlkPd09CmljPJGOZZ4D0fP65sqFSvPkMRKhNi+74MgIJ5QxE6SxqB4t4KyUFGg8+n5zjo6hew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansis": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.3.1.tgz", + "integrity": "sha512-BJ8/l4R5LRE7hW9WdSuGYrLSHi2ynxeFpDFbH0K/CgNeY/tyhk+vO6TYxXC5r5CpUhNVX310xzPsN/H9lCdfOA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + } + }, + "node_modules/ast-kit": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ast-kit/-/ast-kit-2.2.0.tgz", + "integrity": "sha512-m1Q/RaVOnTp9JxPX+F+Zn7IcLYMzM8kZofDImfsKZd8MbR+ikdOzTeztStWqfrqIxZnYWryyI9ePm3NGjnZgGw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "pathe": "^2.0.3" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/ast-walker-scope": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/ast-walker-scope/-/ast-walker-scope-0.9.0.tgz", + "integrity": "sha512-IJdzo2vLiElBxKzwS36VsCue/62d6IdWjnPB2v3nuPKeWGynp6FF/CYoLa5i/3jXH/z97ZDdsXz6abpgM6w07A==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.2", + "@babel/types": "^7.29.0", + "ast-kit": "^2.2.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.10", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.10.tgz", + "integrity": "sha512-35JEvJ5/KKlbCHjMCsONI2w6HE88STjVdHk+C7d8LtcFxUjZR1KeLP9izofn2qs0KUxX5r4z73bwH/rd+JHacw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/birpc": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz", + "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, + "node_modules/brace-expansion": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.7.tgz", + "integrity": "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==", + "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": { + "baseline-browser-mapping": "^2.10.44", + "caniuse-lite": "^1.0.30001806", + "electron-to-chromium": "^1.5.393", + "node-releases": "^2.0.51", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cac": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/cac/-/cac-7.0.0.tgz", + "integrity": "sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==", + "license": "MIT", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001806", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz", + "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==", + "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/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmmirror.com/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "license": "MIT" + }, + "node_modules/confbox": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", + "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmmirror.com/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "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/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "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==", + "license": "MIT" + }, + "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/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/default-browser": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz", + "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defu": { + "version": "6.1.7", + "resolved": "https://registry.npmmirror.com/defu/-/defu-6.1.7.tgz", + "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==", + "license": "MIT" + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "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/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmmirror.com/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.399", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.399.tgz", + "integrity": "sha512-lEcqhErbHjXRvd41rnWLpzbyU/IXfIYo7QwaFWmxGeLiLyY2TBCdHnWY88vB+p3ubnihRypDm66panXl7TylLA==", + "dev": true, + "license": "ISC" + }, + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-stack-parser-es": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz", + "integrity": "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "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/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.8.0.tgz", + "integrity": "sha512-nuKKvN+oIBO0koN7Tm7dlkmnkc21mtt0QJLwAKzjLq14y6lRTdVG36MZHJ8eQHwdJMwZbQNMlPOYedMq/oVJvQ==", + "dev": true, + "license": "MIT", + "workspaces": [ + "packages/*" + ], + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.7.0", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.2", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-oxlint": { + "version": "1.73.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-oxlint/-/eslint-plugin-oxlint-1.73.0.tgz", + "integrity": "sha512-2qsGYkwpas99+jLmB7F3W8Gv+7QkHIr67AMw10UTs/QAMCm2eO0Z8B8ROMvAgyKaWIitkcvH8DwLfTrHMeXzCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsonc-parser": "^3.3.1" + }, + "peerDependencies": { + "oxlint": "~1.73.0" + } + }, + "node_modules/eslint-plugin-vue": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-10.9.2.tgz", + "integrity": "sha512-4g7ZP3pYcuqd7Zp0pzUKcos0W+RkjBz4EGdhJ92FcYk6v03Ti/GK5NwjgsjxHK+98eXDbHeK7VtX1az7/8doZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "natural-compare": "^1.4.0", + "nth-check": "^2.1.1", + "postcss-selector-parser": "^7.1.0", + "semver": "^7.6.3", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "@stylistic/eslint-plugin": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0", + "@typescript-eslint/parser": "^7.0.0 || ^8.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "vue-eslint-parser": "^10.3.0" + }, + "peerDependenciesMeta": { + "@stylistic/eslint-plugin": { + "optional": true + }, + "@typescript-eslint/parser": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-vue/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/exsolve": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.1.1.tgz", + "integrity": "sha512-9U/jZUgjnSGyntRr6y5Muu1MJcwFl6kPu7k8qLF0IMNfLqvw0NZ4nnVDq0RVoZ0RvCyumib4Ez3KYrVfilrw+g==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", + "dev": true, + "license": "ISC" + }, + "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/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/gzip-size": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "license": "MIT", + "dependencies": { + "duplexer": "^0.1.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "license": "MIT" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "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/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-in-ssh": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-in-ssh/-/is-in-ssh-1.0.0.tgz", + "integrity": "sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jalaali-js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jalaali-js/-/jalaali-js-2.0.0.tgz", + "integrity": "sha512-HkWlwO3KxuYwERP1jsn+5M+QA+EKpIJ+zGesLee5VJNn2d2Melue0uQIvd9C/0QYFR7XVWvfF/uiNEz9Jbr9Hw==", + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "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==", + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-6.0.0.tgz", + "integrity": "sha512-2/8adwnK1/+Fdjyts4r6wSpfANWw8zdNhU9U/Llk59c6O+DjSisPWPykwoL8gZmocP9Dy64S7oie2g+Mia123A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^22.22.2 || ^24.15.0 || >=26.0.0" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kolorist": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", + "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "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/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/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/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/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/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/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/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/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/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/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/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/local-pkg": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.2.1.tgz", + "integrity": "sha512-++gUqRDEvcnN6Zhqrr+y/CkVEHhlrR96vZn3nZZPYzMcBUyBtTKzB9NadClFIsIVSsu+3i9tfk/erqy9kAmt7Q==", + "license": "MIT", + "dependencies": { + "mlly": "^1.7.4", + "pkg-types": "^2.3.0", + "quansync": "^0.2.11" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash-es": { + "version": "4.18.1", + "resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.18.1.tgz", + "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==", + "license": "MIT" + }, + "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/magic-regexp": { + "version": "0.10.0", + "resolved": "https://registry.npmmirror.com/magic-regexp/-/magic-regexp-0.10.0.tgz", + "integrity": "sha512-Uly1Bu4lO1hwHUW0CQeSWuRtzCMNO00CmXtS8N6fyvB3B979GOEEeAkiTUDsmbYLAbvpUS/Kt5c4ibosAzVyVg==", + "license": "MIT", + "dependencies": { + "estree-walker": "^3.0.3", + "magic-string": "^0.30.12", + "mlly": "^1.7.2", + "regexp-tree": "^0.1.27", + "type-level-regexp": "~0.1.17", + "ufo": "^1.5.4", + "unplugin": "^2.0.0" + } + }, + "node_modules/magic-regexp/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/magic-regexp/node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/magic-regexp/node_modules/unplugin": { + "version": "2.3.11", + "resolved": "https://registry.npmmirror.com/unplugin/-/unplugin-2.3.11.tgz", + "integrity": "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "acorn": "^8.15.0", + "picomatch": "^4.0.3", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": ">=18.12.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==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/magic-string-ast": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/magic-string-ast/-/magic-string-ast-1.0.3.tgz", + "integrity": "sha512-CvkkH1i81zl7mmb94DsRiFeG9V2fR2JeuK8yDgS8oiZSFa++wWLEgZ5ufEOyLHbvSbD1gTRKv9NdX69Rnvr9JA==", + "license": "MIT", + "dependencies": { + "magic-string": "^0.30.19" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "license": "CC0-1.0" + }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mlly": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", + "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", + "license": "MIT", + "dependencies": { + "acorn": "^8.16.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.3" + } + }, + "node_modules/mlly/node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "license": "MIT" + }, + "node_modules/mlly/node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "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/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", + "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", + "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/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmmirror.com/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.51", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz", + "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/nostics": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/nostics/-/nostics-1.2.0.tgz", + "integrity": "sha512-FGqEfhQjrvo1lL8KFifdTQiNwwQHJxC1jtYE1Rc54qF/jxONUNL+kC9gS1krX8Q65PgrQ5fCqH/I4NhWBvdSqg==", + "license": "MIT" + }, + "node_modules/npm-normalize-package-bin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-6.0.0.tgz", + "integrity": "sha512-tdt4aFn9QamlhdN3HV2D2ccpBwO5/fyjjbXUxYA6uBjyekMZcZvDq0aSj9t5Jo+tih6AYFnt/cuIRn9013e0Uw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^22.22.2 || ^24.15.0 || >=26.0.0" + } + }, + "node_modules/npm-run-all2": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/npm-run-all2/-/npm-run-all2-9.0.3.tgz", + "integrity": "sha512-BQAEdU1PtYc48qYRdghW2BVTQT3VqWCoFQmO87NlM1h1PYwMCKQpUWaNyB20V26caNzFsXQDfyOdznLlHCih6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^7.0.0", + "cross-spawn": "^7.0.6", + "memorystream": "^0.3.1", + "picomatch": "^4.0.2", + "pidtree": "^1.0.0", + "read-package-json-fast": "^6.0.0", + "shell-quote": "^1.8.4", + "which": "^7.0.0" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "npm-run-all2": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, + "engines": { + "node": "^22.22.2 || ^24.15.0 || >=26.0.0", + "npm": ">= 10" + } + }, + "node_modules/npm-run-all2/node_modules/isexe": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=20" + } + }, + "node_modules/npm-run-all2/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/npm-run-all2/node_modules/which": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-7.0.0.tgz", + "integrity": "sha512-RancgH2dmbLdHl6LRhEqvklWMgl/Hdnun0Y90KhBOLkMefg8Qa7/Zel8Sm+8HEcP6DEjzsWzpkuBQEZok58isA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^4.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^22.22.2 || ^24.15.0 || >=26.0.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/obug": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.4.tgz", + "integrity": "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/ofetch": { + "version": "1.5.1", + "resolved": "https://registry.npmmirror.com/ofetch/-/ofetch-1.5.1.tgz", + "integrity": "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==", + "license": "MIT", + "dependencies": { + "destr": "^2.0.5", + "node-fetch-native": "^1.6.7", + "ufo": "^1.6.1" + } + }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/open": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/open/-/open-11.0.0.tgz", + "integrity": "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.4.0", + "define-lazy-prop": "^3.0.0", + "is-in-ssh": "^1.0.0", + "is-inside-container": "^1.0.0", + "powershell-utils": "^0.1.0", + "wsl-utils": "^0.3.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/oxc-parser": { + "version": "0.131.0", + "resolved": "https://registry.npmmirror.com/oxc-parser/-/oxc-parser-0.131.0.tgz", + "integrity": "sha512-SJ3/7ZPbgie8dr5Z9BI/M51zZbpXba+hRSG0MDzVwMW5CRQg2fjYE0jHGlLX4eeiibGgC/mzoDFKSDHwVZEHRQ==", + "license": "MIT", + "dependencies": { + "@oxc-project/types": "^0.131.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/Boshen" + }, + "optionalDependencies": { + "@oxc-parser/binding-android-arm-eabi": "0.131.0", + "@oxc-parser/binding-android-arm64": "0.131.0", + "@oxc-parser/binding-darwin-arm64": "0.131.0", + "@oxc-parser/binding-darwin-x64": "0.131.0", + "@oxc-parser/binding-freebsd-x64": "0.131.0", + "@oxc-parser/binding-linux-arm-gnueabihf": "0.131.0", + "@oxc-parser/binding-linux-arm-musleabihf": "0.131.0", + "@oxc-parser/binding-linux-arm64-gnu": "0.131.0", + "@oxc-parser/binding-linux-arm64-musl": "0.131.0", + "@oxc-parser/binding-linux-ppc64-gnu": "0.131.0", + "@oxc-parser/binding-linux-riscv64-gnu": "0.131.0", + "@oxc-parser/binding-linux-riscv64-musl": "0.131.0", + "@oxc-parser/binding-linux-s390x-gnu": "0.131.0", + "@oxc-parser/binding-linux-x64-gnu": "0.131.0", + "@oxc-parser/binding-linux-x64-musl": "0.131.0", + "@oxc-parser/binding-openharmony-arm64": "0.131.0", + "@oxc-parser/binding-wasm32-wasi": "0.131.0", + "@oxc-parser/binding-win32-arm64-msvc": "0.131.0", + "@oxc-parser/binding-win32-ia32-msvc": "0.131.0", + "@oxc-parser/binding-win32-x64-msvc": "0.131.0" + } + }, + "node_modules/oxc-parser/node_modules/@oxc-project/types": { + "version": "0.131.0", + "resolved": "https://registry.npmmirror.com/@oxc-project/types/-/types-0.131.0.tgz", + "integrity": "sha512-PgnWDfV0h+b16XNKbXU7Daib/BFSt/J2mEzfYIBu6JB/wNdlU+kVYXCkGA1A9fWkTbOgbjh4e6NhPeQOYvFhEA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/oxc-walker": { + "version": "0.7.0", + "resolved": "https://registry.npmmirror.com/oxc-walker/-/oxc-walker-0.7.0.tgz", + "integrity": "sha512-54B4KUhrzbzc4sKvKwVYm7E2PgeROpGba0/2nlNZMqfDyca+yOor5IMb4WLGBatGDT0nkzYdYuzylg7n3YfB7A==", + "license": "MIT", + "dependencies": { + "magic-regexp": "^0.10.0" + }, + "peerDependencies": { + "oxc-parser": ">=0.98.0" + } + }, + "node_modules/oxlint": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.74.0.tgz", + "integrity": "sha512-odGl2s2x5IOJoj3A0v1k0PGBXVFBZeZ2+AK/+K2MJur7Ghi3bkyX5NuLUWHKqa4js1wjep3hJeuTQJOlr+4+dA==", + "dev": true, + "license": "MIT", + "bin": { + "oxlint": "bin/oxlint" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/Boshen" + }, + "optionalDependencies": { + "@oxlint/binding-android-arm-eabi": "1.74.0", + "@oxlint/binding-android-arm64": "1.74.0", + "@oxlint/binding-darwin-arm64": "1.74.0", + "@oxlint/binding-darwin-x64": "1.74.0", + "@oxlint/binding-freebsd-x64": "1.74.0", + "@oxlint/binding-linux-arm-gnueabihf": "1.74.0", + "@oxlint/binding-linux-arm-musleabihf": "1.74.0", + "@oxlint/binding-linux-arm64-gnu": "1.74.0", + "@oxlint/binding-linux-arm64-musl": "1.74.0", + "@oxlint/binding-linux-ppc64-gnu": "1.74.0", + "@oxlint/binding-linux-riscv64-gnu": "1.74.0", + "@oxlint/binding-linux-riscv64-musl": "1.74.0", + "@oxlint/binding-linux-s390x-gnu": "1.74.0", + "@oxlint/binding-linux-x64-gnu": "1.74.0", + "@oxlint/binding-linux-x64-musl": "1.74.0", + "@oxlint/binding-openharmony-arm64": "1.74.0", + "@oxlint/binding-win32-arm64-msvc": "1.74.0", + "@oxlint/binding-win32-ia32-msvc": "1.74.0", + "@oxlint/binding-win32-x64-msvc": "1.74.0" + }, + "peerDependencies": { + "oxlint-tsgolint": ">=0.24.0", + "vite-plus": "*" + }, + "peerDependenciesMeta": { + "oxlint-tsgolint": { + "optional": true + }, + "vite-plus": { + "optional": true + } + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-manager-detector": { + "version": "1.8.0", + "resolved": "https://registry.npmmirror.com/package-manager-detector/-/package-manager-detector-1.8.0.tgz", + "integrity": "sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==", + "license": "MIT" + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "license": "MIT" + }, + "node_modules/perfect-debounce": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.1.0.tgz", + "integrity": "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-1.0.0.tgz", + "integrity": "sha512-avfAvjB9Dd0wdj3rjJX//yS+G79OO0KrS5pJHFJENjYGX6N4SMgEDBBI/yFy0lloOYSaC6XQxzpOAMPfSYFV/Q==", + "dev": true, + "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/pinia": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-4.0.2.tgz", + "integrity": "sha512-yKVVA7bSj5oRZFp/Ab9wLlmyb5gPUYEiIm4ryiWTe/xe7PtkRdMVOp1X1ggvq0c6Uj7Q0Du1HnV2mtAwM0Ks1g==", + "license": "MIT", + "dependencies": { + "nostics": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "@vue/devtools-api": "^8.1.5", + "typescript": ">=5.6.0", + "vue": "^3.5.11" + }, + "peerDependenciesMeta": { + "@vue/devtools-api": { + "optional": false + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/pkg-types": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.1.tgz", + "integrity": "sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==", + "license": "MIT", + "dependencies": { + "confbox": "^0.2.4", + "exsolve": "^1.0.8", + "pathe": "^2.0.3" + } + }, + "node_modules/postcss": { + "version": "8.5.25", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.25.tgz", + "integrity": "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==", + "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.16", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz", + "integrity": "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/powershell-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.1.0.tgz", + "integrity": "sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.9.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.5.tgz", + "integrity": "sha512-/FVl766LpUfB5vXgCYOYa0MeV/441Ia99AeICQIQFTY/Nw0roZwULcXpku5i1/m5kt/baz+s4Zogspd839HSMg==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/quansync": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz", + "integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/read-package-json-fast": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-6.0.0.tgz", + "integrity": "sha512-PNaGjoCnw9DBA2Kl8D+8po957z778q/HOPuY2u3Bkw/JO3eC8MDx7jn/PgMtSgpcBbs+6UOjDbwReGpXmRvs0g==", + "dev": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^6.0.0", + "npm-normalize-package-bin": "^6.0.0" + }, + "engines": { + "node": "^22.22.2 || ^24.15.0 || >=26.0.0" + } + }, + "node_modules/readdirp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/regexp-tree": { + "version": "0.1.27", + "resolved": "https://registry.npmmirror.com/regexp-tree/-/regexp-tree-0.1.27.tgz", + "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", + "license": "MIT", + "bin": { + "regexp-tree": "bin/regexp-tree" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rolldown": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.1.tgz", + "integrity": "sha512-4FKJhg8d3OiyQOA6Q1Q0hoFFpW9/OoX+VsHzpECsdsIZoOArrAK90gl59YK/Z+gnDel45bgJZK03ozH/9bCqEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.142.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.2.1", + "@rolldown/binding-darwin-arm64": "1.2.1", + "@rolldown/binding-darwin-x64": "1.2.1", + "@rolldown/binding-freebsd-x64": "1.2.1", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.1", + "@rolldown/binding-linux-arm64-gnu": "1.2.1", + "@rolldown/binding-linux-arm64-musl": "1.2.1", + "@rolldown/binding-linux-ppc64-gnu": "1.2.1", + "@rolldown/binding-linux-s390x-gnu": "1.2.1", + "@rolldown/binding-linux-x64-gnu": "1.2.1", + "@rolldown/binding-linux-x64-musl": "1.2.1", + "@rolldown/binding-openharmony-arm64": "1.2.1", + "@rolldown/binding-wasm32-wasi": "1.2.1", + "@rolldown/binding-win32-arm64-msvc": "1.2.1", + "@rolldown/binding-win32-x64-msvc": "1.2.1" + } + }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/scule": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/scule/-/scule-1.3.0.tgz", + "integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==", + "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/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.10.0.tgz", + "integrity": "sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sirv": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "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==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyexec": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/tinyexec/-/tinyexec-1.3.0.tgz", + "integrity": "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "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/tinyglobby/node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "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==", + "license": "0BSD", + "optional": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-level-regexp": { + "version": "0.1.17", + "resolved": "https://registry.npmmirror.com/type-level-regexp/-/type-level-regexp-0.1.17.tgz", + "integrity": "sha512-wTk4DH3cxwk196uGLK/E9pE45aLfeKJacKmcEgEOA/q5dnPGNxXt0cfYdFxb57L+sEpf1oJH4Dnx/pnRcku9jg==", + "license": "MIT" + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.65.0.tgz", + "integrity": "sha512-/ggrHAwyjENDusvyxbuqxAC2dTnZg/Z8F+fgQtYIz+L6n/9HfSlEZcFGV/NsMNa6CkGk0xUjUAFwC0vHOflvIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.65.0", + "@typescript-eslint/parser": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0", + "@typescript-eslint/utils": "8.65.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/ufo": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", + "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", + "license": "MIT" + }, + "node_modules/unconfig": { + "version": "7.5.0", + "resolved": "https://registry.npmmirror.com/unconfig/-/unconfig-7.5.0.tgz", + "integrity": "sha512-oi8Qy2JV4D3UQ0PsopR28CzdQ3S/5A1zwsUwp/rosSbfhJ5z7b90bIyTwi/F7hCLD4SGcZVjDzd4XoUQcEanvA==", + "license": "MIT", + "dependencies": { + "@quansync/fs": "^1.0.0", + "defu": "^6.1.4", + "jiti": "^2.6.1", + "quansync": "^1.0.0", + "unconfig-core": "7.5.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/unconfig-core": { + "version": "7.5.0", + "resolved": "https://registry.npmmirror.com/unconfig-core/-/unconfig-core-7.5.0.tgz", + "integrity": "sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==", + "license": "MIT", + "dependencies": { + "@quansync/fs": "^1.0.0", + "quansync": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/unconfig-core/node_modules/quansync": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/quansync/-/quansync-1.0.0.tgz", + "integrity": "sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, + "node_modules/unconfig/node_modules/quansync": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/quansync/-/quansync-1.0.0.tgz", + "integrity": "sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/unocss": { + "version": "66.7.5", + "resolved": "https://registry.npmmirror.com/unocss/-/unocss-66.7.5.tgz", + "integrity": "sha512-nAdmU8TwnQoiLnQjZ6Hm1GmHy9lTexKsAZNEJtZnxN9wyFRc1eLjQgmh9r7UEGxBQMQLD8OZOgmk5HpwObbh0Q==", + "license": "MIT", + "dependencies": { + "@unocss/cli": "66.7.5", + "@unocss/core": "66.7.5", + "@unocss/preset-attributify": "66.7.5", + "@unocss/preset-icons": "66.7.5", + "@unocss/preset-mini": "66.7.5", + "@unocss/preset-tagify": "66.7.5", + "@unocss/preset-typography": "66.7.5", + "@unocss/preset-uno": "66.7.5", + "@unocss/preset-web-fonts": "66.7.5", + "@unocss/preset-wind": "66.7.5", + "@unocss/preset-wind3": "66.7.5", + "@unocss/preset-wind4": "66.7.5", + "@unocss/transformer-attributify-jsx": "66.7.5", + "@unocss/transformer-compile-class": "66.7.5", + "@unocss/transformer-directives": "66.7.5", + "@unocss/transformer-variant-group": "66.7.5", + "@unocss/vite": "66.7.5" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@unocss/astro": "66.7.5", + "@unocss/postcss": "66.7.5", + "@unocss/webpack": "66.7.5" + }, + "peerDependenciesMeta": { + "@unocss/astro": { + "optional": true + }, + "@unocss/postcss": { + "optional": true + }, + "@unocss/webpack": { + "optional": true + } + } + }, + "node_modules/unplugin": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-3.3.0.tgz", + "integrity": "sha512-qa66K+crbfyE6JK10GjvbJeRrOsuC/JpbnHctfyp/i4oBTxWOzJfRZyDiOk1PtErMFRu8JhsU/wPvOdBNWe5Rg==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "picomatch": "^4.0.4", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@farmfe/core": "*", + "@rspack/core": "*", + "bun-types-no-globals": "*", + "esbuild": "*", + "rolldown": "*", + "rollup": "*", + "unloader": "*", + "vite": "*", + "webpack": "*" + }, + "peerDependenciesMeta": { + "@farmfe/core": { + "optional": true + }, + "@rspack/core": { + "optional": true + }, + "bun-types-no-globals": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "rolldown": { + "optional": true + }, + "rollup": { + "optional": true + }, + "unloader": { + "optional": true + }, + "vite": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/unplugin-utils": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/unplugin-utils/-/unplugin-utils-0.3.2.tgz", + "integrity": "sha512-xVToRh2CTmLk2HnEG7ac4rl1MJTT3RFkpS8B++/SnB0kXvuaavD+n3m/vrzyWQOdJNSZQACnbz01pnppbwV5BA==", + "license": "MIT", + "dependencies": { + "pathe": "^2.0.3", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/unplugin-utils/node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/unplugin/node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "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/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.0.tgz", + "integrity": "sha512-pn+CFpM0lwDeKwmOq1ZaBK/9sjorZcgqxki6MbY/jPEVd9vichIlmlD4HmQ5wdP5EgqQCFRaACBxMC7uEGc6lQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.23", + "rolldown": "~1.2.0", + "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", + "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-dev-rpc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/vite-dev-rpc/-/vite-dev-rpc-2.0.0.tgz", + "integrity": "sha512-yKwbTwdHKSD2k/aGqyWpPHepo45OQc8lH3/6IfT4ZqeKE26ooKvi4WIEKzqWav8v+9Is8u1k8q54hvOmqASazA==", + "dev": true, + "license": "MIT", + "dependencies": { + "birpc": "^4.0.0", + "vite-hot-client": "^2.2.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^2.9.0 || ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.1 || ^7.0.0-0 || ^8.0.0" + } + }, + "node_modules/vite-dev-rpc/node_modules/birpc": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-4.0.0.tgz", + "integrity": "sha512-LShSxJP0KTmd101b6DRyGBj57LZxSDYWKitQNW/mi8GRMvZb078Uf9+pveax1DrVL89vm7mWe+TovdI/UDOuPw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/vite-hot-client": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/vite-hot-client/-/vite-hot-client-2.2.0.tgz", + "integrity": "sha512-76Zs9zrHbH7M7wqeyooGQKdX+yg0pQ0xuQ1PbFp4z5a0Lzn2e5IPFoCswnmqZ4GiwqB4Jo3WcDAMO9jARTJl8w==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 || ^8.0.0" + } + }, + "node_modules/vite-plugin-inspect": { + "version": "11.4.1", + "resolved": "https://registry.npmjs.org/vite-plugin-inspect/-/vite-plugin-inspect-11.4.1.tgz", + "integrity": "sha512-ShOFe2PURXGvRS5OrgmOLZOCwDTD7dEBVt0tMpFPKb9AsvqXKCRGM8QgKrUbRbJYFXScHvDPpGRd28rYidC0tA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansis": "^4.3.0", + "error-stack-parser-es": "^1.0.5", + "obug": "^2.1.1", + "ohash": "^2.0.11", + "open": "^11.0.0", + "perfect-debounce": "^2.1.0", + "sirv": "^3.0.2", + "unplugin-utils": "^0.3.1", + "vite-dev-rpc": "^2.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^6.0.0 || ^7.0.0-0 || ^8.0.0-0" + }, + "peerDependenciesMeta": { + "@nuxt/kit": { + "optional": true + } + } + }, + "node_modules/vite-plugin-vue-devtools": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/vite-plugin-vue-devtools/-/vite-plugin-vue-devtools-8.2.1.tgz", + "integrity": "sha512-5JLxXWWCo5lJMw16/xVeNvJ8k2zLwZPf1vITLzya/2IePrCBeGe/p/iAokgXHZpEi39fcYtPXmO8SaKeXmqCAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-core": "^8.2.1", + "@vue/devtools-kit": "^8.2.1", + "@vue/devtools-shared": "^8.2.1", + "sirv": "^3.0.2", + "vite-plugin-inspect": "^11.3.3", + "vite-plugin-vue-inspector": "^6.0.0" + }, + "engines": { + "node": ">=v14.21.3" + }, + "peerDependencies": { + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/vite-plugin-vue-inspector": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/vite-plugin-vue-inspector/-/vite-plugin-vue-inspector-6.0.0.tgz", + "integrity": "sha512-OpyITJLgZNibxlrik1EmRtvXHDjLRxNPsWkGFTERZs2LgMEdG4W0WoFt5GIgp3a3jRou+eJR8U1zOBk/XQgEbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.23.0", + "@babel/plugin-proposal-decorators": "^7.23.0", + "@babel/plugin-syntax-import-attributes": "^7.22.5", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-transform-typescript": "^7.22.15", + "@vue/babel-plugin-jsx": "^1.1.5", + "@vue/compiler-dom": "^3.3.4", + "kolorist": "^1.8.0", + "magic-string": "^0.30.4" + }, + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/vite-plugin-vue-inspector/node_modules/@vue/babel-helper-vue-transform-on": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.5.0.tgz", + "integrity": "sha512-0dAYkerNhhHutHZ34JtTl2czVQHUNWv6xEbkdF5W+Yrv5pCWsqjeORdOgbtW2I9gWlt+wBmVn+ttqN9ZxR5tzA==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite-plugin-vue-inspector/node_modules/@vue/babel-plugin-jsx": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.5.0.tgz", + "integrity": "sha512-mneBhw1oOqCd2247O0Yw/mRwC9jIGACAJUlawkmMBiNmL4dGA2eMzuNZVNqOUfYTa6vqmND4CtOPzmEEEqLKFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.0", + "@babel/types": "^7.28.2", + "@vue/babel-helper-vue-transform-on": "1.5.0", + "@vue/babel-plugin-resolve-type": "1.5.0", + "@vue/shared": "^3.5.18" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + } + } + }, + "node_modules/vite-plugin-vue-inspector/node_modules/@vue/babel-plugin-resolve-type": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vue/babel-plugin-resolve-type/-/babel-plugin-resolve-type-1.5.0.tgz", + "integrity": "sha512-Wm/60o+53JwJODm4Knz47dxJnLDJ9FnKnGZJbUUf8nQRAtt6P+undLUAVU3Ha33LxOJe6IPoifRQ6F/0RrU31w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/parser": "^7.28.0", + "@vue/compiler-sfc": "^3.5.18" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/vite/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/vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.40.tgz", + "integrity": "sha512-+8PJ4SJXdn/cHGImF4CKdxlWHIN5Dkt7DoufRREM6h6uVCx2m7QxgcEQmmzyOK8A9mcafg7sFbJFYsdFVubTig==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.40", + "@vue/compiler-sfc": "3.5.40", + "@vue/runtime-dom": "3.5.40", + "@vue/server-renderer": "3.5.40", + "@vue/shared": "3.5.40" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-eslint-parser": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-10.4.1.tgz", + "integrity": "sha512-Gk6gRDj0n/fkRa3C3l0bBheoBckUq/Rs0F/TvMWIS6nzzx67amAViMe9CkNgsP2tXyQONvGiHQESHwFtZ3aYDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "eslint-scope": "^8.2.0 || ^9.0.0", + "eslint-visitor-keys": "^4.2.0 || ^5.0.0", + "espree": "^10.3.0 || ^11.0.0", + "esquery": "^1.6.0", + "semver": "^7.6.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/vue-eslint-parser/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/vue-eslint-parser/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/vue-router": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-5.2.0.tgz", + "integrity": "sha512-QAC5i0LEb1GLG0LXDQmHu8L7FX12j0KwU/JTKmLQUJMrn04gQdKP6Du+p0QwpHb3iy71vBlqnHQ8WAfOSAWhqw==", + "license": "MIT", + "dependencies": { + "@babel/generator": "^8.0.0", + "@vue-macros/common": "^3.1.3", + "@vue/devtools-api": "^8.1.5", + "ast-walker-scope": "^0.9.0", + "chokidar": "^5.0.0", + "json5": "^2.2.3", + "local-pkg": "^1.2.1", + "magic-string": "^0.30.21", + "mlly": "^1.8.2", + "muggle-string": "^0.4.1", + "nostics": "^1.1.4", + "pathe": "^2.0.3", + "picomatch": "^4.0.5", + "scule": "^1.3.0", + "tinyglobby": "^0.2.17", + "unplugin": "^3.3.0", + "unplugin-utils": "^0.3.2", + "yaml": "^2.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "@pinia/colada": ">=0.21.2", + "@vue/compiler-sfc": "^3.5.34 || ^4.0.0", + "pinia": "^3.0.4 || ^4.0.2", + "vite": "^7.3.0 || ^8.0.0", + "vue": "^3.5.34 || ^4.0.0" + }, + "peerDependenciesMeta": { + "@pinia/colada": { + "optional": true + }, + "@vue/compiler-sfc": { + "optional": true + }, + "pinia": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/vue-router/node_modules/@babel/generator": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-8.0.0.tgz", + "integrity": "sha512-NT9NrVwJsbSV6Y2FSstWa71EETOnzrjkL5/wX3D2mYHtKM+qvqB1DvR4D0Setb/gDBsHzRICifwEWMO8CnTF6g==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^8.0.0", + "@babel/types": "^8.0.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "@types/jsesc": "^2.5.0", + "jsesc": "^3.0.2" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/vue-router/node_modules/@babel/helper-string-parser": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-8.0.0.tgz", + "integrity": "sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg==", + "license": "MIT", + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/vue-router/node_modules/@babel/helper-validator-identifier": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.4.tgz", + "integrity": "sha512-4wFaiLd0bVo4cIoTXI3zKI038NIWE/cr3jvBjejOVYVxV/m8Ltav1USiGzG1fmS5J2RhgEOgXNNK46cRPnRsrg==", + "license": "MIT", + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/vue-router/node_modules/@babel/parser": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-8.0.4.tgz", + "integrity": "sha512-srpptsAkEbbNIC/q8nT7o+m6CQe8CJUTV/t7MYc9NnWlgYVtHOb7JH6SorxMhN0kuRJjVqXbKClG6xSbPtzz+g==", + "license": "MIT", + "dependencies": { + "@babel/types": "^8.0.4" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/vue-router/node_modules/@babel/types": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-8.0.4.tgz", + "integrity": "sha512-eY+Yn3dCqTGmyiq2QRU66lA5FL8lqqqvecHt0fF3uHONIa7ToYsaCiWV8lOKqAs0Rb2SjixiKFROngnulPtt2g==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^8.0.0", + "@babel/helper-validator-identifier": "^8.0.4" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/vue-router/node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vue-tsc": { + "version": "3.3.9", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.3.9.tgz", + "integrity": "sha512-TS3Y1ux/IRoE8OCP2PpACAeOseuIs0UvWrcr7u+w3PmfY+SlCfEf8zjrBgnQksHUgLpthi5vHlffcQTQTdPBZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/typescript": "2.4.28", + "@vue/language-core": "3.3.9" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } + }, + "node_modules/webpack-virtual-modules": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", + "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", + "license": "MIT" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wsl-utils": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.3.1.tgz", + "integrity": "sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0", + "powershell-utils": "^0.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12" + } + }, + "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/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..72ae649 --- /dev/null +++ b/package.json @@ -0,0 +1,52 @@ +{ + "name": "fabric-trade", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "run-p type-check \"build-only {@}\" --", + "preview": "vite preview", + "build-only": "vite build", + "type-check": "vue-tsc --build", + "lint": "run-s \"lint:*\"", + "lint:oxlint": "oxlint . --fix", + "lint:eslint": "eslint . --fix --cache", + "format": "prettier --write --experimental-cli src/" + }, + "dependencies": { + "@unocss/vite": "^66.7.5", + "@vueuse/core": "^14.4.0", + "jalaali-js": "^2.0.0", + "lodash-es": "^4.18.1", + "pinia": "^4.0.2", + "unocss": "^66.7.5", + "vue": "^3.5.40", + "vue-router": "^5.2.0" + }, + "devDependencies": { + "@tsconfig/node24": "^24.0.4", + "@types/lodash-es": "^4.17.12", + "@types/node": "^24.13.3", + "@vitejs/plugin-vue": "^6.0.8", + "@vitejs/plugin-vue-jsx": "^5.1.6", + "@vue/eslint-config-typescript": "^14.9.0", + "@vue/tsconfig": "^0.9.1", + "eslint": "^10.7.0", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-oxlint": "~1.73.0", + "eslint-plugin-vue": "~10.9.2", + "jiti": "^2.7.0", + "npm-run-all2": "^9.0.2", + "oxlint": "~1.74.0", + "prettier": "3.9.5", + "typescript": "~6.0.0", + "vite": "^8.1.5", + "vite-plugin-vue-devtools": "^8.1.5", + "vue-eslint-parser": "^10.4.1", + "vue-tsc": "^3.3.7" + }, + "engines": { + "node": "^22.18.0 || >=24.12.0" + } +} diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..df36fcf Binary files /dev/null and b/public/favicon.ico differ diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 0000000..899ef21 --- /dev/null +++ b/src/App.vue @@ -0,0 +1,72 @@ + + + diff --git a/src/assets/fonts/iranSans/woff2/IRANSansWeb(FaNum).woff2 b/src/assets/fonts/iranSans/woff2/IRANSansWeb(FaNum).woff2 new file mode 100644 index 0000000..c3dcbab Binary files /dev/null and b/src/assets/fonts/iranSans/woff2/IRANSansWeb(FaNum).woff2 differ diff --git a/src/assets/fonts/iranSans/woff2/IRANSansWeb(FaNum)_Black.woff2 b/src/assets/fonts/iranSans/woff2/IRANSansWeb(FaNum)_Black.woff2 new file mode 100644 index 0000000..fc008ef Binary files /dev/null and b/src/assets/fonts/iranSans/woff2/IRANSansWeb(FaNum)_Black.woff2 differ diff --git a/src/assets/fonts/iranSans/woff2/IRANSansWeb(FaNum)_Bold.woff2 b/src/assets/fonts/iranSans/woff2/IRANSansWeb(FaNum)_Bold.woff2 new file mode 100644 index 0000000..69f7c66 Binary files /dev/null and b/src/assets/fonts/iranSans/woff2/IRANSansWeb(FaNum)_Bold.woff2 differ diff --git a/src/assets/fonts/iranSans/woff2/IRANSansWeb(FaNum)_Light.woff2 b/src/assets/fonts/iranSans/woff2/IRANSansWeb(FaNum)_Light.woff2 new file mode 100644 index 0000000..40f5b09 Binary files /dev/null and b/src/assets/fonts/iranSans/woff2/IRANSansWeb(FaNum)_Light.woff2 differ diff --git a/src/assets/fonts/iranSans/woff2/IRANSansWeb(FaNum)_Medium.woff2 b/src/assets/fonts/iranSans/woff2/IRANSansWeb(FaNum)_Medium.woff2 new file mode 100644 index 0000000..8273b0a Binary files /dev/null and b/src/assets/fonts/iranSans/woff2/IRANSansWeb(FaNum)_Medium.woff2 differ diff --git a/src/assets/fonts/iranSans/woff2/IRANSansWeb(FaNum)_UltraLight.woff2 b/src/assets/fonts/iranSans/woff2/IRANSansWeb(FaNum)_UltraLight.woff2 new file mode 100644 index 0000000..8b77d17 Binary files /dev/null and b/src/assets/fonts/iranSans/woff2/IRANSansWeb(FaNum)_UltraLight.woff2 differ diff --git a/src/assets/styles/app-shell.css b/src/assets/styles/app-shell.css new file mode 100644 index 0000000..86745f8 --- /dev/null +++ b/src/assets/styles/app-shell.css @@ -0,0 +1,209 @@ +/* Application shell — global (unscoped) layout breakpoints */ + +.app-shell { + height: 100dvh; + display: flex; + width: 100%; + background: var(--color-shell-bg); + overflow: hidden; +} + +.app-layout { + display: flex; + flex-direction: row; + width: 100%; + height: 100%; + min-height: 0; +} + +.app-frame { + width: 100%; + height: 100%; + background: var(--color-bg); + color: var(--color-text); + display: flex; + flex-direction: column; + overflow: hidden; + min-width: 0; + flex: 1; +} + +.app-main { + flex: 1; + display: flex; + flex-direction: column; + min-height: 0; + overflow: hidden; +} + +.app-main__content { + flex: 1; + display: flex; + flex-direction: column; + min-height: 0; + overflow: hidden; +} + +.app-content { + flex: 1; + display: flex; + flex-direction: column; + min-height: 0; + width: 100%; + max-width: var(--content-max-width); + margin-inline: auto; + padding-inline: 0; + position: relative; +} + +.page-transition { + flex: 1; + min-height: 0; + display: flex; + flex-direction: column; + width: 100%; + overflow: hidden; +} + +/* ── Mobile: full-width native app (< 768px) ── */ +.app-shell--with-nav { + justify-content: stretch; +} + +.app-shell--with-nav .app-frame { + max-width: 100%; + margin: 0; + border-radius: 0; + box-shadow: none; +} + +/* ── Tablet + Desktop: sidebar + header shell (>= 768px) ── */ +@media (min-width: 768px) { + .app-shell--with-nav { + background: var(--color-page-background); + } + + .app-shell--with-nav .app-frame { + background: var(--color-page-background); + } + + .app-shell--with-nav .app-layout { + position: relative; + } + + /* One continuous divider under sidebar brand + top header */ + .app-shell--with-nav .app-layout::after { + content: ''; + position: absolute; + top: var(--header-height); + inset-inline: 0; + height: 1px; + background: var(--color-border); + z-index: 30; + pointer-events: none; + } + + /* + * Scroll lives on app-main__content. Children must not shrink+clip + * (flex + overflow:hidden), or short viewports hide the bottom of pages. + */ + .app-shell--with-nav .app-main__content { + overflow-x: hidden; + overflow-y: auto; + overscroll-behavior: contain; + -webkit-overflow-scrolling: touch; + } + + .app-shell--with-nav .app-content, + .app-shell--with-nav .page-transition { + flex: 1 0 auto; + min-height: min-content; + overflow: visible; + } + + /* Let route roots grow with content so the shell scroll area can work */ + .app-shell--with-nav .page-transition > * { + flex: 1 0 auto; + min-height: min-content; + height: auto; + max-height: none; + overflow: visible; + } +} + +/* ── Guest pages (login, register, checkout) — all breakpoints ── */ +.app-shell--no-nav { + align-items: center; + justify-content: center; + padding: 16px; + background: var(--color-page-background); +} + +.app-shell--no-nav .app-layout { + max-width: var(--form-max-width); + width: 100%; + height: auto; + max-height: calc(100dvh - 32px); + min-height: 0; + overflow: hidden; +} + +.app-shell--no-nav .app-frame { + max-width: var(--form-max-width); + width: 100%; + height: auto; + max-height: calc(100dvh - 32px); + min-height: 0; + border-radius: var(--radius-lg); + box-shadow: var(--shadow-frame); + background: var(--color-surface); + overflow-x: hidden; + overflow-y: auto; + overscroll-behavior: contain; + -webkit-overflow-scrolling: touch; +} + +.app-shell--no-nav .app-main, +.app-shell--no-nav .app-main__content, +.app-shell--no-nav .app-content, +.app-shell--no-nav .page-transition { + flex: 1 0 auto; + min-height: min-content; + overflow: visible; + height: auto; +} + +.app-shell--no-nav .app-content { + padding-inline: var(--page-padding); +} + +.app-shell--no-nav .page { + background: transparent; + overflow: visible; + min-height: auto; +} + +.app-shell--no-nav.app-shell--wide .app-layout, +.app-shell--no-nav.app-shell--wide .app-frame { + max-width: min(var(--content-max-width), calc(100vw - 32px)); +} + +.app-shell--no-nav.app-shell--wide .app-frame { + background: var(--color-bg); +} + +@media (min-width: 768px) { + .app-shell--no-nav { + padding: 24px; + } + + .app-shell--no-nav .app-layout, + .app-shell--no-nav .app-frame { + max-height: calc(100dvh - 48px); + } + + .app-shell--no-nav.app-shell--wide .app-layout, + .app-shell--no-nav.app-shell--wide .app-frame { + max-width: min(var(--content-max-width), calc(100vw - 48px)); + } +} diff --git a/src/assets/styles/desktop-pages.css b/src/assets/styles/desktop-pages.css new file mode 100644 index 0000000..3a72054 --- /dev/null +++ b/src/assets/styles/desktop-pages.css @@ -0,0 +1,245 @@ +/* Desktop page composition — >= 768px shell, >= 1200px full desktop */ + +@media (min-width: 768px) { + .desktop-page { + flex: 1 0 auto; + display: flex; + flex-direction: column; + min-height: auto; + width: 100%; + max-width: var(--content-max-width); + margin-inline: auto; + padding: 20px var(--page-padding) 28px; + background: transparent; + box-sizing: border-box; + } + + .desktop-page__head { + margin-bottom: 20px; + } + + .desktop-page__title { + margin: 0 0 6px; + font-size: 1.375rem; + font-weight: 700; + color: var(--color-text); + } + + .desktop-page__subtitle { + margin: 0; + font-size: 0.875rem; + color: var(--color-text-muted); + } + + .desktop-two-col { + display: grid; + grid-template-columns: minmax(0, 1fr); + gap: 24px; + align-items: start; + } + + .desktop-two-col__main { + min-width: 0; + } + + .desktop-summary-panel { + position: sticky; + top: 16px; + padding: 20px; + background: var(--color-surface); + border: 1px solid var(--color-border); + border-radius: var(--radius-md); + box-shadow: var(--shadow-card); + } + + .desktop-summary-panel__title { + margin: 0 0 16px; + padding-bottom: 12px; + border-bottom: 1px solid var(--color-border); + font-size: 0.9375rem; + font-weight: 700; + } + + .desktop-summary-panel__row { + display: flex; + justify-content: space-between; + align-items: center; + gap: 12px; + padding: 8px 0; + font-size: 0.875rem; + } + + .desktop-summary-panel__row span:first-child { + color: var(--color-text-muted); + } + + .desktop-summary-panel__row strong { + color: var(--color-text); + font-variant-numeric: tabular-nums; + } + + .desktop-summary-panel__action { + margin-top: 16px; + } + + .desktop-table-wrap { + background: var(--color-surface); + border: 1px solid var(--color-border); + border-radius: var(--radius-md); + box-shadow: var(--shadow-card); + overflow: hidden; + min-width: 0; + width: 100%; + } + + .desktop-table-scroll { + overflow-x: auto; + -webkit-overflow-scrolling: touch; + overscroll-behavior-x: contain; + } + + .desktop-table { + width: 100%; + border-collapse: collapse; + font-size: 0.875rem; + min-width: 560px; + } + + .desktop-table th { + padding: 12px 16px; + text-align: start; + font-size: 0.8125rem; + font-weight: 600; + color: var(--color-text-muted); + background: var(--color-bg); + border-bottom: 1px solid var(--color-border); + white-space: nowrap; + } + + .desktop-table td { + padding: 14px 16px; + border-bottom: 1px solid var(--color-border); + color: var(--color-text); + vertical-align: middle; + background: var(--color-surface); + } + + .desktop-table tbody tr:last-child td { + border-bottom: none; + } + + .desktop-table tbody tr:hover td { + background: var(--color-elevated); + } + + .desktop-grid-2 { + display: grid; + grid-template-columns: repeat(2, minmax(280px, 1fr)); + gap: 16px; + } + + .desktop-grid-3 { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 16px; + } + + .desktop-grid-4 { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 14px; + } + + .desktop-toolbar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + margin-bottom: 16px; + flex-wrap: wrap; + } + + .desktop-toolbar__actions { + display: flex; + align-items: center; + gap: 8px; + flex-shrink: 0; + } + + .desktop-btn-compact { + width: auto !important; + min-width: 120px; + padding-inline: 20px !important; + } + + .desktop-form-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 16px; + } + + .desktop-form-grid__full { + grid-column: 1 / -1; + } + + .desktop-checkout-shell { + width: 100%; + max-width: var(--content-max-width); + margin-inline: auto; + padding: 20px var(--page-padding) 28px; + box-sizing: border-box; + } + + .desktop-settlement-grid { + display: grid; + grid-template-columns: repeat(2, minmax(280px, 380px)); + gap: 20px; + justify-content: center; + max-width: 820px; + margin-inline: auto; + } + + .desktop-company-grid { + display: grid; + grid-template-columns: repeat(2, minmax(280px, 400px)); + gap: 20px; + justify-content: center; + max-width: 900px; + margin-inline: auto; + width: 100%; + } +} + +@media (min-width: 1200px) { + .desktop-page { + padding: 24px var(--page-padding) 32px; + } + + .desktop-checkout-shell { + padding: 24px var(--page-padding) 32px; + } + + .desktop-page__head { + margin-bottom: 24px; + } + + .desktop-two-col:has(> .desktop-summary-panel) { + grid-template-columns: minmax(0, 1fr) var(--summary-panel-width, 320px); + } + + .desktop-grid-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 20px; + } + + .desktop-grid-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 16px; + } +} + +@media (min-width: 1440px) { + .desktop-grid-3 { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } +} diff --git a/src/assets/styles/desktop.css b/src/assets/styles/desktop.css new file mode 100644 index 0000000..3897e37 --- /dev/null +++ b/src/assets/styles/desktop.css @@ -0,0 +1,613 @@ +/* App shell page layouts — 768px and above */ + +@media (min-width: 768px) { + :root { + --page-padding: var(--page-padding-tablet); + --nav-height: 0px; + } + + .page { + padding: 20px var(--page-padding-tablet) 28px; + background: transparent; + max-width: var(--content-max-width); + margin-inline: auto; + width: 100%; + overflow: visible; + min-height: auto; + flex: 1 0 auto; + } + + .page__scroll { + overflow: visible; + flex: none; + min-height: auto; + } + + .page__scroll--with-nav { + padding-bottom: 0; + } + + /* Shared page headings */ + .page-head { + margin-bottom: 20px; + } + + .page-head__title { + margin: 0 0 4px; + font-size: 1.25rem; + font-weight: 700; + color: var(--color-text); + } + + .page-head__subtitle { + margin: 0; + font-size: 0.875rem; + color: var(--color-text-muted); + } + + /* Orders dashboard */ + .orders-page__desktop-head { + margin-bottom: 20px; + } + + .orders-page__desktop-title { + margin: 0 0 6px; + font-size: 1.375rem; + font-weight: 700; + color: var(--color-text); + } + + .orders-page__desktop-subtitle { + margin: 0; + font-size: 0.875rem; + color: var(--color-text-muted); + } + + .orders-stats { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 14px; + margin-bottom: 20px; + } + + .orders-stat-card { + padding: 16px 18px 14px; + background: var(--color-surface); + border-radius: var(--radius-md); + border: 1px solid var(--color-border); + box-shadow: var(--shadow-sm); + display: flex; + flex-direction: column; + align-items: stretch; + gap: 0; + min-height: 0; + } + + .orders-stat-card__head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + margin-bottom: 10px; + } + + .orders-stat-card__label { + font-size: 0.8125rem; + font-weight: 600; + line-height: 1.4; + } + + .orders-stat-card__label--total, + .orders-stat-card__label--review { + color: var(--color-text); + } + + .orders-stat-card__label--active { + color: #ea580c; + } + + .orders-stat-card__label--done { + color: var(--color-success); + } + + .orders-stat-card__value { + font-size: 1.75rem; + font-weight: 700; + color: var(--color-text); + line-height: 1.15; + margin: 0 0 12px; + font-variant-numeric: tabular-nums; + } + + .orders-stat-card__link { + margin-top: auto; + padding: 0; + border: none; + background: none; + color: var(--color-primary); + font-family: inherit; + font-size: 0.8125rem; + font-weight: 600; + cursor: pointer; + text-decoration: underline; + text-underline-offset: 3px; + align-self: flex-start; + } + + .orders-stat-card__link:hover { + color: var(--color-primary-hover); + } + + .orders-stat-card__icon { + width: 40px; + height: 40px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + } + + .orders-stat-card__icon--total { + background: var(--color-primary-soft); + color: var(--color-primary); + } + + .orders-stat-card__icon--review { + background: var(--color-primary-soft); + color: var(--color-primary); + } + + .orders-stat-card__icon--active { + background: var(--color-warning-light); + color: #ea580c; + } + + .orders-stat-card__icon--done { + background: var(--color-success-soft); + color: var(--color-success); + } + + .orders-toolbar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + margin-bottom: 16px; + flex-wrap: wrap; + } + + .orders-toolbar__actions { + display: flex; + align-items: center; + gap: 8px; + flex-shrink: 0; + } + + .orders-toolbar__filter-btn { + display: inline-flex; + align-items: center; + gap: 8px; + height: 40px; + padding: 0 18px; + border: none; + border-radius: var(--radius-sm); + background: var(--color-primary); + color: #fff; + font-family: inherit; + font-size: 0.875rem; + font-weight: 600; + cursor: pointer; + white-space: nowrap; + } + + .orders-toolbar__filter-btn:hover { + background: var(--color-primary-hover); + } + + .orders-toolbar__pills { + display: flex; + align-items: center; + gap: 8px; + flex-wrap: wrap; + flex: 1; + min-width: 0; + justify-content: flex-end; + } + + .orders-filter-pill { + height: 36px; + padding: 0 16px; + border: 1px solid var(--color-border); + border-radius: 999px; + background: var(--color-surface); + color: var(--color-text-muted); + font-family: inherit; + font-size: 0.8125rem; + font-weight: 500; + cursor: pointer; + white-space: nowrap; + transition: + background 0.15s, + color 0.15s, + border-color 0.15s; + } + + .orders-filter-pill:hover { + border-color: #cbd5e1; + color: var(--color-text); + } + + .orders-filter-pill--active { + background: var(--color-primary-soft); + border-color: #c7d7ff; + color: var(--color-primary); + font-weight: 600; + } + + .orders-table-wrap { + background: var(--color-surface); + border-radius: var(--radius-md); + border: 1px solid var(--color-border); + box-shadow: var(--shadow-card); + overflow: hidden; + } + + .orders-table-scroll { + overflow-x: auto; + } + + .orders-table { + width: 100%; + border-collapse: collapse; + font-size: 0.875rem; + min-width: 720px; + } + + .orders-table th { + padding: 14px 16px; + text-align: start; + font-weight: 600; + font-size: 0.8125rem; + color: var(--color-text-muted); + background: var(--color-bg); + border-bottom: 1px solid var(--color-border); + white-space: nowrap; + } + + .orders-table td { + padding: 16px; + border-bottom: 1px solid var(--color-border); + color: var(--color-text); + vertical-align: middle; + background: var(--color-surface); + } + + .orders-table tr:last-child td { + border-bottom: none; + } + + .orders-table tbody tr:hover td { + background: var(--color-elevated); + } + + .orders-table__number { + font-weight: 700; + color: var(--color-text); + white-space: nowrap; + } + + .orders-table__amount { + font-weight: 600; + font-variant-numeric: tabular-nums; + white-space: nowrap; + } + + .orders-table__date { + color: var(--color-text-muted); + font-size: 0.8125rem; + white-space: nowrap; + } + + .orders-table__actions { + display: flex; + align-items: center; + gap: 8px; + } + + .orders-table__action { + padding: 7px 16px; + border: 1px solid var(--color-border); + border-radius: var(--radius-sm); + background: var(--color-surface); + color: var(--color-primary); + font-size: 0.8125rem; + font-weight: 600; + cursor: pointer; + font-family: inherit; + white-space: nowrap; + } + + .orders-table__action:hover { + background: var(--color-primary-soft); + border-color: #c7d7ff; + } + + .orders-table__menu { + width: 34px; + height: 34px; + border: none; + border-radius: var(--radius-sm); + background: transparent; + color: var(--color-text-muted); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + padding: 0; + } + + .orders-table__menu:hover { + background: var(--color-elevated); + color: var(--color-text); + } + + .orders-pagination { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + padding: 14px 16px; + border-top: 1px solid var(--color-border); + font-size: 0.8125rem; + color: var(--color-text-muted); + flex-wrap: wrap; + } + + .orders-pagination__summary { + white-space: nowrap; + } + + .orders-pagination__controls { + display: flex; + align-items: center; + gap: 4px; + } + + .orders-pagination__arrow { + width: 32px; + height: 32px; + border: 1px solid var(--color-border); + border-radius: var(--radius-sm); + background: var(--color-surface); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + padding: 0; + color: var(--color-text); + } + + .orders-pagination__arrow:disabled { + opacity: 0.4; + cursor: not-allowed; + } + + .orders-pagination__arrow:not(:disabled):hover { + border-color: var(--color-primary); + color: var(--color-primary); + } + + .orders-pagination__arrow-icon--prev { + transform: scaleX(-1); + } + + .orders-pagination__arrow-icon--next { + transform: none; + } + + .orders-pagination__ellipsis { + min-width: 24px; + text-align: center; + color: var(--color-text-muted); + } + + .orders-pagination__page { + min-width: 32px; + height: 32px; + padding: 0 6px; + border: 1px solid var(--color-border); + border-radius: var(--radius-sm); + background: var(--color-surface); + cursor: pointer; + font-family: inherit; + font-size: 0.8125rem; + color: var(--color-text); + } + + .orders-pagination__page:hover:not(.orders-pagination__page--active) { + border-color: var(--color-primary); + color: var(--color-primary); + } + + .orders-pagination__page--active { + background: var(--color-primary); + border-color: var(--color-primary); + color: #fff; + font-weight: 600; + } + + /* Product page desktop layout */ + .product-page__layout { + display: grid; + grid-template-columns: minmax(0, 1fr) 300px; + gap: 24px; + align-items: start; + } + + .product-page__main { + min-width: 0; + } + + .product-page__sidebar { + position: sticky; + top: 16px; + } + + .cart-summary-panel { + padding: 20px; + background: var(--color-surface); + border-radius: var(--radius-md); + border: 1px solid var(--color-border); + box-shadow: var(--shadow-card); + } + + .cart-summary-panel__title { + margin: 0 0 16px; + font-size: 0.9375rem; + font-weight: 700; + color: var(--color-text); + padding-bottom: 12px; + border-bottom: 1px solid var(--color-border); + } + + .cart-summary-panel__row { + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px 0; + font-size: 0.875rem; + } + + .cart-summary-panel__row span:first-child { + color: var(--color-text-muted); + } + + .cart-summary-panel__row strong { + color: var(--color-text); + } + + .cart-summary-panel__btn { + margin-top: 16px; + } + + .product-page__scroll { + padding-bottom: 0; + } + + .product-page .sticky-footer { + display: none; + } + + .product-list { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + /* Cart desktop */ + .cart-page { + max-width: var(--content-max-width); + margin-inline: auto; + padding: 20px var(--page-padding-tablet) 28px; + background: transparent; + } + + .cart-page__desktop-title { + margin: 0 0 16px; + font-size: 1.25rem; + font-weight: 700; + } + + .cart-page__body { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(280px, 320px); + gap: 20px 24px; + align-items: start; + } + + .cart-page__company { + grid-column: 1 / -1; + } + + /* Order detail desktop */ + .order-detail-page { + max-width: none; + margin-inline: 0; + padding: 24px var(--page-padding) 32px; + background: transparent; + height: 100%; + overflow: hidden; + } + + .order-detail-back { + display: inline-flex; + align-items: center; + gap: 6px; + margin-bottom: 12px; + padding: 0; + border: none; + background: none; + color: var(--color-primary); + font-family: inherit; + font-size: 0.875rem; + font-weight: 600; + cursor: pointer; + } + + /* Company select desktop */ + .company-select-page { + max-width: var(--content-max-width); + margin-inline: auto; + padding: 24px var(--page-padding-desktop); + background: transparent; + } + + /* Catalog desktop */ + .catalog-page { + background: transparent; + } + + .catalog-list { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + /* Profile desktop */ + .profile-page { + background: transparent; + } + + .profile-hero { + border-radius: var(--radius-md); + margin-inline: 0; + } +} + +@media (min-width: 1200px) { + :root { + --page-padding: var(--page-padding-desktop); + } + + .page { + padding: 24px var(--page-padding-desktop) 32px; + } + + .orders-stats { + gap: 16px; + margin-bottom: 24px; + } + + .orders-page__desktop-head { + margin-bottom: 24px; + } + + .orders-toolbar { + margin-bottom: 20px; + } + + .catalog-list { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + .product-list { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} diff --git a/src/assets/styles/fonts.css b/src/assets/styles/fonts.css new file mode 100644 index 0000000..00e1d03 --- /dev/null +++ b/src/assets/styles/fonts.css @@ -0,0 +1,55 @@ +@font-face { + font-family: 'IRANSans'; + src: url('../fonts/iranSans/woff2/IRANSansWeb(FaNum).woff2') format('woff2'); + font-weight: 400; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'IRANSans'; + src: url('../fonts/iranSans/woff2/IRANSansWeb(FaNum)_Light.woff2') format('woff2'); + font-weight: 300; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'IRANSans'; + src: url('../fonts/iranSans/woff2/IRANSansWeb(FaNum)_UltraLight.woff2') format('woff2'); + font-weight: 200; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'IRANSans'; + src: url('../fonts/iranSans/woff2/IRANSansWeb(FaNum)_Medium.woff2') format('woff2'); + font-weight: 500; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'IRANSans'; + src: url('../fonts/iranSans/woff2/IRANSansWeb(FaNum)_Medium.woff2') format('woff2'); + font-weight: 600; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'IRANSans'; + src: url('../fonts/iranSans/woff2/IRANSansWeb(FaNum)_Bold.woff2') format('woff2'); + font-weight: 700; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'IRANSans'; + src: url('../fonts/iranSans/woff2/IRANSansWeb(FaNum)_Black.woff2') format('woff2'); + font-weight: 900; + font-style: normal; + font-display: swap; +} diff --git a/src/assets/styles/main.css b/src/assets/styles/main.css new file mode 100644 index 0000000..abb8ff0 --- /dev/null +++ b/src/assets/styles/main.css @@ -0,0 +1,457 @@ +:root { + /* Brand — sampled from mockups/11-my-profile + FAB/CTA screens */ + --color-primary: #0a353c; + --color-primary-hover: #072a2f; + --color-primary-soft: #e8f4f1; + --color-primary-dark: #062428; + --color-primary-light: #e8f4f1; + --color-primary-rgb: 10, 53, 60; + --color-accent-navy: #1b223c; + --color-accent-navy-rgb: 27, 34, 60; + --color-success: #1b8a5a; + --color-success-light: #eaf8f1; + --color-success-soft: #eaf8f1; + --color-warning: #e8a317; + --color-warning-light: #fff6e5; + --color-danger: #dc3545; + --color-danger-light: #fee2e2; + --color-purple: #7c3aed; + --color-purple-light: #ede9fe; + --color-info: #3b6fa0; + --color-info-light: #eef3f7; + --color-bg: #f6f8f7; + --color-page-background: #f6f8f7; + --color-shell-bg: #eef1f0; + --color-surface: #ffffff; + --color-border: #e5e9e8; + --color-text: #121212; + --color-text-primary: #121212; + --color-text-muted: #6b7280; + --color-text-secondary: #6b7280; + --color-tab-inactive-bg: #f1f5f9; + --color-tab-inactive-text: #64748b; + --color-summary-bg: #f1f5f9; + --color-elevated: #ffffff; + --color-brand-warm: #f3ece3; + --color-brand-warm-text: #1a202c; + --color-brand-warm-muted: #718096; + --color-level-gold: #c9a227; + --color-level-gold-soft: #f7efd6; + --radius-sm: 8px; + --radius-md: 12px; + --radius-lg: 16px; + --shadow-sm: 0 1px 2px rgba(16, 24, 40, 0.04); + --shadow-md: 0 4px 16px rgba(16, 24, 40, 0.06); + --shadow-card: 0 1px 2px rgba(16, 24, 40, 0.04), 0 4px 16px rgba(16, 24, 40, 0.04); + --shadow-frame: 0 8px 32px rgba(15, 23, 42, 0.1); + --nav-height: 64px; + --page-padding: 16px; + --page-padding-tablet: 20px; + --page-padding-desktop: 28px; + --page-gap: 12px; + --content-max-width: 1360px; + --form-max-width: 520px; + --auth-form-max-width: 440px; + --summary-panel-width: 320px; + --sidebar-width: 240px; + --sidebar-width-compact: 72px; + --header-height: 70px; + --app-shell-breakpoint: 768px; + --font-family: 'IRANSans', 'Tahoma', sans-serif; + --btn-height: 48px; + --desktop-breakpoint: 1200px; +} + +html.dark { + --color-primary: #5eb8ad; + --color-primary-hover: #7ad0c5; + --color-primary-soft: #14353a; + --color-primary-dark: #7ad0c5; + --color-primary-light: #14353a; + --color-primary-rgb: 94, 184, 173; + --color-accent-navy: #9bb0d0; + --color-accent-navy-rgb: 155, 176, 208; + --color-success: #3dd68c; + --color-success-light: #123528; + --color-success-soft: #123528; + --color-warning: #f5c14a; + --color-warning-light: #3a2f14; + --color-danger: #f47979; + --color-danger-light: #3f1d22; + --color-purple: #b39afa; + --color-purple-light: #2a2144; + --color-info: #7b9cff; + --color-info-light: #1b2740; + --color-bg: #0b0f14; + --color-page-background: #0b0f14; + --color-shell-bg: #080b10; + --color-surface: #121821; + --color-elevated: #1a2230; + --color-border: #2a3444; + --color-text: #eef2f8; + --color-text-primary: #eef2f8; + --color-text-muted: #9aa8bc; + --color-text-secondary: #9aa8bc; + --color-tab-inactive-bg: #1a2230; + --color-tab-inactive-text: #9aa8bc; + --color-summary-bg: #1a2230; + --color-brand-warm: #2a241c; + --color-brand-warm-text: #f3ebe0; + --color-brand-warm-muted: #b9a88d; + --color-level-gold: #e0c35a; + --color-level-gold-soft: #3a3420; + --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.45); + --shadow-md: 0 8px 24px rgba(0, 0, 0, 0.45); + --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.35), 0 8px 24px rgba(0, 0, 0, 0.35); + --shadow-frame: 0 12px 40px rgba(0, 0, 0, 0.5); +} + +*, +*::before, +*::after { + box-sizing: border-box; +} + +html { + font-size: 16px; + -webkit-text-size-adjust: 100%; + overflow: hidden; + height: 100%; +} + +body { + margin: 0; + font-family: var(--font-family); + background: var(--color-shell-bg); + color: var(--color-text); + line-height: 1.5; + direction: rtl; + overflow: hidden; + height: 100%; +} + +#app { + height: 100dvh; + overflow: hidden; +} + +* { + scrollbar-width: none; + -ms-overflow-style: none; +} + +*::-webkit-scrollbar { + width: 0; + height: 0; + display: none; +} + +a { + color: var(--color-primary); + text-decoration: none; +} + +button, +input, +select, +textarea { + font-family: inherit; +} + +input[type='number']::-webkit-inner-spin-button, +input[type='number']::-webkit-outer-spin-button { + -webkit-appearance: none; + margin: 0; +} + +input[type='number'] { + -moz-appearance: textfield; + text-align: right; +} + +.page { + flex: 1; + display: flex; + flex-direction: column; + min-height: 0; + overflow: hidden; + padding: 0; + background: var(--color-bg); + color: var(--color-text); +} + +.page__scroll { + flex: 1; + min-height: 0; + overflow-y: auto; + overscroll-behavior: contain; + -webkit-overflow-scrolling: touch; +} + +.page__scroll--with-nav { + padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px)); +} + +.page-title { + font-size: 1.25rem; + font-weight: 700; + line-height: 1.35; + margin: 0 0 4px; + color: var(--color-text); +} + +.page-subtitle { + font-size: 0.875rem; + color: var(--color-text-muted); + margin: 0; + line-height: 1.5; +} + +.card { + background: var(--color-elevated); + border-radius: var(--radius-md); + border: 1px solid var(--color-border); + padding: 16px; + box-shadow: var(--shadow-sm); + color: var(--color-text); +} + +.auth-page .card { + background: transparent; + border: none; + box-shadow: none; + padding-inline: 0; +} + +.card + .card { + margin-top: var(--page-gap); +} + +.alert { + padding: 12px 14px; + border-radius: var(--radius-sm); + font-size: 0.875rem; + line-height: 1.5; +} + +.alert-info { + background: var(--color-primary-light); + color: var(--color-primary); + border: 1px solid #cbd5e1; +} + +.alert-warning { + background: var(--color-warning-light); + color: #9a3412; + border: 1px solid #fed7aa; +} + +.alert-success { + background: var(--color-success-light); + color: #155724; + border: 1px solid #c3e6cb; +} + +.alert-danger { + background: var(--color-danger-light); + color: #991b1b; + border: 1px solid #fecaca; +} + +.form-group { + margin-bottom: 16px; +} + +.form-label { + display: block; + font-size: 0.875rem; + font-weight: 600; + margin-bottom: 6px; + color: var(--color-text); +} + +.form-hint { + font-size: 0.75rem; + color: var(--color-text-muted); + margin-top: 4px; +} + +.divider { + height: 1px; + background: var(--color-border); + margin: 16px 0; +} + +.text-muted { + color: var(--color-text-muted); +} + +.text-sm { + font-size: 0.875rem; +} + +.text-center { + text-align: center; +} + +.mt-2 { + margin-top: 8px; +} + +.mt-4 { + margin-top: 16px; +} + +.mb-4 { + margin-bottom: 16px; +} + +.flex-between { + display: flex; + justify-content: space-between; + align-items: center; + gap: 8px; +} + +.sticky-footer { + position: fixed; + bottom: calc(var(--nav-height) + 12px); + inset-inline: 0; + width: 100%; + max-width: 100%; + z-index: 50; + padding-inline: var(--page-padding); + box-sizing: border-box; +} + +.sticky-footer-inner { + max-width: 100%; +} + +.no-nav .page { + padding-bottom: 0; +} + +.no-nav .sticky-footer { + bottom: 0; +} + +.dialog-overlay { + position: fixed; + inset: 0; + background: rgba(15, 23, 42, 0.4); + display: flex; + align-items: center; + justify-content: center; + padding: 24px; + z-index: 200; +} + +.dialog-overlay .dialog { + width: 100%; + max-width: 400px; +} + +.dialog-actions { + display: flex; + flex-direction: column; + gap: 8px; + margin-top: 16px; +} + +/* Responsive visibility */ +.desktop-only { + display: none !important; +} + +.mobile-hidden { + display: block; +} + +.desktop-hidden { + display: none !important; +} + +@media (min-width: 768px) { + .mobile-only, + .mobile-block { + display: none !important; + } + + .desktop-only { + display: block !important; + } + + .mobile-hidden { + display: none !important; + } + + .desktop-hidden { + display: block !important; + } + + .desktop-only-flex { + display: flex !important; + } + + .desktop-only-grid { + display: grid !important; + } + + .page { + padding: 0; + background: transparent; + } + + .page__scroll--with-nav { + padding-bottom: 0; + } +} + +/* Page route transitions */ +.page-fade-enter-active, +.page-fade-leave-active, +.page-forward-enter-active, +.page-forward-leave-active, +.page-back-enter-active, +.page-back-leave-active { + transition: + opacity 0.22s ease, + transform 0.22s ease; +} + +.page-fade-enter-from, +.page-fade-leave-to { + opacity: 0; +} + +.page-forward-enter-from { + opacity: 0; + transform: translateX(-18px); +} + +.page-forward-leave-to { + opacity: 0; + transform: translateX(12px); +} + +.page-back-enter-from { + opacity: 0; + transform: translateX(18px); +} + +.page-back-leave-to { + opacity: 0; + transform: translateX(-12px); +} + +@media (prefers-reduced-motion: reduce) { + .page-fade-enter-active, + .page-fade-leave-active, + .page-forward-enter-active, + .page-forward-leave-active, + .page-back-enter-active, + .page-back-leave-active { + transition: none; + } +} diff --git a/src/assets/styles/responsive.css b/src/assets/styles/responsive.css new file mode 100644 index 0000000..845b5b4 --- /dev/null +++ b/src/assets/styles/responsive.css @@ -0,0 +1,340 @@ +/* Responsive breakpoints: + Mobile: < 768px + Tablet: 768px – 1199px + Desktop: >= 1200px + Large desktop: >= 1440px +*/ + +@media (min-width: 768px) { + :root { + --page-padding: var(--page-padding-tablet); + --nav-height: 0px; + } + + html, + body { + overflow: auto; + height: auto; + } + + #app { + height: 100dvh; + overflow: hidden; + } + + * { + scrollbar-width: thin; + scrollbar-color: #cbd5e1 transparent; + } + + *::-webkit-scrollbar { + width: 6px; + height: 6px; + display: block; + } + + *::-webkit-scrollbar-thumb { + background: #cbd5e1; + border-radius: 999px; + } + + .sticky-footer { + bottom: 0; + max-width: min(var(--content-max-width), calc(100% - 2 * var(--page-padding))); + } + + .no-nav .sticky-footer { + bottom: 0; + } + + /* Auth pages */ + .auth-page__scroll { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding-block: 48px; + } + + .auth-header, + .auth-page .card, + .auth-footer { + width: 100%; + max-width: var(--form-max-width); + } + + /* Company selection */ + .company-select-page__list { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 20px; + align-content: start; + } + + .company-brand-card { + min-height: 220px; + } + + /* Catalog & product grids */ + .catalog-list { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: var(--page-gap); + } + + .product-list { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: var(--page-gap); + align-items: start; + } + + .product-page__scroll { + padding-bottom: 88px; + } + + /* Settlement & check mode */ + .settlement-options, + .mode-options { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 20px; + max-width: 720px; + margin-inline: auto; + } + + /* Company page container */ + .company-select-page { + max-width: var(--content-max-width); + margin-inline: auto; + width: 100%; + padding-inline: var(--page-padding); + } + + .company-select-page__header { + max-width: 560px; + margin-inline: auto; + } + + /* Catalog & orders page containers */ + .catalog-page .page__scroll, + .orders-page__scroll, + .product-page .product-page__scroll { + max-width: var(--content-max-width); + margin-inline: auto; + width: 100%; + } + + /* Cart two-column layout */ + .cart-page { + max-width: var(--content-max-width); + margin-inline: auto; + width: 100%; + padding-inline: var(--page-padding); + } + + .cart-page__body { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(260px, 300px); + grid-template-rows: auto 1fr; + gap: 16px 20px; + align-items: start; + } + + .cart-page__company { + grid-column: 1 / -1; + font-size: 0.9375rem; + margin-bottom: 0; + } + + .cart-page__scroll { + grid-column: 1; + max-height: none; + overflow: visible; + } + + .cart-page__footer { + grid-column: 2; + grid-row: 1 / -1; + position: sticky; + top: 16px; + margin: 0; + padding: 16px; + border-top: none; + box-shadow: var(--shadow-md); + border-radius: var(--radius-md); + border: 1px solid var(--color-border); + } + + .cart-table__head { + position: static; + } + + /* Check list */ + .check-list-page { + max-width: var(--content-max-width); + margin-inline: auto; + width: 100%; + padding-inline: var(--page-padding); + } + + .check-list-page__scroll { + max-width: var(--content-max-width); + margin-inline: auto; + width: 100%; + } + + .summary-cards { + gap: 16px; + } + + .checks-list { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 12px; + align-items: start; + } + + .rass-panel { + max-width: 480px; + margin-inline: auto; + } + + /* Profile */ + .profile-page__scroll { + max-width: var(--content-max-width); + margin-inline: auto; + width: 100%; + } + + .profile-menu { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 12px; + } + + .profile-menu__item { + border-radius: var(--radius-md); + border: 1px solid var(--color-border); + background: var(--color-surface); + box-shadow: var(--shadow-sm); + } + + /* Order detail & tracking */ + .order-detail-page__body { + max-width: none; + margin-inline: 0; + width: 100%; + overflow-y: auto; + -webkit-overflow-scrolling: touch; + } + + .order-detail-page__footer { + max-width: none; + margin-inline: 0; + width: 100%; + } + + /* Checkout forms */ + .checkout-page__scroll, + .cash-page__scroll, + .settlement-page__scroll, + .check-mode-page__scroll { + max-width: var(--form-max-width); + margin-inline: auto; + width: 100%; + } + + /* Cash settlement desktop */ + .cash-page { + max-width: var(--content-max-width); + margin-inline: auto; + width: 100%; + } + + .cash-page__footer { + max-width: var(--form-max-width); + margin-inline: auto; + width: 100%; + display: grid; + grid-template-columns: 1fr 1fr; + gap: 12px; + border-top: none; + box-shadow: none; + padding-top: 16px; + } + + .accounts-panel { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 12px; + } + + .bank-card + .bank-card { + margin-top: 0; + } + + .cash-page__submit { + margin-top: 0; + } + + .order-info-card { + max-width: 520px; + margin-inline: auto; + } + + /* Profile edit */ + .profile-edit-page .page__scroll { + max-width: var(--form-max-width); + margin-inline: auto; + width: 100%; + } + + /* Success page */ + .success-page__scroll { + display: flex; + flex-direction: column; + align-items: center; + max-width: var(--form-max-width); + margin-inline: auto; + width: 100%; + } + + /* Dialogs */ + .dialog-overlay .dialog, + .dialog-overlay .card.dialog { + max-width: 480px; + width: 100%; + } + + .dialog-actions { + flex-direction: row; + justify-content: flex-end; + } +} + +@media (min-width: 1200px) { + :root { + --page-padding: var(--page-padding-desktop); + } + + .catalog-list { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + .checks-list { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +@media (min-width: 1440px) { + .product-list { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } +} + +@media (min-width: 1920px) { + :root { + --content-max-width: 1120px; + } +} diff --git a/src/components/BasicPacks/BasicIcon.vue b/src/components/BasicPacks/BasicIcon.vue new file mode 100644 index 0000000..2984530 --- /dev/null +++ b/src/components/BasicPacks/BasicIcon.vue @@ -0,0 +1,82 @@ + + + + + diff --git a/src/components/BasicPacks/tools/icons/icons.json b/src/components/BasicPacks/tools/icons/icons.json new file mode 100644 index 0000000..40c5978 --- /dev/null +++ b/src/components/BasicPacks/tools/icons/icons.json @@ -0,0 +1,67 @@ +{ + "vuesax-linear:add": "\n\n\n\n", + "vuesax-linear:arrow-down": "\n\n\n", + "vuesax-linear:arrow-left-1": "\n\n\n", + "vuesax-linear:arrow-right-1": "\n\n\n", + "vuesax-linear:bag-2": "\n\n\n\n\n\n", + "vuesax-linear:bag-cross": "\n\n\n\n\n\n\n\n", + "vuesax-linear:box-1": "\n\n\n\n\n", + "vuesax-linear:box-remove": "\n\n\n\n\n\n\n\n", + "vuesax-linear:box-search": "\n\n\n\n\n\n\n", + "vuesax-linear:building": "\n\n\n\n\n\n\n\n", + "vuesax-linear:buildings": "\n\n\n\n\n\n\n\n\n", + "vuesax-linear:calendar": "\n\n\n\n\n\n\n\n\n\n\n\n", + "vuesax-linear:call": "\n\n\n", + "vuesax-linear:card": "\n\n\n\n\n\n", + "vuesax-linear:card-receive": "\n\n\n\n\n\n\n\n", + "vuesax-linear:category": "\n\n\n\n\n\n", + "vuesax-linear:chart-2": "\n\n\n\n\n", + "vuesax-linear:clipboard-close": "\n\n\n\n\n\n", + "vuesax-linear:clock": "\n\n\n\n\n\n", + "vuesax-linear:close-circle": "\n\n\n\n\n", + "vuesax-linear:cpu": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", + "vuesax-linear:diamonds": "\n\n\n\n", + "vuesax-linear:document-text": "\n\n\n\n\n\n", + "vuesax-linear:edit-2": "\n\n\n\n\n", + "vuesax-linear:element-3": "\n\n\n\n\n\n", + "vuesax-linear:eye": "\n\n\n\n", + "vuesax-linear:eye-slash": "\n\n\n\n\n\n\n\n", + "vuesax-linear:filter": "\n\n\n\n", + "vuesax-linear:flash": "\n\n\n\n\n\n", + "vuesax-linear:headphone": "\n\n\n", + "vuesax-linear:home": "\n\n\n\n", + "vuesax-linear:info-circle": "\n\n\n\n\n", + "vuesax-linear:lamp-on": "\n\n\n\n", + "vuesax-linear:location": "\n\n\n\n", + "vuesax-linear:lock": "\n\n\n\n\n\n\n", + "vuesax-linear:logout": "\n\n\n\n\n", + "vuesax-linear:menu": "\n\n\n\n\n", + "vuesax-linear:message": "\n\n\n\n\n\n", + "vuesax-linear:mobile": "\n\n\n\n\n", + "vuesax-linear:money-4": "\n\n\n\n\n\n\n\n", + "vuesax-linear:monitor": "\n\n\n\n\n\n", + "vuesax-linear:moon": "\n\n\n", + "vuesax-linear:more": "\n\n\n\n\n", + "vuesax-linear:note-remove": "\n\n\n\n\n\n\n\n\n", + "vuesax-linear:notification": "\n\n\n\n\n", + "vuesax-linear:pen-add": "\n\n\n\n\n\n\n\n\n", + "vuesax-linear:people": "\n\n\n\n\n\n\n\n", + "vuesax-linear:personalcard": "\n\n\n\n\n\n\n\n", + "vuesax-linear:printer": "\n\n\n\n\n\n\n", + "vuesax-linear:profile-2user": "\n\n\n\n\n\n", + "vuesax-linear:profile-circle": "\n\n\n\n\n", + "vuesax-linear:receipt-search": "\n\n\n\n\n\n\n", + "vuesax-linear:search-normal": "\n\n\n\n", + "vuesax-linear:search-status": "\n\n\n\n\n\n", + "vuesax-linear:setting-2": "\n\n\n\n", + "vuesax-linear:shopping-bag": "\n\n\n\n\n", + "vuesax-linear:sms": "\n\n\n\n", + "vuesax-linear:sun-1": "\n\n\n\n", + "vuesax-linear:tick-circle": "\n\n\n\n", + "vuesax-linear:timer-1": "\n\n\n\n\n", + "vuesax-linear:trash": "\n\n\n\n\n\n\n", + "vuesax-linear:truck-time": "\n\n\n\n\n\n\n\n\n", + "vuesax-linear:verify": "\n\n\n\n", + "vuesax-linear:wallet": "\n\n\n\n\n", + "vuesax-linear:warning-2": "\n\n\n\n\n" +} diff --git a/src/components/icons/IconBell.vue b/src/components/icons/IconBell.vue new file mode 100644 index 0000000..005cc37 --- /dev/null +++ b/src/components/icons/IconBell.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconBolt.vue b/src/components/icons/IconBolt.vue new file mode 100644 index 0000000..3d5287e --- /dev/null +++ b/src/components/icons/IconBolt.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconBookmark.vue b/src/components/icons/IconBookmark.vue new file mode 100644 index 0000000..03dc500 --- /dev/null +++ b/src/components/icons/IconBookmark.vue @@ -0,0 +1,30 @@ + + + diff --git a/src/components/icons/IconBuilding.vue b/src/components/icons/IconBuilding.vue new file mode 100644 index 0000000..c360d54 --- /dev/null +++ b/src/components/icons/IconBuilding.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconCart.vue b/src/components/icons/IconCart.vue new file mode 100644 index 0000000..fd5c71f --- /dev/null +++ b/src/components/icons/IconCart.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconCash.vue b/src/components/icons/IconCash.vue new file mode 100644 index 0000000..ad70898 --- /dev/null +++ b/src/components/icons/IconCash.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconCashSettlement.vue b/src/components/icons/IconCashSettlement.vue new file mode 100644 index 0000000..82e63c8 --- /dev/null +++ b/src/components/icons/IconCashSettlement.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconCatalog.vue b/src/components/icons/IconCatalog.vue new file mode 100644 index 0000000..d3130f9 --- /dev/null +++ b/src/components/icons/IconCatalog.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconCheckBook.vue b/src/components/icons/IconCheckBook.vue new file mode 100644 index 0000000..7ac5959 --- /dev/null +++ b/src/components/icons/IconCheckBook.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconCheckCircleOutline.vue b/src/components/icons/IconCheckCircleOutline.vue new file mode 100644 index 0000000..a63202d --- /dev/null +++ b/src/components/icons/IconCheckCircleOutline.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconChevronBack.vue b/src/components/icons/IconChevronBack.vue new file mode 100644 index 0000000..046b239 --- /dev/null +++ b/src/components/icons/IconChevronBack.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconChevronLeft.vue b/src/components/icons/IconChevronLeft.vue new file mode 100644 index 0000000..4e17f71 --- /dev/null +++ b/src/components/icons/IconChevronLeft.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconClipboardEmpty.vue b/src/components/icons/IconClipboardEmpty.vue new file mode 100644 index 0000000..7eec367 --- /dev/null +++ b/src/components/icons/IconClipboardEmpty.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconDiamond.vue b/src/components/icons/IconDiamond.vue new file mode 100644 index 0000000..59ce446 --- /dev/null +++ b/src/components/icons/IconDiamond.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconEditPen.vue b/src/components/icons/IconEditPen.vue new file mode 100644 index 0000000..3ced2b6 --- /dev/null +++ b/src/components/icons/IconEditPen.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconFamBaftLogo.vue b/src/components/icons/IconFamBaftLogo.vue new file mode 100644 index 0000000..8277c73 --- /dev/null +++ b/src/components/icons/IconFamBaftLogo.vue @@ -0,0 +1,28 @@ + + + + + diff --git a/src/components/icons/IconFeather.vue b/src/components/icons/IconFeather.vue new file mode 100644 index 0000000..d59c3b0 --- /dev/null +++ b/src/components/icons/IconFeather.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconFilter.vue b/src/components/icons/IconFilter.vue new file mode 100644 index 0000000..7c75d7c --- /dev/null +++ b/src/components/icons/IconFilter.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconHeadset.vue b/src/components/icons/IconHeadset.vue new file mode 100644 index 0000000..d890af0 --- /dev/null +++ b/src/components/icons/IconHeadset.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconHeart.vue b/src/components/icons/IconHeart.vue new file mode 100644 index 0000000..1b7264a --- /dev/null +++ b/src/components/icons/IconHeart.vue @@ -0,0 +1,16 @@ + + + diff --git a/src/components/icons/IconHome.vue b/src/components/icons/IconHome.vue new file mode 100644 index 0000000..e7a9261 --- /dev/null +++ b/src/components/icons/IconHome.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconHourglass.vue b/src/components/icons/IconHourglass.vue new file mode 100644 index 0000000..73d367b --- /dev/null +++ b/src/components/icons/IconHourglass.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconIdCard.vue b/src/components/icons/IconIdCard.vue new file mode 100644 index 0000000..5721dbb --- /dev/null +++ b/src/components/icons/IconIdCard.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconLayoutGrid.vue b/src/components/icons/IconLayoutGrid.vue new file mode 100644 index 0000000..d1cf288 --- /dev/null +++ b/src/components/icons/IconLayoutGrid.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconLightbulb.vue b/src/components/icons/IconLightbulb.vue new file mode 100644 index 0000000..6bb5b91 --- /dev/null +++ b/src/components/icons/IconLightbulb.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconLock.vue b/src/components/icons/IconLock.vue new file mode 100644 index 0000000..3a80b09 --- /dev/null +++ b/src/components/icons/IconLock.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconMail.vue b/src/components/icons/IconMail.vue new file mode 100644 index 0000000..1893780 --- /dev/null +++ b/src/components/icons/IconMail.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconMapPin.vue b/src/components/icons/IconMapPin.vue new file mode 100644 index 0000000..0ef53e7 --- /dev/null +++ b/src/components/icons/IconMapPin.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconMedal.vue b/src/components/icons/IconMedal.vue new file mode 100644 index 0000000..62adbf6 --- /dev/null +++ b/src/components/icons/IconMedal.vue @@ -0,0 +1,29 @@ + + + diff --git a/src/components/icons/IconMoreVertical.vue b/src/components/icons/IconMoreVertical.vue new file mode 100644 index 0000000..23c5702 --- /dev/null +++ b/src/components/icons/IconMoreVertical.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconOrders.vue b/src/components/icons/IconOrders.vue new file mode 100644 index 0000000..6feeb16 --- /dev/null +++ b/src/components/icons/IconOrders.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconParLogo.vue b/src/components/icons/IconParLogo.vue new file mode 100644 index 0000000..862ede2 --- /dev/null +++ b/src/components/icons/IconParLogo.vue @@ -0,0 +1,34 @@ + + + + + diff --git a/src/components/icons/IconPhone.vue b/src/components/icons/IconPhone.vue new file mode 100644 index 0000000..e116846 --- /dev/null +++ b/src/components/icons/IconPhone.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconPlus.vue b/src/components/icons/IconPlus.vue new file mode 100644 index 0000000..f8e77a6 --- /dev/null +++ b/src/components/icons/IconPlus.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconProfile.vue b/src/components/icons/IconProfile.vue new file mode 100644 index 0000000..426223d --- /dev/null +++ b/src/components/icons/IconProfile.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconProgressClock.vue b/src/components/icons/IconProgressClock.vue new file mode 100644 index 0000000..886d014 --- /dev/null +++ b/src/components/icons/IconProgressClock.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconSearch.vue b/src/components/icons/IconSearch.vue new file mode 100644 index 0000000..7341e9b --- /dev/null +++ b/src/components/icons/IconSearch.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconSettings.vue b/src/components/icons/IconSettings.vue new file mode 100644 index 0000000..c07c2e3 --- /dev/null +++ b/src/components/icons/IconSettings.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconShoppingBag.vue b/src/components/icons/IconShoppingBag.vue new file mode 100644 index 0000000..d0d4a99 --- /dev/null +++ b/src/components/icons/IconShoppingBag.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconSuccessCircle.vue b/src/components/icons/IconSuccessCircle.vue new file mode 100644 index 0000000..a63202d --- /dev/null +++ b/src/components/icons/IconSuccessCircle.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconTrash.vue b/src/components/icons/IconTrash.vue new file mode 100644 index 0000000..5b537e0 --- /dev/null +++ b/src/components/icons/IconTrash.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/icons/IconVerified.vue b/src/components/icons/IconVerified.vue new file mode 100644 index 0000000..3b8f918 --- /dev/null +++ b/src/components/icons/IconVerified.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/layout/AppLayout.vue b/src/components/layout/AppLayout.vue new file mode 100644 index 0000000..16f8f55 --- /dev/null +++ b/src/components/layout/AppLayout.vue @@ -0,0 +1,9 @@ + + + diff --git a/src/components/layout/BottomNav.vue b/src/components/layout/BottomNav.vue new file mode 100644 index 0000000..8107353 --- /dev/null +++ b/src/components/layout/BottomNav.vue @@ -0,0 +1,170 @@ + + + + + diff --git a/src/components/layout/CheckoutStepper.vue b/src/components/layout/CheckoutStepper.vue new file mode 100644 index 0000000..1328f6c --- /dev/null +++ b/src/components/layout/CheckoutStepper.vue @@ -0,0 +1,141 @@ + + + + + diff --git a/src/components/layout/DesktopHeader.vue b/src/components/layout/DesktopHeader.vue new file mode 100644 index 0000000..db27fe6 --- /dev/null +++ b/src/components/layout/DesktopHeader.vue @@ -0,0 +1,267 @@ + + + + + diff --git a/src/components/layout/DesktopSidebar.vue b/src/components/layout/DesktopSidebar.vue new file mode 100644 index 0000000..3253c20 --- /dev/null +++ b/src/components/layout/DesktopSidebar.vue @@ -0,0 +1,286 @@ + + + + + diff --git a/src/components/layout/PageHeader.vue b/src/components/layout/PageHeader.vue new file mode 100644 index 0000000..c70730c --- /dev/null +++ b/src/components/layout/PageHeader.vue @@ -0,0 +1,169 @@ + + + + + diff --git a/src/components/orders/OrderStatusHero.vue b/src/components/orders/OrderStatusHero.vue new file mode 100644 index 0000000..02f0d60 --- /dev/null +++ b/src/components/orders/OrderStatusHero.vue @@ -0,0 +1,112 @@ + + + + + diff --git a/src/components/orders/OrderTrackingSummary.vue b/src/components/orders/OrderTrackingSummary.vue new file mode 100644 index 0000000..85bf68d --- /dev/null +++ b/src/components/orders/OrderTrackingSummary.vue @@ -0,0 +1,90 @@ + + + + + diff --git a/src/components/orders/OrderTrackingTimeline.vue b/src/components/orders/OrderTrackingTimeline.vue new file mode 100644 index 0000000..9fc9d27 --- /dev/null +++ b/src/components/orders/OrderTrackingTimeline.vue @@ -0,0 +1,175 @@ + + + + + diff --git a/src/components/ui/AuthThemeToggle.vue b/src/components/ui/AuthThemeToggle.vue new file mode 100644 index 0000000..844fa0e --- /dev/null +++ b/src/components/ui/AuthThemeToggle.vue @@ -0,0 +1,75 @@ + + + + + diff --git a/src/components/ui/BaseBadge.vue b/src/components/ui/BaseBadge.vue new file mode 100644 index 0000000..eff59a0 --- /dev/null +++ b/src/components/ui/BaseBadge.vue @@ -0,0 +1,52 @@ + + + + + diff --git a/src/components/ui/BaseButton.vue b/src/components/ui/BaseButton.vue new file mode 100644 index 0000000..4698122 --- /dev/null +++ b/src/components/ui/BaseButton.vue @@ -0,0 +1,110 @@ + + + + + diff --git a/src/components/ui/BaseInput.vue b/src/components/ui/BaseInput.vue new file mode 100644 index 0000000..19581d7 --- /dev/null +++ b/src/components/ui/BaseInput.vue @@ -0,0 +1,108 @@ + + + + + diff --git a/src/components/ui/CapacitySummary.vue b/src/components/ui/CapacitySummary.vue new file mode 100644 index 0000000..0232184 --- /dev/null +++ b/src/components/ui/CapacitySummary.vue @@ -0,0 +1,64 @@ + + + + + diff --git a/src/components/ui/CartMeterageFooter.vue b/src/components/ui/CartMeterageFooter.vue new file mode 100644 index 0000000..26d04e1 --- /dev/null +++ b/src/components/ui/CartMeterageFooter.vue @@ -0,0 +1,124 @@ + + + + + diff --git a/src/components/ui/ConfirmDialog.vue b/src/components/ui/ConfirmDialog.vue new file mode 100644 index 0000000..df0c89f --- /dev/null +++ b/src/components/ui/ConfirmDialog.vue @@ -0,0 +1,66 @@ + + + + + diff --git a/src/components/ui/CurrencyInput.vue b/src/components/ui/CurrencyInput.vue new file mode 100644 index 0000000..b11d46f --- /dev/null +++ b/src/components/ui/CurrencyInput.vue @@ -0,0 +1,84 @@ + + + + + diff --git a/src/components/ui/DisplaySettingsPopup.vue b/src/components/ui/DisplaySettingsPopup.vue new file mode 100644 index 0000000..1cce8b0 --- /dev/null +++ b/src/components/ui/DisplaySettingsPopup.vue @@ -0,0 +1,315 @@ + + + + + diff --git a/src/components/ui/EmptyState.vue b/src/components/ui/EmptyState.vue new file mode 100644 index 0000000..dee482b --- /dev/null +++ b/src/components/ui/EmptyState.vue @@ -0,0 +1,112 @@ + + + + + diff --git a/src/components/ui/FilterTabs.vue b/src/components/ui/FilterTabs.vue new file mode 100644 index 0000000..b17da03 --- /dev/null +++ b/src/components/ui/FilterTabs.vue @@ -0,0 +1,93 @@ + + + + + diff --git a/src/components/ui/FormErrorBanner.vue b/src/components/ui/FormErrorBanner.vue new file mode 100644 index 0000000..2e3bdaf --- /dev/null +++ b/src/components/ui/FormErrorBanner.vue @@ -0,0 +1,25 @@ + + + + + diff --git a/src/components/ui/MeterageBar.vue b/src/components/ui/MeterageBar.vue new file mode 100644 index 0000000..aa36bef --- /dev/null +++ b/src/components/ui/MeterageBar.vue @@ -0,0 +1,73 @@ + + + + + diff --git a/src/components/ui/PageSkeleton.vue b/src/components/ui/PageSkeleton.vue new file mode 100644 index 0000000..96cdefa --- /dev/null +++ b/src/components/ui/PageSkeleton.vue @@ -0,0 +1,235 @@ + + + + + diff --git a/src/components/ui/PersianDateInput.vue b/src/components/ui/PersianDateInput.vue new file mode 100644 index 0000000..e2002e7 --- /dev/null +++ b/src/components/ui/PersianDateInput.vue @@ -0,0 +1,336 @@ + + + + + diff --git a/src/components/ui/SearchField.vue b/src/components/ui/SearchField.vue new file mode 100644 index 0000000..45496a8 --- /dev/null +++ b/src/components/ui/SearchField.vue @@ -0,0 +1,47 @@ + + + + + diff --git a/src/components/ui/SkeletonBlock.vue b/src/components/ui/SkeletonBlock.vue new file mode 100644 index 0000000..d106cb3 --- /dev/null +++ b/src/components/ui/SkeletonBlock.vue @@ -0,0 +1,64 @@ + + + + + diff --git a/src/components/ui/StatusBadge.vue b/src/components/ui/StatusBadge.vue new file mode 100644 index 0000000..7d0c6c9 --- /dev/null +++ b/src/components/ui/StatusBadge.vue @@ -0,0 +1,17 @@ + + + diff --git a/src/components/ui/UserAvatar.vue b/src/components/ui/UserAvatar.vue new file mode 100644 index 0000000..5bb17a0 --- /dev/null +++ b/src/components/ui/UserAvatar.vue @@ -0,0 +1,40 @@ + + + + + diff --git a/src/composables/useCartPage.ts b/src/composables/useCartPage.ts new file mode 100644 index 0000000..59d240b --- /dev/null +++ b/src/composables/useCartPage.ts @@ -0,0 +1,103 @@ +import { computed, ref, onMounted } from 'vue' +import { useRouter } from 'vue-router' +import { useCartStore } from '@/stores/cart' +import { useOrderStore } from '@/stores/order' +import { confirm } from '@/composables/useConfirm' + +export function useCartPage() { + const router = useRouter() + const cartStore = useCartStore() + const orderStore = useOrderStore() + const showFullDialog = ref(false) + const submitting = ref(false) + const submitError = ref('') + + const items = computed(() => cartStore.activeCart?.items ?? []) + const isEmpty = computed(() => items.value.length === 0) + const companyName = computed(() => cartStore.companyNamesLabel || cartStore.activeCart?.companyName || '') + const companySummaries = computed(() => cartStore.companySummaries) + + async function removeItem(itemId: string) { + const item = items.value.find((entry) => entry.id === itemId) + const ok = await confirm({ + title: 'حذف از سبد', + message: item + ? `آیا از حذف «${item.productName}» از سبد خرید مطمئن هستید؟` + : 'آیا از حذف این آیتم از سبد خرید مطمئن هستید؟', + confirmLabel: 'حذف', + cancelLabel: 'انصراف', + tone: 'danger', + }) + if (!ok) return + cartStore.removeItem(itemId) + } + + /** + * ثبت سفارش بدون انتخاب تسویه؛ قیمت‌گذاری و انتخاب روش تسویه + * بعد از بررسی مدیریت انجام می‌شود. + */ + async function submitOrder() { + if (!cartStore.activeCart || items.value.length === 0 || submitting.value) return + submitting.value = true + submitError.value = '' + try { + await cartStore.hydrate() + if (!cartStore.activeCart?.items.length) { + submitError.value = 'سبد خرید خالی است یا با سرور همگام نشد.' + return + } + const order = await orderStore.submitOrder(cartStore.activeCart) + await cartStore.hydrate() + router.replace(`/orders/${order.id}/success`) + } catch (e) { + submitError.value = e instanceof Error ? e.message : 'ثبت سفارش ناموفق بود.' + await cartStore.hydrate() + } finally { + submitting.value = false + } + } + + function proceedToCheckout() { + if (items.value.length === 0) return + if (cartStore.isFull) { + showFullDialog.value = true + return + } + void submitOrder() + } + + function proceedAnyway() { + showFullDialog.value = false + void submitOrder() + } + + async function createNewInvoice() { + await cartStore.createNewCartForSameCompany() + showFullDialog.value = false + const companyId = cartStore.selectedCompanyId || cartStore.activeCart?.companyId + if (companyId) { + router.push(`/companies/${companyId}/catalogs`) + } else { + router.push('/companies') + } + } + + onMounted(() => { + cartStore.ensureActiveCartForCartPage() + }) + + return { + cartStore, + items, + isEmpty, + companyName, + companySummaries, + showFullDialog, + submitting, + submitError, + removeItem, + proceedToCheckout, + proceedAnyway, + createNewInvoice, + } +} diff --git a/src/composables/useConfirm.ts b/src/composables/useConfirm.ts new file mode 100644 index 0000000..5571171 --- /dev/null +++ b/src/composables/useConfirm.ts @@ -0,0 +1,48 @@ +import { reactive } from 'vue' + +export interface ConfirmOptions { + title: string + message: string + confirmLabel?: string + cancelLabel?: string + tone?: 'danger' | 'primary' +} + +interface ConfirmState extends ConfirmOptions { + open: boolean + resolve: ((value: boolean) => void) | null +} + +const state = reactive({ + open: false, + title: '', + message: '', + confirmLabel: 'حذف', + cancelLabel: 'انصراف', + tone: 'danger', + resolve: null, +}) + +export function useConfirmState() { + return state +} + +export function confirm(options: ConfirmOptions): Promise { + return new Promise((resolve) => { + Object.assign(state, { + open: true, + title: options.title, + message: options.message, + confirmLabel: options.confirmLabel ?? 'حذف', + cancelLabel: options.cancelLabel ?? 'انصراف', + tone: options.tone ?? 'danger', + resolve, + }) + }) +} + +export function resolveConfirm(value: boolean) { + state.resolve?.(value) + state.open = false + state.resolve = null +} diff --git a/src/composables/useGoBack.ts b/src/composables/useGoBack.ts new file mode 100644 index 0000000..bcc42be --- /dev/null +++ b/src/composables/useGoBack.ts @@ -0,0 +1,17 @@ +import { useRouter } from 'vue-router' + +/** بازگشت با تاریخچه؛ اگر تاریخچه نباشد به مسیر جایگزین می‌رود */ +export function useGoBack(fallback = '/') { + const router = useRouter() + + function goBack() { + const hasHistory = window.history.length > 1 + if (hasHistory) { + router.back() + return + } + router.push(fallback) + } + + return { goBack } +} diff --git a/src/composables/useOrderList.ts b/src/composables/useOrderList.ts new file mode 100644 index 0000000..e7ea934 --- /dev/null +++ b/src/composables/useOrderList.ts @@ -0,0 +1,233 @@ +import { computed, ref, watch, onMounted } from 'vue' +import { useRouter, useRoute } from 'vue-router' +import { useOrderStore } from '@/stores/order' +import { useAuthStore } from '@/stores/auth' +import { useCatalogStore } from '@/stores/catalog' +import type { Order } from '@/types' +import { + DELIVERED_STATUSES, + IN_PROGRESS_STATUSES, + REVIEW_STATUSES, + matchesDesktopOrderFilter, + matchesMobileOrderTab, + type DesktopOrderFilterId, +} from '@/utils/orderStatusGroups' +import { toPersianDigits } from '@/utils/format' + +const DESKTOP_PAGE_SIZE = 5 +const MOBILE_PAGE_SIZE = 5 + +export function useOrderList() { + const router = useRouter() + const route = useRoute() + const orderStore = useOrderStore() + const authStore = useAuthStore() + const catalogStore = useCatalogStore() + const currentPage = ref(1) + const mobilePage = ref(1) + const submitSuccessMessage = ref('') + + onMounted(async () => { + await Promise.all([orderStore.hydrate(), catalogStore.loadCompanies()]) + }) + + watch( + () => route.query.submitted, + (submitted) => { + const count = Number(submitted) + if (!Number.isFinite(count) || count < 1) { + submitSuccessMessage.value = '' + return + } + submitSuccessMessage.value = + count === 1 + ? 'پیش‌سفارش با موفقیت ثبت شد.' + : `${toPersianDigits(String(count))} پیش‌سفارش (برای شرکت‌های مختلف) ثبت شد.` + const { submitted: _submitted, ...rest } = route.query + void router.replace({ query: rest }) + }, + { immediate: true }, + ) + + const tab = computed({ + get: () => (router.currentRoute.value.query.tab as string) ?? 'all', + set: (v: string) => router.replace({ query: { ...router.currentRoute.value.query, tab: v } }), + }) + + const desktopFilter = computed({ + get: () => (router.currentRoute.value.query.filter as DesktopOrderFilterId) ?? 'all', + set: (v: DesktopOrderFilterId) => + router.replace({ + query: { ...router.currentRoute.value.query, filter: v === 'all' ? undefined : v }, + }), + }) + + const searchQuery = computed(() => (router.currentRoute.value.query.q as string) ?? '') + + const orders = computed(() => { + if (!authStore.user) return [] + return orderStore.userOrders(authStore.user.id) + }) + + const stats = computed(() => ({ + total: orders.value.length, + review: orders.value.filter((o) => REVIEW_STATUSES.includes(o.status)).length, + inProgress: orders.value.filter((o) => IN_PROGRESS_STATUSES.includes(o.status)).length, + done: orders.value.filter((o) => DELIVERED_STATUSES.includes(o.status)).length, + })) + + function matchesSearch(order: Order, q: string): boolean { + if (!q.trim()) return true + const needle = q.trim().toLowerCase() + return ( + order.orderNumber.toLowerCase().includes(needle) || + order.companyName.toLowerCase().includes(needle) + ) + } + + const filteredOrdersMobile = computed(() => + orders.value + .filter((o) => matchesSearch(o, searchQuery.value)) + .filter((o) => matchesMobileOrderTab(o.status, tab.value)), + ) + + const filteredOrdersDesktop = computed(() => + orders.value + .filter((o) => matchesSearch(o, searchQuery.value)) + .filter((o) => matchesDesktopOrderFilter(o.status, desktopFilter.value)), + ) + + const paginatedOrders = computed(() => { + const start = (currentPage.value - 1) * DESKTOP_PAGE_SIZE + return filteredOrdersDesktop.value.slice(start, start + DESKTOP_PAGE_SIZE) + }) + + const totalPages = computed(() => + Math.max(1, Math.ceil(filteredOrdersDesktop.value.length / DESKTOP_PAGE_SIZE)), + ) + + const paginationRange = computed(() => { + if (filteredOrdersDesktop.value.length === 0) return { start: 0, end: 0 } + const start = (currentPage.value - 1) * DESKTOP_PAGE_SIZE + 1 + const end = Math.min(currentPage.value * DESKTOP_PAGE_SIZE, filteredOrdersDesktop.value.length) + return { start, end } + }) + + const visiblePages = computed(() => { + const total = totalPages.value + const current = currentPage.value + if (total <= 7) return Array.from({ length: total }, (_, i) => i + 1) + + const pages: (number | 'ellipsis')[] = [1] + if (current > 3) pages.push('ellipsis') + + const rangeStart = Math.max(2, current - 1) + const rangeEnd = Math.min(total - 1, current + 1) + for (let p = rangeStart; p <= rangeEnd; p += 1) pages.push(p) + + if (current < total - 2) pages.push('ellipsis') + if (total > 1) pages.push(total) + + return pages + }) + + const mobileTotalPages = computed(() => + Math.max(1, Math.ceil(filteredOrdersMobile.value.length / MOBILE_PAGE_SIZE)), + ) + + const paginatedOrdersMobile = computed(() => { + const start = (mobilePage.value - 1) * MOBILE_PAGE_SIZE + return filteredOrdersMobile.value.slice(start, start + MOBILE_PAGE_SIZE) + }) + + const mobilePaginationRange = computed(() => { + if (filteredOrdersMobile.value.length === 0) return { start: 0, end: 0 } + const start = (mobilePage.value - 1) * MOBILE_PAGE_SIZE + 1 + const end = Math.min(mobilePage.value * MOBILE_PAGE_SIZE, filteredOrdersMobile.value.length) + return { start, end } + }) + + const mobileVisiblePages = computed(() => { + const total = mobileTotalPages.value + const current = mobilePage.value + if (total <= 5) return Array.from({ length: total }, (_, i) => i + 1) + + const pages: (number | 'ellipsis')[] = [1] + if (current > 3) pages.push('ellipsis') + + const rangeStart = Math.max(2, current - 1) + const rangeEnd = Math.min(total - 1, current + 1) + for (let p = rangeStart; p <= rangeEnd; p += 1) pages.push(p) + + if (current < total - 2) pages.push('ellipsis') + if (total > 1) pages.push(total) + + return pages + }) + + watch([filteredOrdersDesktop, desktopFilter, searchQuery], () => { + currentPage.value = 1 + }) + + watch([filteredOrdersMobile, tab, searchQuery], () => { + mobilePage.value = 1 + }) + + function companySlug(order: Order) { + return catalogStore.getCompany(order.companyId)?.slug ?? 'par' + } + + function iconVariant(order: Order) { + if (DELIVERED_STATUSES.includes(order.status)) return 'success' + return companySlug(order) === 'fam-baft' ? 'fam-baft' : 'par' + } + + function goToOrder(id: string) { + router.push(`/orders/${id}`) + } + + function setPage(page: number) { + if (page < 1 || page > totalPages.value) return + currentPage.value = page + } + + function setMobilePage(page: number) { + if (page < 1 || page > mobileTotalPages.value) return + mobilePage.value = page + } + + function setDesktopFilter(filter: DesktopOrderFilterId) { + desktopFilter.value = desktopFilter.value === filter && filter !== 'all' ? 'all' : filter + } + + function viewStatFilter(filter: DesktopOrderFilterId) { + desktopFilter.value = filter + } + + return { + tab, + desktopFilter, + searchQuery, + stats, + filteredOrdersMobile, + filteredOrdersDesktop, + paginatedOrders, + paginatedOrdersMobile, + totalPages, + mobileTotalPages, + paginationRange, + mobilePaginationRange, + visiblePages, + mobileVisiblePages, + currentPage, + mobilePage, + companySlug, + iconVariant, + submitSuccessMessage, + goToOrder, + setPage, + setMobilePage, + setDesktopFilter, + viewStatFilter, + } +} diff --git a/src/composables/useProductOrdering.ts b/src/composables/useProductOrdering.ts new file mode 100644 index 0000000..c7adc37 --- /dev/null +++ b/src/composables/useProductOrdering.ts @@ -0,0 +1,123 @@ +import { ref, computed, watch, onMounted } from 'vue' +import { useRoute, useRouter } from 'vue-router' +import { useCatalogStore } from '@/stores/catalog' +import { useCartStore } from '@/stores/cart' + +export function useProductOrdering() { + const route = useRoute() + const router = useRouter() + const catalogStore = useCatalogStore() + const cartStore = useCartStore() + const loading = ref(true) + + const companyId = computed(() => route.params.companyId as string) + const catalogId = computed(() => route.params.catalogId as string) + const catalog = computed(() => catalogStore.getCatalog(catalogId.value)) + const company = computed(() => catalogStore.getCompany(companyId.value)) + + const colorFilter = ref('all') + const colors = computed(() => ['all', ...catalogStore.getUniqueColors(catalogId.value)]) + const colorLabels: Record = { all: 'همه' } + + const products = computed(() => + catalogStore.fetchProducts( + catalogId.value, + colorFilter.value === 'all' ? undefined : colorFilter.value, + ), + ) + + const meterages = ref>({}) + const errors = ref>({}) + const toast = ref('') + + async function load() { + loading.value = true + if (!catalogStore.getCompany(companyId.value)) { + await catalogStore.loadCompanies() + } + if (!catalogStore.getCatalog(catalogId.value)) { + await catalogStore.loadCatalogs(companyId.value) + } + await catalogStore.loadProducts(catalogId.value) + loading.value = false + } + + onMounted(load) + watch([companyId, catalogId], load) + watch(colorFilter, async (color) => { + await catalogStore.loadProducts( + catalogId.value, + color === 'all' ? undefined : color, + ) + }) + + function getMeterage(productId: string) { + return meterages.value[productId] ?? 0 + } + + function adjustMeterage(productId: string, delta: number, step: number) { + const current = getMeterage(productId) + meterages.value[productId] = Math.max(0, current + delta * step) + } + + async function addProduct(productId: string) { + const product = catalogStore.getProduct(productId) + if (!product || !catalog.value) { + errors.value = { + ...errors.value, + [productId]: 'اطلاعات محصول هنوز آماده نیست. صفحه را تازه کنید.', + } + return + } + + const meterage = getMeterage(productId) + if (meterage <= 0) { + errors.value = { ...errors.value, [productId]: 'متراژ را وارد کنید.' } + return + } + + const result = await cartStore.addItem(product, catalog.value.code, meterage) + if (result.success) { + errors.value = { ...errors.value, [productId]: '' } + toast.value = `${product.name} به سبد اضافه شد.` + setTimeout(() => { + toast.value = '' + }, 2000) + } else { + errors.value = { ...errors.value, [productId]: result.message ?? 'خطا' } + } + } + + function setMeterage(productId: string, value: number) { + meterages.value[productId] = Math.max(0, value) + } + + function goToCart() { + router.push('/cart') + } + + function goBack() { + router.push(`/companies/${companyId.value}/catalogs`) + } + + return { + companyId, + catalogId, + catalog, + company, + colorFilter, + colors, + colorLabels, + products, + meterages, + errors, + toast, + loading, + cartStore, + adjustMeterage, + setMeterage, + addProduct, + goToCart, + goBack, + } +} diff --git a/src/composables/useRequirePricedOrder.ts b/src/composables/useRequirePricedOrder.ts new file mode 100644 index 0000000..2de1d51 --- /dev/null +++ b/src/composables/useRequirePricedOrder.ts @@ -0,0 +1,42 @@ +import { computed, watch } from 'vue' +import { useRoute, useRouter } from 'vue-router' +import { useOrderStore } from '@/stores/order' +import { isInvoicePriced } from '@/utils/orderInvoice' +import type { Order } from '@/types' + +/** + * مسیرهای تسویه/چک فقط بعد از قیمت‌گذاری ادمین مجازند. + * در غیر این صورت به جزئیات سفارش برمی‌گردیم. + */ +export function useRequirePricedOrder() { + const route = useRoute() + const router = useRouter() + const orderStore = useOrderStore() + + const orderId = computed(() => route.params.id as string) + const order = computed(() => orderStore.getOrderById(orderId.value)) + const invoiceReady = computed(() => isInvoicePriced(order.value)) + + function redirectIfNeeded(current: Order | null | undefined) { + if (!current) return + if (!isInvoicePriced(current)) { + router.replace(`/orders/${current.id}`) + } + } + + watch( + orderId, + async (id) => { + if (!id) return + const current = orderStore.getOrderById(id) ?? (await orderStore.fetchOrder(id)) + redirectIfNeeded(current) + }, + { immediate: true }, + ) + + watch(order, (current) => { + redirectIfNeeded(current) + }) + + return { orderId, order, invoiceReady } +} diff --git a/src/data/catalogCategories.ts b/src/data/catalogCategories.ts new file mode 100644 index 0000000..6cc35dd --- /dev/null +++ b/src/data/catalogCategories.ts @@ -0,0 +1,33 @@ +import type { ProductCategory, SeasonCategory } from '@/types/crm' + +/** دسته‌بندی کاربرد/فصل — تاکسونومی ثابت اپ (هم‌تراز با سرور) */ +export const CATALOG_USAGE_CATEGORIES: ProductCategory[] = [ + { id: 'trousers', title: 'پارچه‌های شلواری', subtitle: 'مشاهده محصولات' }, + { id: 'suit', title: 'پارچه‌های کت‌شلواری', subtitle: 'مشاهده محصولات' }, + { id: 'shirt', title: 'پارچه‌های پیراهنی', subtitle: 'مشاهده محصولات' }, +] + +export const CATALOG_SEASON_CATEGORIES: SeasonCategory[] = [ + { + id: 'summer', + title: 'تابستانه', + description: 'سبک، خنک و مناسب فصل گرم', + }, + { + id: 'winter', + title: 'زمستانه', + description: 'گرم و مناسب فصل سرد', + }, + { + id: 'all_season', + title: 'چهارفصل', + description: 'مناسب استفاده در بیشتر فصل‌های سال', + }, +] + +export function getCatalogCategories() { + return { + usage: CATALOG_USAGE_CATEGORIES, + seasons: CATALOG_SEASON_CATEGORIES, + } +} diff --git a/src/layouts/DefaultLayout.vue b/src/layouts/DefaultLayout.vue new file mode 100644 index 0000000..98caa5a --- /dev/null +++ b/src/layouts/DefaultLayout.vue @@ -0,0 +1,61 @@ + + + diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..0a34cd0 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,23 @@ +import { createApp } from 'vue' +import { createPinia } from 'pinia' + +import App from './App.vue' +import router from './router' +import { useThemeStore } from '@/stores/theme' +import 'virtual:uno.css' +import './assets/styles/fonts.css' +import './assets/styles/main.css' +import './assets/styles/app-shell.css' +import './assets/styles/responsive.css' +import './assets/styles/desktop.css' +import './assets/styles/desktop-pages.css' + +const app = createApp(App) +const pinia = createPinia() + +app.use(pinia) +app.use(router) + +useThemeStore(pinia) + +app.mount('#app') diff --git a/src/router/index.ts b/src/router/index.ts new file mode 100644 index 0000000..e59e28a --- /dev/null +++ b/src/router/index.ts @@ -0,0 +1,254 @@ +import { createRouter, createWebHistory } from 'vue-router' +import { getCachedUser, isAuthenticated } from '@/services/authService' + +const router = createRouter({ + history: createWebHistory(import.meta.env.BASE_URL), + routes: [ + { + path: '/login', + name: 'login', + component: () => import('@/views/auth/LoginView.vue'), + meta: { guest: true, hideNav: true }, + }, + { + path: '/register', + name: 'register', + component: () => import('@/views/auth/RegisterView.vue'), + meta: { guest: true, hideNav: true }, + }, + { + path: '/otp', + name: 'otp', + component: () => import('@/views/auth/OtpView.vue'), + meta: { guest: true, hideNav: true }, + }, + { + path: '/', + name: 'home', + component: () => import('@/views/home/HomeView.vue'), + meta: { requiresAuth: true }, + }, + { + path: '/companies', + name: 'companies', + component: () => import('@/views/companies/CompanySelectView.vue'), + meta: { requiresAuth: true }, + }, + { + path: '/categories', + name: 'categories', + component: () => import('@/views/categories/CategoriesView.vue'), + meta: { requiresAuth: true }, + }, + { + path: '/products', + name: 'products', + component: () => import('@/views/products/ProductsView.vue'), + meta: { requiresAuth: true }, + }, + { + path: '/product', + name: 'product', + component: () => import('@/views/products/ProductDetailView.vue'), + meta: { requiresAuth: true }, + }, + { + path: '/catalogs', + name: 'catalogs', + component: () => import('@/views/catalogs/CatalogsHubView.vue'), + meta: { requiresAuth: true }, + }, + { + path: '/catalog-request', + name: 'catalog-request', + component: () => import('@/views/catalogs/CatalogRequestView.vue'), + meta: { requiresAuth: true }, + }, + { + path: '/catalog-requests', + name: 'catalog-requests', + component: () => import('@/views/catalogs/CatalogRequestsView.vue'), + meta: { requiresAuth: true }, + }, + { + path: '/favorites', + name: 'favorites', + component: () => import('@/views/products/FavoritesView.vue'), + meta: { requiresAuth: true }, + }, + { + path: '/customers', + name: 'customers', + component: () => import('@/views/customers/CustomersView.vue'), + meta: { requiresAuth: true }, + }, + { + path: '/customers/new', + name: 'customer-new', + component: () => import('@/views/customers/CustomerCreateView.vue'), + meta: { requiresAuth: true, hideNav: true }, + }, + { + path: '/customer', + name: 'customer', + component: () => import('@/views/customers/CustomerDetailView.vue'), + meta: { requiresAuth: true }, + }, + { + path: '/meeting-report', + name: 'meeting-report', + component: () => import('@/views/customers/MeetingReportView.vue'), + meta: { requiresAuth: true, hideNav: true }, + }, + { + path: '/reminders', + name: 'reminders', + component: () => import('@/views/reminders/RemindersView.vue'), + meta: { requiresAuth: true }, + }, + { + path: '/reminder', + name: 'reminder', + component: () => import('@/views/reminders/ReminderDetailView.vue'), + meta: { requiresAuth: true, hideNav: true }, + }, + { + path: '/companies/:companyId/catalogs', + name: 'company-catalogs', + component: () => import('@/views/catalogs/CatalogListView.vue'), + meta: { requiresAuth: true }, + }, + { + path: '/companies/:companyId/catalogs/:catalogId/products', + name: 'company-products', + component: () => import('@/views/products/ProductListView.vue'), + meta: { requiresAuth: true }, + }, + { + path: '/cart', + name: 'cart', + component: () => import('@/views/cart/CartView.vue'), + meta: { requiresAuth: true }, + }, + { + path: '/orders', + name: 'orders', + component: () => import('@/views/OrdersView.vue'), + meta: { requiresAuth: true }, + }, + { + path: '/order', + name: 'order-query', + redirect: (to) => { + const id = String(to.query.id ?? '') + if (!id) return { name: 'orders' } + return { path: `/orders/${id}` } + }, + }, + { + path: '/orders/:id', + name: 'order-detail', + component: () => import('@/views/orders/OrderDetailView.vue'), + meta: { requiresAuth: true }, + }, + { + path: '/orders/:id/success', + name: 'order-success', + component: () => import('@/views/orders/OrderSuccessView.vue'), + meta: { requiresAuth: true, hideNav: true }, + }, + { + path: '/orders/:id/settlement', + name: 'settlement', + component: () => import('@/views/checkout/SettlementTypeView.vue'), + meta: { requiresAuth: true, hideNav: true }, + }, + { + path: '/orders/:id/cash', + name: 'cash-settlement', + component: () => import('@/views/checkout/CashSettlementView.vue'), + meta: { requiresAuth: true, hideNav: true }, + }, + { + path: '/orders/:id/check-mode', + name: 'check-mode', + component: () => import('@/views/checkout/CheckModeSelectView.vue'), + meta: { requiresAuth: true, hideNav: true }, + }, + { + path: '/orders/:id/check-form', + name: 'check-form', + component: () => import('@/views/checkout/CheckFormView.vue'), + meta: { requiresAuth: true, hideNav: true }, + }, + { + path: '/orders/:id/check-list', + name: 'check-list', + component: () => import('@/views/checkout/CheckListView.vue'), + meta: { requiresAuth: true, hideNav: true }, + }, + { + path: '/profile', + name: 'profile', + component: () => import('@/views/profile/ProfileView.vue'), + meta: { requiresAuth: true }, + }, + { + path: '/profile/edit', + name: 'profile-edit', + component: () => import('@/views/profile/ProfileEditView.vue'), + meta: { requiresAuth: true }, + }, + { + path: '/profile/security', + name: 'profile-security', + component: () => import('@/views/profile/ProfileSecurityView.vue'), + meta: { requiresAuth: true }, + }, + { + path: '/profile/notifications', + name: 'profile-notifications', + component: () => import('@/views/profile/ProfileNotificationsView.vue'), + meta: { requiresAuth: true }, + }, + { + path: '/profile/display', + name: 'profile-display', + component: () => import('@/views/profile/ProfileDisplayView.vue'), + meta: { requiresAuth: true }, + }, + { + path: '/profile/checks', + name: 'profile-checks', + component: () => import('@/views/profile/ProfileChecksView.vue'), + meta: { requiresAuth: true }, + }, + ], + scrollBehavior() { + return { top: 0 } + }, +}) + +router.beforeEach((to) => { + const authed = isAuthenticated() + + if (to.meta.requiresAuth && !authed) { + return { name: 'login', query: { redirect: to.fullPath } } + } + + if (to.meta.guest && authed) { + return { name: 'home' } + } + + // صفحات CRM برای همه قابل مشاهده‌اند (طبق طراحی موکاپ)؛ API برای غیرواسطه خطا می‌دهد + if (to.meta.agentOnly) { + const user = getCachedUser() + if (user?.appRole !== 'agent') { + return { name: 'home' } + } + } + + return true +}) + +export default router diff --git a/src/services/agentApi.ts b/src/services/agentApi.ts new file mode 100644 index 0000000..bbd3128 --- /dev/null +++ b/src/services/agentApi.ts @@ -0,0 +1,181 @@ +import { apiRequest, ApiError } from './http' +import { getCatalogCategories } from '@/data/catalogCategories' + +export type AgentProfileDto = { + id: string + firstName: string + lastName: string + code: string + levelLabel: string + nextLevelLabel: string + pointsToNext: number + levelProgress: number + phone: string + customersCount: number + ordersCount: number + salesVolumeToman: number + profitToman: number + periodLabel: string +} + +export type BrowseProductDto = { + id: string + name: string + code: string + companyId?: string + catalogId?: string + catalogCode?: string + color?: string + minMeterage?: number + orderMultiplier?: number + basePrice?: number | null + categoryId?: string + usageCategory?: string + season?: string + widthCm?: number + composition?: string + weightGrams?: number + weaveType?: string + colors?: string[] + tags?: string[] + isNew?: boolean + subtitle?: string + rollMeters?: number + imageUrl?: string + imageUrls?: string[] +} + +const AGENT_UNAVAILABLE = + 'سرویس واسطه روی سرور فعال نیست. پس از migrate و deploy سرور دوباره تلاش کنید.' + +async function agentRequest(path: string, options?: Parameters[1]): Promise { + try { + return await apiRequest(path, options) + } catch (e) { + if (e instanceof ApiError && (e.status === 404 || e.status === 501)) { + throw new ApiError(e.status, AGENT_UNAVAILABLE) + } + throw e + } +} + +export async function fetchCatalogCategories() { + try { + return await apiRequest<{ + usage: Array<{ id: string; title: string; subtitle: string; imageUrl?: string }> + seasons: Array<{ id: string; title: string; description: string; imageUrl?: string }> + }>('/catalog/categories') + } catch { + try { + return await apiRequest<{ + usage: Array<{ id: string; title: string; subtitle: string; imageUrl?: string }> + seasons: Array<{ id: string; title: string; description: string; imageUrl?: string }> + }>('/products/meta/categories') + } catch { + return getCatalogCategories() + } + } +} + +export async function fetchBrowseProducts(query?: { + categoryId?: string + season?: string +}) { + return apiRequest('/products', { + query: { + activeOnly: true, + categoryId: query?.categoryId, + season: query?.season, + }, + }) +} + +export type AppBannerDto = { + id: string + slot: string + title: string + subtitle: string + ctaLabel: string + ctaLink: string + imageUrl: string +} + +export async function fetchBanners(slot?: string) { + return apiRequest('/banners', { query: { slot } }) +} + +export async function fetchAgentProfile() { + return agentRequest('/agent/profile') +} + +export async function fetchAgentCustomers(query?: { q?: string; status?: string }) { + return agentRequest('/agent/customers', { query }) +} + +export type CreateAgentCustomerPayload = { + firstName: string + lastName: string + phone: string + businessName?: string +} + +export async function createAgentCustomer(body: CreateAgentCustomerPayload) { + return agentRequest('/agent/customers', { method: 'POST', body }) +} + +export async function fetchAgentCustomerDetail(id: string) { + return agentRequest<{ + customer: unknown + orders: unknown[] + meetings: unknown[] + }>(`/agent/customers/${id}`) +} + +export type CreateMeetingPayload = { + customerId: string + type: string + title: string + description?: string + subject?: string + content?: string + date: string + time?: string + createReminder?: boolean + reminderTitle?: string + reminderDate?: string + reminderTime?: string + reminderNote?: string +} + +export async function createMeetingReport(body: CreateMeetingPayload) { + return agentRequest('/agent/meetings', { method: 'POST', body }) +} + +export async function fetchReminders(filter?: string) { + return agentRequest('/agent/reminders', { + query: { filter }, + }) +} + +export async function fetchReminderDetail(id: string) { + return agentRequest(`/agent/reminders/${id}`) +} + +export async function completeReminder(id: string, completed = true) { + return agentRequest(`/agent/reminders/${id}/complete`, { + method: 'PATCH', + body: { completed }, + }) +} + +export async function createReminder(body: { + customerId: string + title: string + date: string + time: string + description?: string + note?: string + interactionType?: string +}) { + return agentRequest('/agent/reminders', { method: 'POST', body }) +} diff --git a/src/services/authService.ts b/src/services/authService.ts new file mode 100644 index 0000000..a666dac --- /dev/null +++ b/src/services/authService.ts @@ -0,0 +1,162 @@ +import { extractNumericString, normalizePhone } from '@/utils/format' +import { apiRequest, ApiError, getAccessToken, setAccessToken } from './http' +import type { User } from '@/types' + +const USER_KEY = 'ft_user' +const PENDING_OTP_KEY = 'ft_pending_otp' + +type PendingOtp = { + phone: string + isRegister: boolean + firstName?: string + lastName?: string +} + +let memoryPending: PendingOtp | null = null + +function cacheUser(user: User | null): void { + if (user) localStorage.setItem(USER_KEY, JSON.stringify(user)) + else localStorage.removeItem(USER_KEY) +} + +export function getCachedUser(): User | null { + try { + const raw = localStorage.getItem(USER_KEY) + if (!raw) return null + return JSON.parse(raw) as User + } catch { + return null + } +} + +export function getCurrentUser(): User | null { + if (!getAccessToken()) return null + return getCachedUser() +} + +export function isAuthenticated(): boolean { + return !!getAccessToken() +} + +function setPendingOtp(value: PendingOtp | null) { + memoryPending = value + try { + if (value) sessionStorage.setItem(PENDING_OTP_KEY, JSON.stringify(value)) + else sessionStorage.removeItem(PENDING_OTP_KEY) + } catch { + // ignore + } +} + +export async function sendOtp( + phone: string, + options: { firstName?: string; lastName?: string; isRegister: boolean }, +): Promise<{ success: boolean; message: string }> { + const normalized = normalizePhone(phone) + if (!/^09\d{9}$/.test(normalized)) { + return { success: false, message: 'شماره موبایل معتبر نیست.' } + } + + if (options.isRegister && (!options.firstName?.trim() || !options.lastName?.trim())) { + return { success: false, message: 'نام و نام خانوادگی الزامی است.' } + } + + try { + setPendingOtp({ + phone: normalized, + isRegister: options.isRegister, + firstName: options.firstName, + lastName: options.lastName, + }) + const res = await apiRequest<{ success: boolean; message: string; debugCode?: string }>( + '/auth/otp/request', + { + method: 'POST', + auth: false, + body: { + phone: normalized, + firstName: options.firstName, + lastName: options.lastName, + isRegister: options.isRegister, + }, + }, + ) + const hint = res.debugCode ? ` (کد: ${res.debugCode})` : '' + return { success: true, message: `${res.message}${hint}` } + } catch (e) { + return { + success: false, + message: e instanceof ApiError ? e.message : 'ارسال کد ناموفق بود.', + } + } +} + +export async function verifyOtp( + phone: string, + code: string, +): Promise<{ success: boolean; message: string; user?: User }> { + const normalized = normalizePhone(phone) + try { + const res = await apiRequest<{ accessToken: string; user: User }>('/auth/otp/verify', { + method: 'POST', + auth: false, + body: { phone: normalized, code: extractNumericString(code) }, + }) + setAccessToken(res.accessToken) + cacheUser(res.user) + setPendingOtp(null) + return { success: true, message: 'ورود موفق.', user: res.user } + } catch (e) { + return { + success: false, + message: e instanceof ApiError ? e.message : 'تأیید کد ناموفق بود.', + } + } +} + +export function logout(): void { + setAccessToken(null) + cacheUser(null) + setPendingOtp(null) +} + +export async function fetchMe(): Promise { + const token = getAccessToken() + if (!token) { + cacheUser(null) + return null + } + try { + const me = await apiRequest('/auth/me') + cacheUser(me) + return me + } catch { + return getCachedUser() + } +} + +export async function updateProfile( + data: Partial>, +): Promise { + try { + const updated = await apiRequest('/auth/profile', { + method: 'PATCH', + body: data, + }) + cacheUser(updated) + return updated + } catch (e) { + const avatarOnly = + data.avatarUrl !== undefined && + data.firstName === undefined && + data.lastName === undefined && + data.nationalId === undefined + const current = getCachedUser() + if (!avatarOnly || !current) throw e + const merged: User = { ...current } + if (data.avatarUrl) merged.avatarUrl = data.avatarUrl + else delete merged.avatarUrl + cacheUser(merged) + return merged + } +} diff --git a/src/services/http.ts b/src/services/http.ts new file mode 100644 index 0000000..4901333 --- /dev/null +++ b/src/services/http.ts @@ -0,0 +1,137 @@ +const TOKEN_KEY = 'ft_access_token' + +export function getApiBaseUrl(): string { + return (import.meta.env.VITE_API_BASE_URL as string | undefined)?.replace(/\/$/, '') + || 'https://parche.montest.ir/server/api' +} + +export function getAccessToken(): string | null { + return localStorage.getItem(TOKEN_KEY) +} + +export function setAccessToken(token: string | null): void { + if (token) localStorage.setItem(TOKEN_KEY, token) + else localStorage.removeItem(TOKEN_KEY) +} + +export class ApiError extends Error { + status: number + constructor(status: number, message: string) { + super(message) + this.status = status + } +} + +type RequestOptions = { + method?: string + body?: unknown + auth?: boolean + query?: Record +} + +function buildUrl(path: string, query?: RequestOptions['query']): string { + const base = getApiBaseUrl() + const joined = path.startsWith('http') + ? path + : `${base.replace(/\/$/, '')}/${path.replace(/^\//, '')}` + + const url = joined.startsWith('http') + ? new URL(joined) + : new URL(joined, typeof window !== 'undefined' ? window.location.origin : 'http://127.0.0.1') + + if (query) { + for (const [key, value] of Object.entries(query)) { + if (value === undefined || value === null || value === '') continue + url.searchParams.set(key, String(value)) + } + } + return url.toString() +} + +function errorMessageFromBody(data: unknown, status: number): string { + if (typeof data === 'object' && data) { + if ('message' in data) { + const message = (data as { message: unknown }).message + if (Array.isArray(message)) return message.join('، ') + if (message != null && String(message).trim()) return String(message) + } + if ('error' in data) { + const error = (data as { error: unknown }).error + if (typeof error === 'string' && error.trim()) return `${error} (${status})` + } + } + if (typeof data === 'string') { + if (/upstream error/i.test(data)) { + return 'سرور API در دسترس نیست. کمی بعد دوباره تلاش کنید.' + } + if (data.trim() && !data.trimStart().startsWith('<')) { + return data.trim().slice(0, 200) + } + } + if (status === 400) return 'درخواست نامعتبر است. اطلاعات سفارش را بررسی کنید.' + if (status === 401) return 'نشست شما منقضی شده است. دوباره وارد شوید.' + return `خطای سرور (${status})` +} + +export async function apiRequest(path: string, options: RequestOptions = {}): Promise { + const headers: Record = { + Accept: 'application/json', + } + if (options.body !== undefined) { + headers['Content-Type'] = 'application/json' + } + if (options.auth !== false) { + const token = getAccessToken() + if (token) headers.Authorization = `Bearer ${token}` + } + + let res: Response + try { + res = await fetch(buildUrl(path, options.query), { + method: options.method ?? (options.body !== undefined ? 'POST' : 'GET'), + headers, + body: options.body !== undefined ? JSON.stringify(options.body) : undefined, + signal: AbortSignal.timeout(20_000), + }) + } catch (err) { + const timedOut = + (err instanceof DOMException && err.name === 'TimeoutError') || + (err instanceof Error && err.name === 'TimeoutError') + throw new ApiError( + 0, + timedOut + ? 'پاسخ سرور طول کشید. دوباره تلاش کنید.' + : 'اتصال به سرور برقرار نشد. اینترنت یا آدرس API را بررسی کنید.', + ) + } + + const text = await res.text() + let data: unknown = null + if (text) { + try { + data = JSON.parse(text) + } catch { + data = text + } + } + + if (!res.ok) { + if (res.status === 401 && options.auth !== false) { + setAccessToken(null) + try { + localStorage.removeItem('ft_user') + } catch { + // ignore + } + if (typeof window !== 'undefined' && !window.location.pathname.startsWith('/login')) { + const redirect = encodeURIComponent( + `${window.location.pathname}${window.location.search}`, + ) + window.location.assign(`/login?redirect=${redirect}`) + } + } + throw new ApiError(res.status, errorMessageFromBody(data, res.status)) + } + + return data as T +} diff --git a/src/services/storage.ts b/src/services/storage.ts new file mode 100644 index 0000000..2c4c4d2 --- /dev/null +++ b/src/services/storage.ts @@ -0,0 +1,26 @@ +export function getItem(key: string, fallback: T): T { + try { + const raw = localStorage.getItem(key) + if (!raw) return fallback + return JSON.parse(raw) as T + } catch { + return fallback + } +} + +export function setItem(key: string, value: T): void { + localStorage.setItem(key, JSON.stringify(value)) +} + +export function removeItem(key: string): void { + localStorage.removeItem(key) +} + +export const STORAGE_KEYS = { + users: 'ft_users', + session: 'ft_session', + carts: 'ft_carts', + activeCartId: 'ft_active_cart_id', + orders: 'ft_orders', + pendingOtp: 'ft_pending_otp', +} as const diff --git a/src/stores/auth.ts b/src/stores/auth.ts new file mode 100644 index 0000000..4702a90 --- /dev/null +++ b/src/stores/auth.ts @@ -0,0 +1,141 @@ +import { defineStore } from 'pinia' +import { ref, computed } from 'vue' +import type { User } from '@/types' +import { + getCurrentUser, + logout as authLogout, + sendOtp, + verifyOtp, + updateProfile as authUpdateProfile, + fetchMe, +} from '@/services/authService' +import { + getPendingRegister, + setPendingRegister, + type PendingRegister, +} from '@/utils/pendingRegister' +import { useCartStore } from './cart' +import { useOrderStore } from './order' +import { useCatalogStore } from './catalog' +import { useNotificationStore } from './notification' +import { useFavoritesStore } from './favorites' + +export const useAuthStore = defineStore('auth', () => { + const user = ref(getCurrentUser()) + const loading = ref(false) + const error = ref(null) + const pendingRegister = ref(getPendingRegister()) + + const isAuthenticated = computed(() => !!user.value) + const fullName = computed(() => + user.value ? `${user.value.firstName} ${user.value.lastName}` : '', + ) + const isAgent = computed(() => user.value?.appRole === 'agent') + const isCustomerOnly = computed( + () => !!user.value && user.value.appRole !== 'agent', + ) + + function clearError() { + error.value = null + } + + function clearPendingRegister() { + pendingRegister.value = null + setPendingRegister(null) + } + + async function requestOtp( + phone: string, + options: { firstName?: string; lastName?: string; isRegister: boolean }, + ) { + loading.value = true + error.value = null + + try { + // Re-read storage on every call (survives HMR / refresh / separate tabs). + const stored = getPendingRegister() + pendingRegister.value = stored + + const firstName = options.firstName?.trim() || stored?.firstName + const lastName = options.lastName?.trim() || stored?.lastName + + if (options.isRegister && firstName && lastName) { + pendingRegister.value = { firstName, lastName } + setPendingRegister(pendingRegister.value) + } + + const result = await sendOtp(phone, { + isRegister: options.isRegister, + firstName, + lastName, + }) + if (!result.success) error.value = result.message + return result + } finally { + loading.value = false + } + } + + async function confirmOtp(phone: string, code: string) { + loading.value = true + error.value = null + const result = await verifyOtp(phone, code) + loading.value = false + if (result.success && result.user) { + user.value = result.user + clearPendingRegister() + await Promise.all([ + useCatalogStore().loadCompanies(), + useCartStore().hydrate(), + useOrderStore().hydrate(), + useNotificationStore().hydrate(), + useFavoritesStore().hydrate(), + ]) + } else { + error.value = result.message + } + return result + } + + function logout() { + authLogout() + user.value = null + clearPendingRegister() + useCartStore().resetLocal() + useOrderStore().resetLocal() + useNotificationStore().resetLocal() + useFavoritesStore().resetLocal() + } + + async function updateProfile( + data: Partial>, + ) { + if (!user.value) return null + const updated = await authUpdateProfile(data) + if (updated) user.value = updated + return updated + } + + async function hydrate() { + user.value = getCurrentUser() + if (!user.value) return + const me = await fetchMe() + user.value = me + } + + return { + user, + loading, + error, + isAuthenticated, + fullName, + isAgent, + isCustomerOnly, + clearError, + requestOtp, + confirmOtp, + logout, + updateProfile, + hydrate, + } +}) diff --git a/src/stores/cart.ts b/src/stores/cart.ts new file mode 100644 index 0000000..4272302 --- /dev/null +++ b/src/stores/cart.ts @@ -0,0 +1,407 @@ +import { defineStore } from 'pinia' +import { ref, computed } from 'vue' +import type { Cart, CartItem, Company, Product } from '@/types' +import { validateMeterage } from '@/utils/validation' +import { apiRequest, ApiError, getAccessToken } from '@/services/http' +import { useCatalogStore } from './catalog' + +const DEFAULT_MAX_METERAGE = 5000 + +type ApiCart = Cart & { totalMeterage?: number; itemCount?: number } + +function emptyCart(): Cart { + return { id: 'local', companyId: '', companyName: '', items: [] } +} + +function useLocalCart(): boolean { + return false +} + +function uniqueCompanyNames(items: Cart['items']): string { + const names = [...new Set(items.map((item) => item.companyName).filter(Boolean))] + return names.join('، ') +} + +function companyMeterage(cart: Cart, companyId: string): number { + return cart.items + .filter((item) => item.companyId === companyId) + .reduce((sum, item) => sum + item.meterage, 0) +} + +export const useCartStore = defineStore('cart', () => { + const carts = ref([emptyCart()]) + const activeCartId = ref('local') + const selectedCompanyId = ref(null) + const syncing = ref(false) + + const activeCart = computed( + () => carts.value.find((c) => c.id === activeCartId.value) ?? carts.value[0] ?? null, + ) + + const totalMeterage = computed( + () => activeCart.value?.items.reduce((sum, i) => sum + i.meterage, 0) ?? 0, + ) + + const itemCount = computed(() => activeCart.value?.items.length ?? 0) + + const catalogStore = useCatalogStore() + + const companyNamesLabel = computed(() => { + if (!activeCart.value?.items.length) return '' + return uniqueCompanyNames(activeCart.value.items) + }) + + const companySummaries = computed(() => { + const cart = activeCart.value + if (!cart?.items.length) return [] as { + companyId: string + companyName: string + meterage: number + maxMeterage: number + remaining: number + }[] + + const byCompany = new Map() + for (const item of cart.items) { + const current = byCompany.get(item.companyId) + if (current) { + current.meterage += item.meterage + } else { + byCompany.set(item.companyId, { + companyName: item.companyName || 'شرکت', + meterage: item.meterage, + }) + } + } + + return [...byCompany.entries()].map(([companyId, info]) => { + const max = + catalogStore.getCompany(companyId)?.maxMeteragePerInvoice ?? DEFAULT_MAX_METERAGE + return { + companyId, + companyName: info.companyName, + meterage: info.meterage, + maxMeterage: max, + remaining: Math.max(0, max - info.meterage), + } + }) + }) + + /** سقف نمایشی: مجموع سقف شرکت‌های داخل سبد (هر شرکت جداگانه محدود می‌شود). */ + const maxMeterage = computed(() => { + if (!companySummaries.value.length) { + const selected = selectedCompanyId.value + ? catalogStore.getCompany(selectedCompanyId.value) + : null + return selected?.maxMeteragePerInvoice ?? DEFAULT_MAX_METERAGE + } + return companySummaries.value.reduce((sum, row) => sum + row.maxMeterage, 0) + }) + + const remainingCapacity = computed(() => + companySummaries.value.length + ? companySummaries.value.reduce((sum, row) => sum + row.remaining, 0) + : Math.max(0, maxMeterage.value - totalMeterage.value), + ) + + /** وقتی حداقل یک شرکت به سقف خودش رسیده باشد. */ + const isFull = computed(() => + companySummaries.value.some((row) => row.meterage >= row.maxMeterage), + ) + + function applyServerCart(serverCart: ApiCart) { + const cart: Cart = { + id: serverCart.id, + companyId: serverCart.companyId || '', + companyName: serverCart.companyName || uniqueCompanyNames(serverCart.items), + items: serverCart.items ?? [], + notes: serverCart.notes, + } + carts.value = [cart] + activeCartId.value = cart.id + if (cart.companyId) selectedCompanyId.value = cart.companyId + } + + function replaceActiveCart(cart: Cart) { + carts.value = [{ ...cart, items: cart.items.map((item) => ({ ...item })) }] + activeCartId.value = carts.value[0]!.id + } + + async function hydrate() { + if (!getAccessToken()) { + resetLocal() + return + } + if (useLocalCart()) { + // Keep whatever local cart already exists for mock sessions. + syncing.value = false + return + } + syncing.value = true + try { + const serverCart = await apiRequest('/carts') + applyServerCart(serverCart) + } catch { + resetLocal() + } finally { + syncing.value = false + } + } + + function resetLocal() { + carts.value = [emptyCart()] + activeCartId.value = carts.value[0]!.id + selectedCompanyId.value = null + } + + function selectCompany(company: Company): void { + selectedCompanyId.value = company.id + if (activeCart.value && !activeCart.value.items.length) { + activeCart.value.companyId = company.id + activeCart.value.companyName = company.name + } + } + + function initCartForCompany(company: Company): Cart { + selectCompany(company) + return activeCart.value ?? emptyCart() + } + + function getOrCreateCart(company?: Company): Cart { + if (company) selectCompany(company) + return activeCart.value ?? emptyCart() + } + + async function createNewCartForSameCompany(): Promise { + if (useLocalCart()) { + resetLocal() + return activeCart.value + } + try { + const serverCart = await apiRequest('/carts', { method: 'DELETE' }) + applyServerCart(serverCart) + return activeCart.value + } catch (e) { + console.error(e) + return null + } + } + + function addItemLocal( + product: Product, + catalogCode: string, + meterage: number, + company: Company, + ): { success: boolean; message?: string } { + const cart = getOrCreateCart(company) + const existing = cart.items.find((i) => i.productId === product.id) + const items = [...cart.items] + + if (existing) { + const index = items.findIndex((i) => i.id === existing.id) + items[index] = { ...existing, meterage } + } else { + const item: CartItem = { + id: `local-item-${product.id}-${Date.now()}`, + productId: product.id, + catalogCode, + productName: product.name, + productCode: product.code, + color: product.color, + meterage, + companyId: company.id, + companyName: company.name, + } + items.push(item) + } + + replaceActiveCart({ + id: cart.id || 'local', + companyId: items[0]?.companyId || company.id, + companyName: uniqueCompanyNames(items) || company.name, + items, + notes: cart.notes, + }) + selectedCompanyId.value = company.id + return { success: true } + } + + async function addItem( + product: Product, + catalogCode: string, + meterage: number, + ): Promise<{ success: boolean; message?: string }> { + const company = catalogStore.getCompany(product.companyId) + if (!company) { + return { success: false, message: 'شرکت محصول یافت نشد.' } + } + + const cart = getOrCreateCart(company) + const companyMax = company.maxMeteragePerInvoice + const currentCompanyMeterage = companyMeterage(cart, company.id) + const existing = cart.items.find((i) => i.productId === product.id) + const previousMeterage = existing?.meterage ?? 0 + + const validation = validateMeterage( + meterage, + product, + currentCompanyMeterage, + companyMax, + previousMeterage, + ) + if (!validation.valid) { + return { success: false, message: validation.message } + } + + if (useLocalCart()) { + return addItemLocal(product, catalogCode, meterage, company) + } + + try { + let serverCart: ApiCart + if (existing) { + serverCart = await apiRequest(`/carts/items/${existing.id}`, { + method: 'PATCH', + body: { meterage }, + }) + } else { + serverCart = await apiRequest('/carts/items', { + method: 'POST', + body: { productId: product.id, meterage }, + }) + } + applyServerCart(serverCart) + selectedCompanyId.value = company.id + return { success: true } + } catch (e) { + return { + success: false, + message: e instanceof ApiError ? e.message : 'افزودن به سبد ناموفق بود.', + } + } + } + + async function updateItemMeterage( + itemId: string, + meterage: number, + product: Product, + ): Promise<{ success: boolean; message?: string }> { + if (!activeCart.value) return { success: false, message: 'سبد خرید یافت نشد.' } + const item = activeCart.value.items.find((i) => i.id === itemId) + if (!item) return { success: false, message: 'آیتم یافت نشد.' } + + const company = catalogStore.getCompany(item.companyId || product.companyId) + const companyMax = company?.maxMeteragePerInvoice ?? DEFAULT_MAX_METERAGE + const currentCompanyMeterage = companyMeterage(activeCart.value, item.companyId) + + const validation = validateMeterage( + meterage, + product, + currentCompanyMeterage, + companyMax, + item.meterage, + ) + if (!validation.valid) { + return { success: false, message: validation.message } + } + + if (useLocalCart()) { + const items = activeCart.value.items.map((entry) => + entry.id === itemId ? { ...entry, meterage } : entry, + ) + replaceActiveCart({ ...activeCart.value, items }) + return { success: true } + } + + try { + const serverCart = await apiRequest(`/carts/items/${itemId}`, { + method: 'PATCH', + body: { meterage }, + }) + applyServerCart(serverCart) + return { success: true } + } catch (e) { + return { + success: false, + message: e instanceof ApiError ? e.message : 'به‌روزرسانی متراژ ناموفق بود.', + } + } + } + + async function removeItem(itemId: string) { + if (useLocalCart()) { + if (!activeCart.value) return + const items = activeCart.value.items.filter((entry) => entry.id !== itemId) + replaceActiveCart({ ...activeCart.value, items }) + return + } + try { + const serverCart = await apiRequest(`/carts/items/${itemId}`, { + method: 'DELETE', + }) + applyServerCart(serverCart) + } catch (e) { + console.error(e) + } + } + + async function setNotes(notes: string) { + if (useLocalCart()) { + if (!activeCart.value) return + replaceActiveCart({ ...activeCart.value, notes }) + return + } + try { + const serverCart = await apiRequest('/carts/notes', { + method: 'PATCH', + body: { notes }, + }) + applyServerCart(serverCart) + } catch (e) { + console.error(e) + } + } + + async function clearActiveCart() { + if (useLocalCart()) { + resetLocal() + return + } + try { + const serverCart = await apiRequest('/carts', { method: 'DELETE' }) + applyServerCart(serverCart) + } catch { + resetLocal() + } + } + + function ensureActiveCartForCartPage() { + // server keeps a single cart + } + + return { + carts, + activeCartId, + selectedCompanyId, + syncing, + activeCart, + totalMeterage, + itemCount, + companyNamesLabel, + companySummaries, + maxMeterage, + remainingCapacity, + isFull, + initCartForCompany, + selectCompany, + createNewCartForSameCompany, + addItem, + updateItemMeterage, + removeItem, + setNotes, + clearActiveCart, + ensureActiveCartForCartPage, + hydrate, + resetLocal, + } +}) diff --git a/src/stores/catalog.ts b/src/stores/catalog.ts new file mode 100644 index 0000000..d7040e1 --- /dev/null +++ b/src/stores/catalog.ts @@ -0,0 +1,129 @@ +import { defineStore } from 'pinia' +import { ref } from 'vue' +import { apiRequest } from '@/services/http' +import type { BankAccount, Catalog, Company, Product } from '@/types' + +export const useCatalogStore = defineStore('catalog', () => { + const companies = ref([]) + const catalogs = ref([]) + const products = ref([]) + const bankAccounts = ref([]) + const searchQuery = ref('') + const loading = ref(false) + const loaded = ref(false) + + async function loadCompanies(force = false) { + if (loaded.value && !force) return companies.value + loading.value = true + try { + const list = await apiRequest('/companies', { + query: { activeOnly: true }, + }) + companies.value = list + .filter((c) => c.isActive) + .sort((a, b) => a.displayOrder - b.displayOrder) + loaded.value = true + } finally { + loading.value = false + } + return companies.value + } + + function getActiveCompanies(): Company[] { + return [...companies.value].sort((a, b) => a.displayOrder - b.displayOrder) + } + + function getCompany(id: string): Company | undefined { + return companies.value.find((c) => c.id === id) + } + + async function loadCatalogs(companyId: string) { + const list = await apiRequest('/catalogs', { + query: { companyId, activeOnly: true }, + }) + catalogs.value = [ + ...catalogs.value.filter((c) => c.companyId !== companyId), + ...list, + ] + return list + } + + async function loadProducts(catalogId: string, color?: string) { + const list = await apiRequest('/products', { + query: { + catalogId, + color: color && color !== 'all' ? color : undefined, + activeOnly: true, + }, + }) + const others = products.value.filter((p) => p.catalogId !== catalogId) + products.value = [...others, ...list] + return list + } + + async function loadBankAccounts(companyId: string) { + const list = await apiRequest(`/companies/${companyId}/bank-accounts`) + bankAccounts.value = [ + ...bankAccounts.value.filter((b) => b.companyId !== companyId), + ...list.filter((b) => b.isActive), + ] + return bankAccounts.value.filter((b) => b.companyId === companyId) + } + + function fetchCatalogs(companyId: string, query = ''): Catalog[] { + let list = catalogs.value.filter((c) => c.companyId === companyId && c.isActive) + if (query.trim()) { + const q = query.trim() + list = list.filter((c) => c.code.includes(q) || c.name.includes(q)) + } + return [...list].sort((a, b) => a.displayOrder - b.displayOrder) + } + + function fetchProducts(catalogId: string, colorFilter?: string): Product[] { + let list = products.value.filter((p) => p.catalogId === catalogId && p.isActive) + if (colorFilter && colorFilter !== 'all') { + list = list.filter((p) => p.color.includes(colorFilter)) + } + return list + } + + function getCatalog(id: string) { + return catalogs.value.find((c) => c.id === id) + } + + function getProduct(id: string) { + return products.value.find((p) => p.id === id) + } + + function getUniqueColors(catalogId: string): string[] { + return [...new Set(fetchProducts(catalogId).map((p) => p.color))] + } + + function getBankAccountsByCompany(companyId: string): BankAccount[] { + return bankAccounts.value + .filter((b) => b.companyId === companyId && b.isActive) + .sort((a, b) => a.displayOrder - b.displayOrder) + } + + return { + companies, + catalogs, + products, + bankAccounts, + searchQuery, + loading, + loaded, + loadCompanies, + loadCatalogs, + loadProducts, + loadBankAccounts, + getActiveCompanies, + getCompany, + fetchCatalogs, + fetchProducts, + getCatalog, + getProduct, + getUniqueColors, + getBankAccountsByCompany, + } +}) diff --git a/src/stores/catalogRequests.ts b/src/stores/catalogRequests.ts new file mode 100644 index 0000000..b0cb3d1 --- /dev/null +++ b/src/stores/catalogRequests.ts @@ -0,0 +1,206 @@ +import { defineStore } from 'pinia' +import { computed, ref } from 'vue' +import { apiRequest } from '@/services/http' + +const STORAGE_KEY = 'ft_catalog_requests' + +export type CatalogRequestItem = { + productId: string + productName: string + productCode: string + quantity: number +} + +/** UI statuses (legacy) + server statuses mapped in */ +export type CatalogRequestStatus = + | 'submitted' + | 'reviewing' + | 'preparing' + | 'shipped' + | 'delivered' + | 'new' + | 'approved' + | 'ready_to_ship' + | 'returned' + | 'cancelled' + +export type CatalogRequest = { + id: string + createdAt: string + status: CatalogRequestStatus + note?: string + items: CatalogRequestItem[] +} + +const SERVER_TO_UI: Record = { + new: 'submitted', + approved: 'reviewing', + ready_to_ship: 'preparing', + delivered: 'delivered', + returned: 'delivered', + cancelled: 'submitted', +} + +function readAll(): CatalogRequest[] { + try { + const raw = localStorage.getItem(STORAGE_KEY) + if (!raw) return [] + const parsed = JSON.parse(raw) as unknown + return Array.isArray(parsed) ? (parsed as CatalogRequest[]) : [] + } catch { + return [] + } +} + +function writeAll(list: CatalogRequest[]) { + localStorage.setItem(STORAGE_KEY, JSON.stringify(list)) +} + +const STATUS_LABELS: Record = { + submitted: 'ثبت‌شده', + reviewing: 'در حال بررسی', + preparing: 'آماده‌سازی', + shipped: 'ارسال‌شده', + delivered: 'تحویل‌شده', + new: 'درخواست جدید', + approved: 'تأیید شده', + ready_to_ship: 'آماده ارسال', + returned: 'بازگردانده شده', + cancelled: 'لغو شده', +} + +function mapServerRow(row: { + id: string + createdAt: string + status: string + note?: string + items: Array<{ + productId: string + productName: string + productCode: string + quantity: number + }> +}): CatalogRequest { + return { + id: row.id, + createdAt: row.createdAt, + status: SERVER_TO_UI[row.status] ?? (row.status as CatalogRequestStatus), + note: row.note, + items: row.items.map((i) => ({ + productId: i.productId, + productName: i.productName, + productCode: i.productCode, + quantity: i.quantity, + })), + } +} + +export const useCatalogRequestsStore = defineStore('catalogRequests', () => { + const requests = ref(readAll()) + const useApi = ref(true) + + const sorted = computed(() => + [...requests.value].sort((a, b) => b.createdAt.localeCompare(a.createdAt)), + ) + + function statusLabel(status: CatalogRequest['status']) { + return STATUS_LABELS[status] ?? status + } + + async function hydrate() { + if (!useApi.value) { + requests.value = readAll() + return + } + try { + const rows = await apiRequest< + Array<{ + id: string + createdAt: string + status: string + note?: string + items: Array<{ + productId: string + productName: string + productCode: string + quantity: number + }> + }> + >('/catalog-requests') + requests.value = rows.map(mapServerRow) + writeAll(requests.value) + } catch { + useApi.value = false + requests.value = readAll() + } + } + + async function create(input: { items: CatalogRequestItem[]; note?: string }) { + const items = input.items + .filter((i) => i.productId && i.quantity > 0) + .map((i) => ({ + ...i, + quantity: Math.max(1, Math.floor(i.quantity)), + })) + if (!items.length) { + return { success: false as const, message: 'حداقل یک قلم با تعداد معتبر لازم است.' } + } + + if (useApi.value) { + try { + const row = await apiRequest<{ + id: string + createdAt: string + status: string + note?: string + items: Array<{ + productId: string + productName: string + productCode: string + quantity: number + }> + }>('/catalog-requests', { + method: 'POST', + body: { + note: input.note?.trim() || undefined, + items: items.map((i) => ({ + productId: i.productId, + quantity: i.quantity, + })), + }, + }) + const mapped = mapServerRow(row) + requests.value = [mapped, ...requests.value.filter((r) => r.id !== mapped.id)] + writeAll(requests.value) + return { success: true as const, request: mapped } + } catch (err) { + useApi.value = false + // fall through to local + } + } + + const row: CatalogRequest = { + id: `cr-${Date.now()}`, + createdAt: new Date().toISOString(), + status: 'submitted', + note: input.note?.trim() || undefined, + items, + } + requests.value = [row, ...requests.value] + writeAll(requests.value) + return { success: true as const, request: row } + } + + function getById(id: string) { + return requests.value.find((r) => r.id === id) ?? null + } + + return { + requests, + sorted, + statusLabel, + create, + getById, + hydrate, + } +}) diff --git a/src/stores/crm.ts b/src/stores/crm.ts new file mode 100644 index 0000000..2e4e933 --- /dev/null +++ b/src/stores/crm.ts @@ -0,0 +1,199 @@ +import { defineStore } from 'pinia' +import { ref } from 'vue' +import type { CrmCustomer, CrmMeeting, CrmOrderSummary, CrmReminder } from '@/types/crm' +import type { CatalogProductCard, ProductCategory, SeasonCategory } from '@/types/crm' +import type { AgentProfileDto } from '@/services/agentApi' +import { + completeReminder as apiCompleteReminder, + createAgentCustomer, + createMeetingReport, + createReminder as apiCreateReminder, + fetchAgentCustomerDetail, + fetchAgentCustomers, + fetchAgentProfile, + fetchBrowseProducts, + fetchCatalogCategories, + fetchReminderDetail, + fetchReminders, +} from '@/services/agentApi' +import { apiRequest } from '@/services/http' + +function mapProduct(p: { + id: string + name: string + code: string + companyId?: string + catalogId?: string + catalogCode?: string + color?: string + minMeterage?: number + orderMultiplier?: number + basePrice?: number | null + categoryId?: string + usageCategory?: string + season?: string + widthCm?: number + composition?: string + weightGrams?: number + weaveType?: string + colors?: string[] + tags?: string[] + isNew?: boolean + subtitle?: string + rollMeters?: number + imageUrl?: string + imageUrls?: string[] +}): CatalogProductCard { + return { + id: p.id, + name: p.name, + code: p.code, + categoryId: p.categoryId || p.usageCategory || 'other', + season: (p.season as CatalogProductCard['season']) || 'all_season', + widthCm: p.widthCm ?? 150, + composition: p.composition ?? '', + weightGrams: p.weightGrams, + weaveType: p.weaveType, + colors: p.colors?.length ? p.colors : ['#6b7280'], + tags: p.tags ?? [], + isNew: !!p.isNew, + imageUrl: p.imageUrl || p.imageUrls?.[0], + subtitle: p.subtitle, + rollMeters: p.rollMeters ?? 50, + companyId: p.companyId, + catalogId: p.catalogId, + catalogCode: p.catalogCode, + color: p.color, + minMeterage: p.minMeterage ?? 30, + orderMultiplier: p.orderMultiplier ?? 10, + basePrice: p.basePrice ?? undefined, + } +} + +export const useCrmStore = defineStore('crm', () => { + const loading = ref(false) + const error = ref(null) + const agentProfile = ref(null) + const customers = ref([]) + const reminders = ref([]) + const usageCategories = ref([]) + const seasonCategories = ref([]) + const browseProducts = ref([]) + + async function loadCategories() { + const data = await fetchCatalogCategories() + usageCategories.value = data.usage + seasonCategories.value = data.seasons as SeasonCategory[] + return data + } + + async function loadBrowseProducts(query?: { categoryId?: string; season?: string }) { + const list = await fetchBrowseProducts(query) + browseProducts.value = list.map(mapProduct) + return browseProducts.value + } + + async function loadProductById(id: string) { + const p = await apiRequest<{ + id: string + name: string + code: string + companyId?: string + catalogId?: string + catalogCode?: string + color?: string + minMeterage?: number + orderMultiplier?: number + basePrice?: number | null + categoryId?: string + usageCategory?: string + season?: string + widthCm?: number + composition?: string + weightGrams?: number + weaveType?: string + colors?: string[] + tags?: string[] + isNew?: boolean + subtitle?: string + rollMeters?: number + imageUrl?: string + imageUrls?: string[] + }>(`/products/${id}`) + return mapProduct(p) + } + + async function loadAgentProfile() { + agentProfile.value = await fetchAgentProfile() + return agentProfile.value + } + + async function loadCustomers(query?: { q?: string; status?: string }) { + customers.value = (await fetchAgentCustomers(query)) as CrmCustomer[] + return customers.value + } + + async function loadCustomerDetail(id: string) { + return (await fetchAgentCustomerDetail(id)) as { + customer: CrmCustomer + orders: CrmOrderSummary[] + meetings: CrmMeeting[] + } + } + + async function submitMeeting(body: Parameters[0]) { + return createMeetingReport(body) + } + + async function registerCustomer(body: Parameters[0]) { + return createAgentCustomer(body) + } + + async function loadReminders(filter?: string) { + reminders.value = (await fetchReminders(filter)) as CrmReminder[] + return reminders.value + } + + async function loadReminderDetail(id: string) { + return fetchReminderDetail(id) + } + + async function markReminderComplete(id: string) { + return apiCompleteReminder(id, true) + } + + async function createReminder(body: { + customerId: string + title: string + date: string + time: string + description?: string + note?: string + interactionType?: string + }) { + return apiCreateReminder(body) + } + + return { + loading, + error, + agentProfile, + customers, + reminders, + usageCategories, + seasonCategories, + browseProducts, + loadCategories, + loadBrowseProducts, + loadProductById, + loadAgentProfile, + loadCustomers, + loadCustomerDetail, + submitMeeting, + registerCustomer, + loadReminders, + loadReminderDetail, + markReminderComplete, + createReminder, + } +}) diff --git a/src/stores/favorites.ts b/src/stores/favorites.ts new file mode 100644 index 0000000..7501aa3 --- /dev/null +++ b/src/stores/favorites.ts @@ -0,0 +1,95 @@ +import { defineStore } from 'pinia' +import { computed, ref } from 'vue' +import { apiRequest, ApiError } from '@/services/http' + +const LOCAL_KEY = 'ft_favorites' + +function readLocal(): string[] { + try { + const raw = localStorage.getItem(LOCAL_KEY) + if (!raw) return [] + const parsed = JSON.parse(raw) as unknown + return Array.isArray(parsed) ? parsed.filter((x) => typeof x === 'string') : [] + } catch { + return [] + } +} + +function writeLocal(ids: string[]) { + localStorage.setItem(LOCAL_KEY, JSON.stringify(ids)) +} + +export const useFavoritesStore = defineStore('favorites', () => { + const ids = ref(readLocal()) + const loaded = ref(false) + const pending = ref>({}) + + const idSet = computed(() => new Set(ids.value)) + + function isFavorite(productId: string) { + return idSet.value.has(productId) + } + + async function hydrate() { + try { + const list = await apiRequest('/products/favorites/ids') + ids.value = list + writeLocal(list) + } catch { + ids.value = readLocal() + } finally { + loaded.value = true + } + } + + async function toggle(productId: string) { + if (!productId || pending.value[productId]) return + pending.value = { ...pending.value, [productId]: true } + + const wasFavorite = isFavorite(productId) + ids.value = wasFavorite + ? ids.value.filter((id) => id !== productId) + : [...ids.value, productId] + writeLocal(ids.value) + + try { + const res = await apiRequest<{ productId: string; favorited: boolean }>( + `/products/${productId}/favorite`, + { method: 'POST' }, + ) + ids.value = res.favorited + ? [...new Set([...ids.value.filter((id) => id !== productId), productId])] + : ids.value.filter((id) => id !== productId) + writeLocal(ids.value) + } catch (e) { + // اگر endpoint روی سرور نبود، حالت محلی حفظ می‌شود + if (!(e instanceof ApiError && e.status === 404)) { + ids.value = wasFavorite + ? [...new Set([...ids.value, productId])] + : ids.value.filter((id) => id !== productId) + writeLocal(ids.value) + throw e + } + } finally { + const next = { ...pending.value } + delete next[productId] + pending.value = next + } + } + + function resetLocal() { + ids.value = [] + writeLocal([]) + loaded.value = false + } + + return { + ids, + loaded, + pending, + isFavorite, + hydrate, + toggle, + resetLocal, + } +}) diff --git a/src/stores/notification.ts b/src/stores/notification.ts new file mode 100644 index 0000000..41b547c --- /dev/null +++ b/src/stores/notification.ts @@ -0,0 +1,84 @@ +import { defineStore } from 'pinia' +import { computed, ref } from 'vue' +import { apiRequest, getAccessToken } from '@/services/http' +import type { AppNotification } from '@/types' + +export const useNotificationStore = defineStore('notification', () => { + const items = ref([]) + const unreadCount = ref(0) + const loading = ref(false) + + const hasUnread = computed(() => unreadCount.value > 0) + + async function hydrate() { + if (!getAccessToken()) { + items.value = [] + unreadCount.value = 0 + return + } + loading.value = true + try { + items.value = await apiRequest('/notifications') + unreadCount.value = items.value.filter((n) => !n.isRead).length + } catch { + items.value = [] + unreadCount.value = 0 + } finally { + loading.value = false + } + } + + /** فقط شمارنده؛ برای زنگ هدر که نیازی به کل فهرست ندارد. */ + async function refreshUnreadCount() { + if (!getAccessToken()) { + unreadCount.value = 0 + return + } + try { + const res = await apiRequest<{ count: number }>('/notifications/unread-count') + unreadCount.value = res.count + } catch { + /* شمارنده حیاتی نیست */ + } + } + + async function markRead(id: string) { + const item = items.value.find((n) => n.id === id) + if (!item || item.isRead) return + item.isRead = true + unreadCount.value = Math.max(0, unreadCount.value - 1) + try { + await apiRequest(`/notifications/${id}/read`, { method: 'PATCH' }) + } catch { + await hydrate() + } + } + + async function markAllRead() { + if (!unreadCount.value) return + items.value = items.value.map((n) => ({ ...n, isRead: true })) + unreadCount.value = 0 + try { + await apiRequest('/notifications/read-all', { method: 'PATCH' }) + } catch { + await hydrate() + } + } + + function resetLocal() { + items.value = [] + unreadCount.value = 0 + } + + return { + items, + unreadCount, + loading, + hasUnread, + hydrate, + refreshUnreadCount, + markRead, + markAllRead, + resetLocal, + } +}) diff --git a/src/stores/order.ts b/src/stores/order.ts new file mode 100644 index 0000000..ec30562 --- /dev/null +++ b/src/stores/order.ts @@ -0,0 +1,292 @@ +import { defineStore } from 'pinia' +import { ref, computed } from 'vue' +import type { + Cart, + CheckData, + CashPaymentReceipt, + Order, + SettlementType, + CheckRegistrationMode, +} from '@/types' +import { generateId } from '@/utils/format' +import { calculateCheckRass } from '@/utils/checkRass' +import { apiRequest, ApiError, getAccessToken } from '@/services/http' +import { useCatalogStore } from './catalog' + +export const useOrderStore = defineStore('order', () => { + const orders = ref([]) + const draftChecks = ref([]) + const draftSettlementType = ref(null) + const draftCheckMode = ref(null) + const draftOrderId = ref(null) + const loading = ref(false) + + const userOrders = computed( + () => (_userId: string) => + [...orders.value].sort( + (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(), + ), + ) + + function getOrderById(id: string): Order | undefined { + return orders.value.find((o) => o.id === id) + } + + /** شروع مرحله ثبت چک روی سفارشی که فاکتورش صادر شده است. */ + function startCheckDraft(orderId: string, mode?: CheckRegistrationMode) { + draftOrderId.value = orderId + draftSettlementType.value = 'check' + draftChecks.value = [] + draftCheckMode.value = mode ?? null + } + + /** برای اصلاح چک، چک‌های ثبت‌شده سفارش به فرم برگردانده می‌شوند. */ + function loadChecksIntoDraft(order: Order) { + draftOrderId.value = order.id + draftSettlementType.value = 'check' + draftCheckMode.value = order.checkMode ?? 'full' + draftChecks.value = (order.checks ?? []).map((c) => ({ ...c })) + } + + function clearCheckDraft() { + draftOrderId.value = null + draftChecks.value = [] + draftCheckMode.value = null + draftSettlementType.value = null + } + + function setCheckMode(mode: CheckRegistrationMode) { + draftCheckMode.value = mode + } + + function addDraftCheck(check: Omit) { + draftChecks.value.push({ ...check, id: generateId() }) + } + + function updateDraftCheck(id: string, data: Partial) { + const index = draftChecks.value.findIndex((c) => c.id === id) + if (index >= 0) { + draftChecks.value[index] = { ...draftChecks.value[index]!, ...data } + } + } + + function removeDraftCheck(id: string) { + draftChecks.value = draftChecks.value.filter((c) => c.id !== id) + } + + function getDraftRass(baseDate?: Date) { + return calculateCheckRass(draftChecks.value, baseDate ?? new Date()) + } + + async function hydrate() { + if (!getAccessToken()) { + orders.value = [] + return + } + loading.value = true + try { + orders.value = await apiRequest('/orders') + } catch { + orders.value = [] + } finally { + loading.value = false + } + } + + function resetLocal() { + orders.value = [] + clearCheckDraft() + } + + function upsertOrder(order: Order) { + const idx = orders.value.findIndex((o) => o.id === order.id) + if (idx >= 0) orders.value[idx] = order + else orders.value.unshift(order) + } + + async function fetchOrder(id: string): Promise { + try { + const order = await apiRequest(`/orders/${id}`) + upsertOrder(order) + return order + } catch { + return null + } + } + + /** + * سفارش بدون روش تسویه ثبت می‌شود؛ مشتری بعد از قیمت‌گذاری مدیریت + * روش تسویه را انتخاب می‌کند. + * اقلام همه شرکت‌ها در یک فاکتور واحد ثبت می‌شوند. + */ + async function submitOrder(cart: Cart): Promise { + if (!cart.items.length) { + throw new Error('سبد خرید خالی است.') + } + if (cart.id === 'local' || cart.items.some((item) => item.id.startsWith('local-item-'))) { + throw new Error('سبد خرید با سرور همگام نیست. لطفاً دوباره محصولات را به سبد اضافه کنید.') + } + + const companyIds = [ + ...new Set(cart.items.map((item) => item.companyId).filter(Boolean)), + ] + if (!companyIds.length) { + throw new Error('شرکت سبد مشخص نیست. سبد را خالی کنید و دوباره اضافه کنید.') + } + + try { + const order = await apiRequest('/orders', { + method: 'POST', + body: { + companyId: companyIds[0], + notes: cart.notes, + items: cart.items.map((item) => ({ + productId: item.productId, + meterage: Number(item.meterage), + notes: item.notes, + })), + }, + }) + orders.value.unshift(order) + clearCheckDraft() + return order + } catch (e) { + throw new Error(e instanceof ApiError ? e.message : 'ثبت سفارش ناموفق بود.') + } + } + + /** انتخاب روش تسویه پس از صدور فاکتور. */ + async function chooseSettlement( + orderId: string, + settlementType: SettlementType, + checkMode?: CheckRegistrationMode, + ): Promise { + try { + const order = await apiRequest(`/orders/${orderId}/settlement`, { + method: 'POST', + body: { + settlementType, + checkMode: settlementType === 'check' ? (checkMode ?? 'full') : undefined, + }, + }) + upsertOrder(order) + return order + } catch (e) { + throw new Error(e instanceof ApiError ? e.message : 'ثبت روش تسویه ناموفق بود.') + } + } + + async function submitCashReceipt( + orderId: string, + receipt: Omit, + ) { + try { + await apiRequest(`/orders/${orderId}/cash-receipt`, { + method: 'POST', + body: { + amount: receipt.amount, + transferDate: receipt.transferDate, + transferTime: receipt.transferTime, + trackingNumber: receipt.trackingNumber, + destinationAccountId: receipt.destinationAccountId, + receiptImage: receipt.receiptImage, + notes: receipt.notes, + }, + }) + const order = await fetchOrder(orderId) + if (!order) { + throw new Error('رسید ثبت شد ولی دریافت سفارش ناموفق بود.') + } + return order + } catch (e) { + throw new Error(e instanceof ApiError ? e.message : 'ثبت رسید ناموفق بود.') + } + } + + async function updateOrderChecks( + orderId: string, + checks: CheckData[], + checkMode?: CheckRegistrationMode, + ): Promise { + try { + const order = await apiRequest(`/orders/${orderId}/checks`, { + method: 'PATCH', + body: { + checkMode, + checks: checks.map((c) => ({ + amount: c.amount, + dueDate: c.dueDate, + sayadId: c.sayadId, + checkNumber: c.checkNumber, + ownerNationalId: c.ownerNationalId, + ownerFirstName: c.ownerFirstName, + ownerLastName: c.ownerLastName, + bankName: c.bankName, + branchName: c.branchName, + frontImage: c.frontImage, + backImage: c.backImage, + notes: c.notes, + mode: c.mode, + })), + }, + }) + upsertOrder(order) + clearCheckDraft() + return order + } catch (e) { + throw new Error(e instanceof ApiError ? e.message : 'ثبت چک‌ها ناموفق بود.') + } + } + + async function getBankAccountsForOrder(order: Order) { + const companyIds = [ + ...new Set( + [ + order.companyId, + ...order.items.map((item) => item.companyId).filter(Boolean), + ].filter(Boolean) as string[], + ), + ] + const catalog = useCatalogStore() + const lists = await Promise.all(companyIds.map((id) => catalog.loadBankAccounts(id))) + const byId = new Map() + for (const list of lists) { + for (const account of list) { + byId.set(account.id, account) + } + } + return [...byId.values()] + } + + async function getBankAccountsForCompany(companyId: string) { + return useCatalogStore().loadBankAccounts(companyId) + } + + return { + orders, + draftChecks, + draftSettlementType, + draftCheckMode, + draftOrderId, + loading, + userOrders, + getOrderById, + startCheckDraft, + loadChecksIntoDraft, + clearCheckDraft, + setCheckMode, + addDraftCheck, + updateDraftCheck, + removeDraftCheck, + getDraftRass, + submitOrder, + chooseSettlement, + submitCashReceipt, + updateOrderChecks, + hydrate, + resetLocal, + fetchOrder, + getBankAccountsForOrder, + getBankAccountsForCompany, + } +}) diff --git a/src/stores/theme.ts b/src/stores/theme.ts new file mode 100644 index 0000000..d4adbc9 --- /dev/null +++ b/src/stores/theme.ts @@ -0,0 +1,47 @@ +import { defineStore } from 'pinia' +import { computed, watchEffect } from 'vue' +import { usePreferredDark, useStorage } from '@vueuse/core' + +export type ThemePreference = 'light' | 'dark' | 'system' + +export const useThemeStore = defineStore('theme', () => { + const preference = useStorage('ft_theme', 'system') + const prefersDark = usePreferredDark() + + const resolved = computed<'light' | 'dark'>(() => { + if (preference.value === 'system') return prefersDark.value ? 'dark' : 'light' + return preference.value + }) + + const isDark = computed(() => resolved.value === 'dark') + + watchEffect(() => { + const root = document.documentElement + root.classList.toggle('dark', isDark.value) + root.style.colorScheme = resolved.value + }) + + function setPreference(value: ThemePreference) { + preference.value = value + } + + function cyclePreference() { + const order: ThemePreference[] = ['light', 'dark', 'system'] + const index = order.indexOf(preference.value) + preference.value = order[(index + 1) % order.length]! + } + + /** Explicit light ↔ dark toggle (skips system). */ + function toggleLightDark() { + preference.value = isDark.value ? 'light' : 'dark' + } + + return { + preference, + resolved, + isDark, + setPreference, + cyclePreference, + toggleLightDark, + } +}) diff --git a/src/types/crm.ts b/src/types/crm.ts new file mode 100644 index 0000000..c4819f4 --- /dev/null +++ b/src/types/crm.ts @@ -0,0 +1,128 @@ +export type CustomerStatus = 'active' | 'inactive' | 'new' +export type MeetingInteractionType = 'in_person' | 'call' | 'message' | 'note' +export type ReminderFilter = 'all' | 'today' | 'week' | 'past' +export type CustomerTab = 'info' | 'orders' | 'reports' +export type SeasonKind = 'summer' | 'winter' | 'all_season' + +export interface CrmCustomer { + id: string + name: string + contactName: string + phone: string + status: CustomerStatus + avatarLetter: string + avatarColor: string + totalPurchaseToman: number + orderCount: number + averageOrderToman: number + levelLabel: string + firstPurchaseDate: string + agentCode?: string + agentName?: string + imageUrl?: string +} + +export interface CrmOrderSummary { + id: string + customerId: string + title: string + amountToman: number + statusLabel: string + statusTone: 'warning' | 'success' | 'info' + date: string +} + +export interface CrmReminder { + id: string + customerId: string + title: string + description: string + time: string + date: string + interactionType: MeetingInteractionType + completed: boolean + note?: string + customerName?: string + contactName?: string + avatarLetter?: string + avatarColor?: string +} + +export interface CrmMeeting { + id: string + customerId: string + type: MeetingInteractionType + title: string + description: string + date: string + time?: string +} + +export interface ProductCategory { + id: string + title: string + subtitle: string + imageUrl?: string +} + +export interface SeasonCategory { + id: SeasonKind + title: string + description: string + imageUrl?: string +} + +export interface CatalogProductCard { + id: string + name: string + code: string + categoryId: string + season: SeasonKind + widthCm: number + composition: string + weightGrams?: number + weaveType?: string + colors: string[] + tags: string[] + isNew: boolean + imageUrl?: string + subtitle?: string + rollMeters: number + /** فیلدهای سفارش واقعی */ + companyId?: string + catalogId?: string + catalogCode?: string + color?: string + minMeterage?: number + orderMultiplier?: number + basePrice?: number +} + +export interface HomeBanner { + id: string + title: string + ctaLabel: string + imageUrl?: string +} + +export interface QuickAction { + id: string + label: string + to: string +} + +export interface AgentProfile { + id: string + firstName: string + lastName: string + code: string + levelLabel: string + phone: string + pointsToNext: number + nextLevelLabel: string + customersCount: number + ordersCount: number + salesVolumeToman: number + profitToman: number + periodLabel: string +} diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..2a8b91b --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,235 @@ +export type SettlementType = 'cash' | 'check' +export type CheckRegistrationMode = 'full' | 'brief' + +export type OrderStatus = + | 'cart' + | 'awaiting_settlement' + | 'cash_in_progress' + | 'check_in_progress' + | 'submitted' + | 'awaiting_review' + | 'pricing' + | 'needs_correction' + | 'awaiting_settlement_fix' + | 'cash_invoice_issued' + | 'awaiting_cash_payment' + | 'cash_payment_submitted' + | 'awaiting_cash_confirmation' + | 'cash_confirmed' + | 'awaiting_check_review' + | 'check_needs_fix' + | 'check_rejected' + | 'checks_approved' + | 'final_approved' + | 'preparing' + | 'ready_to_ship' + | 'shipped' + | 'delivered' + | 'cancelled' + | 'rejected' + | 'expired' + +export interface User { + id: string + firstName: string + lastName: string + phone: string + nationalId?: string + createdAt: string + /** نقش اپ کاربر: مشتری یا واسطه */ + appRole?: 'customer' | 'agent' + agentCode?: string + levelLabel?: string + nextLevelLabel?: string + pointsToNext?: number + levelProgress?: number + businessName?: string + avatarUrl?: string +} + +export interface Company { + id: string + name: string + slug: string + logo?: string + description: string + isActive: boolean + displayOrder: number + maxMeteragePerInvoice: number + cashInvoiceValidityHours: number + cashInvoiceValidUntilHour?: number +} + +export interface BankAccount { + id: string + companyId: string + bankName: string + accountHolder: string + accountNumber: string + cardNumber: string + iban: string + isActive: boolean + displayOrder: number + notes?: string +} + +export interface Catalog { + id: string + companyId: string + name: string + code: string + description?: string + displayOrder: number + isActive: boolean + productCount?: number +} + +export interface Product { + id: string + companyId: string + catalogId: string + name: string + code: string + color: string + description?: string + minMeterage: number + orderMultiplier: number + basePrice?: number + stock?: number + isActive: boolean +} + +export interface CartItem { + id: string + productId: string + catalogCode: string + productName: string + productCode: string + color: string + meterage: number + notes?: string + companyId: string + companyName: string +} + +export interface Cart { + id: string + /** Primary / first company — cart may include items from other companies. */ + companyId: string + companyName: string + items: CartItem[] + notes?: string +} + +export interface CheckData { + id: string + amount: number + dueDate: string + sayadId?: string + checkNumber?: string + ownerNationalId?: string + ownerFirstName?: string + ownerLastName?: string + bankName?: string + branchName?: string + frontImage?: string + backImage?: string + notes?: string + mode: CheckRegistrationMode +} + +export interface CashPaymentReceipt { + id: string + amount: number + transferDate: string + transferTime: string + trackingNumber: string + destinationAccountId: string + receiptImage?: string + notes?: string + submittedAt: string +} + +export interface OrderItem { + id: string + productId: string + catalogCode: string + productName: string + productCode: string + color: string + requestedMeterage: number + confirmedMeterage?: number + unitPrice?: number + discount?: number + lineTotal?: number + notes?: string + companyId?: string + companyName?: string +} + +export interface OrderStatusHistoryEntry { + id: string + fromStatus: OrderStatus | null + toStatus: OrderStatus + changedAt: string + changedBy: string + note?: string + reason?: string +} + +export interface Order { + id: string + orderNumber: string + userId: string + companyId: string + companyName: string + items: OrderItem[] + totalMeterage: number + /** تا وقتی مشتری بعد از قیمت‌گذاری روش تسویه را انتخاب نکرده، خالی است. */ + settlementType: SettlementType | null + checkMode?: CheckRegistrationMode + checks?: CheckData[] + checkRassDays?: number + checkRassDate?: string + status: OrderStatus + notes?: string + totalAmount?: number | null + shippingCost?: number | null + totalDiscount?: number | null + finalAmount?: number | null + /** سرور تا پایان قیمت‌گذاری مبالغ را null برمی‌گرداند. */ + pricesVisible?: boolean + cashInvoiceExpiresAt?: string + cashReceipt?: CashPaymentReceipt + statusHistory: OrderStatusHistoryEntry[] + createdAt: string + updatedAt: string +} + +export type NotificationTone = 'info' | 'warning' | 'success' | 'danger' + +export interface AppNotification { + id: string + orderId?: string + event: string + title: string + body: string + tone: NotificationTone + link?: string + isRead: boolean + sentAt: string +} + +export interface CheckRassResult { + checkCount: number + totalAmount: number + nearestDueDate: string | null + farthestDueDate: string | null + rassDays: number + rassDate: string | null +} + +export interface MeterageValidationResult { + valid: boolean + message?: string +} diff --git a/src/utils/checkRass.ts b/src/utils/checkRass.ts new file mode 100644 index 0000000..0cd6c13 --- /dev/null +++ b/src/utils/checkRass.ts @@ -0,0 +1,71 @@ +import type { CheckData, CheckRassResult } from '@/types' +import { parseISODate, toISODate } from '@/utils/jalali' + +function emptyRass(): CheckRassResult { + return { + checkCount: 0, + totalAmount: 0, + nearestDueDate: null, + farthestDueDate: null, + rassDays: 0, + rassDate: null, + } +} + +export function calculateCheckRass( + checks: CheckData[], + baseDate: Date = new Date(), +): CheckRassResult { + if (checks.length === 0) return emptyRass() + + const base = new Date(baseDate) + base.setHours(0, 0, 0, 0) + + let totalAmount = 0 + let weightedDays = 0 + const dueDates: Date[] = [] + + for (const check of checks) { + if (!check.dueDate) { + totalAmount += check.amount + continue + } + + const due = parseISODate(check.dueDate) + if (Number.isNaN(due.getTime())) { + totalAmount += check.amount + continue + } + + due.setHours(0, 0, 0, 0) + dueDates.push(due) + const diffMs = due.getTime() - base.getTime() + const days = Math.max(0, Math.round(diffMs / (1000 * 60 * 60 * 24))) + totalAmount += check.amount + weightedDays += check.amount * days + } + + const rassDays = totalAmount > 0 ? Math.round(weightedDays / totalAmount) : 0 + const rassDate = new Date(base) + rassDate.setDate(rassDate.getDate() + rassDays) + + dueDates.sort((a, b) => a.getTime() - b.getTime()) + + return { + checkCount: checks.length, + totalAmount, + nearestDueDate: dueDates[0] ? toISODate(dueDates[0]) : null, + farthestDueDate: dueDates.length ? toISODate(dueDates[dueDates.length - 1]!) : null, + rassDays, + rassDate: toISODate(rassDate), + } +} + +export function daysUntilDue(dueDate: string): number { + const base = new Date() + base.setHours(0, 0, 0, 0) + const due = parseISODate(dueDate) + if (Number.isNaN(due.getTime())) return 0 + due.setHours(0, 0, 0, 0) + return Math.max(0, Math.round((due.getTime() - base.getTime()) / (1000 * 60 * 60 * 24))) +} diff --git a/src/utils/constants.ts b/src/utils/constants.ts new file mode 100644 index 0000000..8bf1fdd --- /dev/null +++ b/src/utils/constants.ts @@ -0,0 +1,70 @@ +import type { OrderStatus } from '@/types' + +export const DEFAULT_MAX_METERAGE = 5000 + +export const ORDER_STATUS_LABELS: Record = { + cart: 'سبد خرید', + awaiting_settlement: 'در انتظار انتخاب روش تسویه', + cash_in_progress: 'در حال تکمیل اطلاعات نقدی', + check_in_progress: 'در حال ثبت چک‌ها', + submitted: 'سفارش ثبت‌شده', + awaiting_review: 'در انتظار بررسی', + pricing: 'در حال قیمت‌گذاری', + needs_correction: 'نیازمند اصلاح', + awaiting_settlement_fix: 'در انتظار اصلاح تسویه', + cash_invoice_issued: 'فاکتور نقدی صادرشده', + awaiting_cash_payment: 'در انتظار پرداخت نقدی', + cash_payment_submitted: 'پرداخت ثبت‌شده', + awaiting_cash_confirmation: 'در انتظار تأیید پرداخت', + cash_confirmed: 'پرداخت تأییدشده', + awaiting_check_review: 'در انتظار بررسی چک‌ها', + check_needs_fix: 'چک نیازمند اصلاح', + check_rejected: 'چک ردشده', + checks_approved: 'چک‌ها تایید شده', + final_approved: 'تأیید نهایی', + preparing: 'در حال آماده‌سازی', + ready_to_ship: 'آماده ارسال', + shipped: 'ارسال‌شده', + delivered: 'تحویل‌شده', + cancelled: 'لغوشده', + rejected: 'ردشده', + expired: 'منقضی‌شده', +} + +export const ORDER_TRACKING_STEPS: { status: OrderStatus; label: string }[] = [ + { status: 'submitted', label: 'ثبت سفارش' }, + { status: 'awaiting_review', label: 'در انتظار بررسی' }, + { status: 'pricing', label: 'قیمت‌گذاری توسط مدیریت' }, + { status: 'awaiting_settlement', label: 'انتخاب روش تسویه' }, + { status: 'check_in_progress', label: 'ثبت پرداخت / چک' }, + { status: 'awaiting_check_review', label: 'بررسی پرداخت / چک' }, + { status: 'checks_approved', label: 'تأیید پرداخت' }, + { status: 'final_approved', label: 'تأیید نهایی' }, + { status: 'preparing', label: 'آماده‌سازی' }, + { status: 'ready_to_ship', label: 'آماده ارسال' }, + { status: 'shipped', label: 'ارسال شده' }, + { status: 'delivered', label: 'تحویل شده' }, +] + +export const STATUS_BADGE_VARIANT: Record< + string, + 'warning' | 'success' | 'info' | 'purple' | 'danger' | 'neutral' +> = { + awaiting_review: 'info', + awaiting_check_review: 'info', + pricing: 'warning', + preparing: 'warning', + ready_to_ship: 'warning', + check_in_progress: 'warning', + awaiting_cash_confirmation: 'warning', + checks_approved: 'success', + cash_confirmed: 'success', + final_approved: 'success', + shipped: 'purple', + delivered: 'success', + needs_correction: 'danger', + check_needs_fix: 'danger', + rejected: 'danger', + cancelled: 'neutral', + expired: 'danger', +} diff --git a/src/utils/formErrors.ts b/src/utils/formErrors.ts new file mode 100644 index 0000000..6c416b1 --- /dev/null +++ b/src/utils/formErrors.ts @@ -0,0 +1,14 @@ +export type FieldErrors = Record + +export function hasFieldErrors(errors: FieldErrors): boolean { + return Object.keys(errors).length > 0 +} + +export function clearFieldError(errors: FieldErrors, key: string): FieldErrors { + if (!errors[key]) return errors + const next = { ...errors } + delete next[key] + return next +} + +export const FORM_VALIDATION_SUMMARY = 'لطفاً خطاهای مشخص‌شده در فرم را برطرف کنید.' diff --git a/src/utils/format.ts b/src/utils/format.ts new file mode 100644 index 0000000..47b342b --- /dev/null +++ b/src/utils/format.ts @@ -0,0 +1,117 @@ +const persianDigits = '۰۱۲۳۴۵۶۷۸۹' +const numberFormatter = new Intl.NumberFormat('fa-IR', { useGrouping: true }) + +export function normalizeDigits(input: string): string { + return input.replace(/[۰-۹]/g, (digit) => String(persianDigits.indexOf(digit))) +} + +export function extractNumericString(input: string): string { + return normalizeDigits(input).replace(/[^\d]/g, '') +} + +export function parseFormattedNumber(value: string): number { + const numeric = extractNumericString(value) + return numeric ? Number(numeric) : 0 +} + +export function formatNumber(value: number): string { + if (!Number.isFinite(value)) return '—' + return numberFormatter.format(value) +} + +export function formatCurrency(value: number): string { + return `${formatNumber(value)} تومان` +} + +export function formatMeterage(value: number): string { + return `${formatNumber(value)} متر` +} + +import { parseISODate } from '@/utils/jalali' + +const persianDateFormatter = new Intl.DateTimeFormat('fa-IR-u-ca-persian', { + year: 'numeric', + month: '2-digit', + day: '2-digit', +}) + +const persianDateTimeFormatter = new Intl.DateTimeFormat('fa-IR-u-ca-persian', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', +}) + +function isJalaliDateString(value: string): boolean { + const normalized = normalizeDigits(value.trim()).replace(/-/g, '/') + const match = normalized.match(/^(\d{4})\/(\d{1,2})\/(\d{1,2})/) + if (!match) return false + const year = Number(match[1]) + return year >= 1300 && year <= 1500 +} + +function toDateValue(date: string | Date): Date { + if (typeof date !== 'string') return date + const trimmed = date.trim() + // فقط تاریخ بدون ساعت؛ رشتهٔ دارای زمان باید کامل پارس شود + if (/^\d{4}-\d{2}-\d{2}$/.test(trimmed)) return parseISODate(trimmed) + const parsed = new Date(trimmed) + if (Number.isNaN(parsed.getTime())) return new Date(NaN) + return parsed +} + +export function formatDate(date: string | Date | null | undefined): string { + if (!date) return '—' + if (typeof date === 'string' && isJalaliDateString(date)) { + const normalized = normalizeDigits(date.trim()).replace(/-/g, '/') + const match = normalized.match(/^(\d{4})\/(\d{1,2})\/(\d{1,2})/) + if (!match) return toPersianDigits(normalized) + const [, y, m, d] = match + return toPersianDigits(`${y}/${m!.padStart(2, '0')}/${d!.padStart(2, '0')}`) + } + const value = toDateValue(date) + if (Number.isNaN(value.getTime())) return '—' + return persianDateFormatter.format(value) +} + +export function formatDateTime(date: string | Date | null | undefined): string { + if (!date) return '—' + if (typeof date === 'string' && isJalaliDateString(date) && !/[T\s]\d/.test(date)) { + return formatDate(date) + } + const value = toDateValue(date) + if (Number.isNaN(value.getTime())) return '—' + return persianDateTimeFormatter.format(value) +} + +export function formatTime(date: string | Date): string { + const d = typeof date === 'string' ? new Date(date) : date + if (Number.isNaN(d.getTime())) return '—' + return new Intl.DateTimeFormat('fa-IR', { + hour: '2-digit', + minute: '2-digit', + }).format(d) +} + +export function toPersianDigits(input: string): string { + return input.replace(/\d/g, (d) => persianDigits[Number(d)]!) +} + +export function normalizePhone(phone: string): string { + let digits = extractNumericString(phone) + if (digits.startsWith('98')) digits = '0' + digits.slice(2) + else if (digits.startsWith('9') && digits.length === 10) digits = '0' + digits + return digits.slice(0, 11) +} + +export function generateId(): string { + return crypto.randomUUID() +} + +export function generateOrderNumber(): string { + const year = new Intl.DateTimeFormat('fa-IR-u-ca-persian', { year: 'numeric' }).format(new Date()) + const persianYear = year.replace(/[^\d]/g, '') || '1403' + const seq = Math.floor(Math.random() * 9000) + 1000 + return `ORD-${persianYear}-${seq}` +} diff --git a/src/utils/jalali.ts b/src/utils/jalali.ts new file mode 100644 index 0000000..b7b180f --- /dev/null +++ b/src/utils/jalali.ts @@ -0,0 +1,110 @@ +import { jalaaliMonthLength, toGregorian, toJalaali } from 'jalaali-js' + +const persianDigits = '۰۱۲۳۴۵۶۷۸۹' + +function toPersianDigits(input: string): string { + return input.replace(/\d/g, (d) => persianDigits[Number(d)]!) +} + +export const JALALI_MONTH_NAMES = [ + 'فروردین', + 'اردیبهشت', + 'خرداد', + 'تیر', + 'مرداد', + 'شهریور', + 'مهر', + 'آبان', + 'آذر', + 'دی', + 'بهمن', + 'اسفند', +] as const + +export const JALALI_WEEKDAY_NAMES = ['ش', 'ی', 'د', 'س', 'چ', 'پ', 'ج'] as const + +export interface JalaliDate { + jy: number + jm: number + jd: number +} + +export function parseISODate(iso: string): Date { + const match = iso.trim().match(/^(\d{4})-(\d{2})-(\d{2})/) + if (match) { + return new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3])) + } + + const parsed = new Date(iso) + if (Number.isNaN(parsed.getTime())) { + return new Date(NaN) + } + + return new Date(parsed.getFullYear(), parsed.getMonth(), parsed.getDate()) +} + +export function toISODate(date: Date): string { + const year = date.getFullYear() + const month = String(date.getMonth() + 1).padStart(2, '0') + const day = String(date.getDate()).padStart(2, '0') + return `${year}-${month}-${day}` +} + +export function toJalali(date: Date): JalaliDate { + const { jy, jm, jd } = toJalaali(date.getFullYear(), date.getMonth() + 1, date.getDate()) + return { jy, jm, jd } +} + +export function fromJalali(jy: number, jm: number, jd: number): Date { + const { gy, gm, gd } = toGregorian(jy, jm, jd) + return new Date(gy, gm - 1, gd) +} + +export function isoToJalali(iso: string): JalaliDate | null { + if (!iso) return null + return toJalali(parseISODate(iso)) +} + +export function jalaliToISO(jy: number, jm: number, jd: number): string { + return toISODate(fromJalali(jy, jm, jd)) +} + +export function formatJalaliDate(jy: number, jm: number, jd: number): string { + const month = String(jm).padStart(2, '0') + const day = String(jd).padStart(2, '0') + return toPersianDigits(`${jy}/${month}/${day}`) +} + +export function formatJalaliLabel(jy: number, jm: number, jd: number): string { + return `${toPersianDigits(String(jd))} ${JALALI_MONTH_NAMES[jm - 1]} ${toPersianDigits(String(jy))}` +} + +export function getPersianWeekday(jy: number, jm: number, jd: number): number { + const { gy, gm, gd } = toGregorian(jy, jm, jd) + const jsDay = new Date(gy, gm - 1, gd).getDay() + return (jsDay + 1) % 7 +} + +export function getMonthLength(jy: number, jm: number): number { + return jalaaliMonthLength(jy, jm) +} + +export function buildCalendarDays(jy: number, jm: number): Array { + const length = getMonthLength(jy, jm) + const startWeekday = getPersianWeekday(jy, jm, 1) + const cells: Array = Array.from({ length: startWeekday }, () => null) + + for (let day = 1; day <= length; day += 1) { + cells.push(day) + } + + while (cells.length % 7 !== 0) { + cells.push(null) + } + + return cells +} + +export function todayJalali(): JalaliDate { + return toJalali(new Date()) +} diff --git a/src/utils/navigation.ts b/src/utils/navigation.ts new file mode 100644 index 0000000..75bbac6 --- /dev/null +++ b/src/utils/navigation.ts @@ -0,0 +1,84 @@ +import type { Component } from 'vue' +import IconHome from '@/components/icons/IconHome.vue' +import IconCart from '@/components/icons/IconCart.vue' +import IconOrders from '@/components/icons/IconOrders.vue' +import IconProfile from '@/components/icons/IconProfile.vue' +import IconCatalog from '@/components/icons/IconCatalog.vue' +import IconCheckBook from '@/components/icons/IconCheckBook.vue' +import IconPlus from '@/components/icons/IconPlus.vue' + +export interface NavItem { + name: string + path: string + label: string + icon: Component + dynamicPath?: boolean +} + +/** Mobile bottom navigation — mockup shell with center FAB */ +export const MAIN_NAV_ITEMS: NavItem[] = [ + { name: 'home', path: '/', label: 'خانه', icon: IconHome }, + { name: 'catalogs', path: '/catalogs', label: 'کالیته‌ها', icon: IconCatalog }, + { name: 'order-fab', path: '/cart', label: 'ثبت سفارش', icon: IconPlus }, + { name: 'orders', path: '/orders', label: 'سفارش‌ها', icon: IconOrders }, + { name: 'profile', path: '/profile', label: 'پروفایل', icon: IconProfile }, +] + +/** Desktop sidebar — matches mockup */ +export const DESKTOP_NAV_ITEMS: NavItem[] = [ + { name: 'home', path: '/', label: 'خانه', icon: IconHome }, + { name: 'ordering', path: '/', label: 'سفارش‌گذاری', icon: IconCatalog, dynamicPath: true }, + { name: 'cart', path: '/cart', label: 'سبد خرید', icon: IconCart }, + { name: 'orders', path: '/orders', label: 'سفارش‌ها', icon: IconOrders }, + { name: 'checks', path: '/profile/checks', label: 'لیست چک‌ها', icon: IconCheckBook }, + { name: 'profile', path: '/profile', label: 'پروفایل', icon: IconProfile }, +] + +export function getOrderingPath(activeCompanyId: string | null | undefined): string { + if (activeCompanyId) return `/companies/${activeCompanyId}/catalogs` + return '/companies' +} + +const CATALOG_PATHS = [ + '/catalogs', + '/catalog-request', + '/catalog-requests', + '/categories', + '/products', + '/product', + '/favorites', +] + +export function isNavItemActive(name: string, path: string, currentPath: string): boolean { + if (name === 'ordering') { + return currentPath.startsWith('/companies/') + } + if (name === 'home') { + return currentPath === '/' + } + if (name === 'catalogs') { + return CATALOG_PATHS.some( + (p) => currentPath === p || currentPath.startsWith(`${p}/`), + ) + } + if (name === 'order-fab') { + return currentPath === '/cart' || currentPath.startsWith('/cart/') + } + if (name === 'checks') { + return currentPath.startsWith('/profile/checks') + } + if (name === 'profile') { + return currentPath === '/profile' + } + if (path === '/profile') return currentPath === '/profile' + if (path === '/') return currentPath === '/' + return currentPath.startsWith(path) +} + +export function resolveNavPath( + item: NavItem, + activeCompanyId: string | null | undefined, +): string { + if (item.name === 'ordering') return getOrderingPath(activeCompanyId) + return item.path +} diff --git a/src/utils/orderInvoice.ts b/src/utils/orderInvoice.ts new file mode 100644 index 0000000..304b78c --- /dev/null +++ b/src/utils/orderInvoice.ts @@ -0,0 +1,53 @@ +import type { Order, OrderStatus } from '@/types' + +/** سفارش وقتی قابل تسویه/چک است که مبلغ فاکتور توسط ادمین تعیین شده باشد. */ +export function isInvoicePriced(order: Order | null | undefined): boolean { + return !!order && order.finalAmount != null && Number(order.finalAmount) > 0 +} + +const SETTLEMENT_STATUSES: OrderStatus[] = [ + 'awaiting_settlement', + 'awaiting_settlement_fix', + 'cash_invoice_issued', +] + +const CHECK_ENTRY_STATUSES: OrderStatus[] = [ + 'check_in_progress', + 'check_needs_fix', + 'check_rejected', + 'awaiting_settlement', + 'awaiting_settlement_fix', +] + +const PRE_PRICE_STATUSES: OrderStatus[] = [ + 'submitted', + 'awaiting_review', + 'pricing', + 'needs_correction', +] + +const CASH_RECEIPT_STATUSES: OrderStatus[] = [ + 'awaiting_cash_payment', + 'cash_invoice_issued', +] + +export function canChooseSettlement(order: Order | null | undefined): boolean { + return isInvoicePriced(order) && !!order && SETTLEMENT_STATUSES.includes(order.status) +} + +/** ثبت رسید فقط بعد از صدور فاکتور نقدی / انتظار پرداخت. */ +export function canSubmitCashReceipt(order: Order | null | undefined): boolean { + return isInvoicePriced(order) && !!order && CASH_RECEIPT_STATUSES.includes(order.status) +} + +export function canEnterChecks(order: Order | null | undefined): boolean { + if (!isInvoicePriced(order) || !order) return false + if (CHECK_ENTRY_STATUSES.includes(order.status)) return true + return order.settlementType === 'check' && order.status === 'check_in_progress' +} + +export function isAwaitingAdminPricing(order: Order | null | undefined): boolean { + if (!order) return false + if (isInvoicePriced(order)) return false + return PRE_PRICE_STATUSES.includes(order.status) || !order.finalAmount +} diff --git a/src/utils/orderStatusGroups.ts b/src/utils/orderStatusGroups.ts new file mode 100644 index 0000000..c7b6845 --- /dev/null +++ b/src/utils/orderStatusGroups.ts @@ -0,0 +1,69 @@ +import type { OrderStatus } from '@/types' + +export type DesktopOrderFilterId = + | 'all' + | 'review' + | 'in_progress' + | 'shipped' + | 'delivered' + | 'cancelled' + +export const REVIEW_STATUSES: OrderStatus[] = ['awaiting_review', 'awaiting_check_review'] + +export const IN_PROGRESS_STATUSES: OrderStatus[] = [ + 'submitted', + 'pricing', + 'check_in_progress', + 'awaiting_cash_confirmation', + 'preparing', + 'ready_to_ship', + 'needs_correction', + 'awaiting_settlement_fix', + 'cash_in_progress', + 'awaiting_cash_payment', + 'cash_payment_submitted', + 'check_needs_fix', + 'awaiting_settlement', + 'cash_invoice_issued', +] + +export const SHIPPED_STATUSES: OrderStatus[] = ['shipped'] + +export const DELIVERED_STATUSES: OrderStatus[] = [ + 'delivered', + 'final_approved', + 'checks_approved', + 'cash_confirmed', +] + +export const CANCELLED_STATUSES: OrderStatus[] = ['cancelled', 'rejected', 'expired', 'check_rejected'] + +export const MOBILE_ACTIVE_STATUSES: OrderStatus[] = [ + ...REVIEW_STATUSES, + ...IN_PROGRESS_STATUSES, + ...SHIPPED_STATUSES, +] + +export function matchesDesktopOrderFilter(status: OrderStatus, filter: DesktopOrderFilterId): boolean { + switch (filter) { + case 'review': + return REVIEW_STATUSES.includes(status) + case 'in_progress': + return IN_PROGRESS_STATUSES.includes(status) + case 'shipped': + return SHIPPED_STATUSES.includes(status) + case 'delivered': + return DELIVERED_STATUSES.includes(status) + case 'cancelled': + return CANCELLED_STATUSES.includes(status) + default: + return true + } +} + +export function matchesMobileOrderTab(status: OrderStatus, tabId: string): boolean { + if (tabId === 'active') return MOBILE_ACTIVE_STATUSES.includes(status) + if (tabId === 'done') return DELIVERED_STATUSES.includes(status) + if (tabId === 'cancelled') return CANCELLED_STATUSES.includes(status) + return true +} diff --git a/src/utils/orderTracking.ts b/src/utils/orderTracking.ts new file mode 100644 index 0000000..4019a98 --- /dev/null +++ b/src/utils/orderTracking.ts @@ -0,0 +1,229 @@ +import type { Order, OrderStatus, SettlementType } from '@/types' +import { ORDER_TRACKING_STEPS } from '@/utils/constants' + +export type StatusHeroVariant = 'waiting' | 'success' | 'info' | 'none' + +export interface StatusHeroConfig { + variant: StatusHeroVariant + title: string + description?: string +} + +export interface TrackingStep { + status: OrderStatus + label: string +} + +/** Index into getOrderTrackingSteps(); must stay aligned with that list. */ +const STATUS_STEP_INDEX: Partial> = { + submitted: 0, + awaiting_review: 1, + pricing: 2, + needs_correction: 1, + awaiting_settlement: 3, + awaiting_settlement_fix: 3, + cash_in_progress: 4, + cash_invoice_issued: 4, + awaiting_cash_payment: 4, + cash_payment_submitted: 4, + check_in_progress: 4, + awaiting_cash_confirmation: 5, + awaiting_check_review: 5, + check_needs_fix: 5, + check_rejected: 5, + checks_approved: 6, + cash_confirmed: 6, + final_approved: 7, + preparing: 8, + ready_to_ship: 9, + shipped: 10, + delivered: 11, + expired: 3, + cancelled: 0, + rejected: 1, +} + +const STATUS_HERO: Partial> = { + submitted: { + variant: 'waiting', + title: 'پیش‌سفارش ثبت شد', + description: + 'سفارش شما برای بررسی و قیمت‌گذاری به مدیریت ارسال شد. پس از صدور فاکتور می‌توانید روش تسویه (نقد یا چک) را انتخاب کنید.', + }, + awaiting_review: { + variant: 'waiting', + title: 'در انتظار بررسی مدیریت', + description: + 'پیش‌سفارش شما در صف بررسی است. پس از قیمت‌گذاری و صدور فاکتور، از طریق همین صفحه برای انتخاب تسویه مطلع می‌شوید.', + }, + pricing: { + variant: 'waiting', + title: 'در حال قیمت‌گذاری', + description: + 'مدیریت در حال تعیین مبلغ نهایی فاکتور است. تا قبل از صدور فاکتور امکان ثبت چک یا پرداخت وجود ندارد.', + }, + awaiting_settlement: { + variant: 'info', + title: 'فاکتور آماده است', + description: 'مبلغ فاکتور مشخص شده. لطفاً روش تسویه را انتخاب کنید و مسیر پرداخت را ادامه دهید.', + }, + awaiting_settlement_fix: { + variant: 'info', + title: 'نیاز به اصلاح تسویه', + description: 'لطفاً روش تسویه یا اطلاعات پرداخت را اصلاح کنید.', + }, + cash_invoice_issued: { + variant: 'info', + title: 'فاکتور نقدی صادر شد', + description: 'مبلغ را به حساب‌های اعلام‌شده واریز و رسید پرداخت را ثبت کنید.', + }, + awaiting_cash_payment: { + variant: 'info', + title: 'در انتظار پرداخت نقدی', + description: 'لطفاً مبلغ فاکتور را واریز و رسید پرداخت را ثبت کنید.', + }, + cash_payment_submitted: { + variant: 'waiting', + title: 'رسید ثبت شد', + description: 'رسید پرداخت شما دریافت شد و در صف بررسی مدیریت است.', + }, + awaiting_cash_confirmation: { + variant: 'waiting', + title: 'در انتظار تأیید پرداخت', + description: 'رسید پرداخت در حال بررسی است. نتیجه به‌زودی اعلام می‌شود.', + }, + awaiting_check_review: { + variant: 'waiting', + title: 'در حال بررسی چک‌ها', + description: 'چک‌های ثبت‌شده در حال بررسی هستند. نتیجه بررسی به‌زودی اعلام می‌شود.', + }, + checks_approved: { + variant: 'success', + title: 'چک‌ها تأیید شده', + description: + 'چک‌های شما تأیید شد. سفارش در انتظار تأیید نهایی مدیریت است و پس از آن وارد آماده‌سازی می‌شود.', + }, + cash_confirmed: { + variant: 'success', + title: 'پرداخت تأیید شد', + description: 'رسید پرداخت شما تأیید شد و سفارش وارد مراحل بعدی می‌شود.', + }, + final_approved: { + variant: 'success', + title: 'تأیید نهایی', + description: 'سفارش شما تأیید نهایی شد و آماده آماده‌سازی است.', + }, + preparing: { + variant: 'info', + title: 'در حال آماده‌سازی', + description: 'سفارش شما در انبار در حال آماده‌سازی است.', + }, + ready_to_ship: { + variant: 'info', + title: 'آماده ارسال', + description: 'سفارش آماده ارسال است و به‌زودی تحویل باربری می‌شود.', + }, + shipped: { + variant: 'info', + title: 'ارسال شده', + description: 'سفارش ارسال شده است. جزئیات بارنامه را می‌توانید مشاهده کنید.', + }, + delivered: { + variant: 'success', + title: 'تحویل شده', + description: 'سفارش با موفقیت تحویل داده شد.', + }, + expired: { + variant: 'waiting', + title: 'فاکتور منقضی شده', + description: 'مهلت پرداخت این فاکتور به پایان رسیده است. برای ادامه با پشتیبانی یا مدیریت هماهنگ کنید.', + }, +} + +function paymentStepLabels(settlementType: SettlementType | null | undefined): { + payment: string + review: string + approved: string +} { + if (settlementType === 'cash') { + return { + payment: 'ثبت رسید پرداخت', + review: 'بررسی پرداخت نقدی', + approved: 'پرداخت تأیید شده', + } + } + if (settlementType === 'check') { + return { + payment: 'ثبت چک', + review: 'در حال بررسی چک‌ها', + approved: 'چک‌ها تأیید شده', + } + } + return { + payment: 'ثبت پرداخت / چک', + review: 'بررسی پرداخت / چک', + approved: 'تأیید پرداخت', + } +} + +/** Customer-facing timeline steps; payment labels follow settlement type. */ +export function getOrderTrackingSteps(order: Order): TrackingStep[] { + const labels = paymentStepLabels(order.settlementType) + return ORDER_TRACKING_STEPS.map((step) => { + if (step.status === 'check_in_progress') return { ...step, label: labels.payment } + if (step.status === 'awaiting_check_review') return { ...step, label: labels.review } + if (step.status === 'checks_approved') return { ...step, label: labels.approved } + return step + }) +} + +export function getTrackingStepIndex(status: OrderStatus): number { + return STATUS_STEP_INDEX[status] ?? 0 +} + +export function getStatusHero(status: OrderStatus): StatusHeroConfig | null { + return STATUS_HERO[status] ?? null +} + +/** Statuses that may mark a timeline step as reached in history. */ +const STEP_HISTORY_ALIASES: Partial> = { + awaiting_settlement: ['awaiting_settlement', 'awaiting_settlement_fix'], + check_in_progress: [ + 'check_in_progress', + 'cash_in_progress', + 'cash_invoice_issued', + 'awaiting_cash_payment', + 'cash_payment_submitted', + ], + awaiting_check_review: [ + 'awaiting_check_review', + 'awaiting_cash_confirmation', + 'check_needs_fix', + 'check_rejected', + ], + checks_approved: ['checks_approved', 'cash_confirmed'], + pricing: ['pricing'], +} + +export function getStepTimestamp(order: Order, stepStatus: OrderStatus): string | null { + if (stepStatus === 'submitted') { + return order.createdAt + } + + const aliases = STEP_HISTORY_ALIASES[stepStatus] ?? [stepStatus] + const entry = [...order.statusHistory] + .reverse() + .find((item) => aliases.includes(item.toStatus)) + + return entry?.changedAt ?? null +} + +export function getTrackingFooterLabel(status: OrderStatus): string { + if (status === 'shipped') return 'مشاهده جزئیات و بارنامه' + if (['checks_approved', 'cash_confirmed', 'delivered', 'final_approved'].includes(status)) { + return 'مشاهده جزئیات' + } + return 'مشاهده جزئیات سفارش' +} + +export { ORDER_TRACKING_STEPS } diff --git a/src/utils/pendingRegister.ts b/src/utils/pendingRegister.ts new file mode 100644 index 0000000..bd1a1d4 --- /dev/null +++ b/src/utils/pendingRegister.ts @@ -0,0 +1,46 @@ +const PENDING_REGISTER_KEY = 'ft_pending_register' + +export type JobCategory = 'garment_producer' | 'atelier' | 'fabric_wholesaler' + +export type PendingRegister = { + firstName: string + lastName: string + jobCategory?: JobCategory +} + +export const JOB_CATEGORY_OPTIONS: Array<{ id: JobCategory; label: string }> = [ + { id: 'garment_producer', label: 'تولیدکننده پوشاک عمده' }, + { id: 'atelier', label: 'مزون' }, + { id: 'fabric_wholesaler', label: 'عمده‌فروش پارچه' }, +] + +export function getPendingRegister(): PendingRegister | null { + try { + const raw = sessionStorage.getItem(PENDING_REGISTER_KEY) + if (!raw) return null + const parsed = JSON.parse(raw) as PendingRegister + if (!parsed?.firstName?.trim() || !parsed?.lastName?.trim()) return null + return { + firstName: parsed.firstName.trim(), + lastName: parsed.lastName.trim(), + jobCategory: parsed.jobCategory, + } + } catch { + return null + } +} + +export function setPendingRegister(value: PendingRegister | null) { + if (value?.firstName?.trim() && value?.lastName?.trim()) { + sessionStorage.setItem( + PENDING_REGISTER_KEY, + JSON.stringify({ + firstName: value.firstName.trim(), + lastName: value.lastName.trim(), + jobCategory: value.jobCategory, + }), + ) + return + } + sessionStorage.removeItem(PENDING_REGISTER_KEY) +} diff --git a/src/utils/profileImage.ts b/src/utils/profileImage.ts new file mode 100644 index 0000000..0578fc3 --- /dev/null +++ b/src/utils/profileImage.ts @@ -0,0 +1,46 @@ +const MAX_EDGE = 512 +const JPEG_QUALITY = 0.82 + +export async function fileToProfileAvatar(file: File): Promise { + if (!file.type.startsWith('image/')) { + throw new Error('فقط فایل تصویری مجاز است.') + } + if (file.size > 8 * 1024 * 1024) { + throw new Error('حجم تصویر نباید بیشتر از ۸ مگابایت باشد.') + } + + const dataUrl = await readFileAsDataUrl(file) + return resizeDataUrl(dataUrl) +} + +function readFileAsDataUrl(file: File): Promise { + return new Promise((resolve, reject) => { + const reader = new FileReader() + reader.onload = () => resolve(String(reader.result ?? '')) + reader.onerror = () => reject(new Error('خواندن تصویر ناموفق بود.')) + reader.readAsDataURL(file) + }) +} + +function resizeDataUrl(src: string): Promise { + return new Promise((resolve, reject) => { + const image = new Image() + image.onload = () => { + const scale = Math.min(1, MAX_EDGE / Math.max(image.width, image.height)) + const width = Math.max(1, Math.round(image.width * scale)) + const height = Math.max(1, Math.round(image.height * scale)) + const canvas = document.createElement('canvas') + canvas.width = width + canvas.height = height + const ctx = canvas.getContext('2d') + if (!ctx) { + reject(new Error('پردازش تصویر ناموفق بود.')) + return + } + ctx.drawImage(image, 0, 0, width, height) + resolve(canvas.toDataURL('image/jpeg', JPEG_QUALITY)) + } + image.onerror = () => reject(new Error('تصویر نامعتبر است.')) + image.src = src + }) +} diff --git a/src/utils/validation.ts b/src/utils/validation.ts new file mode 100644 index 0000000..8b1e44a --- /dev/null +++ b/src/utils/validation.ts @@ -0,0 +1,75 @@ +import type { MeterageValidationResult, Product } from '@/types' +import { normalizePhone } from '@/utils/format' + +export function validateNationalId(nationalId: string): boolean { + if (!/^\d{10}$/.test(nationalId)) return false + if (/^(\d)\1{9}$/.test(nationalId)) return false + const check = Number(nationalId[9]) + let sum = 0 + for (let i = 0; i < 9; i++) { + sum += Number(nationalId[i]) * (10 - i) + } + const remainder = sum % 11 + return remainder < 2 ? check === remainder : check === 11 - remainder +} + +export function validateSayadId(sayadId: string): boolean { + return /^\d{16}$/.test(sayadId.replace(/\s/g, '')) +} + +export function validatePhone(phone: string): boolean { + return /^09\d{9}$/.test(normalizePhone(phone)) +} + +export function validateMeterage( + meterage: number, + product: Product, + currentCartTotal: number, + maxMeterage: number, + existingItemMeterage = 0, +): MeterageValidationResult { + if (!Number.isFinite(meterage) || meterage <= 0) { + return { valid: false, message: 'متراژ باید عددی بزرگ‌تر از صفر باشد.' } + } + if (meterage < product.minMeterage) { + return { + valid: false, + message: `حداقل متراژ سفارش برای این محصول ${product.minMeterage} متر است.`, + } + } + if (meterage % product.orderMultiplier !== 0) { + return { + valid: false, + message: `متراژ باید مضربی از ${product.orderMultiplier} باشد.`, + } + } + const newTotal = currentCartTotal - existingItemMeterage + meterage + if (newTotal > maxMeterage) { + const remaining = maxMeterage - (currentCartTotal - existingItemMeterage) + return { + valid: false, + message: `حداکثر متراژ قابل ثبت در هر فاکتور ${maxMeterage} متر است. شما فقط می‌توانید ${Math.max(0, remaining)} متر دیگر به این سفارش اضافه کنید.`, + } + } + return { valid: true } +} + +export function validateImageFile(file: File): { valid: boolean; message?: string } { + const allowed = ['image/jpeg', 'image/png', 'image/webp'] + if (!allowed.includes(file.type)) { + return { valid: false, message: 'فرمت تصویر مجاز نیست. (JPG, PNG, WEBP)' } + } + if (file.size > 5 * 1024 * 1024) { + return { valid: false, message: 'حجم تصویر نباید بیشتر از ۵ مگابایت باشد.' } + } + return { valid: true } +} + +export async function fileToBase64(file: File): Promise { + return new Promise((resolve, reject) => { + const reader = new FileReader() + reader.onload = () => resolve(reader.result as string) + reader.onerror = reject + reader.readAsDataURL(file) + }) +} diff --git a/src/views/OrdersView.vue b/src/views/OrdersView.vue new file mode 100644 index 0000000..8c6ac44 --- /dev/null +++ b/src/views/OrdersView.vue @@ -0,0 +1,121 @@ + + + + + diff --git a/src/views/auth/LoginView.vue b/src/views/auth/LoginView.vue new file mode 100644 index 0000000..5bd20b8 --- /dev/null +++ b/src/views/auth/LoginView.vue @@ -0,0 +1,119 @@ + + + diff --git a/src/views/auth/OtpView.vue b/src/views/auth/OtpView.vue new file mode 100644 index 0000000..0a894c4 --- /dev/null +++ b/src/views/auth/OtpView.vue @@ -0,0 +1,363 @@ + + + + + diff --git a/src/views/auth/RegisterView.vue b/src/views/auth/RegisterView.vue new file mode 100644 index 0000000..a4ea1ff --- /dev/null +++ b/src/views/auth/RegisterView.vue @@ -0,0 +1,203 @@ + + + + + diff --git a/src/views/cart/CartView.vue b/src/views/cart/CartView.vue new file mode 100644 index 0000000..939f86f --- /dev/null +++ b/src/views/cart/CartView.vue @@ -0,0 +1,118 @@ + + + + + diff --git a/src/views/cart/components/DesktopCartPage.vue b/src/views/cart/components/DesktopCartPage.vue new file mode 100644 index 0000000..cfb3c4d --- /dev/null +++ b/src/views/cart/components/DesktopCartPage.vue @@ -0,0 +1,658 @@ + + + + + diff --git a/src/views/cart/components/MobileCartPage.vue b/src/views/cart/components/MobileCartPage.vue new file mode 100644 index 0000000..777150b --- /dev/null +++ b/src/views/cart/components/MobileCartPage.vue @@ -0,0 +1,291 @@ + + + + + diff --git a/src/views/catalogs/CatalogListView.vue b/src/views/catalogs/CatalogListView.vue new file mode 100644 index 0000000..35ac0d0 --- /dev/null +++ b/src/views/catalogs/CatalogListView.vue @@ -0,0 +1,209 @@ + + + + + diff --git a/src/views/catalogs/CatalogRequestView.vue b/src/views/catalogs/CatalogRequestView.vue new file mode 100644 index 0000000..4c2d2d2 --- /dev/null +++ b/src/views/catalogs/CatalogRequestView.vue @@ -0,0 +1,211 @@ + + +