This commit is contained in:
2025-01-04 21:01:04 +03:30
parent aad48fca63
commit 57d328bbfe
33 changed files with 1752 additions and 261 deletions
+2 -2
View File
@@ -5,11 +5,11 @@
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sakai Vue</title>
<title>نیلگون</title>
<link href="https://fonts.cdnfonts.com/css/lato" rel="stylesheet">
</head>
<body>
<body dir="rtl">
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
+404 -103
View File
@@ -1,18 +1,33 @@
{
"name": "sakai-vue",
"version": "4.2.0-SNAPSHOT",
"name": "bm-dashboard",
"version": "4.2.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "sakai-vue",
"version": "4.2.0-SNAPSHOT",
"name": "bm-dashboard",
"version": "4.2.0",
"dependencies": {
"@fortawesome/fontawesome-free": "^6.7.2",
"@fortawesome/fontawesome-svg-core": "^6.7.2",
"@fortawesome/free-brands-svg-icons": "^6.7.2",
"@fortawesome/free-regular-svg-icons": "^6.7.2",
"@fortawesome/free-solid-svg-icons": "^6.7.2",
"@fortawesome/vue-fontawesome": "^3.0.3",
"@primevue/core": "^4.2.5",
"@primevue/themes": "^4.2.4",
"axios": "^1.7.9",
"chart.js": "3.3.2",
"filepond": "^4.32.6",
"filepond-plugin-file-validate-type": "^1.2.9",
"filepond-plugin-image-preview": "^4.6.12",
"js-cookie": "^3.0.5",
"photoswipe": "^5.4.4",
"primeicons": "^7.0.0",
"primevue": "^4.2.4",
"vue": "^3.4.34",
"vue-cookies": "^1.8.3",
"vue-filepond": "^7.0.4",
"vue-router": "^4.4.0"
},
"devDependencies": {
@@ -488,6 +503,75 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/@fortawesome/fontawesome-common-types": {
"version": "6.7.2",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.7.2.tgz",
"integrity": "sha512-Zs+YeHUC5fkt7Mg1l6XTniei3k4bwG/yo3iFUtZWd/pMx9g3fdvkSK9E0FOC+++phXOka78uJcYb8JaFkW52Xg==",
"engines": {
"node": ">=6"
}
},
"node_modules/@fortawesome/fontawesome-free": {
"version": "6.7.2",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.7.2.tgz",
"integrity": "sha512-JUOtgFW6k9u4Y+xeIaEiLr3+cjoUPiAuLXoyKOJSia6Duzb7pq+A76P9ZdPDoAoxHdHzq6gE9/jKBGXlZT8FbA==",
"engines": {
"node": ">=6"
}
},
"node_modules/@fortawesome/fontawesome-svg-core": {
"version": "6.7.2",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.7.2.tgz",
"integrity": "sha512-yxtOBWDrdi5DD5o1pmVdq3WMCvnobT0LU6R8RyyVXPvFRd2o79/0NCuQoCjNTeZz9EzA9xS3JxNWfv54RIHFEA==",
"dependencies": {
"@fortawesome/fontawesome-common-types": "6.7.2"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@fortawesome/free-brands-svg-icons": {
"version": "6.7.2",
"resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.7.2.tgz",
"integrity": "sha512-zu0evbcRTgjKfrr77/2XX+bU+kuGfjm0LbajJHVIgBWNIDzrhpRxiCPNT8DW5AdmSsq7Mcf9D1bH0aSeSUSM+Q==",
"dependencies": {
"@fortawesome/fontawesome-common-types": "6.7.2"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@fortawesome/free-regular-svg-icons": {
"version": "6.7.2",
"resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.7.2.tgz",
"integrity": "sha512-7Z/ur0gvCMW8G93dXIQOkQqHo2M5HLhYrRVC0//fakJXxcF1VmMPsxnG6Ee8qEylA8b8Q3peQXWMNZ62lYF28g==",
"dependencies": {
"@fortawesome/fontawesome-common-types": "6.7.2"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@fortawesome/free-solid-svg-icons": {
"version": "6.7.2",
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.7.2.tgz",
"integrity": "sha512-GsBrnOzU8uj0LECDfD5zomZJIjrPhIlWU82AHwa2s40FKH+kcxQaBvBo3Z4TxyZHIyX8XTDxsyA33/Vx9eFuQA==",
"dependencies": {
"@fortawesome/fontawesome-common-types": "6.7.2"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@fortawesome/vue-fontawesome": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@fortawesome/vue-fontawesome/-/vue-fontawesome-3.0.8.tgz",
"integrity": "sha512-yyHHAj4G8pQIDfaIsMvQpwKMboIZtcHTUvPqXjOHyldh1O1vZfH4W03VDPv5RvI9P6DLTzJQlmVgj9wCf7c2Fw==",
"peerDependencies": {
"@fortawesome/fontawesome-svg-core": "~1 || ~6",
"vue": ">= 3.0.0 < 4"
}
},
"node_modules/@humanwhocodes/config-array": {
"version": "0.11.14",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
@@ -703,9 +787,9 @@
}
},
"node_modules/@primevue/core": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/@primevue/core/-/core-4.2.4.tgz",
"integrity": "sha512-QFvPcGSvyIhZPLdnjJnYrwbDtwbA1/FyGLI7VYDgYv4twsgtLw0kgKDyWB1uwM0xdJhv8CCmu7hfxcsPaLuIFg==",
"version": "4.2.5",
"resolved": "https://registry.npmjs.org/@primevue/core/-/core-4.2.5.tgz",
"integrity": "sha512-+oWBIQs5dLd2Ini4KEVOlvPILk989EHAskiFS3R/dz3jeOllJDMZFcSp8V9ddV0R3yDaPdLVkfHm2Q5t42kU2Q==",
"dependencies": {
"@primeuix/styled": "^0.3.2",
"@primeuix/utils": "^0.3.2"
@@ -729,6 +813,21 @@
"node": ">=12.11.0"
}
},
"node_modules/@primevue/icons/node_modules/@primevue/core": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/@primevue/core/-/core-4.2.4.tgz",
"integrity": "sha512-QFvPcGSvyIhZPLdnjJnYrwbDtwbA1/FyGLI7VYDgYv4twsgtLw0kgKDyWB1uwM0xdJhv8CCmu7hfxcsPaLuIFg==",
"dependencies": {
"@primeuix/styled": "^0.3.2",
"@primeuix/utils": "^0.3.2"
},
"engines": {
"node": ">=12.11.0"
},
"peerDependencies": {
"vue": "^3.3.0"
}
},
"node_modules/@primevue/metadata": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/@primevue/metadata/-/metadata-4.2.4.tgz",
@@ -772,9 +871,9 @@
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.19.1.tgz",
"integrity": "sha512-XzqSg714++M+FXhHfXpS1tDnNZNpgxxuGZWlRG/jSj+VEPmZ0yg6jV4E0AL3uyBKxO8mO3xtOsP5mQ+XLfrlww==",
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.29.1.tgz",
"integrity": "sha512-ssKhA8RNltTZLpG6/QNkCSge+7mBQGUqJRisZ2MDQcEGaK93QESEgWK2iOpIDZ7k9zPVkG5AS3ksvD5ZWxmItw==",
"cpu": [
"arm"
],
@@ -785,9 +884,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.19.1.tgz",
"integrity": "sha512-thFUbkHteM20BGShD6P08aungq4irbIZKUNbG70LN8RkO7YztcGPiKTTGZS7Kw+x5h8hOXs0i4OaHwFxlpQN6A==",
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.29.1.tgz",
"integrity": "sha512-CaRfrV0cd+NIIcVVN/jx+hVLN+VRqnuzLRmfmlzpOzB87ajixsN/+9L5xNmkaUUvEbI5BmIKS+XTwXsHEb65Ew==",
"cpu": [
"arm64"
],
@@ -798,9 +897,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.19.1.tgz",
"integrity": "sha512-8o6eqeFZzVLia2hKPUZk4jdE3zW7LCcZr+MD18tXkgBBid3lssGVAYuox8x6YHoEPDdDa9ixTaStcmx88lio5Q==",
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.29.1.tgz",
"integrity": "sha512-2ORr7T31Y0Mnk6qNuwtyNmy14MunTAMx06VAPI6/Ju52W10zk1i7i5U3vlDRWjhOI5quBcrvhkCHyF76bI7kEw==",
"cpu": [
"arm64"
],
@@ -811,9 +910,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.19.1.tgz",
"integrity": "sha512-4T42heKsnbjkn7ovYiAdDVRRWZLU9Kmhdt6HafZxFcUdpjlBlxj4wDrt1yFWLk7G4+E+8p2C9tcmSu0KA6auGA==",
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.29.1.tgz",
"integrity": "sha512-j/Ej1oanzPjmN0tirRd5K2/nncAhS9W6ICzgxV+9Y5ZsP0hiGhHJXZ2JQ53iSSjj8m6cRY6oB1GMzNn2EUt6Ng==",
"cpu": [
"x64"
],
@@ -823,10 +922,36 @@
"darwin"
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.29.1.tgz",
"integrity": "sha512-91C//G6Dm/cv724tpt7nTyP+JdN12iqeXGFM1SqnljCmi5yTXriH7B1r8AD9dAZByHpKAumqP1Qy2vVNIdLZqw==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.29.1.tgz",
"integrity": "sha512-hEioiEQ9Dec2nIRoeHUP6hr1PSkXzQaCUyqBDQ9I9ik4gCXQZjJMIVzoNLBRGet+hIUb3CISMh9KXuCcWVW/8w==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.19.1.tgz",
"integrity": "sha512-MXg1xp+e5GhZ3Vit1gGEyoC+dyQUBy2JgVQ+3hUrD9wZMkUw/ywgkpK7oZgnB6kPpGrxJ41clkPPnsknuD6M2Q==",
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.29.1.tgz",
"integrity": "sha512-Py5vFd5HWYN9zxBv3WMrLAXY3yYJ6Q/aVERoeUFwiDGiMOWsMs7FokXihSOaT/PMWUty/Pj60XDQndK3eAfE6A==",
"cpu": [
"arm"
],
@@ -837,9 +962,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.19.1.tgz",
"integrity": "sha512-DZNLwIY4ftPSRVkJEaxYkq7u2zel7aah57HESuNkUnz+3bZHxwkCUkrfS2IWC1sxK6F2QNIR0Qr/YXw7nkF3Pw==",
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.29.1.tgz",
"integrity": "sha512-RiWpGgbayf7LUcuSNIbahr0ys2YnEERD4gYdISA06wa0i8RALrnzflh9Wxii7zQJEB2/Eh74dX4y/sHKLWp5uQ==",
"cpu": [
"arm"
],
@@ -850,9 +975,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.19.1.tgz",
"integrity": "sha512-C7evongnjyxdngSDRRSQv5GvyfISizgtk9RM+z2biV5kY6S/NF/wta7K+DanmktC5DkuaJQgoKGf7KUDmA7RUw==",
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.29.1.tgz",
"integrity": "sha512-Z80O+taYxTQITWMjm/YqNoe9d10OX6kDh8X5/rFCMuPqsKsSyDilvfg+vd3iXIqtfmp+cnfL1UrYirkaF8SBZA==",
"cpu": [
"arm64"
],
@@ -863,9 +988,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.19.1.tgz",
"integrity": "sha512-89tFWqxfxLLHkAthAcrTs9etAoBFRduNfWdl2xUs/yLV+7XDrJ5yuXMHptNqf1Zw0UCA3cAutkAiAokYCkaPtw==",
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.29.1.tgz",
"integrity": "sha512-fOHRtF9gahwJk3QVp01a/GqS4hBEZCV1oKglVVq13kcK3NeVlS4BwIFzOHDbmKzt3i0OuHG4zfRP0YoG5OF/rA==",
"cpu": [
"arm64"
],
@@ -875,10 +1000,23 @@
"linux"
]
},
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.29.1.tgz",
"integrity": "sha512-5a7q3tnlbcg0OodyxcAdrrCxFi0DgXJSoOuidFUzHZ2GixZXQs6Tc3CHmlvqKAmOs5eRde+JJxeIf9DonkmYkw==",
"cpu": [
"loong64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.19.1.tgz",
"integrity": "sha512-PromGeV50sq+YfaisG8W3fd+Cl6mnOOiNv2qKKqKCpiiEke2KiKVyDqG/Mb9GWKbYMHj5a01fq/qlUR28PFhCQ==",
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.29.1.tgz",
"integrity": "sha512-9b4Mg5Yfz6mRnlSPIdROcfw1BU22FQxmfjlp/CShWwO3LilKQuMISMTtAu/bxmmrE6A902W2cZJuzx8+gJ8e9w==",
"cpu": [
"ppc64"
],
@@ -889,9 +1027,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.19.1.tgz",
"integrity": "sha512-/1BmHYh+iz0cNCP0oHCuF8CSiNj0JOGf0jRlSo3L/FAyZyG2rGBuKpkZVH9YF+x58r1jgWxvm1aRg3DHrLDt6A==",
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.29.1.tgz",
"integrity": "sha512-G5pn0NChlbRM8OJWpJFMX4/i8OEU538uiSv0P6roZcbpe/WfhEO+AT8SHVKfp8qhDQzaz7Q+1/ixMy7hBRidnQ==",
"cpu": [
"riscv64"
],
@@ -902,9 +1040,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.19.1.tgz",
"integrity": "sha512-0cYP5rGkQWRZKy9/HtsWVStLXzCF3cCBTRI+qRL8Z+wkYlqN7zrSYm6FuY5Kd5ysS5aH0q5lVgb/WbG4jqXN1Q==",
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.29.1.tgz",
"integrity": "sha512-WM9lIkNdkhVwiArmLxFXpWndFGuOka4oJOZh8EP3Vb8q5lzdSCBuhjavJsw68Q9AKDGeOOIHYzYm4ZFvmWez5g==",
"cpu": [
"s390x"
],
@@ -915,9 +1053,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.19.1.tgz",
"integrity": "sha512-XUXeI9eM8rMP8aGvii/aOOiMvTs7xlCosq9xCjcqI9+5hBxtjDpD+7Abm1ZhVIFE1J2h2VIg0t2DX/gjespC2Q==",
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.29.1.tgz",
"integrity": "sha512-87xYCwb0cPGZFoGiErT1eDcssByaLX4fc0z2nRM6eMtV9njAfEE6OW3UniAoDhX4Iq5xQVpE6qO9aJbCFumKYQ==",
"cpu": [
"x64"
],
@@ -928,9 +1066,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.19.1.tgz",
"integrity": "sha512-V7cBw/cKXMfEVhpSvVZhC+iGifD6U1zJ4tbibjjN+Xi3blSXaj/rJynAkCFFQfoG6VZrAiP7uGVzL440Q6Me2Q==",
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.29.1.tgz",
"integrity": "sha512-xufkSNppNOdVRCEC4WKvlR1FBDyqCSCpQeMMgv9ZyXqqtKBfkw1yfGMTUTs9Qsl6WQbJnsGboWCp7pJGkeMhKA==",
"cpu": [
"x64"
],
@@ -941,9 +1079,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.19.1.tgz",
"integrity": "sha512-88brja2vldW/76jWATlBqHEoGjJLRnP0WOEKAUbMcXaAZnemNhlAHSyj4jIwMoP2T750LE9lblvD4e2jXleZsA==",
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.29.1.tgz",
"integrity": "sha512-F2OiJ42m77lSkizZQLuC+jiZ2cgueWQL5YC9tjo3AgaEw+KJmVxHGSyQfDUoYR9cci0lAywv2Clmckzulcq6ig==",
"cpu": [
"arm64"
],
@@ -954,9 +1092,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.19.1.tgz",
"integrity": "sha512-LdxxcqRVSXi6k6JUrTah1rHuaupoeuiv38du8Mt4r4IPer3kwlTo+RuvfE8KzZ/tL6BhaPlzJ3835i6CxrFIRQ==",
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.29.1.tgz",
"integrity": "sha512-rYRe5S0FcjlOBZQHgbTKNrqxCBUmgDJem/VQTCcTnA2KCabYSWQDrytOzX7avb79cAAweNmMUb/Zw18RNd4mng==",
"cpu": [
"ia32"
],
@@ -967,9 +1105,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.19.1.tgz",
"integrity": "sha512-2bIrL28PcK3YCqD9anGxDxamxdiJAxA+l7fWIwM5o8UqNy1t3d1NdAweO2XhA0KTDJ5aH1FsuiT5+7VhtHliXg==",
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.29.1.tgz",
"integrity": "sha512-+10CMg9vt1MoHj6x1pxyjPSMjHTIlqs8/tBztXvPAx24SKs9jwVnKqHJumlH/IzhaPUaj3T6T6wfZr8okdXaIg==",
"cpu": [
"x64"
],
@@ -986,9 +1124,9 @@
"dev": true
},
"node_modules/@types/estree": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
"integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
"dev": true
},
"node_modules/@ungap/structured-clone": {
@@ -1212,6 +1350,11 @@
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/autoprefixer": {
"version": "10.4.19",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz",
@@ -1249,6 +1392,16 @@
"postcss": "^8.1.0"
}
},
"node_modules/axios": {
"version": "1.7.9",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz",
"integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -1440,6 +1593,17 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"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==",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/commander": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
@@ -1462,9 +1626,9 @@
"dev": true
},
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"dev": true,
"dependencies": {
"path-key": "^3.1.0",
@@ -1515,6 +1679,14 @@
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
"dev": true
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/didyoumean": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
@@ -1911,6 +2083,27 @@
"node": "^10.12.0 || >=12.0.0"
}
},
"node_modules/filepond": {
"version": "4.32.6",
"resolved": "https://registry.npmjs.org/filepond/-/filepond-4.32.6.tgz",
"integrity": "sha512-9EM6Vvny3K5s7guxegedByQx8n6ycUFaoF+Wo20+t1DYNt+vQaOtkGy2jnY8/2roAlskUnwJI1rAnAhzBPXwiw=="
},
"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==",
"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==",
"peerDependencies": {
"filepond": ">=4.x <5.x"
}
},
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
@@ -1959,6 +2152,25 @@
"integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
"dev": true
},
"node_modules/follow-redirects": {
"version": "1.15.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/foreground-child": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz",
@@ -1975,6 +2187,19 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/form-data": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
"integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/fraction.js": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
@@ -2254,6 +2479,14 @@
"jiti": "bin/jiti.js"
}
},
"node_modules/js-cookie": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz",
"integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==",
"engines": {
"node": ">=14"
}
},
"node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
@@ -2388,9 +2621,9 @@
}
},
"node_modules/micromatch": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz",
"integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==",
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"dev": true,
"dependencies": {
"braces": "^3.0.3",
@@ -2400,6 +2633,25 @@
"node": ">=8.6"
}
},
"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==",
"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==",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -2451,9 +2703,9 @@
}
},
"node_modules/nanoid": {
"version": "3.3.7",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
"version": "3.3.8",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
"integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
"funding": [
{
"type": "github",
@@ -2656,10 +2908,18 @@
"integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
"dev": true
},
"node_modules/photoswipe": {
"version": "5.4.4",
"resolved": "https://registry.npmjs.org/photoswipe/-/photoswipe-5.4.4.tgz",
"integrity": "sha512-WNFHoKrkZNnvFFhbHL93WDkW3ifwVOXSW3w1UuZZelSmgXpIGiZSNlZJq37rR8YejqME2rHs9EhH9ZvlvFH2NA==",
"engines": {
"node": ">= 0.12.0"
}
},
"node_modules/picocolors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
"integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew=="
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
},
"node_modules/picomatch": {
"version": "2.3.1",
@@ -2703,9 +2963,9 @@
}
},
"node_modules/postcss": {
"version": "8.4.40",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.40.tgz",
"integrity": "sha512-YF2kKIUzAofPMpfH6hOi2cGnv/HrUlfucspc7pDyvv7kGdqXrfj8SCl/t8owkEgKEuu8ZcRjSOxFxVLqwChZ2Q==",
"version": "8.4.49",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
"integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
"funding": [
{
"type": "opencollective",
@@ -2722,8 +2982,8 @@
],
"dependencies": {
"nanoid": "^3.3.7",
"picocolors": "^1.0.1",
"source-map-js": "^1.2.0"
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
@@ -2911,6 +3171,26 @@
"node": ">=12.11.0"
}
},
"node_modules/primevue/node_modules/@primevue/core": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/@primevue/core/-/core-4.2.4.tgz",
"integrity": "sha512-QFvPcGSvyIhZPLdnjJnYrwbDtwbA1/FyGLI7VYDgYv4twsgtLw0kgKDyWB1uwM0xdJhv8CCmu7hfxcsPaLuIFg==",
"dependencies": {
"@primeuix/styled": "^0.3.2",
"@primeuix/utils": "^0.3.2"
},
"engines": {
"node": ">=12.11.0"
},
"peerDependencies": {
"vue": "^3.3.0"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -3014,12 +3294,12 @@
}
},
"node_modules/rollup": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.19.1.tgz",
"integrity": "sha512-K5vziVlg7hTpYfFBI+91zHBEMo6jafYXpkMlqZjg7/zhIG9iHqazBf4xz9AVdjS9BruRn280ROqLI7G3OFRIlw==",
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.29.1.tgz",
"integrity": "sha512-RaJ45M/kmJUzSWDs1Nnd5DdV4eerC98idtUOVr6FfKcgxqvjwHmxc5upLF9qZU9EpsVzzhleFahrT3shLuJzIw==",
"dev": true,
"dependencies": {
"@types/estree": "1.0.5"
"@types/estree": "1.0.6"
},
"bin": {
"rollup": "dist/bin/rollup"
@@ -3029,22 +3309,25 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.19.1",
"@rollup/rollup-android-arm64": "4.19.1",
"@rollup/rollup-darwin-arm64": "4.19.1",
"@rollup/rollup-darwin-x64": "4.19.1",
"@rollup/rollup-linux-arm-gnueabihf": "4.19.1",
"@rollup/rollup-linux-arm-musleabihf": "4.19.1",
"@rollup/rollup-linux-arm64-gnu": "4.19.1",
"@rollup/rollup-linux-arm64-musl": "4.19.1",
"@rollup/rollup-linux-powerpc64le-gnu": "4.19.1",
"@rollup/rollup-linux-riscv64-gnu": "4.19.1",
"@rollup/rollup-linux-s390x-gnu": "4.19.1",
"@rollup/rollup-linux-x64-gnu": "4.19.1",
"@rollup/rollup-linux-x64-musl": "4.19.1",
"@rollup/rollup-win32-arm64-msvc": "4.19.1",
"@rollup/rollup-win32-ia32-msvc": "4.19.1",
"@rollup/rollup-win32-x64-msvc": "4.19.1",
"@rollup/rollup-android-arm-eabi": "4.29.1",
"@rollup/rollup-android-arm64": "4.29.1",
"@rollup/rollup-darwin-arm64": "4.29.1",
"@rollup/rollup-darwin-x64": "4.29.1",
"@rollup/rollup-freebsd-arm64": "4.29.1",
"@rollup/rollup-freebsd-x64": "4.29.1",
"@rollup/rollup-linux-arm-gnueabihf": "4.29.1",
"@rollup/rollup-linux-arm-musleabihf": "4.29.1",
"@rollup/rollup-linux-arm64-gnu": "4.29.1",
"@rollup/rollup-linux-arm64-musl": "4.29.1",
"@rollup/rollup-linux-loongarch64-gnu": "4.29.1",
"@rollup/rollup-linux-powerpc64le-gnu": "4.29.1",
"@rollup/rollup-linux-riscv64-gnu": "4.29.1",
"@rollup/rollup-linux-s390x-gnu": "4.29.1",
"@rollup/rollup-linux-x64-gnu": "4.29.1",
"@rollup/rollup-linux-x64-musl": "4.29.1",
"@rollup/rollup-win32-arm64-msvc": "4.29.1",
"@rollup/rollup-win32-ia32-msvc": "4.29.1",
"@rollup/rollup-win32-x64-msvc": "4.29.1",
"fsevents": "~2.3.2"
}
},
@@ -3134,9 +3417,9 @@
}
},
"node_modules/source-map-js": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
"integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
"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==",
"engines": {
"node": ">=0.10.0"
}
@@ -3598,14 +3881,14 @@
"dev": true
},
"node_modules/vite": {
"version": "5.3.5",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.3.5.tgz",
"integrity": "sha512-MdjglKR6AQXQb9JGiS7Rc2wC6uMjcm7Go/NHNO63EwiJXfuk9PgqiP/n5IDJCziMkfw9n4Ubp7lttNwz+8ZVKA==",
"version": "5.4.11",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz",
"integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==",
"dev": true,
"dependencies": {
"esbuild": "^0.21.3",
"postcss": "^8.4.39",
"rollup": "^4.13.0"
"postcss": "^8.4.43",
"rollup": "^4.20.0"
},
"bin": {
"vite": "bin/vite.js"
@@ -3624,6 +3907,7 @@
"less": "*",
"lightningcss": "^1.21.0",
"sass": "*",
"sass-embedded": "*",
"stylus": "*",
"sugarss": "*",
"terser": "^5.4.0"
@@ -3641,6 +3925,9 @@
"sass": {
"optional": true
},
"sass-embedded": {
"optional": true
},
"stylus": {
"optional": true
},
@@ -3672,6 +3959,11 @@
}
}
},
"node_modules/vue-cookies": {
"version": "1.8.5",
"resolved": "https://registry.npmjs.org/vue-cookies/-/vue-cookies-1.8.5.tgz",
"integrity": "sha512-7BbERTTMntFq+sGIcQYCoANjb3aAEgFx0/9wpyC5EoJo5FAStdJO+/i/zzZwHCix11sQgCQKiCHa/kJ8dt/N3w=="
},
"node_modules/vue-eslint-parser": {
"version": "9.4.3",
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz",
@@ -3696,6 +3988,15 @@
"eslint": ">=6.0.0"
}
},
"node_modules/vue-filepond": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/vue-filepond/-/vue-filepond-7.0.4.tgz",
"integrity": "sha512-HvWlCG8qjvyykrpPTDjJ+JqsHTXt5/0zWMX9vu7kU3JXCUODPUkGi9prZcauOnIZ4MqyeSZ9M0sDVwdMATyt1g==",
"peerDependencies": {
"filepond": ">=4.7.4 < 5.x",
"vue": ">=3 < 4"
}
},
"node_modules/vue-router": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.4.0.tgz",
+16 -1
View File
@@ -1,5 +1,5 @@
{
"name": "sakai-vue",
"name": "bm-dashboard",
"version": "4.2.0",
"scripts": {
"dev": "vite",
@@ -8,11 +8,26 @@
"lint": "eslint --fix . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore"
},
"dependencies": {
"@fortawesome/fontawesome-free": "^6.7.2",
"@fortawesome/fontawesome-svg-core": "^6.7.2",
"@fortawesome/free-brands-svg-icons": "^6.7.2",
"@fortawesome/free-regular-svg-icons": "^6.7.2",
"@fortawesome/free-solid-svg-icons": "^6.7.2",
"@fortawesome/vue-fontawesome": "^3.0.3",
"@primevue/core": "^4.2.5",
"@primevue/themes": "^4.2.4",
"axios": "^1.7.9",
"chart.js": "3.3.2",
"filepond": "^4.32.6",
"filepond-plugin-file-validate-type": "^1.2.9",
"filepond-plugin-image-preview": "^4.6.12",
"js-cookie": "^3.0.5",
"photoswipe": "^5.4.4",
"primeicons": "^7.0.0",
"primevue": "^4.2.4",
"vue": "^3.4.34",
"vue-cookies": "^1.8.3",
"vue-filepond": "^7.0.4",
"vue-router": "^4.4.0"
},
"devDependencies": {
+4 -1
View File
@@ -1,4 +1,7 @@
<script setup></script>
<script setup>
import axios from 'axios'
axios.defaults.baseURL = import.meta.env.VITE_API_URL
</script>
<template>
<router-view />
+17
View File
@@ -0,0 +1,17 @@
@font-face {
font-family: 'Estedad';
src: url('./fonts/estedad/variable/Estedad[KSHD,wght].woff2') format('woff2 supports variations'),
url('./fonts/estedad/variable/Estedad[KSHD,wght].woff2') format('woff2-variations');
font-weight: 100 900;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Estedad FD';
src: url('./fonts/estedad/variable/Estedad-FD[KSHD,wght].woff2') format('woff2 supports variations'),
url('./fonts/estedad/variable/Estedad-FD[KSHD,wght].woff2') format('woff2-variations');
font-weight: 100 900;
font-style: normal;
font-display: swap;
}
+1 -1
View File
@@ -4,7 +4,7 @@
min-height: 100vh;
justify-content: space-between;
padding: 6rem 2rem 0 2rem;
transition: margin-left var(--layout-section-transition-duration);
transition: margin-right var(--layout-section-transition-duration);
}
.layout-main {
+3 -1
View File
@@ -1,6 +1,7 @@
@use 'mixins' as *;
.layout-sidebar {
direction: ltr;
position: fixed;
width: 20rem;
height: calc(100vh - 8rem);
@@ -8,13 +9,14 @@
overflow-y: auto;
user-select: none;
top: 6rem;
left: 2rem;
right: 2rem;
transition:
transform var(--layout-section-transition-duration),
left var(--layout-section-transition-duration);
background-color: var(--surface-overlay);
border-radius: var(--content-border-radius);
padding: 0.5rem 1.5rem;
scrollbar-width: thin;
}
.layout-menu {
+3 -3
View File
@@ -41,17 +41,17 @@
&.layout-static {
.layout-main-container {
margin-left: 22rem;
margin-right: 22rem;
}
&.layout-static-inactive {
.layout-sidebar {
transform: translateX(-100%);
transform: translateX(110%);
left: 0;
}
.layout-main-container {
margin-left: 0;
margin-right: 0;
padding-left: 2rem;
}
}
+15
View File
@@ -1,3 +1,18 @@
@import "./estedad.css";
@tailwind base;
@tailwind components;
@tailwind utilities;
.eng-digits {
font-family: "Estedad", sans-serif!important;
}
.farsi-digits {
font-family: "Estedad FD", sans-serif!important;
}
body {
font-family: "Estedad FD", sans-serif!important;
}
+2 -2
View File
@@ -16,8 +16,8 @@ const presetOptions = ref(Object.keys(presets));
const menuMode = ref(layoutConfig.menuMode);
const menuModeOptions = ref([
{ label: 'Static', value: 'static' },
{ label: 'Overlay', value: 'overlay' }
{ label: 'Static', value: 'static' }
// { label: 'Overlay', value: 'overlay' }
]);
const primaryColors = ref([
+1 -4
View File
@@ -1,8 +1,5 @@
<script setup></script>
<template>
<div class="layout-footer">
SAKAI by
<a href="https://primevue.org" target="_blank" rel="noopener noreferrer" class="text-primary font-bold hover:underline">PrimeVue</a>
</div>
<div class="layout-footer">ساخته شده توسط تیم نیلگون</div>
</template>
+1
View File
@@ -53,6 +53,7 @@ function isOutsideClicked(event) {
return !(sidebarEl.isSameNode(event.target) || sidebarEl.contains(event.target) || topbarEl.isSameNode(event.target) || topbarEl.contains(event.target));
}
</script>
<template>
+8 -122
View File
@@ -5,135 +5,21 @@ import AppMenuItem from './AppMenuItem.vue';
const model = ref([
{
label: 'Home',
items: [{ label: 'Dashboard', icon: 'pi pi-fw pi-home', to: '/' }]
label: 'خانه',
items: [{ label: 'داشبورد', icon: 'pi pi-fw pi-home', to: '/' }]
},
{
label: 'UI Components',
label: 'مدیریت اموال',
items: [
{ label: 'Form Layout', icon: 'pi pi-fw pi-id-card', to: '/uikit/formlayout' },
{ label: 'Input', icon: 'pi pi-fw pi-check-square', to: '/uikit/input' },
{ label: 'Button', icon: 'pi pi-fw pi-mobile', to: '/uikit/button', class: 'rotated-icon' },
{ label: 'Table', icon: 'pi pi-fw pi-table', to: '/uikit/table' },
{ label: 'List', icon: 'pi pi-fw pi-list', to: '/uikit/list' },
{ label: 'Tree', icon: 'pi pi-fw pi-share-alt', to: '/uikit/tree' },
{ label: 'Panel', icon: 'pi pi-fw pi-tablet', to: '/uikit/panel' },
{ label: 'Overlay', icon: 'pi pi-fw pi-clone', to: '/uikit/overlay' },
{ label: 'Media', icon: 'pi pi-fw pi-image', to: '/uikit/media' },
{ label: 'Menu', icon: 'pi pi-fw pi-bars', to: '/uikit/menu' },
{ label: 'Message', icon: 'pi pi-fw pi-comment', to: '/uikit/message' },
{ label: 'File', icon: 'pi pi-fw pi-file', to: '/uikit/file' },
{ label: 'Chart', icon: 'pi pi-fw pi-chart-bar', to: '/uikit/charts' },
{ label: 'Timeline', icon: 'pi pi-fw pi-calendar', to: '/uikit/timeline' },
{ label: 'Misc', icon: 'pi pi-fw pi-circle', to: '/uikit/misc' }
{ label: 'ایجاد اموال', icon: 'pi pi-fw pi-file-plus', to: '/belonging/create-belonging' },
{ label: 'لیست اموال', icon: 'pi pi-fw pi-file', to: '/belonging/list-belongings' }
]
},
{
label: 'Pages',
icon: 'pi pi-fw pi-briefcase',
to: '/pages',
label: 'دسته بندی اموال',
items: [
{
label: 'Landing',
icon: 'pi pi-fw pi-globe',
to: '/landing'
},
{
label: 'Auth',
icon: 'pi pi-fw pi-user',
items: [
{
label: 'Login',
icon: 'pi pi-fw pi-sign-in',
to: '/auth/login'
},
{
label: 'Error',
icon: 'pi pi-fw pi-times-circle',
to: '/auth/error'
},
{
label: 'Access Denied',
icon: 'pi pi-fw pi-lock',
to: '/auth/access'
}
]
},
{
label: 'Crud',
icon: 'pi pi-fw pi-pencil',
to: '/pages/crud'
},
{
label: 'Not Found',
icon: 'pi pi-fw pi-exclamation-circle',
to: '/pages/notfound'
},
{
label: 'Empty',
icon: 'pi pi-fw pi-circle-off',
to: '/pages/empty'
}
]
},
{
label: 'Hierarchy',
items: [
{
label: 'Submenu 1',
icon: 'pi pi-fw pi-bookmark',
items: [
{
label: 'Submenu 1.1',
icon: 'pi pi-fw pi-bookmark',
items: [
{ label: 'Submenu 1.1.1', icon: 'pi pi-fw pi-bookmark' },
{ label: 'Submenu 1.1.2', icon: 'pi pi-fw pi-bookmark' },
{ label: 'Submenu 1.1.3', icon: 'pi pi-fw pi-bookmark' }
]
},
{
label: 'Submenu 1.2',
icon: 'pi pi-fw pi-bookmark',
items: [{ label: 'Submenu 1.2.1', icon: 'pi pi-fw pi-bookmark' }]
}
]
},
{
label: 'Submenu 2',
icon: 'pi pi-fw pi-bookmark',
items: [
{
label: 'Submenu 2.1',
icon: 'pi pi-fw pi-bookmark',
items: [
{ label: 'Submenu 2.1.1', icon: 'pi pi-fw pi-bookmark' },
{ label: 'Submenu 2.1.2', icon: 'pi pi-fw pi-bookmark' }
]
},
{
label: 'Submenu 2.2',
icon: 'pi pi-fw pi-bookmark',
items: [{ label: 'Submenu 2.2.1', icon: 'pi pi-fw pi-bookmark' }]
}
]
}
]
},
{
label: 'Get Started',
items: [
{
label: 'Documentation',
icon: 'pi pi-fw pi-book',
to: '/documentation'
},
{
label: 'View Source',
icon: 'pi pi-fw pi-github',
url: 'https://github.com/primefaces/sakai-vue',
target: '_blank'
}
{ label: 'لیست دسته بندی ها', icon: 'pi pi-fw pi-th-large', to: '/categories/list-categories' },
{ label: 'ویژگی دسته بندی ها', icon: 'pi pi-fw pi-sitemap', to: '/features/list-features' }
]
}
]);
+5 -2
View File
@@ -2,6 +2,7 @@
import { useLayout } from '@/layout/composables/layout';
import { onBeforeMount, ref, watch } from 'vue';
import { useRoute } from 'vue-router';
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
const route = useRoute();
@@ -72,12 +73,14 @@ function checkActiveRoute(item) {
<li :class="{ 'layout-root-menuitem': root, 'active-menuitem': isActiveMenu }">
<div v-if="root && item.visible !== false" class="layout-menuitem-root-text">{{ item.label }}</div>
<a v-if="(!item.to || item.items) && item.visible !== false" :href="item.url" @click="itemClick($event, item, index)" :class="item.class" :target="item.target" tabindex="0">
<i :class="item.icon" class="layout-menuitem-icon"></i>
<i :class="item.icon" class="layout-menuitem-icon mx-1"></i>
<font-awesome-icon :icon="item.faIcon" />
<span class="layout-menuitem-text">{{ item.label }}</span>
<i class="pi pi-fw pi-angle-down layout-submenu-toggler" v-if="item.items"></i>
</a>
<router-link v-if="item.to && !item.items && item.visible !== false" @click="itemClick($event, item, index)" :class="[item.class, { 'active-route': checkActiveRoute(item) }]" tabindex="0" :to="item.to">
<i :class="item.icon" class="layout-menuitem-icon"></i>
<i :class="item.icon" class="layout-menuitem-icon mx-1"></i>
<font-awesome-icon :icon="item.faIcon" />
<span class="layout-menuitem-text">{{ item.label }}</span>
<i class="pi pi-fw pi-angle-down layout-submenu-toggler" v-if="item.items"></i>
</router-link>
+4 -1
View File
@@ -1,10 +1,13 @@
<script setup>
import AppMenu from './AppMenu.vue';
import { ScrollPanel } from 'primevue';
</script>
<template>
<div class="layout-sidebar">
<app-menu></app-menu>
<div style="direction: rtl">
<app-menu></app-menu>
</div>
</div>
</template>
+12 -1
View File
@@ -1,6 +1,13 @@
<script setup>
import { useLayout } from '@/layout/composables/layout';
import AppConfigurator from './AppConfigurator.vue';
import Cookies from 'js-cookie';
import { useRouter } from 'vue-router';
const router = useRouter()
const signOut = () => {
Cookies.set('bm-token', '')
router.push('/auth/login')
}
const { toggleMenu, toggleDarkMode, isDarkTheme } = useLayout();
</script>
@@ -30,7 +37,7 @@ const { toggleMenu, toggleDarkMode, isDarkTheme } = useLayout();
</g>
</svg>
<span>SAKAI</span>
<span>نیلگون</span>
</router-link>
</div>
@@ -72,6 +79,10 @@ const { toggleMenu, toggleDarkMode, isDarkTheme } = useLayout();
<i class="pi pi-user"></i>
<span>Profile</span>
</button>
<button type="button" class="layout-topbar-action" @click="signOut">
<i class="pi pi-power-off"></i>
<span>Profile</span>
</button>
</div>
</div>
</div>
+1 -1
View File
@@ -15,7 +15,7 @@ const layoutState = reactive({
configSidebarVisible: false,
staticMenuMobileActive: false,
menuHoverActive: false,
activeMenuItem: null
activeMenuItem: null,
});
export function useLayout() {
+10 -1
View File
@@ -1,15 +1,21 @@
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import { library } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
import { fas } from '@fortawesome/free-solid-svg-icons';
import '@fortawesome/fontawesome-free/css/all.css';
import Aura from '@primevue/themes/aura';
import PrimeVue from 'primevue/config';
import ConfirmationService from 'primevue/confirmationservice';
import ToastService from 'primevue/toastservice';
import * as VueCookies from 'vue-cookies';
import '@/assets/styles.scss';
import '@/assets/tailwind.css';
library.add(fas);
const app = createApp(App);
app.use(router);
@@ -23,5 +29,8 @@ app.use(PrimeVue, {
});
app.use(ToastService);
app.use(ConfirmationService);
app.use(VueCookies, { expires: '1000d', sameSite: 'None', secure: true });
app.component('font-awesome-icon', FontAwesomeIcon);
app.mount('#app');
+45
View File
@@ -1,5 +1,29 @@
import AppLayout from '@/layout/AppLayout.vue';
import { createRouter, createWebHistory } from 'vue-router';
import axios from 'axios';
import Cookies from 'js-cookie';
const loginGuard = async (to, from, next) => {
const token = Cookies.get('bm-token');
if (!token) {
next('/auth/login'); // Redirect to log in if no token
return;
}
try {
const response = await axios.get('/api/users/user/get-self', {
headers: {
Authorization: `Bearer ${token}`
},
withCredentials: true
});
next();
} catch (error) {
console.log('Authentication error:', error);
next('/auth/login');
}
};
const router = createRouter({
history: createWebHistory(),
@@ -7,12 +31,33 @@ const router = createRouter({
{
path: '/',
component: AppLayout,
beforeEnter: loginGuard,
children: [
{
path: '/',
name: 'dashboard',
component: () => import('@/views/Dashboard.vue')
},
{
path: '/belonging/create-belonging',
name: 'create-belonging',
component: () => import('@/views/pages/Belonging/CreateBelonging.vue')
},
{
path: '/belonging/list-belongings',
name: 'list-belongings',
component: () => import('@/views/pages/Belonging/ListBelongings.vue')
},
{
path: '/categories/list-categories',
name: 'list-categories',
component: () => import('@/views/pages/Category/ListCategories.vue')
},
{
path: '/features/list-features',
name: 'list-features',
component: () => import('@/views/pages/Features/ListFeatures.vue')
},
{
path: '/uikit/formlayout',
name: 'formlayout',
+17
View File
@@ -0,0 +1,17 @@
import axios from 'axios';
export const BelongingService = {
getAllBelongings: async ()=> {
const config = {
method: 'get',
url: '/api/belongings/admin/get-all',
withCredentials: true,
}
try {
const response = await axios(config)
return response.data.data
} catch (err) {
console.log(err)
}
}
}
+47
View File
@@ -0,0 +1,47 @@
import axios from 'axios';
export const CategoryService = {
getAllCategories: async () => {
const config = {
method: 'get',
url: '/api/categories/admin/get-all',
withCredentials: true
};
try {
const response = await axios(config);
return response.data.data;
} catch (err) {
console.log(err);
}
},
createCategory: async (data) => {
const config = {
method: 'post',
url: '/api/categories/admin/create',
data,
withCredentials: true
};
try {
const response = await axios(config);
return 'success';
} catch (err) {
return err.response.data.message;
}
},
updateCategory: async (categoryId, data) => {
const config = {
method: 'put',
url: '/api/categories/admin/update/' + categoryId,
data,
withCredentials: true
};
try {
const response = await axios(config);
return 'success';
} catch (err) {
return err.response.data.message;
}
}
};
+47
View File
@@ -0,0 +1,47 @@
import axios from 'axios';
export const FeatureService = {
getAllFeatures: async () => {
const config = {
method: 'get',
url: '/api/categories/admin/get-all',
withCredentials: true
};
try {
const response = await axios(config);
return response.data.data;
} catch (err) {
console.log(err);
}
},
createFeature: async (data) => {
const config = {
method: 'post',
url: '/api/features/admin/create',
data,
withCredentials: true
};
try {
const response = await axios(config);
return 'success';
} catch (err) {
return err.response.data.message;
}
},
updateFeature: async (featureId, data) => {
const config = {
method: 'put',
url: '/api/features/admin/update/' + featureId,
data,
withCredentials: true
};
try {
const response = await axios(config);
return 'success';
} catch (err) {
return err.response.data.message;
}
}
};
@@ -0,0 +1,63 @@
<script setup>
import { Listbox, InputText } from 'primevue';
import { onBeforeMount, ref } from 'vue';
import { CategoryService } from '@/service/CategoryService';
import ImageUploader from '@/views/uikit/ImageUploader.vue';
import ImageGallery from '@/views/uikit/ImageGallery.vue';
const categories = ref([]);
const selectedCategory = ref();
const belongingName = ref('');
const description = ref('');
const imageNames = ref([]);
const documentNames = ref([]);
const mainImageName = ref([]);
const tags = ref('');
const signedImageUrls = ref([{ url: 'https://upload.hitromall.com/product-images/0ff87b0cc333539a361e007d20aa9685-co3.jpg' }]);
onBeforeMount(async () => {
categories.value = await CategoryService.getAllCategories();
});
</script>
<template>
<div className="card">
<div class="font-semibold text-xl mb-4">ایجاد اموال</div>
<p>از طریق این صفحه می توانید اموال جدید را به سوابق اضافه کنید.</p>
<div class="flex my-6">
<div class="w-3/4 ml-3">
<div class="">
<label> نام دارایی </label>
<InputText v-model="belongingName" class="w-full my-2" placeholder="مثلا: 100 عدد کولر گازی" />
</div>
<div class="my-6">
<label> توضیحات </label>
<Textarea v-model="description" class="w-full my-2" placeholder="مثلا: در تاریخ 1403/09/11 خریداری شد." rows="6" />
</div>
<div class="my-6" v-if="signedImageUrls.length">
<label> تصاویر </label>
<ImageGallery v-model="signedImageUrls" gallery-id="images" />
</div>
<div class="my-6" v-if="signedImageUrls.length">
<label> فایل ها و مدارک </label>
</div>
</div>
<div class="w-1/4">
<div>
<label> دسته بندی </label>
<Listbox v-model="selectedCategory" :options="categories" optionLabel="nativeName" optionValue="_id" checkmark :highlightOnSelect="false" class="w-full my-2" />
</div>
<div class="my-6">
<label> تصویر اصلی </label>
<ImageUploader v-model="mainImageName" :max-files="1" mode="private" delete-endpoint="/api/files/bucket/delete/belonging-images" upload-endpoint="/api/files/bucket/upload-multi-filepond/belonging-images" />
</div>
<div class="my-8">
<label> برچسب ها </label>
<Chips v-model="tags" separator="," class="w-full" placeholder="مثلا: زمین، ملک، کیانپارس..." />
</div>
</div>
</div>
<div class="flex justify-center">
<Button>ذخیره</Button>
</div>
</div>
</template>
@@ -0,0 +1,279 @@
<script setup>
import { ProductService } from '@/service/ProductService';
import { FilterMatchMode } from '@primevue/core/api';
import { useToast } from 'primevue/usetoast';
import { onMounted, ref } from 'vue';
import { BelongingService } from '@/service/BelongingService';
onMounted(() => {
BelongingService.getAllBelongings().then((data) => (belongings.value = data));
});
const toast = useToast();
const dt = ref();
const belongings = ref();
const productDialog = ref(false);
const deleteProductDialog = ref(false);
const deleteProductsDialog = ref(false);
const product = ref({});
const selectedBelongings = ref();
const filters = ref({
global: { value: null, matchMode: FilterMatchMode.CONTAINS }
});
const submitted = ref(false);
const statuses = ref([
{ label: 'INSTOCK', value: 'instock' },
{ label: 'LOWSTOCK', value: 'lowstock' },
{ label: 'OUTOFSTOCK', value: 'outofstock' }
]);
function formatCurrency(value) {
if (value) return value.toLocaleString('en-US', { style: 'currency', currency: 'USD' });
return;
}
function openNew() {
product.value = {};
submitted.value = false;
productDialog.value = true;
}
function hideDialog() {
productDialog.value = false;
submitted.value = false;
}
function saveProduct() {
submitted.value = true;
if (product?.value.name?.trim()) {
if (product.value.id) {
product.value.inventoryStatus = product.value.inventoryStatus.value ? product.value.inventoryStatus.value : product.value.inventoryStatus;
products.value[findIndexById(product.value.id)] = product.value;
toast.add({ severity: 'success', summary: 'Successful', detail: 'Product Updated', life: 3000 });
} else {
product.value.id = createId();
product.value.code = createId();
product.value.image = 'product-placeholder.svg';
product.value.inventoryStatus = product.value.inventoryStatus ? product.value.inventoryStatus.value : 'INSTOCK';
products.value.push(product.value);
toast.add({ severity: 'success', summary: 'Successful', detail: 'Product Created', life: 3000 });
}
productDialog.value = false;
product.value = {};
}
}
function editProduct(prod) {
product.value = { ...prod };
productDialog.value = true;
}
function confirmDeleteProduct(prod) {
product.value = prod;
deleteProductDialog.value = true;
}
function deleteProduct() {
products.value = products.value.filter((val) => val.id !== product.value.id);
deleteProductDialog.value = false;
product.value = {};
toast.add({ severity: 'success', summary: 'Successful', detail: 'Product Deleted', life: 3000 });
}
function findIndexById(id) {
let index = -1;
for (let i = 0; i < products.value.length; i++) {
if (products.value[i].id === id) {
index = i;
break;
}
}
return index;
}
function createId() {
let id = '';
var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for (var i = 0; i < 5; i++) {
id += chars.charAt(Math.floor(Math.random() * chars.length));
}
return id;
}
function exportCSV() {
dt.value.exportCSV();
}
function confirmDeleteSelected() {
deleteProductsDialog.value = true;
}
function deleteSelectedBelongings() {
products.value = products.value.filter((val) => !selectedBelongings.value.includes(val));
deleteProductsDialog.value = false;
selectedBelongings.value = null;
toast.add({ severity: 'success', summary: 'Successful', detail: 'Products Deleted', life: 3000 });
}
function getStatusLabel(status) {
switch (status) {
case 'INSTOCK':
return 'success';
case 'LOWSTOCK':
return 'warn';
case 'OUTOFSTOCK':
return 'danger';
default:
return null;
}
}
</script>
<template>
<div>
<div class="card">
<Toolbar class="mb-6">
<template #start>
<Button label="ساخت دارایی جدید" icon="pi pi-plus" severity="secondary" class="mr-2 mx-3" @click="openNew" />
<Button label="حذف دارایی" icon="pi pi-trash" severity="secondary" @click="confirmDeleteSelected" :disabled="!selectedBelongings || !selectedBelongings.length" />
</template>
<template #end>
</template>
</Toolbar>
<DataTable
ref="dt"
v-model:selection="selectedBelongings"
:value="belongings"
dataKey="id"
:paginator="true"
:rows="20"
:filters="filters"
paginatorTemplate="FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink CurrentPageReport RowsPerPageDropdown"
:rowsPerPageOptions="[20, 40, 80]"
currentPageReportTemplate="نمایش {first} تا {last} از {totalRecords} دارایی"
>
<template #header>
<div class="flex flex-wrap gap-2 items-center justify-between">
<h4 class="m-0">مدیریت اموال</h4>
<IconField>
<InputIcon>
<i class="pi pi-search" />
</InputIcon>
<InputText v-model="filters['global'].value" placeholder="جستجو..." />
</IconField>
</div>
</template>
<Column selectionMode="multiple" style="width: 3rem" :exportable="false"></Column>
<Column header="تصویر">
<template #body="slotProps">
<img :src="`https://primefaces.org/cdn/primevue/images/product/${slotProps.data.image}`" :alt="slotProps.data.image" class="rounded" style="width: 64px" />
</template>
</Column>
<Column field="belongingCode" header="کد" style="min-width: 12rem"></Column>
<Column field="name" header="نام" sortable style="min-width: 16rem"></Column>
<Column field="category" header="دسته بندی" sortable style="min-width: 10rem">
<template #body="slotProps">
{{ slotProps.data.nativeName }}
</template>
</Column>
<Column :exportable="false" style="min-width: 12rem">
<template #body="slotProps">
<Button icon="pi pi-pencil" outlined rounded class="mr-2" @click="editProduct(slotProps.data)" />
<Button icon="pi pi-trash" outlined rounded severity="danger" @click="confirmDeleteProduct(slotProps.data)" />
</template>
</Column>
</DataTable>
</div>
<Dialog v-model:visible="productDialog" :style="{ width: '450px' }" header="Product Details" :modal="true">
<div class="flex flex-col gap-6">
<img v-if="product.image" :src="`https://primefaces.org/cdn/primevue/images/product/${product.image}`" :alt="product.image" class="block m-auto pb-4" />
<div>
<label for="name" class="block font-bold mb-3">Name</label>
<InputText id="name" v-model.trim="product.name" required="true" autofocus :invalid="submitted && !product.name" fluid />
<small v-if="submitted && !product.name" class="text-red-500">Name is required.</small>
</div>
<div>
<label for="description" class="block font-bold mb-3">Description</label>
<Textarea id="description" v-model="product.description" required="true" rows="3" cols="20" fluid />
</div>
<div>
<label for="inventoryStatus" class="block font-bold mb-3">Inventory Status</label>
<Select id="inventoryStatus" v-model="product.inventoryStatus" :options="statuses" optionLabel="label" placeholder="Select a Status" fluid></Select>
</div>
<div>
<span class="block font-bold mb-4">Category</span>
<div class="grid grid-cols-12 gap-4">
<div class="flex items-center gap-2 col-span-6">
<RadioButton id="category1" v-model="product.category" name="category" value="Accessories" />
<label for="category1">Accessories</label>
</div>
<div class="flex items-center gap-2 col-span-6">
<RadioButton id="category2" v-model="product.category" name="category" value="Clothing" />
<label for="category2">Clothing</label>
</div>
<div class="flex items-center gap-2 col-span-6">
<RadioButton id="category3" v-model="product.category" name="category" value="Electronics" />
<label for="category3">Electronics</label>
</div>
<div class="flex items-center gap-2 col-span-6">
<RadioButton id="category4" v-model="product.category" name="category" value="Fitness" />
<label for="category4">Fitness</label>
</div>
</div>
</div>
<div class="grid grid-cols-12 gap-4">
<div class="col-span-6">
<label for="price" class="block font-bold mb-3">Price</label>
<InputNumber id="price" v-model="product.price" mode="currency" currency="USD" locale="en-US" fluid />
</div>
<div class="col-span-6">
<label for="quantity" class="block font-bold mb-3">Quantity</label>
<InputNumber id="quantity" v-model="product.quantity" integeronly fluid />
</div>
</div>
</div>
<template #footer>
<Button label="Cancel" icon="pi pi-times" text @click="hideDialog" />
<Button label="Save" icon="pi pi-check" @click="saveProduct" />
</template>
</Dialog>
<Dialog v-model:visible="deleteProductDialog" :style="{ width: '450px' }" header="Confirm" :modal="true">
<div class="flex items-center gap-4">
<i class="pi pi-exclamation-triangle !text-3xl" />
<span v-if="product"
>Are you sure you want to delete <b>{{ product.name }}</b
>?</span
>
</div>
<template #footer>
<Button label="No" icon="pi pi-times" text @click="deleteProductDialog = false" />
<Button label="Yes" icon="pi pi-check" @click="deleteProduct" />
</template>
</Dialog>
<Dialog v-model:visible="deleteProductsDialog" :style="{ width: '450px' }" header="Confirm" :modal="true">
<div class="flex items-center gap-4">
<i class="pi pi-exclamation-triangle !text-3xl" />
<span v-if="product">Are you sure you want to delete the selected products?</span>
</div>
<template #footer>
<Button label="No" icon="pi pi-times" text @click="deleteProductsDialog = false" />
<Button label="Yes" icon="pi pi-check" text @click="deleteselectedBelongings" />
</template>
</Dialog>
</div>
</template>
+194
View File
@@ -0,0 +1,194 @@
<script setup>
import { FilterMatchMode } from '@primevue/core/api';
import { onMounted, ref } from 'vue';
import { CategoryService } from '@/service/CategoryService';
import { useToast } from 'primevue/usetoast';
const toast = useToast();
import axios from 'axios';
onMounted(() => {
CategoryService.getAllCategories().then((data) => (categories.value = data));
});
const dt = ref();
const categories = ref();
const categoryDialog = ref(false);
const deleteCategoryDialog = ref(false);
const deleteProductsDialog = ref(false);
const category = ref({});
const selectedCategories = ref();
const filters = ref({
global: { value: null, matchMode: FilterMatchMode.CONTAINS }
});
const submitted = ref(false);
function openNew() {
category.value = {};
submitted.value = false;
categoryDialog.value = true;
}
function hideDialog() {
categoryDialog.value = false;
submitted.value = false;
}
async function saveCategory() {
submitted.value = true;
let status = 'failed';
if (category?.value._id?.trim()) {
status = await CategoryService.updateCategory(category.value._id, category.value);
} else {
status = await CategoryService.createCategory(category.value);
}
if (status === 'success') {
toast.add({ severity: 'success', summary: 'موفق', detail: 'دسته بندی جدید ساخته شد.', life: 3000 });
} else {
toast.add({ severity: 'error', summary: 'ناموفق', detail: status, life: 3000 });
}
categoryDialog.value = false;
category.value = {};
CategoryService.getAllCategories().then((data) => (categories.value = data));
}
function editCategory(cat) {
category.value = { ...cat };
categoryDialog.value = true;
}
function confirmDeleteCategory(prod) {
category.value = prod;
deleteCategoryDialog.value = true;
}
async function deleteCategory() {
const config = {
method: 'delete',
url: '/api/categories/admin/delete/' + category.value._id,
data: category.value,
withCredentials: true
};
try {
const response = await axios(config);
toast.add({ severity: 'success', summary: 'موفق', detail: 'دسته بندی جدید حذف شد.', life: 3000 });
} catch (err) {
toast.add({ severity: 'error', summary: 'ناموفق', detail: err.response.data.message, life: 3000 });
}
CategoryService.getAllCategories().then((data) => (categories.value = data));
deleteCategoryDialog.value = false;
category.value = {};
}
function exportCSV() {
dt.value.exportCSV();
}
function confirmDeleteSelected() {
deleteProductsDialog.value = true;
}
function deleteSelectedCategories() {
categories.value = categories.value.filter((val) => !selectedCategories.value.includes(val));
deleteProductsDialog.value = false;
selectedCategories.value = null;
toast.add({ severity: 'success', summary: 'موفقیت آمیز', detail: 'دسته بندی مورد نظر حذف شد.', life: 3000 });
}
</script>
<template>
<div>
<Toast></Toast>
<div class="card">
<Toolbar class="mb-6">
<template #start>
<Button label="ساخت دسته بندی جدید" icon="pi pi-plus" severity="secondary" class="mr-2 mx-3" @click="openNew" />
<Button label="حذف دسته بندی" icon="pi pi-trash" severity="secondary" @click="confirmDeleteSelected" :disabled="!selectedCategories || !selectedCategories.length" />
</template>
<template #end> </template>
</Toolbar>
<DataTable
ref="dt"
v-model:selection="selectedCategories"
:value="categories"
dataKey="id"
:paginator="true"
:rows="20"
:filters="filters"
paginatorTemplate="FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink CurrentPageReport RowsPerPageDropdown"
:rowsPerPageOptions="[20, 40, 80]"
currentPageReportTemplate="نمایش {first} تا {last} از {totalRecords} دارایی"
>
<template #header>
<div class="flex flex-wrap gap-2 items-center justify-between">
<h4 class="m-0">مدیریت اموال</h4>
<IconField>
<InputIcon>
<i class="pi pi-search" />
</InputIcon>
<InputText v-model="filters['global'].value" placeholder="جستجو..." />
</IconField>
</div>
</template>
<Column selectionMode="multiple" style="width: 3rem" :exportable="false"></Column>
<Column field="nativeName" header="نام فارسی" sortable style="min-width: 16rem"></Column>
<Column field="englishName" header="نام انگلیسی" sortable style="min-width: 16rem"></Column>
<Column :exportable="false" style="min-width: 12rem">
<template #body="slotProps">
<Button icon="pi pi-pencil" outlined rounded class="mr-2 mx-3" @click="editCategory(slotProps.data)" />
<Button icon="pi pi-trash" class="mx-3" outlined rounded severity="danger" @click="confirmDeleteCategory(slotProps.data)" />
</template>
</Column>
</DataTable>
</div>
<Dialog v-model:visible="categoryDialog" :style="{ width: '450px' }" header="جزئیات دسته بندی" :modal="true">
<div class="flex flex-col gap-6">
<div>
<label for="nativeName" class="block font-bold mb-3">نام فارسی</label>
<InputText id="nativeName" v-model.trim="category.nativeName" required="true" autofocus :invalid="submitted && !category.nativeName" fluid />
<small v-if="submitted && !category.nativeName" class="text-red-500">ورود نام فارسی الزامی است.</small>
</div>
<div>
<label for="englishName" class="block font-bold mb-3">نام انگلیسی</label>
<InputText id="englishName" v-model.trim="category.englishName" required="true" autofocus :invalid="submitted && !category.englishName" fluid />
<small v-if="submitted && !category.englishName" class="text-red-500">ورود نام انگلیسی الزامی است.</small>
</div>
</div>
<template #footer>
<Button label="لغو" icon="pi pi-times" text @click="hideDialog" />
<Button label="ذخیره" icon="pi pi-check" @click="saveCategory" />
</template>
</Dialog>
<Dialog v-model:visible="deleteCategoryDialog" :style="{ width: '450px' }" header="تایید نهایی" :modal="true">
<div class="flex items-center gap-4">
<i class="pi pi-exclamation-triangle !text-3xl" />
<span v-if="category"
>آیا از حذف دسته بندی <b>{{ category.nativeName }} </b> مطمئن هستید؟</span
>
</div>
<template #footer>
<Button label="خیر" icon="pi pi-times" text @click="deleteCategoryDialog = false" />
<Button label="بله" icon="pi pi-check" @click="deleteCategory" />
</template>
</Dialog>
<Dialog v-model:visible="deleteProductsDialog" :style="{ width: '450px' }" header="Confirm" :modal="true">
<div class="flex items-center gap-4">
<i class="pi pi-exclamation-triangle !text-3xl" />
<span v-if="product">Are you sure you want to delete the selected products?</span>
</div>
<template #footer>
<Button label="No" icon="pi pi-times" text @click="deleteProductsDialog = false" />
<Button label="Yes" icon="pi pi-check" text @click="deleteSelectedCategories" />
</template>
</Dialog>
</div>
</template>
+124
View File
@@ -0,0 +1,124 @@
<script setup>
import { onBeforeMount, onMounted, ref } from 'vue';
import { CategoryService } from '@/service/CategoryService';
import { Tabs, TabList, Tab } from 'primevue';
import { useToast } from 'primevue/usetoast';
import axios from 'axios';
import { FeatureService } from '@/service/FeatureService';
const toast = useToast();
const selectedTab = ref('');
const selectedFeature = ref();
const submitted = ref(false);
const feature = ref({});
const categories = ref([]);
const featureDialog = ref(false);
onMounted(async () => {
categories.value = await CategoryService.getAllCategories();
});
const featureType = [
{
value: 'string',
name: 'متن'
},
{
value: 'boolean',
name: 'بله یا خیر'
},
{
value: 'number',
name: 'عدد'
},
{
value: 'date',
name: 'تاریخ'
}
];
function hideDialog() {
featureDialog.value = false;
submitted.value = false;
}
function openNew(categoryId) {
feature.value = {};
feature.value.category = categoryId;
submitted.value = false;
featureDialog.value = true;
}
const editFeature = (event, categoryId) => {
feature.value = event.value;
submitted.value = false;
featureDialog.value = true;
};
const saveFeature = async () => {
submitted.value = true;
let status = 'failed';
if (feature?.value._id?.trim()) {
status = await FeatureService.updateFeature(feature.value._id, feature.value);
} else {
status = await FeatureService.createFeature(feature.value);
}
if (status === 'success') {
toast.add({ severity: 'success', summary: 'موفق', detail: 'ویژگی جدید ساخته شد.', life: 3000 });
} else {
toast.add({ severity: 'error', summary: 'ناموفق', detail: status, life: 3000 });
}
featureDialog.value = false;
feature.value = {};
CategoryService.getAllCategories().then((data) => (categories.value = data));
};
</script>
<template>
<div className="card">
<div class="font-semibold text-xl mb-4">ویژگی دسته بندی ها</div>
<Tabs :value="selectedTab" :pt="{}">
<TabList>
<Tab v-for="category in categories" :key="category._id" :value="category._id">
{{ category.nativeName }}
</Tab>
</TabList>
<TabPanels>
<TabPanel v-for="category in categories" :key="category._id + '_content'" :value="category._id">
<p>لیست ویژگی های {{ category.nativeName }}</p>
<div class="w-1/3 my-3">
<Listbox filter checkmark :options="category.features" option-value="_id" option-label="nativeName" @item-dblclick="editFeature($event, category._id)" emptyMessage="موردی برای نمایش وجود ندارد."></Listbox>
<Button class="my-3" :categoryId="category._id" @click="openNew(category._id)">افزودن</Button>
</div>
</TabPanel>
</TabPanels>
</Tabs>
<Dialog v-model:visible="featureDialog" :style="{ width: '450px' }" header="ساخت ویژگی" :modal="true">
<div class="flex flex-col gap-6">
<div>
<label for="nativeName" class="block font-bold mb-3">نام فارسی</label>
<InputText id="nativeName" v-model.trim="feature.nativeName" required="true" autofocus :invalid="submitted && !feature.nativeName" fluid />
<small v-if="submitted && !feature.nativeName" class="text-red-500">ورود نام فارسی الزامی است.</small>
</div>
<div>
<label for="englishName" class="block font-bold mb-3">نام انگلیسی</label>
<InputText id="englishName" v-model.trim="feature.englishName" required="true" autofocus :invalid="submitted && !feature.englishName" fluid />
<small v-if="submitted && !feature.englishName" class="text-red-500">ورود نام انگلیسی الزامی است.</small>
</div>
<div>
<label for="unit" class="block font-bold mb-3">واحد</label>
<InputText id="unit" v-model.trim="feature.unit" required="true" autofocus :invalid="submitted && !feature.unit" fluid />
</div>
<div class="mb-6">
<label for="featureType" class="block font-bold mb-3">نوع ویژگی</label>
<Select id="featureType" option-label="name" option-value="value" :options="featureType" v-model.trim="feature.featureType" autofocus :invalid="submitted && !feature.featureType" fluid />
<small v-if="submitted && !feature.featureType" class="text-red-500">ورود نوع ویژگی الزامی است.</small>
</div>
</div>
<template #footer>
<Button label="لغو" icon="pi pi-times" text @click="hideDialog" />
<Button label="ذخیره" icon="pi pi-check" @click="saveFeature" />
</template>
</Dialog>
</div>
</template>
+45 -14
View File
@@ -1,10 +1,45 @@
<script setup>
import FloatingConfigurator from '@/components/FloatingConfigurator.vue';
import { ref } from 'vue';
import { onBeforeMount, ref } from 'vue';
import axios from 'axios';
import { useRouter } from 'vue-router';
const router = useRouter()
const email = ref('');
const password = ref('');
const checked = ref(false);
const loginLoading = ref(false)
import Cookies from 'js-cookie';
const login = async () => {
try {
loginLoading.value = true
const response = await axios.post('/api/users/user/login-email', {
email: email.value,
password: password.value,
})
console.log(response)
Cookies.set('bm-token', response.data.data.token)
localStorage.setItem('bm-token', response.data.data.token)
await router.push('/')
} catch (error) {
console.log('err', error)
loginLoading.value = false
}
}
onBeforeMount(async () => {
const token = Cookies.get('bm-token')
try {
const response = await axios.get('/api/users/user/get-self', {
headers: {
Authorization: `Bearer ${token}`
}
})
console.log(response)
await router.push('/')
} catch (error) {
console.log(error)
}
})
</script>
<template>
@@ -31,25 +66,21 @@ const checked = ref(false);
/>
</g>
</svg>
<div class="text-surface-900 dark:text-surface-0 text-3xl font-medium mb-4">Welcome to PrimeLand!</div>
<span class="text-muted-color font-medium">Sign in to continue</span>
<div class="text-surface-900 dark:text-surface-0 text-3xl font-medium mb-4">نرم افزار مدیریت اموال نیلگون</div>
<span class="text-muted-color font-medium">برای ادامه وارد شوید</span>
</div>
<div>
<label for="email1" class="block text-surface-900 dark:text-surface-0 text-xl font-medium mb-2">Email</label>
<InputText id="email1" type="text" placeholder="Email address" class="w-full md:w-[30rem] mb-8" v-model="email" />
<label for="email1" class="block text-surface-900 dark:text-surface-0 text-xl font-medium mb-2">ایمیل</label>
<InputText id="email1" type="text" placeholder="آدرس ایمیل" class="w-full md:w-[30rem] mb-8" v-model="email" />
<label for="password1" class="block text-surface-900 dark:text-surface-0 font-medium text-xl mb-2">Password</label>
<Password id="password1" v-model="password" placeholder="Password" :toggleMask="true" class="mb-4" fluid :feedback="false"></Password>
<label for="password1" class="block text-surface-900 dark:text-surface-0 font-medium text-xl mb-2">رمز عبور</label>
<Password id="password1" v-model="password" placeholder="رمز عبور" :toggleMask="true" class="mb-4" fluid :feedback="false"></Password>
<div class="flex items-center justify-between mt-2 mb-8 gap-8">
<div class="flex items-center">
<Checkbox v-model="checked" id="rememberme1" binary class="mr-2"></Checkbox>
<label for="rememberme1">Remember me</label>
</div>
<span class="font-medium no-underline ml-2 text-right cursor-pointer text-primary">Forgot password?</span>
<span class="font-medium no-underline ml-2 text-right cursor-pointer text-primary">فراموشی رمز عبور</span>
</div>
<Button label="Sign In" class="w-full" as="router-link" to="/"></Button>
<Button :loading="loginLoading" label="ورود" class="w-full" @click="login"></Button>
</div>
</div>
</div>
+95
View File
@@ -0,0 +1,95 @@
<template>
<div
:id="galleryId"
class="pswp-gallery pswp-gallery--single-column my-3 flex min-w-0 flex-row flex-nowrap overflow-x-auto rounded-md bg-brand-dark-3 p-3"
>
<div v-for="(image, key) in imagesData" :key="key" class="relative">
<a
:href="image.url"
:data-pswp-width="image.width"
:data-pswp-height="image.height"
target="_blank"
rel="noreferrer"
class="mx-3 block flex-shrink-0"
>
<img :src="image.url" alt="" class="max-h-60 rounded-md" :style="{ height: '150px' }" />
</a>
<button
class="absolute right-4 top-2 flex h-6 w-6 items-center justify-center rounded-full text-brand-danger"
@click="removeImage(key)"
>
<font-awesome-icon icon="fa-solid fa-x" />
</button>
</div>
</div>
</template>
<script setup>
import { ref, watch, onMounted } from 'vue'
import PhotoSwipeLightbox from 'photoswipe/lightbox'
import 'photoswipe/style.css'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
function updateImagesData(images) {
for (let i = 0; i < images.length; i++) {
const img = new Image()
img.src = images[i].url
img.onload = function () {
images[i].height = img.naturalHeight
images[i].width = img.naturalWidth
}
}
return images
}
// Props
const props = defineProps({
galleryId: {
type: String,
required: true
},
modelValue: {
type: Array,
required: true
}
})
const imagesData = ref([])
let lightbox = null
// Lifecycle hook: mounted
onMounted(() => {
if (!lightbox) {
console.log('Launching Gallery')
console.log({
gallery: '#' + props.galleryId,
children: 'a'
})
lightbox = new PhotoSwipeLightbox({
gallery: '#' + props.galleryId,
children: 'a',
pswpModule: () => import('photoswipe')
})
lightbox.init()
}
imagesData.value = updateImagesData(props.modelValue)
})
watch(
() => props.modelValue,
(newImages) => {
console.log('propsimages', newImages)
imagesData.value = updateImagesData(newImages)
},
{ deep: true, immediate: true }
)
const emit = defineEmits(['update:modelValue'])
function removeImage(index) {
const newImages = [...props.modelValue]
newImages.splice(index, 1)
emit('update:modelValue', newImages)
}
</script>
+95
View File
@@ -0,0 +1,95 @@
<template>
<div
:id="galleryId"
class="pswp-gallery pswp-gallery--single-column my-3 flex min-w-0 flex-row flex-nowrap overflow-x-auto rounded-md bg-brand-dark-3 p-3"
>
<div v-for="(image, key) in imagesData" :key="key" class="relative">
<a
:href="image.url"
:data-pswp-width="image.width"
:data-pswp-height="image.height"
target="_blank"
rel="noreferrer"
class="mx-3 block flex-shrink-0"
>
<img :src="image.url" alt="" class="max-h-60 rounded-md" :style="{ height: '150px' }" />
</a>
<button
class="absolute right-4 top-2 flex h-6 w-6 items-center justify-center rounded-full text-brand-danger"
@click="removeImage(key)"
>
<font-awesome-icon icon="fa-solid fa-x" />
</button>
</div>
</div>
</template>
<script setup>
import { ref, watch, onMounted } from 'vue'
import PhotoSwipeLightbox from 'photoswipe/lightbox'
import 'photoswipe/style.css'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
function updateImagesData(images) {
for (let i = 0; i < images.length; i++) {
const img = new Image()
img.src = images[i].url
img.onload = function () {
images[i].height = img.naturalHeight
images[i].width = img.naturalWidth
}
}
return images
}
// Props
const props = defineProps({
galleryId: {
type: String,
required: true
},
modelValue: {
type: Array,
required: true
}
})
const imagesData = ref([])
let lightbox = null
// Lifecycle hook: mounted
onMounted(() => {
if (!lightbox) {
console.log('Launching Gallery')
console.log({
gallery: '#' + props.galleryId,
children: 'a'
})
lightbox = new PhotoSwipeLightbox({
gallery: '#' + props.galleryId,
children: 'a',
pswpModule: () => import('photoswipe')
})
lightbox.init()
}
imagesData.value = updateImagesData(props.modelValue)
})
watch(
() => props.modelValue,
(newImages) => {
console.log('propsimages', newImages)
imagesData.value = updateImagesData(newImages)
},
{ deep: true, immediate: true }
)
const emit = defineEmits(['update:modelValue'])
function removeImage(index) {
const newImages = [...props.modelValue]
newImages.splice(index, 1)
emit('update:modelValue', newImages)
}
</script>
+143
View File
@@ -0,0 +1,143 @@
<template>
<div id="app">
<FilePond
ref="pond"
name="filepond-image"
:max-files="maxFiles"
store-as-file="true"
:server="{
process: {
url: API_URL + uploadEndpoint,
method: 'POST',
withCredentials: true,
headers: {
authorization: 'Bearer ' + Cookies.get('bm-token')
},
onload: (response) => handleProcessResponse(response)
},
revert: {
url: API_URL + deleteEndpoint,
method: 'DELETE',
withCredentials: true,
headers: {
authorization: 'Bearer ' + Cookies.get('bm-token')
},
onload: (response) => handleRevertResponse(response)
}
}"
label-idle="برای آپلود تصاویر کلیک کنید"
label-tap-to-undo=" برای حذف کلیک کنید "
label-file-processing=" در حال آپلود "
label-file-processing-complete=" آپلود فایل تکمیل شد "
:allow-multiple="true"
accepted-file-types="image/jpeg, image/png"
v-model="uploadedFiles"
/>
</div>
</template>
<script setup>
import { ref, watch, defineProps, computed } from 'vue';
import Cookies from 'js-cookie';
import vueFilePond from 'vue-filepond';
import 'filepond/dist/filepond.min.css';
import 'filepond-plugin-image-preview/dist/filepond-plugin-image-preview.min.css';
import FilePondPluginFileValidateType from 'filepond-plugin-file-validate-type';
import FilePondPluginImagePreview from 'filepond-plugin-image-preview';
// Define props
const props = defineProps({
uploadEndpoint: {
type: String,
required: true
},
deleteEndpoint: {
type: String,
required: true
},
maxFiles: {
type: Number,
default: 1
},
mode: {
type: String,
required: true,
default: 'public'
},
modelValue: {
type: Array,
required: false,
default: () => []
}
});
// Setup reactive variables
const API_URL = import.meta.env.VITE_API_URL;
const uploadedFiles = ref([...props.modelValue]);
// Define FilePond component
const FilePond = vueFilePond(FilePondPluginFileValidateType, FilePondPluginImagePreview);
const emit = defineEmits(['update:modelValue']);
// Handle server responses for file processing
function handleProcessResponse(response) {
const data = JSON.parse(response);
const fileName = data.data[0].fileRecord.fileName;
uploadedFiles.value.push(fileName);
emit('update:modelValue', uploadedFiles);
return fileName;
}
// Handle server responses for file revert
function handleRevertResponse(response) {
const data = JSON.parse(response);
const fileId = data.data.fileRecord.id;
// Find and remove the file name from uploadedFiles
const fileIndex = uploadedFiles.value.findIndex((name) => name === fileId);
if (fileIndex !== -1) {
uploadedFiles.value.splice(fileIndex, 1);
emit('update:modelValue', uploadedFiles);
}
return fileId;
}
</script>
<style>
/* Apply dark mode styles to FilePond component */
.filepond--root {
font-family: 'Estedad FD', sans-serif;
background-color: var(--surface-ground);
color: var(--text-color);
border-radius: 8px;
}
.app-dark .filepond--root {
background-color: var(--code-background);
border-radius: 8px;
}
.filepond--label {
color: var(--text-color-secondary);
}
.app-dark .filepond--label {
color: var(--code-color);
}
.app-dark .filepond--drop-label {
background-color: var(--code-background);
color: var(--code-color);
border: 1px solid var(--surface-border);
border-radius: 8px;
}
.filepond--item-panel {
background-color: var(--surface-card);
}
.app-dark .filepond--item-panel {
background-color: var(--code-background);
}
</style>
+48
View File
@@ -0,0 +1,48 @@
<template>
<FloatLabel>
<ToggleSwitch :id="id" v-model="computedValue" :class="inputClass" @change="handleChange" @keyup="handleKeyUp" />
<label :for="id">label</label>
</FloatLabel>
</template>
<script setup>
import ToggleSwitch from 'primevue/toggleswitch';
import { computed } from 'vue';
const props = defineProps({
label: {
type: String,
default: ''
},
inputClass: {
type: String,
required: false,
default: ''
},
modelValue: {
type: Boolean,
default: false
},
id: {
type: String,
default: ''
}
});
const emit = defineEmits(['update:modelValue', 'key-up-event', 'change-event']);
const computedValue = computed({
get: () => props.modelValue,
set: (newValue) => {
emit('update:modelValue', newValue);
}
});
const handleChange = (event) => {
emit('change-event', event);
};
const handleKeyUp = (event) => {
emit('key-up-event', event);
};
</script>