From 8fefb4568b51b4def88090a6f8c73a5ad9e4690d Mon Sep 17 00:00:00 2001 From: sajjadtalkhabi <97734061+sajjadtalkhabi@users.noreply.github.com> Date: Thu, 30 Jul 2026 18:53:56 +0330 Subject: [PATCH] fix: bugs --- package-lock.json | 241 ++++++++++++++++++ package.json | 1 + .../dashboard/DashboardStatCard.vue | 28 +- .../admin/pages/AdminDashboardPage.vue | 68 +++-- .../modals/UserVerificationDetailsModal.vue | 144 ++++++++++- .../pages/CounselorDashboardPage.vue | 4 + .../pages/MissionaryDashboardPage.vue | 4 + .../student/pages/StudentDashboardPage.vue | 4 + .../pages/components/DashboardStatCard.vue | 28 +- .../services/components/CreateServiceForm.vue | 15 +- src/layouts/StudentLayout.vue | 6 - src/services/mock/fixtures/admin-dashboard.js | 14 +- src/services/mock/routes/admin-dashboard.js | 6 +- .../mock/routes/counselor-dashboard.js | 6 +- .../mock/routes/missionary-dashboard.js | 6 +- src/services/mock/routes/student-dashboard.js | 6 +- 16 files changed, 529 insertions(+), 52 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7e0d180..6b1fd67 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@tanstack/vue-query-devtools": "^5.62.2", "@tinymce/tinymce-vue": "^4.0.7", "axios": "^1.12.2", + "html2pdf.js": "^0.14.0", "husky": "^8.0.0", "jalaali-js": "^1.2.8", "lodash": "^4.18.1", @@ -102,6 +103,15 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/types": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", @@ -1722,6 +1732,26 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/pako": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.4.tgz", + "integrity": "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==", + "license": "MIT" + }, + "node_modules/@types/raf": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz", + "integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==", + "license": "MIT", + "optional": true + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true + }, "node_modules/@ungap/structured-clone": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz", @@ -2168,6 +2198,15 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, + "node_modules/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/baseline-browser-mapping": { "version": "2.10.29", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.29.tgz", @@ -2371,6 +2410,26 @@ ], "license": "CC-BY-4.0" }, + "node_modules/canvg": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.11.tgz", + "integrity": "sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "@types/raf": "^3.4.0", + "core-js": "^3.8.3", + "raf": "^3.4.1", + "regenerator-runtime": "^0.13.7", + "rgbcolor": "^1.0.1", + "stackblur-canvas": "^2.0.0", + "svg-pathdata": "^6.0.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -2656,6 +2715,18 @@ "url": "https://github.com/sponsors/mesqueeb" } }, + "node_modules/core-js": { + "version": "3.49.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.49.0.tgz", + "integrity": "sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/core-js-compat": { "version": "3.49.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.49.0.tgz", @@ -2722,6 +2793,15 @@ "node": ">=12" } }, + "node_modules/css-line-break": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", + "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", + "license": "MIT", + "dependencies": { + "utrie": "^1.0.2" + } + }, "node_modules/css-select": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", @@ -3032,6 +3112,15 @@ "url": "https://github.com/fb55/domhandler?sponsor=1" } }, + "node_modules/dompurify": { + "version": "3.4.12", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.12.tgz", + "integrity": "sha512-zQvGet8Z2sWbQhCmfFz/T5QWH2oBmjnqK3qvOjaqaNLrLEF912WamU+ohnTp0TCep/MFVHpdJuCZEdFOdTnEFg==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/domutils": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", @@ -3853,6 +3942,17 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-png": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/fast-png/-/fast-png-6.4.0.tgz", + "integrity": "sha512-kAqZq1TlgBjZcLr5mcN6NP5Rv4V2f22z00c3g8vRrwkcqjerx7BEhPbOnWCPqaHUl2XWQBJQvOT/FQhdMT7X/Q==", + "license": "MIT", + "dependencies": { + "@types/pako": "^2.0.3", + "iobuffer": "^5.3.2", + "pako": "^2.1.0" + } + }, "node_modules/fast-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", @@ -3908,6 +4008,12 @@ } } }, + "node_modules/fflate": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz", + "integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==", + "license": "MIT" + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -4469,6 +4575,30 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/html2canvas": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", + "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", + "license": "MIT", + "dependencies": { + "css-line-break": "^2.1.0", + "text-segmentation": "^1.0.3" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/html2pdf.js": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/html2pdf.js/-/html2pdf.js-0.14.0.tgz", + "integrity": "sha512-yvNJgE/8yru2UeGflkPdjW8YEY+nDH5X7/2WG4uiuSCwYiCp8PZ8EKNiTAa6HxJ1NjC51fZSIEq6xld5CADKBQ==", + "license": "MIT", + "dependencies": { + "dompurify": "^3.3.1", + "html2canvas": "^1.0.0", + "jspdf": "^4.0.0" + } + }, "node_modules/htmlparser2": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", @@ -4607,6 +4737,12 @@ "node": ">= 0.4" } }, + "node_modules/iobuffer": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/iobuffer/-/iobuffer-5.4.0.tgz", + "integrity": "sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA==", + "license": "MIT" + }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -5171,6 +5307,23 @@ "json5": "lib/cli.js" } }, + "node_modules/jspdf": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-4.2.1.tgz", + "integrity": "sha512-YyAXyvnmjTbR4bHQRLzex3CuINCDlQnBqoSYyjJwTP2x9jDLuKDzy7aKUl0hgx3uhcl7xzg32agn5vlie6HIlQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.6", + "fast-png": "^6.2.0", + "fflate": "^0.8.1" + }, + "optionalDependencies": { + "canvg": "^3.0.11", + "core-js": "^3.6.0", + "dompurify": "^3.3.1", + "html2canvas": "^1.0.0-rc.5" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -6022,6 +6175,22 @@ "node": ">=6" } }, + "node_modules/pako": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.2.0.tgz", + "integrity": "sha512-zJq6RP/5q+TO2OpFV3FHzlPnFjmkb7Nc99a5SNjJE+uu/PkpChs+NIZSSzbBoD+6kjiISXjfYdwj1ZRQ81dz/w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "(MIT AND Zlib)" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -6106,6 +6275,13 @@ "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", "license": "MIT" }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "license": "MIT", + "optional": true + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -6416,6 +6592,16 @@ ], "license": "MIT" }, + "node_modules/raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "license": "MIT", + "optional": true, + "dependencies": { + "performance-now": "^2.1.0" + } + }, "node_modules/read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -6563,6 +6749,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "license": "MIT", + "optional": true + }, "node_modules/regexp-tree": { "version": "0.1.27", "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", @@ -6716,6 +6909,16 @@ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", "license": "MIT" }, + "node_modules/rgbcolor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz", + "integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==", + "license": "MIT OR SEE LICENSE IN FEEL-FREE.md", + "optional": true, + "engines": { + "node": ">= 0.8.15" + } + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -7140,6 +7343,16 @@ "node": ">=0.10.0" } }, + "node_modules/stackblur-canvas": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz", + "integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.14" + } + }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", @@ -7685,6 +7898,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/svg-pathdata": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz", + "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/svg-tags": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", @@ -7779,6 +8002,15 @@ "dev": true, "license": "MIT" }, + "node_modules/text-segmentation": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", + "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", + "license": "MIT", + "dependencies": { + "utrie": "^1.0.2" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -8018,6 +8250,15 @@ "dev": true, "license": "MIT" }, + "node_modules/utrie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", + "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", + "license": "MIT", + "dependencies": { + "base64-arraybuffer": "^1.0.2" + } + }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", diff --git a/package.json b/package.json index bb49a0c..3947059 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@tanstack/vue-query-devtools": "^5.62.2", "@tinymce/tinymce-vue": "^4.0.7", "axios": "^1.12.2", + "html2pdf.js": "^0.14.0", "husky": "^8.0.0", "jalaali-js": "^1.2.8", "lodash": "^4.18.1", diff --git a/src/components/dashboard/DashboardStatCard.vue b/src/components/dashboard/DashboardStatCard.vue index 02ce94b..69e94b4 100644 --- a/src/components/dashboard/DashboardStatCard.vue +++ b/src/components/dashboard/DashboardStatCard.vue @@ -1,5 +1,10 @@ @@ -41,6 +48,23 @@ defineProps({ background: rgba(255, 255, 255, 58%); box-shadow: 0 6.75px 19.425px rgba(0, 0, 0, 4%); border-radius: 0.9375rem; + color: inherit; + text-decoration: none; + transition: transform 0.2s ease, box-shadow 0.2s ease; + + &--linked { + cursor: pointer; + + &:hover { + transform: translateY(-2px); + box-shadow: 0 9px 24px rgba(0, 0, 0, 7%); + } + + &:focus-visible { + outline: 2px solid var(--color-primary); + outline-offset: 3px; + } + } &__corner { position: absolute; diff --git a/src/features/admin/pages/AdminDashboardPage.vue b/src/features/admin/pages/AdminDashboardPage.vue index df427bd..53b1c61 100644 --- a/src/features/admin/pages/AdminDashboardPage.vue +++ b/src/features/admin/pages/AdminDashboardPage.vue @@ -9,7 +9,7 @@ - +
@@ -95,37 +96,56 @@ const ACCESS_LABELS = { limited: 'محدود', } -const formatThousands = (value) => { - const n = Number(value ?? 0) - return n >= 1000 - ? { value: toFaDigits(Math.round(n / 1000)), unit: 'هزار' } - : { value: toFaDigits(n), unit: 'نفر' } -} - const institutionStats = computed(() => { const stats = dashboard.value.stats ?? {} - const users = formatThousands(stats.activeUsers) return [ { - icon: 'user', - label: 'آمار کلی کاربران', - value: users.value, - unit: users.unit, - subtitle: 'کاربر فعال', + icon: 'users-three', + label: 'مجموع کل متقاضیان', + value: toFaDigits(stats.totalApplicants ?? 0), + unit: 'نفر', + subtitle: 'احراز شده و نشده', + to: { name: 'admin-users' }, }, { - icon: 'video', - label: 'دوره های ایجاد شده', - value: toFaDigits(stats.coursesCreated ?? 0), - unit: 'دوره', - subtitle: 'ایجاد شده', + icon: 'clock', + label: 'تعداد کاربران در انتظار ارزیابی', + value: toFaDigits(stats.awaitingEvaluation ?? 0), + unit: 'نفر', + subtitle: 'در انتظار ارزیابی', + to: { name: 'admin-user-verification' }, }, { - icon: 'shopping-bag', - label: 'دوره های فروش رفته', - value: toFaDigits(stats.coursesSold ?? 0), - unit: 'عدد', - subtitle: 'دوره فروش رفته', + icon: 'book', + label: 'آمار طلبه‌های ترم ۱', + value: toFaDigits(stats.term1Students ?? 0), + unit: 'نفر', + subtitle: 'ترم اول', + to: { name: 'admin-terms' }, + }, + { + icon: 'book', + label: 'آمار طلبه‌های ترم ۲', + value: toFaDigits(stats.term2Students ?? 0), + unit: 'نفر', + subtitle: 'ترم دوم', + to: { name: 'admin-terms' }, + }, + { + icon: 'book', + label: 'آمار طلبه‌های ترم ۳', + value: toFaDigits(stats.term3Students ?? 0), + unit: 'نفر', + subtitle: 'ترم سوم', + to: { name: 'admin-terms' }, + }, + { + icon: 'graduation-cap', + label: 'آمار فارغ‌التحصیلان', + value: toFaDigits(stats.graduates ?? 0), + unit: 'نفر', + subtitle: 'فارغ‌التحصیل', + to: { name: 'admin-users' }, }, ] }) diff --git a/src/features/admin/users/components/verifications/modals/UserVerificationDetailsModal.vue b/src/features/admin/users/components/verifications/modals/UserVerificationDetailsModal.vue index 68bd434..50e719a 100644 --- a/src/features/admin/users/components/verifications/modals/UserVerificationDetailsModal.vue +++ b/src/features/admin/users/components/verifications/modals/UserVerificationDetailsModal.vue @@ -8,10 +8,15 @@ :show-close-button="true" > diff --git a/src/features/counselor/pages/CounselorDashboardPage.vue b/src/features/counselor/pages/CounselorDashboardPage.vue index 72ed559..abe0466 100644 --- a/src/features/counselor/pages/CounselorDashboardPage.vue +++ b/src/features/counselor/pages/CounselorDashboardPage.vue @@ -19,6 +19,7 @@ :value="stat.value" :unit="stat.unit" :subtitle="stat.subtitle" + :to="stat.to" /> @@ -91,6 +92,7 @@ const ticketStats = computed(() => { value: toFaDigits(stats.openTickets ?? 0), unit: 'تیکت', subtitle: 'در انتظار پاسخ', + to: { name: 'counselor-consultations' }, }, { icon: 'check-circle', @@ -98,6 +100,7 @@ const ticketStats = computed(() => { value: toFaDigits(stats.answeredTickets ?? 0), unit: 'تیکت', subtitle: 'پاسخ داده شده', + to: { name: 'counselor-consultations' }, }, { icon: 'check', @@ -105,6 +108,7 @@ const ticketStats = computed(() => { value: toFaDigits(stats.closedTickets ?? 0), unit: 'تیکت', subtitle: 'بسته شده', + to: { name: 'counselor-consultations' }, }, ] }) diff --git a/src/features/missionary/pages/MissionaryDashboardPage.vue b/src/features/missionary/pages/MissionaryDashboardPage.vue index c2d1613..da8d1be 100644 --- a/src/features/missionary/pages/MissionaryDashboardPage.vue +++ b/src/features/missionary/pages/MissionaryDashboardPage.vue @@ -19,6 +19,7 @@ :value="stat.value" :unit="stat.unit" :subtitle="stat.subtitle" + :to="stat.to" /> @@ -91,6 +92,7 @@ const requestStats = computed(() => { value: toFaDigits(stats.totalRequests ?? 0), unit: 'درخواست', subtitle: 'ثبت شده', + to: { name: 'missionary-requests' }, }, { icon: 'check-circle', @@ -98,6 +100,7 @@ const requestStats = computed(() => { value: toFaDigits(stats.acceptedRequests ?? 0), unit: 'درخواست', subtitle: 'پذیرفته شده', + to: { name: 'missionary-requests' }, }, { icon: 'clock', @@ -105,6 +108,7 @@ const requestStats = computed(() => { value: toFaDigits(stats.pendingRequests ?? 0), unit: 'درخواست', subtitle: 'در انتظار', + to: { name: 'missionary-requests' }, }, ] }) diff --git a/src/features/student/pages/StudentDashboardPage.vue b/src/features/student/pages/StudentDashboardPage.vue index 71a1d14..384df9b 100644 --- a/src/features/student/pages/StudentDashboardPage.vue +++ b/src/features/student/pages/StudentDashboardPage.vue @@ -19,6 +19,7 @@ :value="stat.value" :unit="stat.unit" :subtitle="stat.subtitle" + :to="stat.to" /> @@ -95,6 +96,7 @@ const performanceStats = computed(() => { value: toFaDigits(stats.attendedSessions ?? 0), unit: 'جلسه', subtitle: 'اعزام شده است', + to: { name: 'student-terms' }, }, { icon: 'video', @@ -102,6 +104,7 @@ const performanceStats = computed(() => { value: toFaDigits(stats.enrolledTerms ?? 0), unit: 'دوره', subtitle: 'خریداری شده', + to: { name: 'student-terms' }, }, { icon: 'clock', @@ -109,6 +112,7 @@ const performanceStats = computed(() => { value: toFaDigits(stats.hoursSpent ?? 0), unit: 'ساعت', subtitle: 'گذرانده شده', + to: { name: 'student-terms' }, }, ] }) diff --git a/src/features/student/pages/components/DashboardStatCard.vue b/src/features/student/pages/components/DashboardStatCard.vue index 02ce94b..69e94b4 100644 --- a/src/features/student/pages/components/DashboardStatCard.vue +++ b/src/features/student/pages/components/DashboardStatCard.vue @@ -1,5 +1,10 @@ @@ -41,6 +48,23 @@ defineProps({ background: rgba(255, 255, 255, 58%); box-shadow: 0 6.75px 19.425px rgba(0, 0, 0, 4%); border-radius: 0.9375rem; + color: inherit; + text-decoration: none; + transition: transform 0.2s ease, box-shadow 0.2s ease; + + &--linked { + cursor: pointer; + + &:hover { + transform: translateY(-2px); + box-shadow: 0 9px 24px rgba(0, 0, 0, 7%); + } + + &:focus-visible { + outline: 2px solid var(--color-primary); + outline-offset: 3px; + } + } &__corner { position: absolute; diff --git a/src/features/student/services/components/CreateServiceForm.vue b/src/features/student/services/components/CreateServiceForm.vue index fb0017c..bbbf41c 100644 --- a/src/features/student/services/components/CreateServiceForm.vue +++ b/src/features/student/services/components/CreateServiceForm.vue @@ -3,11 +3,14 @@
-
@@ -71,6 +74,7 @@ import useYup from '@/composables/useYup' import BaseButton from '@/components/BaseButton.vue' import SvgIcon from '@/components/icons/SvgIcon.vue' import TextField from '@/components/form/TextField.vue' +import SelectField from '@/components/form/SelectField.vue' import FileUploader from '@/components/form/FileUploader.vue' import { objectToFormData } from '@/utils/object-to-formdata' import { useUploadMediaMutation } from '@/services/query/auth' @@ -82,6 +86,11 @@ defineProps({ const emit = defineEmits(['submit', 'file-error']) +const serviceTypeOptions = [ + { value: 'خدمات رفاهی', label: 'خدمات رفاهی' }, + { value: 'خدمات آموزشی', label: 'خدمات آموزشی' }, +] + const form = ref({ category: '', subject: '', @@ -94,7 +103,7 @@ const form = ref({ const files = ref([]) const schema = yup.object({ - category: yup.string().trim().required('نوع خدمت را وارد کنید'), + category: yup.string().trim().required('نوع خدمت را انتخاب کنید'), subject: yup.string().trim().required('عنوان خدمت را وارد کنید'), message: yup .string() diff --git a/src/layouts/StudentLayout.vue b/src/layouts/StudentLayout.vue index a12b40c..fc59737 100644 --- a/src/layouts/StudentLayout.vue +++ b/src/layouts/StudentLayout.vue @@ -84,12 +84,6 @@ const menuItems = computed(() => [ to: { name: 'student-services' }, active: route.name === 'student-services', }, - { - title: 'مشاوره', - icon: 'chat-teardrop-text', - to: { name: 'student-consultants' }, - active: route.name === 'student-consultants', - }, { title: 'گواهینامه', icon: 'certificate', diff --git a/src/services/mock/fixtures/admin-dashboard.js b/src/services/mock/fixtures/admin-dashboard.js index a37164c..e385d8e 100644 --- a/src/services/mock/fixtures/admin-dashboard.js +++ b/src/services/mock/fixtures/admin-dashboard.js @@ -11,10 +11,16 @@ export const adminDashboard = { openTickets: 32, }, stats: { - activeUsers: 11_000, - totalUsers: 11_540, - coursesCreated: 6, - coursesSold: 12, + totalApplicants: 18, + awaitingEvaluation: 1, + term1Students: 2, + term2Students: 0, + term3Students: 1, + graduates: 0, + activeUsers: 11, + totalUsers: 24, + coursesCreated: 4, + coursesSold: 3, }, recentRegistrations: [ { diff --git a/src/services/mock/routes/admin-dashboard.js b/src/services/mock/routes/admin-dashboard.js index 0bca5d0..203c807 100644 --- a/src/services/mock/routes/admin-dashboard.js +++ b/src/services/mock/routes/admin-dashboard.js @@ -2,4 +2,8 @@ import { register } from '@/services/mock/registry' import { endpoints } from '@/services/api/endpoints' import { adminDashboard } from '@/services/mock/fixtures/admin-dashboard' -register('GET', endpoints.getAdminDashboard, () => ({ data: adminDashboard })) +register('GET', endpoints.getAdminDashboard, () => ({ + success: true, + message: 'انجام شد.', + data: adminDashboard, +})) diff --git a/src/services/mock/routes/counselor-dashboard.js b/src/services/mock/routes/counselor-dashboard.js index 3b1218b..576275a 100644 --- a/src/services/mock/routes/counselor-dashboard.js +++ b/src/services/mock/routes/counselor-dashboard.js @@ -2,4 +2,8 @@ import { register } from '@/services/mock/registry' import { endpoints } from '@/services/api/endpoints' import { counselorDashboard } from '@/services/mock/fixtures/counselor-dashboard' -register('GET', endpoints.getCounselorDashboard, () => ({ data: counselorDashboard })) +register('GET', endpoints.getCounselorDashboard, () => ({ + success: true, + message: 'انجام شد.', + data: counselorDashboard, +})) diff --git a/src/services/mock/routes/missionary-dashboard.js b/src/services/mock/routes/missionary-dashboard.js index ea7c98c..eab70ec 100644 --- a/src/services/mock/routes/missionary-dashboard.js +++ b/src/services/mock/routes/missionary-dashboard.js @@ -2,4 +2,8 @@ import { register } from '@/services/mock/registry' import { endpoints } from '@/services/api/endpoints' import { missionaryDashboard } from '@/services/mock/fixtures/missionary-dashboard' -register('GET', endpoints.getMissionaryDashboard, () => ({ data: missionaryDashboard })) +register('GET', endpoints.getMissionaryDashboard, () => ({ + success: true, + message: 'انجام شد.', + data: missionaryDashboard, +})) diff --git a/src/services/mock/routes/student-dashboard.js b/src/services/mock/routes/student-dashboard.js index 99f440a..6fee2f5 100644 --- a/src/services/mock/routes/student-dashboard.js +++ b/src/services/mock/routes/student-dashboard.js @@ -2,4 +2,8 @@ import { register } from '@/services/mock/registry' import { endpoints } from '@/services/api/endpoints' import { studentDashboard } from '@/services/mock/fixtures/student-dashboard' -register('GET', endpoints.getStudentDashboard, () => ({ data: studentDashboard })) +register('GET', endpoints.getStudentDashboard, () => ({ + success: true, + message: 'انجام شد.', + data: studentDashboard, +}))