commit 1a34c3b133a144aac0280fa906b69635493e403f Author: Kavehhn174 Date: Sun Aug 9 04:18:09 2026 +0200 Initial commit: admin dashboard for GameNo. Vue 3 + Vite dashboard with PrimeVue, i18n, and API integration for institution management. diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..26a8408 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +node_modules +dist +npm-debug.log +.git +.env diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..64c0b37 --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +VITE_API_BASE_URL=http://localhost:5000/api diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..438657a --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local +.env + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..a7cea0b --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["Vue.volar"] +} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..2ad8da5 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,13 @@ +FROM node:24-alpine + +WORKDIR /app + +COPY package*.json ./ + +RUN npm install + +COPY . . + +EXPOSE 5173 + +CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..efe50a7 --- /dev/null +++ b/README.md @@ -0,0 +1,90 @@ +# Gameno Dashboard - Persian Teaching Institution Admin Dashboard + +A complete, production-ready admin dashboard built with Vue 3 (Composition API), PrimeVue 4, PrimeFlex, Pinia, Vue Router 4, vue-i18n, and Axios. Designed specifically for managing an educational institution in Persian (RTL layout) with role-based access control (RBAC). + +--- + +## 🚀 Setup and Installation + +### Prerequisites +- Node.js v18+ and npm v9+ + +### Commands + +```bash +# 1. Install dependencies +npm install + +# 2. Run local development server +npm run dev + +# 3. Build production bundle +npm run build + +# 4. Preview production build +npm run preview +``` + +--- + +## 🗺️ Route Permission Mapping + +All routes enforce client-side permission checks via global navigation guards (`/src/router/guards.js`) and UI element visibility gates (``). + +| Route Path | View Component | Required Permission | Description | +| :--- | :--- | :--- | :--- | +| `/login` | `LoginView.vue` | Public | Admin login page | +| `/` | `DashboardHomeView.vue` | Authenticated | Dashboard home statistics & shortcuts | +| `/users` | `UserListView.vue` | `users:read` | List students & staff | +| `/users/create` | `UserFormView.vue` | `users:create` | Create new user | +| `/users/edit/:id` | `UserFormView.vue` | `users:update` | Edit user profile | +| `/users/:id` | `UserDetailView.vue` | `users:read` | Profile summary, courses, payments, certificates | +| `/professors` | `ProfessorListView.vue` | `professors:read` | List instructors | +| `/professors/create` | `ProfessorFormView.vue` | `professors:create` | Add instructor | +| `/professors/edit/:id` | `ProfessorFormView.vue` | `professors:update` | Edit instructor | +| `/courses` | `CourseListView.vue` | `courses:read` | List courses | +| `/courses/create` | `CourseFormView.vue` | `courses:create` | Create course & auto/manual session generator | +| `/courses/edit/:id` | `CourseFormView.vue` | `courses:update` | Edit course & sessions | +| `/courses/:id` | `CourseDetailView.vue` | `courses:read` | Course details & sessions overview | +| `/sessions` | `SessionListView.vue` | `sessions:read` | List scheduled sessions | +| `/sessions/create` | `SessionFormView.vue` | `sessions:create` | Schedule single session | +| `/sessions/edit/:id` | `SessionFormView.vue` | `sessions:update` | Edit session details | +| `/attendances/session/:id` | `AttendanceEntryView.vue` | `sessions:attendance` | Bulk session attendance entry | +| `/payments` | `PaymentListView.vue` | `payments:read` | Payment ledgers & invoices | +| `/payments/:id` | `PaymentDetailView.vue` | `payments:read` | Payment detail & record transactions | +| `/roles` | `RoleListView.vue` | `roles:read` | List custom roles | +| `/roles/create` | `RoleFormView.vue` | `roles:create` | Create role & permission matrix | +| `/roles/edit/:id` | `RoleFormView.vue` | `roles:update` | Edit role permissions | +| `/notifications` | `NotificationListView.vue` | `notifications:read` | Delivery log & manual retry | +| `/forbidden` | `ForbiddenView.vue` | Authenticated | 403 Forbidden page | +| `/*` | `NotFoundView.vue` | Public | 404 Not Found page | + +--- + +## ⚙️ Automatic Session Date Generation Algorithm + +In `CourseFormView.vue` (Automatic Tab): +1. **Inputs**: Admin specifies requested total sessions count (e.g. `12`), start date in Jalali Shamsi format (e.g. `1403/01/01`), start/end time, and selected weekdays (e.g. Saturday = 6, Tuesday = 2). +2. **Rolling Calendar Loop**: + - Converts the start date into a `moment` instance in Jalali locale. + - Iteratively increments day by day. + - Checks if `currentMoment.day()` matches any of the chosen weekdays. + - When matched, increments session counter and pushes a preview row object (`sessionNumber`, `date` formatted in Jalali `YYYY/MM/DD`, `startTime`, `endTime`, `topic`). +3. **Interactive Preview**: Renders an inline editable data table so the admin can review or manually adjust individual row dates or topics before final submission. +4. **Bulk Dispatch**: Sends the generated session array to `POST /sessions/admin/create` in a single API request. + +--- + +## 📝 Key Architecture Assumptions + +1. **Authentication & Token Storage**: + - JWT `accessToken` and `refreshToken` are stored in `localStorage` and synchronized with HTTP cookies via `js-cookie`. + - Axios request interceptor attaches `Authorization: Bearer `. + - On `401 Unauthorized` responses, Axios automatically triggers silent token renewal calling `POST /auth/refresh`. +2. **Permission Store Rehydration**: + - Permissions array is loaded once on login from `response.user.role.permissions`. + - On hard page refresh, if a valid token exists but Pinia store is unpopulated, `guards.js` triggers `GET /users/user/get-self` once to rehydrate permissions. +3. **Certificate Upload Flow**: + - Uses two-stage S3/MinIO upload strategy. + - `ImageManager.vue` uploads binary file to `POST /files/admin/upload-temp` returning a temporary filename (`tempFileName`). + - Committing a certificate form dispatches `tempFileName` to `/certificates/user/upload`, moving the file to persistent storage on the backend. diff --git a/index.html b/index.html new file mode 100644 index 0000000..9bd0130 --- /dev/null +++ b/index.html @@ -0,0 +1,13 @@ + + + + + + + گام نو + + +
+ + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..60f8d2b --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3439 @@ +{ + "name": "gameno-dashboard", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "gameno-dashboard", + "version": "0.0.0", + "dependencies": { + "@fontsource/vazirmatn": "^5.3.0", + "@primevue/themes": "4.0.7", + "axios": "^1.19.0", + "filepond": "^4.32.12", + "filepond-plugin-file-validate-type": "^1.2.9", + "filepond-plugin-image-preview": "^4.6.12", + "jalali-moment": "^3.3.11", + "js-cookie": "^3.0.8", + "photoswipe": "^5.4.4", + "pinia": "^4.0.2", + "primeflex": "^4.0.0", + "primeicons": "^8.0.0", + "primevue": "4.0.7", + "sass": "^1.102.0", + "vee-validate": "^4.15.1", + "vue": "^3.5.40", + "vue-filepond": "^8.0.0", + "vue-i18n": "^11.4.8", + "vue-router": "^5.2.0", + "vue3-persian-datetime-picker": "^1.2.2", + "yup": "^1.7.1" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^6.0.8", + "vite": "^8.2.0" + } + }, + "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/@babel/generator/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/@babel/generator/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/@babel/generator/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/@babel/generator/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/@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/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/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/@fontsource/vazirmatn": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@fontsource/vazirmatn/-/vazirmatn-5.3.0.tgz", + "integrity": "sha512-XC/2yN2r5gwhFT3klSuWaqncODX2DOfRYfNmbyzgtWYxHxMC+tcoaX+tgggrm6hSkEgpR1TuSDEf1PiI91G7qg==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@inquirer/external-editor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", + "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", + "license": "MIT", + "dependencies": { + "chardet": "^2.1.1", + "iconv-lite": "^0.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@intlify/core-base": { + "version": "11.4.8", + "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-11.4.8.tgz", + "integrity": "sha512-A+Q7SKm5oEcy1E/cghqd7n/St4XjTqLhiiyDuieNcMrJcrHlkY5n0jp7Q9dD3txvVHzvsmBVV5M9wD5/s1zfzw==", + "license": "MIT", + "dependencies": { + "@intlify/devtools-types": "11.4.8", + "@intlify/message-compiler": "11.4.8", + "@intlify/shared": "11.4.8" + }, + "engines": { + "node": ">= 22" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@intlify/devtools-types": { + "version": "11.4.8", + "resolved": "https://registry.npmjs.org/@intlify/devtools-types/-/devtools-types-11.4.8.tgz", + "integrity": "sha512-MGpID+rlfzGUbNcnC20bm5NMSBHPrvx0atLTfv9dftn3kjXw1hGKDcIcwrO99tSrZEc2i+hczRL7ks8qXsHPkQ==", + "license": "MIT", + "dependencies": { + "@intlify/core-base": "11.4.8", + "@intlify/shared": "11.4.8" + }, + "engines": { + "node": ">= 22" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@intlify/message-compiler": { + "version": "11.4.8", + "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-11.4.8.tgz", + "integrity": "sha512-vbzk17dYwduYiv52EK61+FDCyhfVg1uPUtPmiD/d45W99uJIcXywrweOBcHv7n9/iEqmXiMGT52bgJbZDQqK3w==", + "license": "MIT", + "dependencies": { + "@intlify/shared": "11.4.8", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": ">= 22" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@intlify/shared": { + "version": "11.4.8", + "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-11.4.8.tgz", + "integrity": "sha512-XbRgrv+XEuvDr7UCY55oibVrh+o4u+A0VB6nSL0F5Z8LcZxE/8j573LYG6bCrOigIcHdGpSNI7Rh5UpC5/B/eg==", + "license": "MIT", + "engines": { + "node": ">= 22" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "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/@oxc-project/types": { + "version": "0.143.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.143.0.tgz", + "integrity": "sha512-u6JZdLBTLotrNC9Vd6vPssINdzcCzleKAH6EJKImQb7GtYvX5keN2dxkoK44stCc4tffE6QQRtZTXVSzsLUlWA==", + "devOptional": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.6.0.tgz", + "integrity": "sha512-7FNeNl8NCE7aINx7WXiKQrPYZWC/hvrTsmk6zmxbI7LTXE7hVek/n8AfVgpe2y82zl3w0HvCHN0bVKMBoJcC0w==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.3", + "is-glob": "^4.0.3", + "node-addon-api": "^7.0.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.6.0", + "@parcel/watcher-darwin-arm64": "2.6.0", + "@parcel/watcher-darwin-x64": "2.6.0", + "@parcel/watcher-freebsd-x64": "2.6.0", + "@parcel/watcher-linux-arm-glibc": "2.6.0", + "@parcel/watcher-linux-arm-musl": "2.6.0", + "@parcel/watcher-linux-arm64-glibc": "2.6.0", + "@parcel/watcher-linux-arm64-musl": "2.6.0", + "@parcel/watcher-linux-x64-glibc": "2.6.0", + "@parcel/watcher-linux-x64-musl": "2.6.0", + "@parcel/watcher-win32-arm64": "2.6.0", + "@parcel/watcher-win32-x64": "2.6.0" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.6.0.tgz", + "integrity": "sha512-trgpLSCKRC/huFjXX/Smh+0sWe4+YtKfktIToiMl59ghz7z+qkH6kMvNnUbLyRs9N11t8l4svSCs1+5B3rOAhA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.6.0.tgz", + "integrity": "sha512-Y3QV0gl7Q1zbfueunkWIERICbEojQFCgpyG7YqOGNFLsckXyI1xu9mAIUpKY9QBYzBtSkN8dBPwd3yiAO9ovMw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.6.0.tgz", + "integrity": "sha512-Ohv6OpzhUfKYD7Beb8kDvG0jbIxORCYY1JRdZnaBtnjjkJxgD7ZVL0nw2sCYd0yTMKTvz3nnTnOF3cDifK+kvw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.6.0.tgz", + "integrity": "sha512-5HmXvDgs8VK+74jF9y9/2FE3/OnlcKmc56tjmSrEuZjpSZOGL+fvAu+HKJBdPs9uwoP2hE6TlSUpXZ/C5jUFmQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.6.0.tgz", + "integrity": "sha512-Ps/hui3A+vMbjdqlqAowK2ZL8+BO8dBjxeWXj6npTBs3jx4wWmbPpaLuqwrQrSqIVMCnpWo238bJ1U37GhQOYg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.6.0.tgz", + "integrity": "sha512-9c6AUHgHoG+IY88MRIHupztQiQnrbqHYQjkM2btA+Bf/wQnQMuiD0Wfk1EVv3TlNT3x41uU71rn6E4xh/+zvkw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.6.0.tgz", + "integrity": "sha512-yHRqS2owEXe6Hic9z6Mh1ECsCd+ODVOGvZDyciqRd21+v+o+DnXMOrw50DSpIG2sb8GPEaPPmfeCAWKPJdq46g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.6.0.tgz", + "integrity": "sha512-WhB2e/V7rqdHHWZusBSPuy5Ei8S6lSz6FE5TKKQz5h3a0O+C+mhY7vxU9b/stqvMb8beLnPY82ZrFTLKs+SrKA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.6.0.tgz", + "integrity": "sha512-ulGE6x6Oz6iAwg75T8YQSoguBWasniIbX+QWpaYPcCnDOpdWX3k+4xbEYPZVLxOuoJI+svJJPD3sEj8G7lrQ3A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.6.0.tgz", + "integrity": "sha512-tkBYKt7YQrjIJWYDnto2YgO8MRkjlMTSNoRHzsXinBqbLdeOM3L32wPZJvIZxqaLMfSlS/4sUjH/6STVP/XDLw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.6.0.tgz", + "integrity": "sha512-gIZAP23jaHjGWasY/TY6yL7NHFClf0Ga7FN+iINvk+KN94rhm94lYZhFsbYFNcA04/onvGD9kKmiJLJB2HbNwQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.6.0.tgz", + "integrity": "sha512-cA+/pXV2YkfxlIcXOQ5fSWqAzzPyD78/x5qbK/I0vUkrlYHA8TIz+MXjAbGouguKVSI4bOmkTSJ1/poVSsgt+A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@primeuix/styled": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/@primeuix/styled/-/styled-0.0.5.tgz", + "integrity": "sha512-pVoGn/uPkVm/DyF3TR3EmH/pL/dP4nR42FcYbVduFq9VfO3KVeOEqvcCULHXos66RZO9MCbCFUoLy6ctf9GUGQ==", + "license": "MIT", + "dependencies": { + "@primeuix/utils": "^0.0.5" + }, + "engines": { + "node": ">=12.11.0" + } + }, + "node_modules/@primeuix/utils": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/@primeuix/utils/-/utils-0.0.5.tgz", + "integrity": "sha512-ntUiUgtRtkF8KuaxHffzhYxQxoXk6LAPHm7CVlFjdqS8Rx8xRkLkZVyo84E+pO2hcNFkOGVP/GxHhQ2s94O8zA==", + "license": "MIT", + "engines": { + "node": ">=12.11.0" + } + }, + "node_modules/@primevue/core": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@primevue/core/-/core-4.0.7.tgz", + "integrity": "sha512-SvWiNBEeR6hm4wjnze+rITUjHMFLwIzpRFlq+GqmJyZmjJy4h8UUksi0EoyqAWCAwKgmwlxY6XNqGJmMVyOguQ==", + "license": "MIT", + "dependencies": { + "@primeuix/styled": "^0.0.5", + "@primeuix/utils": "^0.0.5" + }, + "engines": { + "node": ">=12.11.0" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/@primevue/icons": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@primevue/icons/-/icons-4.0.7.tgz", + "integrity": "sha512-tj4dfRdV5iN6O0mbkpjhMsGlT3wZTqOPL779ndY5gKuCwN5zcFmKmABWVQmr/ClRivnMkw6Yr1x6gRTV/N0ydg==", + "license": "MIT", + "dependencies": { + "@primeuix/utils": "^0.0.5", + "@primevue/core": "4.0.7" + }, + "engines": { + "node": ">=12.11.0" + } + }, + "node_modules/@primevue/themes": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@primevue/themes/-/themes-4.0.7.tgz", + "integrity": "sha512-ZbDUrpBmtuqdeegNwUaJTubaLDBBJWOc4Z6UoQM3DG2c7EAE19wQbuh+cG9zqA7sT/Xsp+ACC/Z9e4FnfqB55g==", + "deprecated": "Deprecated. This package is no longer maintained. Please migrate to @primeuix/themes: https://www.npmjs.com/package/@primeuix/themes", + "license": "MIT", + "dependencies": { + "@primeuix/styled": "^0.0.5" + }, + "engines": { + "node": ">=12.11.0" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.3.tgz", + "integrity": "sha512-zrJtHDcaZJ1Fp7xf4hNl+7seH9Cn/N5TwLYkhgXREtBwAd/jaqW3uqeHxpDugJLVICWg4eW44kOQEGJ1r6jCGw==", + "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.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.3.tgz", + "integrity": "sha512-ieIiibVCp0tX7TLu2cafoNPv8wJyYi01ekXpbf8q2j7F4rGAhhXb/eQh7ge9DRBY78GwmRQtvjZDux7EDbA8kA==", + "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.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.3.tgz", + "integrity": "sha512-Zh9tCon19eDXJoihx0rqKhMUlMYqzwj3aPsSuHmI4RWZh62dWUL+DJN4C5YQya5TcQBJU/Fe8+rY0jhXTQITqA==", + "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.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.3.tgz", + "integrity": "sha512-nGbJWewA1wrXXZiQhjAT5rhibGfns5ZNkDVqxsO6zJ3f3YvpoDNNmGMSbbhLuXKjNScaBJVOAboztAWVespQMg==", + "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.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.3.tgz", + "integrity": "sha512-QNniJr5Kml0kDEB98jiDOJjXNroxIIi0IXIbdYzY26Xt1pVbeP62+KnoIZLwirOymX/0jDk/2gI/bNUv7A7OIw==", + "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.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.3.tgz", + "integrity": "sha512-TkqEAcmmvH3I/q4114NB4RVt6241Dao48pF45uLcFGrwAaIn0iITgTAKP/dLjbN0R4buJjGb91+UHSoFmpgIWw==", + "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.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.3.tgz", + "integrity": "sha512-NHqjnxpsndf4MPymxteFAWHHfkTL8HjWh1KB7z23ofZ6QO2euONuxDXjat69dKZRALnGypg8k8SsK8vZJoXv1Q==", + "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.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.3.tgz", + "integrity": "sha512-6tbrbwfz5GB9DQ4Jwo6hy9v+vR31xZlvzZ6n5Xut6Hhx5PvrA9q/HsK8KMaYQp063iqZGXwNvZtYNLD7EM/x0w==", + "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.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.3.tgz", + "integrity": "sha512-oyuXxXmoZHjXC917IAPFAAv4wWAa0cM9afk8nx1+9/jNNOX1uPf8yDA6p7G0RypOfw/X0PQt5IfoquY1um+zSg==", + "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.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.3.tgz", + "integrity": "sha512-TytMwF2KVGqP2tgd0I1OY0PAv78dZRAYcF5ssDzjM34SUXCED3uXvSd5+lHoC0bTD6eEdFz7LdQNCO1y0oVk9w==", + "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.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.3.tgz", + "integrity": "sha512-/E9m3qstrJFVPoULV25mVQblSNExY2+kBsYe4sy0Tn0yOOgJ8wZbZt3KnRbF/XeU2Gl1STKUQnDNTqhIE5MD4A==", + "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.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.3.tgz", + "integrity": "sha512-Kr0OcsoQI816i6HOl3vFHpd1K0eZyh76zgfj4c1nTyaTsd5r2Mj1lwM4R90y/qaCfmTn9eHy0SKwi98eitRxug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.3.tgz", + "integrity": "sha512-hOtMwTqnME+/gJcH/PCZ0wn0zPUjiWOgkHpxbSJpfGKMezHltx1S7/k1SitzVa7Ww2cqrDDaFbZEhcJZO8o+Jw==", + "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.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.3.tgz", + "integrity": "sha512-ekcqMMkI2PlhYnfzQnB/cEdYUVVJViWvoUyLrbzgDoi3Snfc1mVBwdnc306ufA5ejy8JSPjT2RlW1nQSjW7efg==", + "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==", + "devOptional": true, + "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/@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/@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/compiler-core": { + "version": "3.5.41", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.41.tgz", + "integrity": "sha512-q0Xtv/F9w2YO/7htQhtiL+Ev2WCJbe5N2hc+XfgyKkEKqWpSxknmT8QOuGdEKNdjPq0c3F7rNpFkTo3Kfrm7pg==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@vue/shared": "3.5.41", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.41", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.41.tgz", + "integrity": "sha512-oKacVfNglLvGjnS6BXOlGL7EyG2h8X03pqXCjzotRZUaXGjbrTJUnVAQjrCqUnS+lyu31nwQjZY/d817GmCnfw==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.41", + "@vue/shared": "3.5.41" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.41", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.41.tgz", + "integrity": "sha512-XJhip7R2wy6vX3knCxdZN4KracFaZUef58s1KYewqluedHIJaPIVfXoYT7MF1F8nCvv6k8bWWxDC8opMkg1VTQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@vue/compiler-core": "3.5.41", + "@vue/compiler-dom": "3.5.41", + "@vue/compiler-ssr": "3.5.41", + "@vue/shared": "3.5.41", + "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.41", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.41.tgz", + "integrity": "sha512-U3v5OejKEGqOI0Wy0+Sz7hGuIFZHA4LSXzrNM3IMIeDyJEBBfTpX26n3SDgToRpP2bLc9FfI2j/kSgcJ8Emq5A==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.41", + "@vue/shared": "3.5.41" + } + }, + "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-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/reactivity": { + "version": "3.5.41", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.41.tgz", + "integrity": "sha512-rznsqKM0np0x18EjzF8x88MpEhdNsffbvFbckLL5+oUKz1BxAImEmO7J1ArRYSyo6aQaVoBDp7jEkT91OOxydA==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.41" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.41", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.41.tgz", + "integrity": "sha512-Vcry58hiAKwGen9Z1jUZE0feFsNArPCMOImYI8el48A9Idf6DuQYD0U05zZIF2Iad1hGhPSvcbBbAOhNr55fhg==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.41", + "@vue/shared": "3.5.41" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.41", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.41.tgz", + "integrity": "sha512-3vVBahVBS9+U6cmXBLyb8nE6/yYo4J/CGI9eVFs3KiMc0YHuudwKyShTD65jtJy/L9PUUxNAFu4cj4LiJ0UFbw==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.41", + "@vue/runtime-core": "3.5.41", + "@vue/shared": "3.5.41", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.41", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.41.tgz", + "integrity": "sha512-n6hx/pNFfbD6SuyeuMVkvqox8bwf/ET9JlA/kAz/imw8sw++wkqKe2mHX5KutjPpbKE4Z56yTHszoOjGMI9igQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.41", + "@vue/runtime-dom": "3.5.41", + "@vue/shared": "3.5.41" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.41", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.41.tgz", + "integrity": "sha512-IOnwSCma8j+9xJT6b8H0dEYidC80NsYmNMlZxRsukYcSoGaDBohog5hDxzeUXdFeGWFA++vWvxqOmrr96VlqMA==", + "license": "MIT" + }, + "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/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "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/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.19.0.tgz", + "integrity": "sha512-ht/iuYZXEjFxLH/Hkezgd7m6JKlHHXEUSneaDz8uZe1Gj5QZtCnpyDsckvAiEnT89OEbCLmnte4R4sn7P0EKFw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.6", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "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/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/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "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": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.2.0.tgz", + "integrity": "sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==", + "license": "MIT" + }, + "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/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "license": "ISC", + "engines": { + "node": ">= 10" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "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/copy-anything": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-4.0.5.tgz", + "integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==", + "license": "MIT", + "dependencies": { + "is-what": "^5.2.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "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==", + "devOptional": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "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/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.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/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/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/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/filepond": { + "version": "4.32.12", + "resolved": "https://registry.npmjs.org/filepond/-/filepond-4.32.12.tgz", + "integrity": "sha512-wro0/deLwua9CkL7HJygOlDaPo1c7Ov8kfzbZjH7m4hdi56gdHGDeRWnYa+qXnRm3Lty69gUGVFb3JZy/9vT8g==", + "license": "MIT" + }, + "node_modules/filepond-plugin-file-validate-type": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/filepond-plugin-file-validate-type/-/filepond-plugin-file-validate-type-1.2.9.tgz", + "integrity": "sha512-Tzv07aNdZvjUXDRA3XL16QMEvh6llDrXlcZ6W0eTHQ+taHaVg/JKJTFs/AViO+6ZcpPCcQStbhYEL2HoS+vldw==", + "license": "MIT", + "peerDependencies": { + "filepond": ">=1.x <5.x" + } + }, + "node_modules/filepond-plugin-image-preview": { + "version": "4.6.12", + "resolved": "https://registry.npmjs.org/filepond-plugin-image-preview/-/filepond-plugin-image-preview-4.6.12.tgz", + "integrity": "sha512-Y8ETX5QVV0mbPB0586UH8AUmG9tZg8PuN5bdEAIlZVJFTct5ebViJ7+Am94/VhTPjLqZjBf1zmDq5JU6XRsZKw==", + "license": "MIT", + "peerDependencies": { + "filepond": ">=4.x <5.x" + } + }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.4", + "mime-types": "^2.1.35" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "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/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "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": "BSD-3-Clause" + }, + "node_modules/immutable": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.9.tgz", + "integrity": "sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==", + "license": "MIT" + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/inquirer": { + "version": "8.2.7", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.7.tgz", + "integrity": "sha512-UjOaSel/iddGZJ5xP/Eixh6dY1XghiBw4XK13rCCIJcJfyhhoul/7KhLLUGtebEj6GDYM6Vnx/mVsjx2L/mFIA==", + "license": "MIT", + "dependencies": { + "@inquirer/external-editor": "^1.0.0", + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^6.0.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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==", + "license": "MIT", + "optional": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-what": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-5.5.0.tgz", + "integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/jalaali-js": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/jalaali-js/-/jalaali-js-1.2.8.tgz", + "integrity": "sha512-Jl/EwY84JwjW2wsWqeU4pNd22VNQ7EkjI36bDuLw31wH98WQW4fPjD0+mG7cdCK+Y8D6s9R3zLiQ3LaKu6bD8A==", + "license": "MIT" + }, + "node_modules/jalali-moment": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/jalali-moment/-/jalali-moment-3.3.11.tgz", + "integrity": "sha512-tdSaRs9cjWjOIaWhcsGFZMhZQhfgok5J0TwqFpBIZPudZxxa6yjUPoLCOwuvbAtRpiZn7k/mvazAJh+vEN5suw==", + "license": "MIT", + "dependencies": { + "commander": "^7.0.0", + "inquirer": "^8.0.0", + "moment": "^2.26.0" + }, + "bin": { + "jalalim": "cli.js" + } + }, + "node_modules/js-cookie": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.8.tgz", + "integrity": "sha512-yeJd4aNAdYZQjaon2bpD/Gb0B/omw7HQOsynXXcOiWVCacbBcPlgn8S/d1X6blFSaHao7ozqtW7NZW19xpCtIw==", + "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/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/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "devOptional": 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/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "license": "MIT" + }, + "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/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/moment-jalaali": { + "version": "0.9.6", + "resolved": "https://registry.npmjs.org/moment-jalaali/-/moment-jalaali-0.9.6.tgz", + "integrity": "sha512-v8wXjQplvk5ez+sUqgsWIrafwIf1BEXXvzTYwsg1wHcqh27nSgKPCJ6FnZRrCz03MoNyB9N31L0oms+vE8Rq7g==", + "license": "MIT", + "dependencies": { + "jalaali-js": "^1.1.0", + "moment": "^2.22.2", + "moment-timezone": "^0.5.21", + "rimraf": "^3.0.2" + } + }, + "node_modules/moment-timezone": { + "version": "0.5.48", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.48.tgz", + "integrity": "sha512-f22b8LV1gbTO2ms2j2z13MuPogNoh5UzxL3nzNAYKGraILnbGc9NEE6dyiiiLv46DGRb8A4kg8UKWLjPthxBHw==", + "license": "MIT", + "dependencies": { + "moment": "^2.29.4" + }, + "engines": { + "node": "*" + } + }, + "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==", + "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/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "license": "ISC" + }, + "node_modules/nanoid": { + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", + "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/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT", + "optional": true + }, + "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/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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/photoswipe": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/photoswipe/-/photoswipe-5.4.4.tgz", + "integrity": "sha512-WNFHoKrkZNnvFFhbHL93WDkW3ifwVOXSW3w1UuZZelSmgXpIGiZSNlZJq37rR8YejqME2rHs9EhH9ZvlvFH2NA==", + "license": "MIT", + "engines": { + "node": ">= 0.12.0" + } + }, + "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": "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/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.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.17", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/primeflex": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/primeflex/-/primeflex-4.0.0.tgz", + "integrity": "sha512-UOEZCRjR36+sm5bUpDhS1xbA068l9VC6y1aTNVqQPtXuKIdPTqAWHRUxj3mKAoPrQ9W373ooJJMgNVXfiaw04g==", + "license": "MIT" + }, + "node_modules/primeicons": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/primeicons/-/primeicons-8.0.0.tgz", + "integrity": "sha512-eZGk+Mgb4fE+LkJi9LvllbnB4RVUF9kzmnLVnT3ZMY6stqKVh7TUJ0mFoMNIEgTnJSvhaGcJotDiSvtVvkHd7g==", + "license": "SEE LICENSE IN LICENSE.md" + }, + "node_modules/primevue": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/primevue/-/primevue-4.0.7.tgz", + "integrity": "sha512-88qazHqldkqsCxvhjnjO65XMBfJyHQoFW3BQvrJYO6RqPheHB4f7cY61eqtBpJAjnM5x+YKTZiWx/gBuUzqT7Q==", + "license": "MIT", + "dependencies": { + "@primeuix/styled": "^0.0.5", + "@primeuix/utils": "^0.0.5", + "@primevue/core": "4.0.7", + "@primevue/icons": "4.0.7" + }, + "engines": { + "node": ">=12.11.0" + } + }, + "node_modules/property-expr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz", + "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==", + "license": "MIT" + }, + "node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "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/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz", + "integrity": "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rolldown": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.3.tgz", + "integrity": "sha512-rn9wpmxplLf7NLNyCk9FyWh3FM43DbY8jOzCdEPzH7uflhTftRbCEpqi6Ly2osgoU8OwObtmavMbWLaWy4LX7A==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.143.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.3", + "@rolldown/binding-darwin-arm64": "1.2.3", + "@rolldown/binding-darwin-x64": "1.2.3", + "@rolldown/binding-freebsd-x64": "1.2.3", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.3", + "@rolldown/binding-linux-arm64-gnu": "1.2.3", + "@rolldown/binding-linux-arm64-musl": "1.2.3", + "@rolldown/binding-linux-ppc64-gnu": "1.2.3", + "@rolldown/binding-linux-s390x-gnu": "1.2.3", + "@rolldown/binding-linux-x64-gnu": "1.2.3", + "@rolldown/binding-linux-x64-musl": "1.2.3", + "@rolldown/binding-openharmony-arm64": "1.2.3", + "@rolldown/binding-win32-arm64-msvc": "1.2.3", + "@rolldown/binding-win32-x64-msvc": "1.2.3" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "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/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/sass": { + "version": "1.102.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.102.0.tgz", + "integrity": "sha512-NSOyTnaQF7rTAEOtI2fwb386vL+akyiQLBZu8Na7hXCb+umJy0GAqlcMIaqACZ6Z1VgTBS4K9PG6B3IdjHGJsw==", + "license": "MIT", + "dependencies": { + "chokidar": "^5.0.0", + "immutable": "^5.1.5", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=20.19.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "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/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "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/speakingurl": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/superjson": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.6.tgz", + "integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==", + "license": "MIT", + "dependencies": { + "copy-anything": "^4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "license": "MIT" + }, + "node_modules/tiny-case": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz", + "integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==", + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "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/toposort": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==", + "license": "MIT" + }, + "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" + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/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/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/vee-validate": { + "version": "4.15.1", + "resolved": "https://registry.npmjs.org/vee-validate/-/vee-validate-4.15.1.tgz", + "integrity": "sha512-DkFsiTwEKau8VIxyZBGdO6tOudD+QoUBPuHj3e6QFqmbfCRj1ArmYWue9lEp6jLSWBIw4XPlDLjFIZNLdRAMSg==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^7.5.2", + "type-fest": "^4.8.3" + }, + "peerDependencies": { + "vue": "^3.4.26" + } + }, + "node_modules/vee-validate/node_modules/@vue/devtools-api": { + "version": "7.7.10", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.10.tgz", + "integrity": "sha512-KxtEpUOOpFz/qOGRrAwA36QF7DqIA+FXgCYit9mk9wjbaZt0sXOFz81ElOZtKA4HbWHUdwNjZHBFsFFyp5BZiA==", + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^7.7.10" + } + }, + "node_modules/vee-validate/node_modules/@vue/devtools-kit": { + "version": "7.7.10", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.10.tgz", + "integrity": "sha512-3WNi2Kq4tbpVbmhml7RiphmAt0279oh3fKNeWMQIrltfX8Q91b4i5PL8DtyNKdwmcsGrV4fg+erwWOmD05CLIw==", + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^7.7.10", + "birpc": "^2.3.0", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.2" + } + }, + "node_modules/vee-validate/node_modules/@vue/devtools-shared": { + "version": "7.7.10", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.10.tgz", + "integrity": "sha512-wOPslzB8vTvpxwdaOcR2qAbwmuSP0L+rhpoC6Cf56V3Jip+HWb7PQQXOUPgBNQARpXsbQX/+mvi8kKucmBGRwQ==", + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/vee-validate/node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "license": "MIT" + }, + "node_modules/vee-validate/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vite": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.1.tgz", + "integrity": "sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.25", + "rolldown": "~1.2.1", + "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/vue": { + "version": "3.5.41", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.41.tgz", + "integrity": "sha512-2laE0p+aK+/AOPG/XL/WepOs/GlK755LJ1XECi9kDUrz1FKNw8rb2Xzlw9JS1rqEV55nb0ttsKxVlTCcd+R5cg==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.41", + "@vue/compiler-sfc": "3.5.41", + "@vue/runtime-dom": "3.5.41", + "@vue/server-renderer": "3.5.41", + "@vue/shared": "3.5.41" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-filepond": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vue-filepond/-/vue-filepond-8.0.0.tgz", + "integrity": "sha512-FluypMbKJIcOfDi9gT34RQm7MkLDxnPbHQ4VrzNydAUTI1aMYVb1GhJGJjsz/iEtqAfR4V61BCN1TtWuXmTLsg==", + "license": "MIT", + "peerDependencies": { + "filepond": ">=4.7.4 < 5.x", + "vue": ">=3 < 4" + } + }, + "node_modules/vue-i18n": { + "version": "11.4.8", + "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-11.4.8.tgz", + "integrity": "sha512-0ULeHP6Z9CGvAm67S77ZEp41cfGXIREGL8qfhos2BMgcQQewtQcDKuojt6jjasAD/S8GwfTp2ySPmDSpwvrCMQ==", + "license": "MIT", + "dependencies": { + "@intlify/core-base": "11.4.8", + "@intlify/devtools-types": "11.4.8", + "@intlify/shared": "11.4.8", + "@vue/devtools-api": "^6.5.0" + }, + "engines": { + "node": ">= 22" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/vue-i18n/node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, + "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/vue3-persian-datetime-picker": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/vue3-persian-datetime-picker/-/vue3-persian-datetime-picker-1.2.2.tgz", + "integrity": "sha512-d7nkj5vgtUvEXZboSdRmP1uwBfXvXgXqdvsOOMQb34jiMZU/aBDrTYWTEe1N+XKF9pvTTJn8Rws9ttJmyhK/hw==", + "license": "MIT", + "dependencies": { + "moment-jalaali": "^0.9.4" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "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/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "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/yup": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/yup/-/yup-1.7.1.tgz", + "integrity": "sha512-GKHFX2nXul2/4Dtfxhozv701jLQHdf6J34YDh2cEkpqoo8le5Mg6/LrdseVLrFarmFygZTlfIhHx/QKfb/QWXw==", + "license": "MIT", + "dependencies": { + "property-expr": "^2.0.5", + "tiny-case": "^1.0.3", + "toposort": "^2.0.2", + "type-fest": "^2.19.0" + } + }, + "node_modules/yup/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..a77986f --- /dev/null +++ b/package.json @@ -0,0 +1,38 @@ +{ + "name": "gameno-dashboard", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "@fontsource/vazirmatn": "^5.3.0", + "@primevue/themes": "4.0.7", + "axios": "^1.19.0", + "filepond": "^4.32.12", + "filepond-plugin-file-validate-type": "^1.2.9", + "filepond-plugin-image-preview": "^4.6.12", + "jalali-moment": "^3.3.11", + "js-cookie": "^3.0.8", + "photoswipe": "^5.4.4", + "pinia": "^4.0.2", + "primeflex": "^4.0.0", + "primeicons": "^8.0.0", + "primevue": "4.0.7", + "sass": "^1.102.0", + "vee-validate": "^4.15.1", + "vue": "^3.5.40", + "vue-filepond": "^8.0.0", + "vue-i18n": "^11.4.8", + "vue-router": "^5.2.0", + "vue3-persian-datetime-picker": "^1.2.2", + "yup": "^1.7.1" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^6.0.8", + "vite": "^8.2.0" + } +} diff --git a/public/favicon.svg b/public/favicon.svg new file mode 100644 index 0000000..6893eb1 --- /dev/null +++ b/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/icons.svg b/public/icons.svg new file mode 100644 index 0000000..e952219 --- /dev/null +++ b/public/icons.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 0000000..434fd38 --- /dev/null +++ b/src/App.vue @@ -0,0 +1,27 @@ + + + + + + diff --git a/src/api/activityLogApi.js b/src/api/activityLogApi.js new file mode 100644 index 0000000..9081953 --- /dev/null +++ b/src/api/activityLogApi.js @@ -0,0 +1,7 @@ +// /src/api/activityLogApi.js +import axiosInstance from './axiosInstance'; + +export const activityLogApi = { + getAll: (params) => axiosInstance.get('/activity-logs/admin/get-all', { params }), + getActions: () => axiosInstance.get('/activity-logs/admin/actions') +}; diff --git a/src/api/attendanceApi.js b/src/api/attendanceApi.js new file mode 100644 index 0000000..06546d3 --- /dev/null +++ b/src/api/attendanceApi.js @@ -0,0 +1,8 @@ +// /src/api/attendanceApi.js +import axiosInstance from './axiosInstance'; + +export const attendanceApi = { + recordSessionAttendance: (sessionId, attendanceList) => + axiosInstance.put(`/sessions/admin/${sessionId}/attendance`, { attendanceList }), + getSessionAttendance: (sessionId) => axiosInstance.get(`/sessions/admin/get-one/${sessionId}`) +}; diff --git a/src/api/authApi.js b/src/api/authApi.js new file mode 100644 index 0000000..10759ab --- /dev/null +++ b/src/api/authApi.js @@ -0,0 +1,9 @@ +// /src/api/authApi.js +import axiosInstance from './axiosInstance'; + +export const authApi = { + login: (data) => axiosInstance.post('/auth/login', data), + refresh: (refreshToken) => axiosInstance.post('/auth/refresh', { refreshToken }), + logout: (refreshToken) => axiosInstance.post('/auth/logout', { refreshToken }), + getSelf: () => axiosInstance.get('/users/user/get-self') +}; diff --git a/src/api/axiosInstance.js b/src/api/axiosInstance.js new file mode 100644 index 0000000..3ecc8fc --- /dev/null +++ b/src/api/axiosInstance.js @@ -0,0 +1,121 @@ +// /src/api/axiosInstance.js +import axios from 'axios'; +import Cookies from 'js-cookie'; + +const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:5000/api'; + +const axiosInstance = axios.create({ + baseURL: API_BASE_URL, + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json' + }, + timeout: 15000 +}); + +// Request interceptor: attach token +axiosInstance.interceptors.request.use( + (config) => { + const token = localStorage.getItem('accessToken') || Cookies.get('accessToken'); + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } + + // Ask backend for Persian (or stored) localized error messages + const lang = localStorage.getItem('lang') || 'fa'; + config.headers['Accept-Language'] = lang; + if (!config.params) config.params = {}; + if (config.params.lang == null) { + config.params.lang = lang; + } + + return config; + }, + (error) => Promise.reject(error) +); + +// Response interceptor: auto-refresh token on 401 +let isRefreshing = false; +let failedQueue = []; + +const processQueue = (error, token = null) => { + failedQueue.forEach(prom => { + if (error) { + prom.reject(error); + } else { + prom.resolve(token); + } + }); + failedQueue = []; +}; + +axiosInstance.interceptors.response.use( + (response) => response.data, + async (error) => { + const originalRequest = error.config; + + if (error.response?.status === 401 && !originalRequest._retry) { + if (originalRequest.url.includes('/auth/login') || originalRequest.url.includes('/auth/refresh')) { + return Promise.reject(error); + } + + if (isRefreshing) { + return new Promise((resolve, reject) => { + failedQueue.push({ resolve, reject }); + }) + .then((token) => { + originalRequest.headers.Authorization = `Bearer ${token}`; + return axiosInstance(originalRequest); + }) + .catch((err) => Promise.reject(err)); + } + + originalRequest._retry = true; + isRefreshing = true; + + const refreshToken = localStorage.getItem('refreshToken') || Cookies.get('refreshToken'); + + if (!refreshToken) { + isRefreshing = false; + localStorage.removeItem('accessToken'); + localStorage.removeItem('refreshToken'); + window.location.href = '/login'; + return Promise.reject(error); + } + + try { + const refreshResponse = await axios.post(`${API_BASE_URL}/auth/refresh`, { refreshToken }); + const data = refreshResponse.data?.data || refreshResponse.data; + const newAccessToken = data.accessToken; + const newRefreshToken = data.refreshToken; + + localStorage.setItem('accessToken', newAccessToken); + Cookies.set('accessToken', newAccessToken); + if (newRefreshToken) { + localStorage.setItem('refreshToken', newRefreshToken); + Cookies.set('refreshToken', newRefreshToken); + } + + axiosInstance.defaults.headers.common.Authorization = `Bearer ${newAccessToken}`; + originalRequest.headers.Authorization = `Bearer ${newAccessToken}`; + + processQueue(null, newAccessToken); + return axiosInstance(originalRequest); + } catch (refreshErr) { + processQueue(refreshErr, null); + localStorage.removeItem('accessToken'); + localStorage.removeItem('refreshToken'); + Cookies.remove('accessToken'); + Cookies.remove('refreshToken'); + window.location.href = '/login'; + return Promise.reject(refreshErr); + } finally { + isRefreshing = false; + } + } + + return Promise.reject(error); + } +); + +export default axiosInstance; diff --git a/src/api/certificateApi.js b/src/api/certificateApi.js new file mode 100644 index 0000000..a957741 --- /dev/null +++ b/src/api/certificateApi.js @@ -0,0 +1,16 @@ +// /src/api/certificateApi.js +import axiosInstance from './axiosInstance'; + +export const certificateApi = { + getAll: (params) => axiosInstance.get('/certificates/admin/get-all', { params }), + search: (params) => axiosInstance.get('/certificates/admin/search', { params }), + getOne: (id) => axiosInstance.get(`/certificates/admin/get-one/${id}`), + create: (data) => axiosInstance.post('/certificates/admin/create', data), + update: (id, data) => axiosInstance.put(`/certificates/admin/update/${id}`, data), + delete: (id) => axiosInstance.delete(`/certificates/admin/delete/${id}`), + uploadTemp: (formData) => axiosInstance.post('/files/admin/upload-temp', formData, { + headers: { 'Content-Type': 'multipart/form-data' } + }), + userUpload: (data) => axiosInstance.post('/certificates/user/upload', data), + getUserCertificates: () => axiosInstance.get('/certificates/user/my-certificates') +}; diff --git a/src/api/classApi.js b/src/api/classApi.js new file mode 100644 index 0000000..feede01 --- /dev/null +++ b/src/api/classApi.js @@ -0,0 +1,12 @@ +// /src/api/classApi.js +import axiosInstance from './axiosInstance'; + +export const classApi = { + getAll: (params) => axiosInstance.get('/classes/admin/get-all', { params }), + search: (params) => axiosInstance.get('/classes/admin/search', { params }), + getOne: (id) => axiosInstance.get(`/classes/admin/get-one/${id}`), + create: (data) => axiosInstance.post('/classes/admin/create', data), + update: (id, data) => axiosInstance.put(`/classes/admin/update/${id}`, data), + delete: (id) => axiosInstance.delete(`/classes/admin/delete/${id}`), + registerUsers: (classId, userIds) => axiosInstance.post(`/classes/admin/${classId}/register-users`, { userIds }) +}; diff --git a/src/api/contactInquiryApi.js b/src/api/contactInquiryApi.js new file mode 100644 index 0000000..20f2170 --- /dev/null +++ b/src/api/contactInquiryApi.js @@ -0,0 +1,7 @@ +import axiosInstance from './axiosInstance'; + +export const contactInquiryApi = { + getAll: (params) => axiosInstance.get('/contact-inquiries/admin/get-all', { params }), + getOne: (id) => axiosInstance.get(`/contact-inquiries/admin/get-one/${id}`), + update: (id, data) => axiosInstance.put(`/contact-inquiries/admin/update/${id}`, data) +}; diff --git a/src/api/courseApi.js b/src/api/courseApi.js new file mode 100644 index 0000000..237307a --- /dev/null +++ b/src/api/courseApi.js @@ -0,0 +1,11 @@ +// /src/api/courseApi.js +import axiosInstance from './axiosInstance'; + +export const courseApi = { + getAll: (params) => axiosInstance.get('/courses/admin/get-all', { params }), + search: (params) => axiosInstance.get('/courses/admin/search', { params }), + getOne: (id) => axiosInstance.get(`/courses/admin/get-one/${id}`), + create: (data) => axiosInstance.post('/courses/admin/create', data), + update: (id, data) => axiosInstance.put(`/courses/admin/update/${id}`, data), + delete: (id) => axiosInstance.delete(`/courses/admin/delete/${id}`) +}; diff --git a/src/api/dashboardApi.js b/src/api/dashboardApi.js new file mode 100644 index 0000000..4e597df --- /dev/null +++ b/src/api/dashboardApi.js @@ -0,0 +1,6 @@ +// /src/api/dashboardApi.js +import axiosInstance from './axiosInstance'; + +export const dashboardApi = { + getStats: () => axiosInstance.get('/dashboard/admin/stats') +}; diff --git a/src/api/notificationApi.js b/src/api/notificationApi.js new file mode 100644 index 0000000..f996289 --- /dev/null +++ b/src/api/notificationApi.js @@ -0,0 +1,12 @@ +// /src/api/notificationApi.js +import axiosInstance from './axiosInstance'; + +export const notificationApi = { + getAll: (params) => axiosInstance.get('/notifications/admin/get-all', { params }), + search: (params) => axiosInstance.get('/notifications/admin/search', { params }), + getOne: (id) => axiosInstance.get(`/notifications/admin/get-one/${id}`), + create: (data) => axiosInstance.post('/notifications/admin/create', data), + update: (id, data) => axiosInstance.put(`/notifications/admin/update/${id}`, data), + delete: (id) => axiosInstance.delete(`/notifications/admin/delete/${id}`), + retry: (id) => axiosInstance.post(`/notifications/admin/${id}/retry`) +}; diff --git a/src/api/paymentApi.js b/src/api/paymentApi.js new file mode 100644 index 0000000..7c26f1b --- /dev/null +++ b/src/api/paymentApi.js @@ -0,0 +1,12 @@ +// /src/api/paymentApi.js +import axiosInstance from './axiosInstance'; + +export const paymentApi = { + getAll: (params) => axiosInstance.get('/payments/admin/get-all', { params }), + search: (params) => axiosInstance.get('/payments/admin/search', { params }), + getOne: (id) => axiosInstance.get(`/payments/admin/get-one/${id}`), + create: (data) => axiosInstance.post('/payments/admin/create', data), + update: (id, data) => axiosInstance.put(`/payments/admin/update/${id}`, data), + delete: (id) => axiosInstance.delete(`/payments/admin/delete/${id}`), + recordTransaction: (paymentId, data) => axiosInstance.post(`/payments/user/pay/${paymentId}`, data) +}; diff --git a/src/api/professorApi.js b/src/api/professorApi.js new file mode 100644 index 0000000..fdd4eda --- /dev/null +++ b/src/api/professorApi.js @@ -0,0 +1,11 @@ +// /src/api/professorApi.js +import axiosInstance from './axiosInstance'; + +export const professorApi = { + getAll: (params) => axiosInstance.get('/professors/admin/get-all', { params }), + search: (params) => axiosInstance.get('/professors/admin/search', { params }), + getOne: (id) => axiosInstance.get(`/professors/admin/get-one/${id}`), + create: (data) => axiosInstance.post('/professors/admin/create', data), + update: (id, data) => axiosInstance.put(`/professors/admin/update/${id}`, data), + delete: (id) => axiosInstance.delete(`/professors/admin/delete/${id}`) +}; diff --git a/src/api/roleApi.js b/src/api/roleApi.js new file mode 100644 index 0000000..96514b4 --- /dev/null +++ b/src/api/roleApi.js @@ -0,0 +1,11 @@ +// /src/api/roleApi.js +import axiosInstance from './axiosInstance'; + +export const roleApi = { + getAll: (params) => axiosInstance.get('/roles/admin/get-all', { params }), + search: (params) => axiosInstance.get('/roles/admin/search', { params }), + getOne: (id) => axiosInstance.get(`/roles/admin/get-one/${id}`), + create: (data) => axiosInstance.post('/roles/admin/create', data), + update: (id, data) => axiosInstance.put(`/roles/admin/update/${id}`, data), + delete: (id) => axiosInstance.delete(`/roles/admin/delete/${id}`) +}; diff --git a/src/api/sessionApi.js b/src/api/sessionApi.js new file mode 100644 index 0000000..d5bdc1f --- /dev/null +++ b/src/api/sessionApi.js @@ -0,0 +1,14 @@ +// /src/api/sessionApi.js +import axiosInstance from './axiosInstance'; + +export const sessionApi = { + getAll: (params) => axiosInstance.get('/sessions/admin/get-all', { params }), + search: (params) => axiosInstance.get('/sessions/admin/search', { params }), + getOne: (id) => axiosInstance.get(`/sessions/admin/get-one/${id}`), + create: (data) => axiosInstance.post('/sessions/admin/create', data), + update: (id, data) => axiosInstance.put(`/sessions/admin/update/${id}`, data), + delete: (id) => axiosInstance.delete(`/sessions/admin/delete/${id}`), + bulkDelete: (ids) => axiosInstance.post('/sessions/admin/bulk-delete', { ids }), + bulkUpdateStatus: (ids, status) => axiosInstance.post('/sessions/admin/bulk-status', { ids, status }), + recordAttendance: (sessionId, data) => axiosInstance.put(`/sessions/admin/${sessionId}/attendance`, data) +}; diff --git a/src/api/userApi.js b/src/api/userApi.js new file mode 100644 index 0000000..3d8d79d --- /dev/null +++ b/src/api/userApi.js @@ -0,0 +1,12 @@ +// /src/api/userApi.js +import axiosInstance from './axiosInstance'; + +export const userApi = { + getAll: (params) => axiosInstance.get('/users/admin/get-all', { params }), + search: (params) => axiosInstance.get('/users/admin/search', { params }), + getOne: (id) => axiosInstance.get(`/users/admin/get-one/${id}`), + create: (data) => axiosInstance.post('/users/admin/create', data), + update: (id, data) => axiosInstance.put(`/users/admin/update/${id}`, data), + delete: (id) => axiosInstance.delete(`/users/admin/delete/${id}`), + enroll: (userId, data) => axiosInstance.post(`/users/admin/${userId}/enroll`, data) +}; diff --git a/src/assets/hero.png b/src/assets/hero.png new file mode 100644 index 0000000..02251f4 Binary files /dev/null and b/src/assets/hero.png differ diff --git a/src/assets/styles/dark-theme.scss b/src/assets/styles/dark-theme.scss new file mode 100644 index 0000000..4f6f297 --- /dev/null +++ b/src/assets/styles/dark-theme.scss @@ -0,0 +1,13 @@ +// /src/assets/styles/dark-theme.scss +:root.dark-mode { + --bg-primary: #121212; + --surface-card: #1e1e1e; + --surface-hover: #2a2a2a; + --border-color: #333333; + --text-primary: #f3f4f6; + --text-secondary: #9ca3af; + --text-muted: #6b7280; + --primary-color: #10b981; + --primary-color-hover: #059669; + --primary-color-light: rgba(16, 185, 129, 0.15); +} diff --git a/src/assets/styles/light-theme.scss b/src/assets/styles/light-theme.scss new file mode 100644 index 0000000..c92823e --- /dev/null +++ b/src/assets/styles/light-theme.scss @@ -0,0 +1,13 @@ +// /src/assets/styles/light-theme.scss +:root { + --bg-primary: #f8fafc; + --surface-card: #ffffff; + --surface-hover: #f1f5f9; + --border-color: #e2e8f0; + --text-primary: #0f172a; + --text-secondary: #475569; + --text-muted: #94a3b8; + --primary-color: #10b981; + --primary-color-hover: #059669; + --primary-color-light: rgba(16, 185, 129, 0.1); +} diff --git a/src/assets/styles/main.scss b/src/assets/styles/main.scss new file mode 100644 index 0000000..f2f3486 --- /dev/null +++ b/src/assets/styles/main.scss @@ -0,0 +1,272 @@ +// /src/assets/styles/main.scss + +@import '@fontsource/vazirmatn/400.css'; +@import '@fontsource/vazirmatn/500.css'; +@import '@fontsource/vazirmatn/700.css'; + +@import './light-theme.scss'; +@import './dark-theme.scss'; +@import './rtl-overrides.scss'; + +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +html, body { + font-family: 'Vazirmatn', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; + font-size: 14px; + background-color: var(--bg-primary); + color: var(--text-primary); + min-height: 100vh; + transition: background-color 0.2s ease, color 0.2s ease; +} + +a { + color: var(--primary-color); + text-decoration: none; + &:hover { + color: var(--primary-color-hover); + } +} + +/* Custom Scrollbar */ +::-webkit-scrollbar { + width: 6px; + height: 6px; +} + +::-webkit-scrollbar-track { + background: var(--bg-tertiary); +} + +::-webkit-scrollbar-thumb { + background: var(--text-muted); + border-radius: 3px; + &:hover { + background: var(--text-secondary); + } +} + +/* ========================================== + Global Border Polish (Removing Harsh Outlines) + ========================================== */ +.border-color, +.border-1, +.border-top-1, +.border-bottom-1, +.border-left-1, +.border-right-1 { + border-color: var(--border-color) !important; +} + +.surface-card, +.p-component, +.p-card, +.p-panel, +.p-datatable, +.p-datatable-header, +.p-datatable-thead > tr > th, +.p-datatable-tbody > tr > td, +.p-paginator, +.p-dialog, +.p-sidebar, +.p-menu, +.p-select, +.p-inputtext, +.p-button { + border-color: var(--border-color) !important; +} + +/* ========================================== + Vue Persian Datetime Picker Styling & Theme + Theme & coloring rebuilt from scratch using CSS variables + ========================================== */ +.vpd-wrapper { + font-family: 'Vazirmatn', -apple-system, BlinkMacSystemFont, sans-serif !important; + + .vpd-container { + border-radius: 16px !important; + overflow: hidden !important; + box-shadow: var(--shadow-lg) !important; + border: 1px solid var(--border-color) !important; + background-color: var(--surface-card) !important; + } + + .vpd-header { + background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-color-hover) 100%) !important; + color: #ffffff !important; + + .vpd-year-label, + .vpd-year-label span { + color: rgba(255, 255, 255, 0.85) !important; + } + + .vpd-date, + .vpd-date span { + color: #ffffff !important; + font-weight: 700 !important; + } + } + + .vpd-content { + background-color: var(--surface-card) !important; + color: var(--text-primary) !important; + } + + .vpd-controls { + color: var(--text-primary) !important; + + button, span, i, svg, .vpd-month-label { + color: var(--text-primary) !important; + fill: var(--text-primary) !important; + } + + button:hover { + background-color: var(--surface-hover) !important; + border-radius: 50%; + } + + .vpd-month-label span { + border-color: var(--border-color) !important; + + &:hover { + background-color: var(--surface-hover) !important; + } + } + } + + .vpd-week { + .vpd-weekday { + color: var(--text-muted) !important; + font-weight: 600 !important; + } + } + + .vpd-days { + .vpd-day { + color: var(--text-primary) !important; + + .vpd-day-text { + color: var(--text-primary) !important; + font-weight: 500; + } + + &:not([disabled="true"]):hover:not(.vpd-selected):not(.vpd-empty) { + .vpd-day-effect { + background-color: var(--surface-hover) !important; + opacity: 0.9 !important; + } + } + + &.vpd-selected { + .vpd-day-effect { + background-color: var(--primary-color) !important; + opacity: 1 !important; + } + .vpd-day-text { + color: #ffffff !important; + font-weight: 700 !important; + } + } + + &.vpd-range-first, + &.vpd-range-last, + &.vpd-range-between, + &.vpd-range-hover { + .vpd-day-effect { + background-color: var(--primary-color-light) !important; + opacity: 1 !important; + } + .vpd-day-text { + color: var(--primary-color) !important; + } + } + + &[disabled="true"] { + .vpd-day-text { + color: var(--text-muted) !important; + opacity: 0.4 !important; + } + } + + &.vpd-empty { + background: transparent !important; + } + } + } + + .vpd-actions { + border-top: 1px solid var(--border-color) !important; + background-color: var(--surface-card) !important; + + button { + color: var(--primary-color) !important; + font-weight: 700 !important; + font-family: inherit !important; + border-radius: 8px !important; + transition: background-color 0.15s ease, color 0.15s ease; + + &:hover { + background-color: var(--primary-color-light) !important; + } + } + } + + .vpd-addon-list { + background-color: var(--surface-card) !important; + border-color: var(--border-color) !important; + + .vpd-addon-list-item { + color: var(--text-secondary) !important; + background-color: transparent !important; + + &:hover { + background-color: var(--surface-hover) !important; + color: var(--text-primary) !important; + } + + &.vpd-selected { + background-color: var(--primary-color-light) !important; + color: var(--primary-color) !important; + font-weight: 700 !important; + } + + &[disabled="true"] { + color: var(--text-muted) !important; + opacity: 0.4 !important; + } + } + } + + .vpd-close-addon { + color: var(--text-primary) !important; + background-color: var(--surface-hover) !important; + } +} + +/* ========================================== + Global High-Contrast Hover & Selection States + ========================================== */ +.p-datatable-tbody > tr { + transition: background-color 0.15s ease; + &:hover { + background-color: var(--surface-hover) !important; + } +} + +.p-select-option, +.p-dropdown-item { + transition: all 0.15s ease; + &:hover { + background-color: var(--primary-color-light) !important; + color: var(--primary-color) !important; + } + &.p-select-option-selected, + &.p-highlight { + background-color: var(--primary-color) !important; + color: #ffffff !important; + } +} + diff --git a/src/assets/styles/rtl-overrides.scss b/src/assets/styles/rtl-overrides.scss new file mode 100644 index 0000000..c5f1062 --- /dev/null +++ b/src/assets/styles/rtl-overrides.scss @@ -0,0 +1,158 @@ +// /src/assets/styles/rtl-overrides.scss + +html[dir="rtl"] { + body { + direction: rtl; + text-align: right; + } + + /* PrimeVue DataTable RTL Fixes */ + .p-datatable { + direction: rtl; + + .p-datatable-header { + direction: rtl; + text-align: right; + } + + .p-datatable-thead > tr > th { + text-align: right !important; + direction: rtl !important; + font-weight: 600; + + .p-column-header-content { + display: flex; + align-items: center; + justify-content: flex-start; + gap: 0.5rem; + } + + .p-sortable-column-icon { + margin-left: 0; + margin-right: 0.5rem; + } + } + + .p-datatable-tbody > tr > td { + text-align: right !important; + direction: rtl !important; + } + } + + /* PrimeVue RTL Icon and Layout Fixes */ + .p-iconfield { + direction: rtl; + .p-inputicon { + right: 0.85rem; + left: auto; + } + } + + .p-paginator { + direction: rtl; + justify-content: center; + gap: 0.25rem; + + .p-paginator-first, + .p-paginator-prev, + .p-paginator-next, + .p-paginator-last { + transform: scaleX(-1); + } + } + + .p-breadcrumb ul { + direction: rtl; + li .p-breadcrumb-chevron { + transform: scaleX(-1); + } + } + + .p-sidebar, + .p-dialog, + .p-menu, + .p-popover, + .p-select-option, + .p-select-overlay, + .p-toast { + direction: rtl; + text-align: right; + } + + /* PrimeVue SelectButton RTL Fixes */ + .p-selectbutton { + direction: rtl; + display: inline-flex; + max-width: 100%; + overflow-x: auto; + + .p-togglebutton { + border-radius: 0 !important; + border-width: 1px 0 1px 1px !important; + text-align: center; + justify-content: center; + + &:first-child { + border-right-width: 1px !important; + border-top-right-radius: var(--p-selectbutton-border-radius, 6px) !important; + border-bottom-right-radius: var(--p-selectbutton-border-radius, 6px) !important; + border-top-left-radius: 0 !important; + border-bottom-left-radius: 0 !important; + } + + &:last-child { + border-left-width: 1px !important; + border-top-left-radius: var(--p-selectbutton-border-radius, 6px) !important; + border-bottom-left-radius: var(--p-selectbutton-border-radius, 6px) !important; + border-top-right-radius: 0 !important; + border-bottom-right-radius: 0 !important; + } + + &:focus-visible, + &.p-togglebutton-checked, + &.p-highlight { + position: relative; + z-index: 1; + } + } + } + + /* PrimeVue ButtonGroup RTL Fixes */ + .p-button-group { + direction: rtl; + display: inline-flex; + + .p-button { + border-radius: 0 !important; + border-width: 1px 0 1px 1px !important; + + &:first-child { + border-right-width: 1px !important; + border-top-right-radius: var(--p-button-border-radius, 6px) !important; + border-bottom-right-radius: var(--p-button-border-radius, 6px) !important; + border-top-left-radius: 0 !important; + border-bottom-left-radius: 0 !important; + } + + &:last-child { + border-left-width: 1px !important; + border-top-left-radius: var(--p-button-border-radius, 6px) !important; + border-bottom-left-radius: var(--p-button-border-radius, 6px) !important; + border-top-right-radius: 0 !important; + border-bottom-right-radius: 0 !important; + } + + &:focus-visible, + &:active { + position: relative; + z-index: 1; + } + } + } + + /* Vue FilePond RTL adjustment */ + .filepond--root { + direction: rtl; + font-family: inherit; + } +} diff --git a/src/assets/vite.svg b/src/assets/vite.svg new file mode 100644 index 0000000..5101b67 --- /dev/null +++ b/src/assets/vite.svg @@ -0,0 +1 @@ +Vite diff --git a/src/assets/vue.svg b/src/assets/vue.svg new file mode 100644 index 0000000..770e9d3 --- /dev/null +++ b/src/assets/vue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/HelloWorld.vue b/src/components/HelloWorld.vue new file mode 100644 index 0000000..f91553d --- /dev/null +++ b/src/components/HelloWorld.vue @@ -0,0 +1,95 @@ + + + diff --git a/src/components/classes/ClassSessionsSection.vue b/src/components/classes/ClassSessionsSection.vue new file mode 100644 index 0000000..770aa81 --- /dev/null +++ b/src/components/classes/ClassSessionsSection.vue @@ -0,0 +1,548 @@ + + + + diff --git a/src/components/common/ConfirmDeleteDialog.vue b/src/components/common/ConfirmDeleteDialog.vue new file mode 100644 index 0000000..8ae83d7 --- /dev/null +++ b/src/components/common/ConfirmDeleteDialog.vue @@ -0,0 +1,57 @@ + + + + diff --git a/src/components/common/DataTableWrapper.vue b/src/components/common/DataTableWrapper.vue new file mode 100644 index 0000000..b4105ce --- /dev/null +++ b/src/components/common/DataTableWrapper.vue @@ -0,0 +1,188 @@ + + + + + + diff --git a/src/components/common/EmptyState.vue b/src/components/common/EmptyState.vue new file mode 100644 index 0000000..e46e824 --- /dev/null +++ b/src/components/common/EmptyState.vue @@ -0,0 +1,36 @@ + + + + + + diff --git a/src/components/common/PageHeader.vue b/src/components/common/PageHeader.vue new file mode 100644 index 0000000..a3b1323 --- /dev/null +++ b/src/components/common/PageHeader.vue @@ -0,0 +1,25 @@ + + + + diff --git a/src/components/common/PermissionGate.vue b/src/components/common/PermissionGate.vue new file mode 100644 index 0000000..b007531 --- /dev/null +++ b/src/components/common/PermissionGate.vue @@ -0,0 +1,32 @@ + + + + diff --git a/src/components/common/StatusTag.vue b/src/components/common/StatusTag.vue new file mode 100644 index 0000000..72ef625 --- /dev/null +++ b/src/components/common/StatusTag.vue @@ -0,0 +1,101 @@ + + + + diff --git a/src/components/common/TableSkeleton.vue b/src/components/common/TableSkeleton.vue new file mode 100644 index 0000000..3ef29b3 --- /dev/null +++ b/src/components/common/TableSkeleton.vue @@ -0,0 +1,32 @@ + + + + diff --git a/src/components/layout/AppBreadcrumb.vue b/src/components/layout/AppBreadcrumb.vue new file mode 100644 index 0000000..d18d239 --- /dev/null +++ b/src/components/layout/AppBreadcrumb.vue @@ -0,0 +1,41 @@ + + + + + + diff --git a/src/components/layout/AppLayout.vue b/src/components/layout/AppLayout.vue new file mode 100644 index 0000000..2fb878c --- /dev/null +++ b/src/components/layout/AppLayout.vue @@ -0,0 +1,8 @@ + + + + diff --git a/src/components/layout/AppSidebar.vue b/src/components/layout/AppSidebar.vue new file mode 100644 index 0000000..e6fbf60 --- /dev/null +++ b/src/components/layout/AppSidebar.vue @@ -0,0 +1,69 @@ + + + + + + diff --git a/src/components/layout/AppTopbar.vue b/src/components/layout/AppTopbar.vue new file mode 100644 index 0000000..48aeb8b --- /dev/null +++ b/src/components/layout/AppTopbar.vue @@ -0,0 +1,166 @@ + + + + + + diff --git a/src/components/uploader/CertificateUploader.vue b/src/components/uploader/CertificateUploader.vue new file mode 100644 index 0000000..4ca9095 --- /dev/null +++ b/src/components/uploader/CertificateUploader.vue @@ -0,0 +1,37 @@ + + + + diff --git a/src/components/uploader/ImageManager.vue b/src/components/uploader/ImageManager.vue new file mode 100644 index 0000000..1ade362 --- /dev/null +++ b/src/components/uploader/ImageManager.vue @@ -0,0 +1,198 @@ + + + + + + diff --git a/src/composables/useDataTable.js b/src/composables/useDataTable.js new file mode 100644 index 0000000..f8cf68c --- /dev/null +++ b/src/composables/useDataTable.js @@ -0,0 +1,163 @@ +// /src/composables/useDataTable.js +import { ref, reactive } from 'vue'; + +export function useDataTable(fetchApiFn, defaultParams = {}) { + const items = ref([]); + const totalCount = ref(0); + const totalPages = ref(1); + const isLoading = ref(false); + + const queryParams = reactive({ + page: 1, + limit: 20, + sortBy: 'createdAt', + sortOrder: 'desc', + q: '', + ...defaultParams + }); + + let searchTimeout = null; + let requestSeq = 0; + + async function loadData() { + const seq = ++requestSeq; + isLoading.value = true; + try { + const cleanParams = {}; + Object.keys(queryParams).forEach((key) => { + if (queryParams[key] !== '' && queryParams[key] !== null && queryParams[key] !== undefined) { + cleanParams[key] = queryParams[key]; + } + }); + + const response = await fetchApiFn(cleanParams); + if (seq !== requestSeq) return; + + // listResponse shape: { success, data: [], meta } + const body = response && typeof response === 'object' ? response : {}; + const data = Array.isArray(body.data) ? body : (body.data || body); + + if (Array.isArray(data)) { + items.value = data; + totalCount.value = data.length; + totalPages.value = 1; + } else if (Array.isArray(data.data)) { + items.value = data.data; + totalCount.value = data.meta?.totalCount ?? data.data.length; + totalPages.value = data.meta?.totalPages ?? Math.ceil(totalCount.value / queryParams.limit) ?? 1; + } else if ( + data.items || + data.docs || + data.users || + data.courses || + data.sessions || + data.payments || + data.roles || + data.notifications || + data.certificates || + data.professors || + data.logs || + data.activityLogs + ) { + items.value = + data.items || + data.docs || + data.users || + data.courses || + data.sessions || + data.payments || + data.roles || + data.notifications || + data.certificates || + data.professors || + data.logs || + data.activityLogs || + []; + totalCount.value = data.meta?.totalCount ?? data.total ?? items.value.length; + totalPages.value = data.meta?.totalPages ?? Math.ceil(totalCount.value / queryParams.limit) ?? 1; + } else { + items.value = []; + totalCount.value = 0; + totalPages.value = 1; + } + } catch (error) { + if (seq !== requestSeq) return; + console.error('Error fetching datatable data:', error); + items.value = []; + totalCount.value = 0; + } finally { + if (seq === requestSeq) { + isLoading.value = false; + } + } + } + + function onPageChange(event = {}) { + // DataTableWrapper emits { page (1-based), limit } + // Raw PrimeVue Paginator emits { page (0-based), rows } + if (event.limit != null) { + queryParams.page = Number(event.page) || 1; + queryParams.limit = Number(event.limit) || queryParams.limit; + } else if (event.rows != null) { + queryParams.page = (Number(event.page) || 0) + 1; + queryParams.limit = Number(event.rows) || queryParams.limit; + } else if (event.page != null) { + queryParams.page = Number(event.page) || 1; + } + loadData(); + } + + function onSort(event = {}) { + // DataTableWrapper emits { sortBy, sortOrder: 'asc'|'desc' } + // Raw PrimeVue emits { sortField, sortOrder: 1|-1 } + const field = event.sortBy || event.sortField; + if (!field) return; + + queryParams.sortBy = field; + if (event.sortOrder === 'asc' || event.sortOrder === 'desc') { + queryParams.sortOrder = event.sortOrder; + } else { + queryParams.sortOrder = event.sortOrder === 1 ? 'asc' : 'desc'; + } + loadData(); + } + + function onSearch(value) { + queryParams.q = value; + queryParams.page = 1; + if (searchTimeout) clearTimeout(searchTimeout); + searchTimeout = setTimeout(() => { + loadData(); + }, 400); + } + + function setFilter(key, value) { + queryParams[key] = value; + queryParams.page = 1; + loadData(); + } + + function resetFilters() { + Object.keys(defaultParams).forEach((k) => { + queryParams[k] = defaultParams[k]; + }); + queryParams.page = 1; + queryParams.limit = defaultParams.limit ?? 20; + queryParams.q = ''; + loadData(); + } + + return { + items, + totalCount, + totalPages, + isLoading, + queryParams, + loadData, + onPageChange, + onSort, + onSearch, + setFilter, + resetFilters + }; +} diff --git a/src/composables/usePermission.js b/src/composables/usePermission.js new file mode 100644 index 0000000..0b5b5b4 --- /dev/null +++ b/src/composables/usePermission.js @@ -0,0 +1,20 @@ +// /src/composables/usePermission.js +import { usePermissionStore } from '@/stores/permissionStore'; + +export function usePermission() { + const permissionStore = usePermissionStore(); + + const hasPermission = (perm) => { + return permissionStore.hasPermission(perm); + }; + + const hasAnyPermission = (permsArray) => { + return permissionStore.hasAnyPermission(permsArray); + }; + + return { + hasPermission, + hasAnyPermission, + roleName: permissionStore.roleName + }; +} diff --git a/src/composables/usePersianDate.js b/src/composables/usePersianDate.js new file mode 100644 index 0000000..de1cc4a --- /dev/null +++ b/src/composables/usePersianDate.js @@ -0,0 +1,52 @@ +// /src/composables/usePersianDate.js +import moment from 'jalali-moment'; + +export function usePersianDate() { + const toPersianDigits = (num) => { + if (num === null || num === undefined) return ''; + const str = String(num); + const farsiDigits = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹']; + return str.replace(/\d/g, (x) => farsiDigits[parseInt(x)]); + }; + + const toLatinDigits = (str) => { + if (!str) return ''; + const farsiDigits = [/۰/g, /۱/g, /۲/g, /۳/g, /۴/g, /۵/g, /۶/g, /۷/g, /۸/g, /۹/g]; + let result = String(str); + for (let i = 0; i < 10; i++) { + result = result.replace(farsiDigits[i], String(i)); + } + return result; + }; + + const formatJalali = (date, formatStr = 'YYYY/MM/DD') => { + if (!date) return '-'; + try { + return moment(date).locale('fa').format(formatStr); + } catch (e) { + return '-'; + } + }; + + const formatJalaliWithTime = (date) => { + return formatJalali(date, 'YYYY/MM/DD - HH:mm'); + }; + + const jalaliToIso = (jalaliStr) => { + if (!jalaliStr) return null; + try { + const latinStr = toLatinDigits(jalaliStr); + return moment(latinStr, 'jYYYY/jMM/jDD').toISOString(); + } catch (e) { + return null; + } + }; + + return { + toPersianDigits, + toLatinDigits, + formatJalali, + formatJalaliWithTime, + jalaliToIso + }; +} diff --git a/src/composables/useToast.js b/src/composables/useToast.js new file mode 100644 index 0000000..f36c57e --- /dev/null +++ b/src/composables/useToast.js @@ -0,0 +1,31 @@ +// /src/composables/useToast.js +import { useToast as usePrimeToast } from 'primevue/usetoast'; +import { getApiErrorMessage } from '@/utils/apiError'; + +export function useToast() { + const toast = usePrimeToast(); + + const showSuccess = (detail, summary = 'موفقیت‌آمیز') => { + toast.add({ severity: 'success', summary, detail, life: 4000 }); + }; + + const showError = (detail, summary = 'خطا') => { + const message = getApiErrorMessage(detail); + toast.add({ severity: 'error', summary, detail: message, life: 6000 }); + }; + + const showInfo = (detail, summary = 'اطلاعیه') => { + toast.add({ severity: 'info', summary, detail, life: 4000 }); + }; + + const showWarn = (detail, summary = 'هشدار') => { + toast.add({ severity: 'warn', summary, detail, life: 4000 }); + }; + + return { + showSuccess, + showError, + showInfo, + showWarn + }; +} diff --git a/src/constants/permissions.js b/src/constants/permissions.js new file mode 100644 index 0000000..8028696 --- /dev/null +++ b/src/constants/permissions.js @@ -0,0 +1,217 @@ +// /src/constants/permissions.js +// Mirrors the backend constants/permissions.js + +export const PERMISSIONS = { + USERS_CREATE: 'users:create', + USERS_READ: 'users:read', + USERS_UPDATE: 'users:update', + USERS_DELETE: 'users:delete', + USERS_SEARCH: 'users:search', + USERS_ENROLL: 'users:enroll', + + PROFESSORS_CREATE: 'professors:create', + PROFESSORS_READ: 'professors:read', + PROFESSORS_UPDATE: 'professors:update', + PROFESSORS_DELETE: 'professors:delete', + PROFESSORS_SEARCH: 'professors:search', + + COURSES_CREATE: 'courses:create', + COURSES_READ: 'courses:read', + COURSES_UPDATE: 'courses:update', + COURSES_DELETE: 'courses:delete', + COURSES_SEARCH: 'courses:search', + + CLASSES_CREATE: 'classes:create', + CLASSES_READ: 'classes:read', + CLASSES_UPDATE: 'classes:update', + CLASSES_DELETE: 'classes:delete', + CLASSES_SEARCH: 'classes:search', + CLASSES_REGISTER_USERS: 'classes:register_users', + + SESSIONS_CREATE: 'sessions:create', + SESSIONS_READ: 'sessions:read', + SESSIONS_UPDATE: 'sessions:update', + SESSIONS_DELETE: 'sessions:delete', + SESSIONS_SEARCH: 'sessions:search', + SESSIONS_ATTENDANCE: 'sessions:attendance', + + PAYMENTS_CREATE: 'payments:create', + PAYMENTS_READ: 'payments:read', + PAYMENTS_UPDATE: 'payments:update', + PAYMENTS_DELETE: 'payments:delete', + PAYMENTS_SEARCH: 'payments:search', + + ROLES_CREATE: 'roles:create', + ROLES_READ: 'roles:read', + ROLES_UPDATE: 'roles:update', + ROLES_DELETE: 'roles:delete', + ROLES_SEARCH: 'roles:search', + + NOTIFICATIONS_CREATE: 'notifications:create', + NOTIFICATIONS_READ: 'notifications:read', + NOTIFICATIONS_UPDATE: 'notifications:update', + NOTIFICATIONS_DELETE: 'notifications:delete', + NOTIFICATIONS_SEARCH: 'notifications:search', + NOTIFICATIONS_RETRY: 'notifications:retry', + + CERTIFICATES_CREATE: 'certificates:create', + CERTIFICATES_READ: 'certificates:read', + CERTIFICATES_UPDATE: 'certificates:update', + CERTIFICATES_DELETE: 'certificates:delete', + CERTIFICATES_SEARCH: 'certificates:search', + + FILES_UPLOAD: 'files:upload', + FILES_READ: 'files:read', + FILES_DELETE: 'files:delete', + + LOGS_READ: 'logs:read', + LOGS_SEARCH: 'logs:search', + + CONTACT_INQUIRIES_READ: 'contact_inquiries:read', + CONTACT_INQUIRIES_UPDATE: 'contact_inquiries:update' +}; + +export const PERMISSION_GROUPS = [ + { + key: 'users', + label: 'مدیریت کاربران', + icon: 'pi pi-users', + permissions: [ + { key: PERMISSIONS.USERS_CREATE, label: 'ایجاد کاربر' }, + { key: PERMISSIONS.USERS_READ, label: 'مشاهده کاربران' }, + { key: PERMISSIONS.USERS_UPDATE, label: 'ویرایش کاربر' }, + { key: PERMISSIONS.USERS_DELETE, label: 'حذف کاربر' }, + { key: PERMISSIONS.USERS_SEARCH, label: 'جستجوی کاربران' }, + { key: PERMISSIONS.USERS_ENROLL, label: 'ثبت‌نام کاربر در دوره' } + ] + }, + { + key: 'professors', + label: 'مدیریت اساتید', + icon: 'pi pi-id-card', + permissions: [ + { key: PERMISSIONS.PROFESSORS_CREATE, label: 'ایجاد استاد' }, + { key: PERMISSIONS.PROFESSORS_READ, label: 'مشاهده اساتید' }, + { key: PERMISSIONS.PROFESSORS_UPDATE, label: 'ویرایش استاد' }, + { key: PERMISSIONS.PROFESSORS_DELETE, label: 'حذف استاد' }, + { key: PERMISSIONS.PROFESSORS_SEARCH, label: 'جستجوی اساتید' } + ] + }, + { + key: 'courses', + label: 'دوره‌های آموزشی', + icon: 'pi pi-book', + permissions: [ + { key: PERMISSIONS.COURSES_CREATE, label: 'ایجاد دوره' }, + { key: PERMISSIONS.COURSES_READ, label: 'مشاهده دوره‌ها' }, + { key: PERMISSIONS.COURSES_UPDATE, label: 'ویرایش دوره' }, + { key: PERMISSIONS.COURSES_DELETE, label: 'حذف دوره' }, + { key: PERMISSIONS.COURSES_SEARCH, label: 'جستجوی دوره‌ها' } + ] + }, + { + key: 'classes', + label: 'کلاس‌ها', + icon: 'pi pi-desktop', + permissions: [ + { key: PERMISSIONS.CLASSES_CREATE, label: 'ایجاد کلاس' }, + { key: PERMISSIONS.CLASSES_READ, label: 'مشاهده کلاس‌ها' }, + { key: PERMISSIONS.CLASSES_UPDATE, label: 'ویرایش کلاس' }, + { key: PERMISSIONS.CLASSES_DELETE, label: 'حذف کلاس' }, + { key: PERMISSIONS.CLASSES_SEARCH, label: 'جستجوی کلاس‌ها' }, + { key: PERMISSIONS.CLASSES_REGISTER_USERS, label: 'ثبت‌نام کاربران در کلاس' } + ] + }, + { + key: 'sessions', + label: 'جلسات آموزشی', + icon: 'pi pi-calendar', + permissions: [ + { key: PERMISSIONS.SESSIONS_CREATE, label: 'ایجاد جلسه' }, + { key: PERMISSIONS.SESSIONS_READ, label: 'مشاهده جلسات' }, + { key: PERMISSIONS.SESSIONS_UPDATE, label: 'ویرایش جلسه' }, + { key: PERMISSIONS.SESSIONS_DELETE, label: 'حذف جلسه' }, + { key: PERMISSIONS.SESSIONS_SEARCH, label: 'جستجوی جلسات' }, + { key: PERMISSIONS.SESSIONS_ATTENDANCE, label: 'ثبت حضور و غیاب' } + ] + }, + { + key: 'payments', + label: 'امور مالی و پرداخت‌ها', + icon: 'pi pi-wallet', + permissions: [ + { key: PERMISSIONS.PAYMENTS_CREATE, label: 'ایجاد صورتحساب' }, + { key: PERMISSIONS.PAYMENTS_READ, label: 'مشاهده پرداخت‌ها' }, + { key: PERMISSIONS.PAYMENTS_UPDATE, label: 'ویرایش / ثبت تراکنش' }, + { key: PERMISSIONS.PAYMENTS_DELETE, label: 'حذف صورتحساب' }, + { key: PERMISSIONS.PAYMENTS_SEARCH, label: 'جستجوی پرداخت‌ها' } + ] + }, + { + key: 'roles', + label: 'نقش‌ها و دسترسی‌ها', + icon: 'pi pi-shield', + permissions: [ + { key: PERMISSIONS.ROLES_CREATE, label: 'ایجاد نقش' }, + { key: PERMISSIONS.ROLES_READ, label: 'مشاهده نقش‌ها' }, + { key: PERMISSIONS.ROLES_UPDATE, label: 'ویرایش نقش' }, + { key: PERMISSIONS.ROLES_DELETE, label: 'حذف نقش' }, + { key: PERMISSIONS.ROLES_SEARCH, label: 'جستجوی نقش‌ها' } + ] + }, + { + key: 'notifications', + label: 'اطلاعیه‌ها و پیام‌رسانی', + icon: 'pi pi-bell', + permissions: [ + { key: PERMISSIONS.NOTIFICATIONS_CREATE, label: 'ارسال اطلاعیه' }, + { key: PERMISSIONS.NOTIFICATIONS_READ, label: 'مشاهده اطلاعیه‌ها' }, + { key: PERMISSIONS.NOTIFICATIONS_UPDATE, label: 'ویرایش اطلاعیه' }, + { key: PERMISSIONS.NOTIFICATIONS_DELETE, label: 'حذف اطلاعیه' }, + { key: PERMISSIONS.NOTIFICATIONS_SEARCH, label: 'جستجوی اطلاعیه‌ها' }, + { key: PERMISSIONS.NOTIFICATIONS_RETRY, label: 'ارسال مجدد اطلاعیه' } + ] + }, + { + key: 'certificates', + label: 'گواهینامه‌ها', + icon: 'pi pi-verified', + permissions: [ + { key: PERMISSIONS.CERTIFICATES_CREATE, label: 'صدور گواهینامه' }, + { key: PERMISSIONS.CERTIFICATES_READ, label: 'مشاهده گواهینامه‌ها' }, + { key: PERMISSIONS.CERTIFICATES_UPDATE, label: 'ویرایش گواهینامه' }, + { key: PERMISSIONS.CERTIFICATES_DELETE, label: 'حذف گواهینامه' }, + { key: PERMISSIONS.CERTIFICATES_SEARCH, label: 'جستجوی گواهینامه‌ها' } + ] + }, + { + key: 'files', + label: 'مدیریت فایل‌ها', + icon: 'pi pi-folder', + permissions: [ + { key: PERMISSIONS.FILES_UPLOAD, label: 'آپلود فایل' }, + { key: PERMISSIONS.FILES_READ, label: 'مشاهده فایل‌ها' }, + { key: PERMISSIONS.FILES_DELETE, label: 'حذف فایل' } + ] + }, + { + key: 'logs', + label: 'گزارش فعالیت‌ها', + icon: 'pi pi-history', + permissions: [ + { key: PERMISSIONS.LOGS_READ, label: 'مشاهده لاگ‌ها' }, + { key: PERMISSIONS.LOGS_SEARCH, label: 'جستجوی لاگ‌ها' } + ] + }, + { + key: 'contact_inquiries', + label: 'درخواست‌های تماس', + icon: 'pi pi-comments', + permissions: [ + { key: PERMISSIONS.CONTACT_INQUIRIES_READ, label: 'مشاهده درخواست‌ها' }, + { key: PERMISSIONS.CONTACT_INQUIRIES_UPDATE, label: 'ویرایش / پیگیری درخواست‌ها' } + ] + } +]; + +export const ALL_PERMISSIONS = Object.values(PERMISSIONS); diff --git a/src/layouts/AuthLayout.vue b/src/layouts/AuthLayout.vue new file mode 100644 index 0000000..e3fef87 --- /dev/null +++ b/src/layouts/AuthLayout.vue @@ -0,0 +1,26 @@ + + + + + + diff --git a/src/layouts/DashboardLayout.vue b/src/layouts/DashboardLayout.vue new file mode 100644 index 0000000..92483fe --- /dev/null +++ b/src/layouts/DashboardLayout.vue @@ -0,0 +1,29 @@ + + + + + + diff --git a/src/locales/fa.json b/src/locales/fa.json new file mode 100644 index 0000000..d13eb3c --- /dev/null +++ b/src/locales/fa.json @@ -0,0 +1,139 @@ +{ + "app": { + "title": "داشبورد مدیریتی موسسه آموزشی", + "dashboard": "داشبورد", + "search": "جستجو...", + "save": "ذخیره", + "cancel": "انصراف", + "delete": "حذف", + "edit": "ویرایش", + "actions": "عملیات", + "status": "وضعیت", + "created": "تاریخ ایجاد", + "updated": "تاریخ بروزرسانی", + "loading": "در حال بارگذاری...", + "noData": "اطلاعاتی یافت نشد", + "profile": "پروفایل", + "logout": "خروج" + }, + "auth": { + "loginTitle": "ورود به پنل مدیریتی", + "welcome": "خوش آمدید! اطلاعات حساب خود را وارد کنید", + "login": "ورود به سیستم", + "username": "نام کاربری", + "password": "رمز عبور", + "loginButton": "ورود به داشبورد", + "submit": "ورود", + "logout": "خروج", + "loginSuccess": "ورود با موفقیت انجام شد", + "loginError": "نام کاربری یا رمز عبور اشتباه است", + "loginFailed": "نام کاربری یا رمز عبور اشتباه است" + }, + "navigation": { + "dashboard": "داشبورد اصلی", + "users": "مدیریت کاربران", + "professors": "مدیریت اساتید", + "courses": "دوره های آموزشی", + "classes": "کلاس ها", + "sessions": "جلسات آموزشی", + "payments": "امور مالی و پرداخت ها", + "roles": "نقش ها و دسترسی ها", + "notifications": "اطلاعیه ها", + "contactInquiries": "درخواست‌های تماس", + "certificates": "گواهی نامه ها", + "logs": "گزارش فعالیت‌ها" + }, + "users": { + "title": "مدیریت کاربران", + "subtitle": "لیست و مدیریت تمام کاربران موسسه", + "addUser": "افزودن کاربر جدید", + "editUser": "ویرایش کاربر", + "name": "نام", + "surname": "نام خانوادگی", + "nationalId": "کد ملی", + "phone": "شماره همراه", + "email": "ایمیل", + "preferredMessenger": "پیام‌رسان ترجیحی", + "role": "نقش کاربر", + "status": "وضعیت حساب", + "tabInfo": "اطلاعات شخصی", + "tabCourses": "دوره‌های ثبت‌نامی", + "tabSessions": "حضور و غیاب", + "tabPayments": "پرداخت‌ها", + "tabCertificates": "گواهینامه‌ها" + }, + "professors": { + "title": "مدیریت اساتید", + "subtitle": "لیست اساتید و مربیان موسسه", + "addProfessor": "افزودن استاد جدید", + "editProfessor": "ویرایش اطلاعات استاد", + "bio": "بیوگرافی / رزومه", + "expertise": "تخصص ها" + }, + "courses": { + "title": "دوره‌های آموزشی", + "subtitle": "تعریف و مدیریت دوره‌های موسسه", + "addCourse": "افزودن دوره جدید", + "editCourse": "ویرایش دوره", + "courseTitle": "عنوان دوره", + "type": "نوع دوره", + "typeGeneral": "عمومی", + "typePrivate": "خصوصی", + "official": "دارای مدرک رسمی", + "unofficial": "بدون مدرک رسمی", + "price": "شهریه (تومان)", + "capacity": "ظرفیت", + "isOfficial": "دوره رسمی", + "description": "توضیحات دوره", + "professor": "استاد مدرس", + "sessionsTabTitle": "جلسات دوره", + "autoTab": "ایجاد خودکار جلسات", + "manualTab": "تعریف دستی جلسات", + "classesSectionTitle": "کلاس‌های این دوره", + "addClass": "تعریف کلاس جدید", + "sessionCount": "تعداد جلسات", + "startDate": "تاریخ شروع", + "startTime": "ساعت شروع", + "endTime": "ساعت پایان", + "daysOfWeek": "روزهای هفته" + }, + "sessions": { + "title": "جلسات آموزشی", + "subtitle": "برنامه‌ریزی و مدیریت جلسات", + "addSession": "افزودن جلسه جدید", + "editSession": "ویرایش جلسه", + "date": "تاریخ جلسه", + "topic": "موضوع جلسه", + "attendance": "حضور و غیاب", + "enterAttendance": "ثبت حضور و غیاب" + }, + "attendances": { + "title": "حضور و غیاب جلسه", + "submitAttendance": "ثبت حضور و غیاب" + }, + "roles": { + "title": "نقش‌ها و دسترسی‌ها", + "subtitle": "مدیریت نقش‌های سیستمی و مجوزها", + "addRole": "افزودن نقش جدید", + "editRole": "ویرایش نقش", + "roleName": "نام نقش", + "systemRoleWarning": "نقش‌های سیستمی قابل حذف یا تغییر نام نیستند", + "permissionsTitle": "مجوزهای دسترسی", + "selectAll": "انتخاب همه" + }, + "notifications": { + "title": "مدیریت اطلاعیه‌ها", + "subtitle": "ارسال پیامک و ایمیل گروهی", + "addNotification": "ارسال اطلاعیه جدید" + }, + "logs": { + "title": "گزارش فعالیت کاربران", + "subtitle": "مشاهده و فیلتر تمام عملیات ایجاد، ویرایش، حذف و ورود کاربران" + }, + "payments": { + "title": "امور مالی و پرداخت‌ها", + "subtitle": "مدیریت تراکنش‌ها و شهریه‌ها", + "addPayment": "ثبت پرداخت جدید", + "paymentDetail": "جزئیات صورتحساب" + } +} diff --git a/src/locales/i18n.js b/src/locales/i18n.js new file mode 100644 index 0000000..62b7bec --- /dev/null +++ b/src/locales/i18n.js @@ -0,0 +1,14 @@ +// /src/locales/i18n.js +import { createI18n } from 'vue-i18n'; +import fa from './fa.json'; + +export default function setupI18n() { + return createI18n({ + legacy: false, + locale: 'fa', + fallbackLocale: 'fa', + messages: { + fa + } + }); +} diff --git a/src/main.js b/src/main.js new file mode 100644 index 0000000..5f2eb0e --- /dev/null +++ b/src/main.js @@ -0,0 +1,48 @@ +// /src/main.js +import { createApp } from 'vue'; +import { createPinia } from 'pinia'; +import createI18n from '@/locales/i18n'; +import router from '@/router'; +import App from '@/App.vue'; + +// PrimeVue Core & Theme Preset +import PrimeVue from 'primevue/config'; +import Aura from '@primevue/themes/aura'; +import ToastService from 'primevue/toastservice'; +import ConfirmationService from 'primevue/confirmationservice'; +import Tooltip from 'primevue/tooltip'; + +// PrimeIcons & PrimeFlex Layout +import 'primeicons/primeicons.css'; +import 'primeflex/primeflex.css'; + +// RTL & Main Custom Styling +import '@/assets/styles/main.scss'; + +const app = createApp(App); + +const pinia = createPinia(); +app.use(pinia); + +const i18n = createI18n(); +app.use(i18n); + +app.use(router); + +app.use(PrimeVue, { + theme: { + preset: Aura, + options: { + darkModeSelector: '.dark-mode', + cssLayer: false + } + }, + ripple: true +}); + +app.use(ToastService); +app.use(ConfirmationService); + +app.directive('tooltip', Tooltip); + +app.mount('#app'); diff --git a/src/router/guards.js b/src/router/guards.js new file mode 100644 index 0000000..5874050 --- /dev/null +++ b/src/router/guards.js @@ -0,0 +1,56 @@ +// /src/router/guards.js +import { useAuthStore } from '@/stores/authStore'; +import { usePermissionStore } from '@/stores/permissionStore'; + +function isPublicRoute(to) { + if (to.meta.public === true) return true; + if (to.meta.requiresAuth === false) return true; + return to.matched.some( + (record) => record.meta.public === true || record.meta.requiresAuth === false + ); +} + +export function setupGuards(router) { + router.beforeEach(async (to, from, next) => { + const authStore = useAuthStore(); + const permissionStore = usePermissionStore(); + + // 1. Public / unauthenticated routes (e.g. Login) + if (isPublicRoute(to)) { + if (to.name === 'Login' && authStore.isAuthenticated) { + return next({ name: 'DashboardHome' }); + } + return next(); + } + + // 2. Auth check + if (!authStore.isAuthenticated) { + if (to.name === 'Login') { + return next(); + } + return next({ name: 'Login', query: { redirect: to.fullPath } }); + } + + // 3. Rehydrate permission store if token exists but permissions store is unpopulated + if (!permissionStore.isLoaded || permissionStore.permissions.length === 0) { + try { + await permissionStore.fetchPermissions(); + } catch (err) { + console.error('Failed to rehydrate permissions, logging out:', err); + await authStore.logout(); + return next({ name: 'Login' }); + } + } + + // 4. Permission check against stored permissions list + const requiredPermission = to.meta.permission; + if (requiredPermission) { + const hasAccess = permissionStore.hasPermission(requiredPermission); + if (!hasAccess) { + return next({ name: 'Forbidden' }); + } + } + + next(); + }); +} diff --git a/src/router/index.js b/src/router/index.js new file mode 100644 index 0000000..318a1b1 --- /dev/null +++ b/src/router/index.js @@ -0,0 +1,13 @@ +// /src/router/index.js +import { createRouter, createWebHistory } from 'vue-router'; +import { routes } from './routes'; +import { setupGuards } from './guards'; + +const router = createRouter({ + history: createWebHistory(import.meta.env.BASE_URL), + routes +}); + +setupGuards(router); + +export default router; diff --git a/src/router/routes.js b/src/router/routes.js new file mode 100644 index 0000000..1804949 --- /dev/null +++ b/src/router/routes.js @@ -0,0 +1,197 @@ +// /src/router/routes.js +import DashboardLayout from '@/layouts/DashboardLayout.vue'; +import AuthLayout from '@/layouts/AuthLayout.vue'; + +export const routes = [ + { + path: '/login', + component: AuthLayout, + children: [ + { + path: '', + name: 'Login', + component: () => import('@/views/auth/LoginView.vue'), + meta: { requiresAuth: false } + } + ] + }, + { + path: '/', + component: DashboardLayout, + meta: { requiresAuth: true }, + children: [ + { + path: '', + name: 'DashboardHome', + component: () => import('@/views/dashboard/DashboardHomeView.vue') + }, + + // Users + { + path: 'users', + name: 'UserList', + component: () => import('@/views/users/UserListView.vue') + }, + { + path: 'users/create', + name: 'UserCreate', + component: () => import('@/views/users/UserFormView.vue') + }, + { + path: 'users/edit/:id', + name: 'UserEdit', + component: () => import('@/views/users/UserFormView.vue') + }, + + // Professors + { + path: 'professors', + name: 'ProfessorList', + component: () => import('@/views/professors/ProfessorListView.vue') + }, + { + path: 'professors/create', + name: 'ProfessorCreate', + component: () => import('@/views/professors/ProfessorFormView.vue') + }, + { + path: 'professors/edit/:id', + name: 'ProfessorEdit', + component: () => import('@/views/professors/ProfessorFormView.vue') + }, + + // Courses + { + path: 'courses', + name: 'CourseList', + component: () => import('@/views/courses/CourseListView.vue') + }, + { + path: 'courses/create', + name: 'CourseCreate', + component: () => import('@/views/courses/CourseFormView.vue') + }, + { + path: 'courses/edit/:id', + name: 'CourseEdit', + component: () => import('@/views/courses/CourseFormView.vue') + }, + { + path: 'courses/view/:id', + name: 'CourseDetail', + component: () => import('@/views/courses/CourseDetailView.vue') + }, + + // Classes + { + path: 'classes', + name: 'ClassList', + component: () => import('@/views/classes/ClassListView.vue') + }, + { + path: 'classes/create', + name: 'ClassCreate', + component: () => import('@/views/classes/ClassFormView.vue') + }, + { + path: 'classes/edit/:id', + name: 'ClassEdit', + component: () => import('@/views/classes/ClassFormView.vue') + }, + { + path: 'classes/view/:id', + name: 'ClassDetail', + component: () => import('@/views/classes/ClassDetailView.vue') + }, + + // Sessions + { + path: 'sessions', + name: 'SessionList', + component: () => import('@/views/sessions/SessionListView.vue') + }, + { + path: 'sessions/create', + name: 'SessionCreate', + component: () => import('@/views/sessions/SessionFormView.vue') + }, + { + path: 'sessions/edit/:id', + name: 'SessionEdit', + component: () => import('@/views/sessions/SessionFormView.vue') + }, + { + path: 'sessions/attendance/:id', + name: 'AttendanceEntry', + component: () => import('@/views/attendances/AttendanceEntryView.vue') + }, + + // Payments + { + path: 'payments', + name: 'PaymentList', + component: () => import('@/views/payments/PaymentListView.vue') + }, + { + path: 'payments/view/:id', + name: 'PaymentDetail', + component: () => import('@/views/payments/PaymentDetailView.vue') + }, + + // Roles + { + path: 'roles', + name: 'RoleList', + component: () => import('@/views/roles/RoleListView.vue') + }, + { + path: 'roles/create', + name: 'RoleCreate', + component: () => import('@/views/roles/RoleFormView.vue') + }, + { + path: 'roles/edit/:id', + name: 'RoleEdit', + component: () => import('@/views/roles/RoleFormView.vue') + }, + + // Notifications + { + path: 'notifications', + name: 'NotificationList', + component: () => import('@/views/notifications/NotificationListView.vue') + }, + + // Contact inquiries + { + path: 'contact-inquiries', + name: 'ContactInquiryList', + component: () => import('@/views/contactInquiries/ContactInquiryListView.vue') + }, + { + path: 'contact-inquiries/view/:id', + name: 'ContactInquiryDetail', + component: () => import('@/views/contactInquiries/ContactInquiryDetailView.vue') + }, + + { + path: 'logs', + name: 'LogList', + component: () => import('@/views/logs/LogListView.vue'), + meta: { permission: 'logs:read' } + }, + + // Errors + { + path: '403', + name: 'Forbidden', + component: () => import('@/views/errors/ForbiddenView.vue') + } + ] + }, + { + path: '/:pathMatch(.*)*', + name: 'NotFound', + component: () => import('@/views/errors/NotFoundView.vue') + } +]; diff --git a/src/stores/authStore.js b/src/stores/authStore.js new file mode 100644 index 0000000..71f30b5 --- /dev/null +++ b/src/stores/authStore.js @@ -0,0 +1,84 @@ +// /src/stores/authStore.js +import { defineStore } from 'pinia'; +import { ref, computed } from 'vue'; +import { authApi } from '@/api/authApi'; +import Cookies from 'js-cookie'; +import { usePermissionStore } from './permissionStore'; + +export const useAuthStore = defineStore('auth', () => { + const accessToken = ref(localStorage.getItem('accessToken') || Cookies.get('accessToken') || ''); + const refreshToken = ref(localStorage.getItem('refreshToken') || Cookies.get('refreshToken') || ''); + const user = ref(JSON.parse(localStorage.getItem('user') || 'null')); + + const isAuthenticated = computed(() => !!accessToken.value); + + function setTokens(access, refresh) { + accessToken.value = access; + refreshToken.value = refresh; + if (access) { + localStorage.setItem('accessToken', access); + Cookies.set('accessToken', access); + } else { + localStorage.removeItem('accessToken'); + Cookies.remove('accessToken'); + } + if (refresh) { + localStorage.setItem('refreshToken', refresh); + Cookies.set('refreshToken', refresh); + } else { + localStorage.removeItem('refreshToken'); + Cookies.remove('refreshToken'); + } + } + + function setUser(userData) { + user.value = userData; + if (userData) { + localStorage.setItem('user', JSON.stringify(userData)); + } else { + localStorage.removeItem('user'); + } + } + + async function login(credentials) { + const response = await authApi.login(credentials); + const data = response.data || response; + + // Server response: accessToken, refreshToken, user, permissions / role + setTokens(data.accessToken, data.refreshToken); + setUser(data.user); + + const permissionStore = usePermissionStore(); + const perms = data.user?.role?.permissions || data.permissions || []; + const roleName = data.user?.role?.name || data.role || ''; + permissionStore.setPermissions(perms, roleName); + + return data; + } + + async function logout() { + try { + if (refreshToken.value) { + await authApi.logout(refreshToken.value); + } + } catch (e) { + console.warn('Logout API error:', e); + } finally { + setTokens('', ''); + setUser(null); + const permissionStore = usePermissionStore(); + permissionStore.clearPermissions(); + } + } + + return { + accessToken, + refreshToken, + user, + isAuthenticated, + setTokens, + setUser, + login, + logout + }; +}); diff --git a/src/stores/permissionStore.js b/src/stores/permissionStore.js new file mode 100644 index 0000000..2efe1ca --- /dev/null +++ b/src/stores/permissionStore.js @@ -0,0 +1,63 @@ +// /src/stores/permissionStore.js +import { defineStore } from 'pinia'; +import { ref } from 'vue'; +import { authApi } from '@/api/authApi'; + +export const usePermissionStore = defineStore('permission', () => { + const permissions = ref(JSON.parse(localStorage.getItem('permissions') || '[]')); + const roleName = ref(localStorage.getItem('roleName') || ''); + const isLoaded = ref(permissions.value.length > 0); + + function setPermissions(permsArray = [], role = '') { + permissions.value = Array.isArray(permsArray) ? permsArray : []; + roleName.value = role; + isLoaded.value = true; + localStorage.setItem('permissions', JSON.stringify(permissions.value)); + localStorage.setItem('roleName', roleName.value); + } + + function clearPermissions() { + permissions.value = []; + roleName.value = ''; + isLoaded.value = false; + localStorage.removeItem('permissions'); + localStorage.removeItem('roleName'); + } + + async function fetchPermissions() { + try { + const response = await authApi.getSelf(); + const userData = response.data || response; + const perms = userData.role?.permissions || []; + const role = userData.role?.name || ''; + setPermissions(perms, role); + return permissions.value; + } catch (err) { + console.error('Failed to fetch user permissions:', err); + clearPermissions(); + throw err; + } + } + + function hasPermission(perm) { + if (!perm) return true; + if (permissions.value.includes('*') || permissions.value.includes('admin:all')) return true; + return permissions.value.includes(perm); + } + + function hasAnyPermission(permsArray) { + if (!Array.isArray(permsArray) || permsArray.length === 0) return true; + return permsArray.some(p => hasPermission(p)); + } + + return { + permissions, + roleName, + isLoaded, + setPermissions, + clearPermissions, + fetchPermissions, + hasPermission, + hasAnyPermission + }; +}); diff --git a/src/stores/themeStore.js b/src/stores/themeStore.js new file mode 100644 index 0000000..1bef0ba --- /dev/null +++ b/src/stores/themeStore.js @@ -0,0 +1,35 @@ +// /src/stores/themeStore.js +import { defineStore } from 'pinia'; +import { ref } from 'vue'; + +export const useThemeStore = defineStore('theme', () => { + const savedTheme = localStorage.getItem('theme'); + const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; + const isDarkMode = ref(savedTheme ? savedTheme === 'dark' : prefersDark); + + function applyTheme() { + const htmlEl = document.documentElement; + if (isDarkMode.value) { + htmlEl.classList.add('dark-mode', 'dark'); + localStorage.setItem('theme', 'dark'); + } else { + htmlEl.classList.remove('dark-mode', 'dark'); + localStorage.setItem('theme', 'light'); + } + } + + function toggleTheme() { + isDarkMode.value = !isDarkMode.value; + applyTheme(); + } + + function initTheme() { + applyTheme(); + } + + return { + isDarkMode, + toggleTheme, + initTheme + }; +}); diff --git a/src/stores/uiStore.js b/src/stores/uiStore.js new file mode 100644 index 0000000..c950c09 --- /dev/null +++ b/src/stores/uiStore.js @@ -0,0 +1,30 @@ +// /src/stores/uiStore.js +import { defineStore } from 'pinia'; +import { ref } from 'vue'; + +export const useUiStore = defineStore('ui', () => { + const isSidebarOpen = ref(true); + const isMobileSidebarOpen = ref(false); + const breadcrumbs = ref([]); + + function toggleSidebar() { + isSidebarOpen.value = !isSidebarOpen.value; + } + + function toggleMobileSidebar() { + isMobileSidebarOpen.value = !isMobileSidebarOpen.value; + } + + function setBreadcrumbs(items) { + breadcrumbs.value = items; + } + + return { + isSidebarOpen, + isMobileSidebarOpen, + breadcrumbs, + toggleSidebar, + toggleMobileSidebar, + setBreadcrumbs + }; +}); diff --git a/src/style.css b/src/style.css new file mode 100644 index 0000000..2c9ad66 --- /dev/null +++ b/src/style.css @@ -0,0 +1,2 @@ +/* /src/style.css */ +@import './assets/styles/main.scss'; diff --git a/src/utils/apiError.js b/src/utils/apiError.js new file mode 100644 index 0000000..4a35f57 --- /dev/null +++ b/src/utils/apiError.js @@ -0,0 +1,52 @@ +// /src/utils/apiError.js + +/** + * Extracts the user-facing error message from an Axios/API error, + * preferring the localized message returned by the backend. + */ +export function getApiErrorMessage(error, fallback = 'خطایی رخ داده است') { + if (!error) return fallback; + + if (typeof error === 'string') { + return error.trim() || fallback; + } + + const payload = error?.response?.data; + const apiError = payload?.error; + + if (apiError && typeof apiError === 'object') { + if (typeof apiError.message === 'string' && apiError.message.trim()) { + return apiError.message.trim(); + } + } + + if (typeof payload?.message === 'string' && payload.message.trim()) { + return payload.message.trim(); + } + + // Network / timeout / cancel — Axios message is useful here + if (!error.response && typeof error.message === 'string' && error.message.trim()) { + if (error.code === 'ECONNABORTED') { + return 'زمان پاسخ‌گویی سرور به پایان رسید.'; + } + if (error.message === 'Network Error') { + return 'ارتباط با سرور برقرار نشد.'; + } + return error.message; + } + + // Avoid showing generic "Request failed with status code XXX" + if ( + typeof error.message === 'string' && + error.message.trim() && + !/^Request failed with status code \d+/i.test(error.message) + ) { + return error.message.trim(); + } + + return fallback; +} + +export function getApiErrorCode(error) { + return error?.response?.data?.error?.code || null; +} diff --git a/src/views/attendances/AttendanceEntryView.vue b/src/views/attendances/AttendanceEntryView.vue new file mode 100644 index 0000000..eff0085 --- /dev/null +++ b/src/views/attendances/AttendanceEntryView.vue @@ -0,0 +1,158 @@ + + + + diff --git a/src/views/auth/LoginView.vue b/src/views/auth/LoginView.vue new file mode 100644 index 0000000..ba960c0 --- /dev/null +++ b/src/views/auth/LoginView.vue @@ -0,0 +1,158 @@ + + + + + + diff --git a/src/views/classes/ClassDetailView.vue b/src/views/classes/ClassDetailView.vue new file mode 100644 index 0000000..c23bff6 --- /dev/null +++ b/src/views/classes/ClassDetailView.vue @@ -0,0 +1,162 @@ + + + + diff --git a/src/views/classes/ClassFormView.vue b/src/views/classes/ClassFormView.vue new file mode 100644 index 0000000..a5f4230 --- /dev/null +++ b/src/views/classes/ClassFormView.vue @@ -0,0 +1,316 @@ + + + + diff --git a/src/views/classes/ClassListView.vue b/src/views/classes/ClassListView.vue new file mode 100644 index 0000000..26b5b55 --- /dev/null +++ b/src/views/classes/ClassListView.vue @@ -0,0 +1,93 @@ + + + + diff --git a/src/views/contactInquiries/ContactInquiryDetailView.vue b/src/views/contactInquiries/ContactInquiryDetailView.vue new file mode 100644 index 0000000..4f19691 --- /dev/null +++ b/src/views/contactInquiries/ContactInquiryDetailView.vue @@ -0,0 +1,173 @@ + +