base structure
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
VITE_TOKEN_NAME = 'dpage-editor-t'
|
||||||
|
VITE_REFRESH_TOKEN_NAME = 'dpage-editor-t'
|
||||||
Generated
+193
-6
@@ -9,10 +9,15 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tailwindcss/vite": "^4.1.17",
|
"@tailwindcss/vite": "^4.1.17",
|
||||||
|
"i18next": "^25.6.2",
|
||||||
|
"iconsax-react": "^0.0.8",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
"react-dom": "^19.2.0",
|
"react-dom": "^19.2.0",
|
||||||
|
"react-i18next": "^16.3.0",
|
||||||
"react-router-dom": "^7.9.5",
|
"react-router-dom": "^7.9.5",
|
||||||
"tailwindcss": "^4.1.17"
|
"tailwind-merge": "^3.4.0",
|
||||||
|
"tailwindcss": "^4.1.17",
|
||||||
|
"zustand": "^5.0.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.39.1",
|
"@eslint/js": "^9.39.1",
|
||||||
@@ -265,6 +270,15 @@
|
|||||||
"@babel/core": "^7.0.0-0"
|
"@babel/core": "^7.0.0-0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@babel/runtime": {
|
||||||
|
"version": "7.28.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz",
|
||||||
|
"integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@babel/template": {
|
"node_modules/@babel/template": {
|
||||||
"version": "7.27.2",
|
"version": "7.27.2",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
|
||||||
@@ -1644,7 +1658,7 @@
|
|||||||
"version": "19.2.2",
|
"version": "19.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz",
|
||||||
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
|
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -2214,7 +2228,7 @@
|
|||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
@@ -2711,6 +2725,59 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/html-parse-stringify": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"void-elements": "3.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/i18next": {
|
||||||
|
"version": "25.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.6.2.tgz",
|
||||||
|
"integrity": "sha512-0GawNyVUw0yvJoOEBq1VHMAsqdM23XrHkMtl2gKEjviJQSLVXsrPqsoYAxBEugW5AB96I2pZkwRxyl8WZVoWdw==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://locize.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://locize.com/i18next.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.27.6"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "^5"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"typescript": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/iconsax-react": {
|
||||||
|
"version": "0.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/iconsax-react/-/iconsax-react-0.0.8.tgz",
|
||||||
|
"integrity": "sha512-l3dVk4zGtkkJHgvNYqAf0wDKqnKxXykee5/DoESGo2JvSYwaxajJUHSX2YrPRXSov8Hd8ClGFwJxCEaEjrFD1Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"prop-types": "^15.7.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ignore": {
|
"node_modules/ignore": {
|
||||||
"version": "5.3.2",
|
"version": "5.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
|
||||||
@@ -2801,7 +2868,6 @@
|
|||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/js-yaml": {
|
"node_modules/js-yaml": {
|
||||||
@@ -3160,6 +3226,18 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/loose-envify": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"loose-envify": "cli.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/lru-cache": {
|
"node_modules/lru-cache": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
|
||||||
@@ -3255,6 +3333,15 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/object-assign": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
|
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/optionator": {
|
"node_modules/optionator": {
|
||||||
"version": "0.9.4",
|
"version": "0.9.4",
|
||||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
||||||
@@ -3395,6 +3482,17 @@
|
|||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/prop-types": {
|
||||||
|
"version": "15.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||||
|
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"loose-envify": "^1.4.0",
|
||||||
|
"object-assign": "^4.1.1",
|
||||||
|
"react-is": "^16.13.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/punycode": {
|
"node_modules/punycode": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||||
@@ -3431,7 +3529,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
|
||||||
"integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
|
"integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@@ -3449,6 +3546,39 @@
|
|||||||
"react": "^19.2.0"
|
"react": "^19.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-i18next": {
|
||||||
|
"version": "16.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-16.3.0.tgz",
|
||||||
|
"integrity": "sha512-XGYIVU6gCOL4UQsfp87WbbvBc2WvgdkEDI8r4TwACzFg1bXY8pd1d9Cw6u9WJ2soTKHKaF1xQEyWA3/dUvtAGw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.27.6",
|
||||||
|
"html-parse-stringify": "^3.0.1",
|
||||||
|
"use-sync-external-store": "^1.6.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"i18next": ">= 25.6.2",
|
||||||
|
"react": ">= 16.8.0",
|
||||||
|
"typescript": "^5"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"react-dom": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react-native": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"typescript": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-is": {
|
||||||
|
"version": "16.13.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
|
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/react-refresh": {
|
"node_modules/react-refresh": {
|
||||||
"version": "0.18.0",
|
"version": "0.18.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz",
|
||||||
@@ -3663,6 +3793,16 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tailwind-merge": {
|
||||||
|
"version": "3.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz",
|
||||||
|
"integrity": "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/dcastil"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tailwindcss": {
|
"node_modules/tailwindcss": {
|
||||||
"version": "4.1.17",
|
"version": "4.1.17",
|
||||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz",
|
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz",
|
||||||
@@ -3771,7 +3911,7 @@
|
|||||||
"version": "5.9.3",
|
"version": "5.9.3",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -3854,6 +3994,15 @@
|
|||||||
"punycode": "^2.1.0"
|
"punycode": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/use-sync-external-store": {
|
||||||
|
"version": "1.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
|
||||||
|
"integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "7.2.2",
|
"version": "7.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-7.2.2.tgz",
|
||||||
@@ -3959,6 +4108,15 @@
|
|||||||
"url": "https://github.com/sponsors/jonschlinkert"
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/void-elements": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/which": {
|
"node_modules/which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
@@ -4004,6 +4162,35 @@
|
|||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"node_modules/zustand": {
|
||||||
|
"version": "5.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.8.tgz",
|
||||||
|
"integrity": "sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.20.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": ">=18.0.0",
|
||||||
|
"immer": ">=9.0.6",
|
||||||
|
"react": ">=18.0.0",
|
||||||
|
"use-sync-external-store": ">=1.2.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"immer": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"use-sync-external-store": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-1
@@ -11,10 +11,15 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tailwindcss/vite": "^4.1.17",
|
"@tailwindcss/vite": "^4.1.17",
|
||||||
|
"i18next": "^25.6.2",
|
||||||
|
"iconsax-react": "^0.0.8",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
"react-dom": "^19.2.0",
|
"react-dom": "^19.2.0",
|
||||||
|
"react-i18next": "^16.3.0",
|
||||||
"react-router-dom": "^7.9.5",
|
"react-router-dom": "^7.9.5",
|
||||||
"tailwindcss": "^4.1.17"
|
"tailwind-merge": "^3.4.0",
|
||||||
|
"tailwindcss": "^4.1.17",
|
||||||
|
"zustand": "^5.0.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.39.1",
|
"@eslint/js": "^9.39.1",
|
||||||
|
|||||||
+17
-1
@@ -1,11 +1,27 @@
|
|||||||
import { type FC } from 'react'
|
import { type FC } from 'react'
|
||||||
import './assets/fonts/irancell/style.css'
|
import './assets/fonts/irancell/style.css'
|
||||||
import { BrowserRouter } from 'react-router-dom'
|
import { BrowserRouter } from 'react-router-dom'
|
||||||
|
import MainRouter from './router/MainRouter'
|
||||||
|
import i18next from 'i18next'
|
||||||
|
import FaJson from '@/langs/fa.json'
|
||||||
|
import { I18nextProvider } from 'react-i18next'
|
||||||
|
|
||||||
|
i18next.init({
|
||||||
|
interpolation: { escapeValue: false },
|
||||||
|
lng: 'fa',
|
||||||
|
resources: {
|
||||||
|
fa: {
|
||||||
|
global: FaJson
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const App: FC = () => {
|
const App: FC = () => {
|
||||||
return (
|
return (
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
|
<I18nextProvider i18n={i18next}>
|
||||||
|
<MainRouter />
|
||||||
|
</I18nextProvider>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<svg width="39" height="54" viewBox="0 0 39 54" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M31.1559 34.8412C31.1559 34.8412 24.6651 41.094 19.2416 46.3804V53.5924C18.6935 54.0179 38.4905 34.8845 38.4905 34.8845L19.2416 14.6909V22.3573L31.1559 34.8412Z" fill="#1D1D1B"/>
|
||||||
|
<path d="M19.1479 39.0818L0 18.643L0.237992 18.405L19.3931 38.8583L19.1479 39.0818Z" fill="#1D1D1B"/>
|
||||||
|
<path d="M19.1407 37.1635L1.75977 18.6719L1.99776 18.4412L19.3787 36.9399L19.1407 37.1635Z" fill="#1D1D1B"/>
|
||||||
|
<path d="M19.1334 35.2523L3.52661 18.708L3.7574 18.47L19.3786 35.0215L19.1334 35.2523Z" fill="#1D1D1B"/>
|
||||||
|
<path d="M19.1262 33.3337L5.28638 18.7366L5.50995 18.5059L19.3714 33.1029L19.1262 33.3337Z" fill="#1D1D1B"/>
|
||||||
|
<path d="M19.1191 31.4155L7.05347 18.7729L7.26982 18.5349L19.3571 31.1848L19.1191 31.4155Z" fill="#1D1D1B"/>
|
||||||
|
<path d="M0.375071 18.7368L0.144287 18.4988L19.1407 0L19.3715 0.237996L0.375071 18.7368Z" fill="#1D1D1B"/>
|
||||||
|
<path d="M2.15645 18.7442L1.91846 18.5134L19.1263 1.73828L19.3571 1.97628L2.15645 18.7442Z" fill="#1D1D1B"/>
|
||||||
|
<path d="M3.93049 18.7587L3.69971 18.5279L19.1117 3.48364L19.3425 3.71443L3.93049 18.7587Z" fill="#1D1D1B"/>
|
||||||
|
<path d="M5.71187 18.7658L5.47388 18.5422L19.0973 5.22168L19.3281 5.45968L5.71187 18.7658Z" fill="#1D1D1B"/>
|
||||||
|
<path d="M7.49326 18.7799L7.24805 18.5564L19.0901 6.95947L19.3137 7.19747L7.49326 18.7799Z" fill="#1D1D1B"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,91 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 27.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 961.6 222.5" style="enable-background:new 0 0 961.6 222.5;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#1D1D1B;}
|
||||||
|
</style>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path class="st0" d="M246,180.4h-8.7v24.3h-3.6v-24.3H225v-2.9h21V180.4z"/>
|
||||||
|
<path class="st0" d="M279.1,205c-2.7,0-5-0.9-6.7-2.7s-2.6-4.2-2.6-7.2v-0.6c0-2,0.4-3.8,1.1-5.4s1.8-2.8,3.2-3.7
|
||||||
|
s2.9-1.3,4.5-1.3c2.6,0,4.7,0.9,6.1,2.6c1.5,1.7,2.2,4.2,2.2,7.4v1.4h-13.7c0,2,0.6,3.6,1.7,4.8s2.5,1.8,4.2,1.8
|
||||||
|
c1.2,0,2.3-0.2,3.1-0.7s1.6-1.2,2.2-2l2.1,1.6C285,203.7,282.5,205,279.1,205z M278.7,186.9c-1.4,0-2.6,0.5-3.5,1.5
|
||||||
|
s-1.5,2.4-1.8,4.3h10.1v-0.3c-0.1-1.8-0.6-3.1-1.4-4.1C281.3,187.4,280.1,186.9,278.7,186.9z"/>
|
||||||
|
<path class="st0" d="M320.4,202.2c1.2,0,2.3-0.4,3.2-1.1c0.9-0.7,1.4-1.7,1.5-2.8h3.3c-0.1,1.2-0.5,2.3-1.2,3.3
|
||||||
|
s-1.7,1.9-2.9,2.5c-1.2,0.6-2.5,0.9-3.9,0.9c-2.8,0-4.9-0.9-6.6-2.8c-1.6-1.8-2.4-4.3-2.4-7.5v-0.6c0-2,0.4-3.7,1.1-5.2
|
||||||
|
s1.8-2.7,3.1-3.6c1.4-0.8,2.9-1.3,4.8-1.3c2.3,0,4.1,0.7,5.6,2c1.5,1.4,2.3,3.1,2.4,5.3h-3.3c-0.1-1.3-0.6-2.4-1.5-3.2
|
||||||
|
s-2-1.3-3.3-1.3c-1.8,0-3.1,0.6-4.1,1.9s-1.4,3.1-1.4,5.5v0.7c0,2.3,0.5,4.1,1.4,5.4C317.2,201.6,318.6,202.2,320.4,202.2z"/>
|
||||||
|
<path class="st0" d="M357,186.9c1.5-1.9,3.5-2.8,6-2.8c4.3,0,6.4,2.4,6.5,7.2v13.4H366v-13.4c0-1.5-0.3-2.5-1-3.2s-1.7-1-3.1-1
|
||||||
|
c-1.1,0-2.1,0.3-3,0.9s-1.5,1.4-2,2.4v14.4h-3.4v-28.7h3.5V186.9z"/>
|
||||||
|
<path class="st0" d="M399.1,184.5l0.1,2.5c1.5-1.9,3.6-2.9,6.1-2.9c4.3,0,6.4,2.4,6.5,7.2v13.4h-3.5v-13.4c0-1.5-0.3-2.5-1-3.2
|
||||||
|
s-1.7-1-3.1-1c-1.1,0-2.1,0.3-3,0.9s-1.5,1.4-2,2.4v14.4h-3.5v-20.2h3.4V184.5z"/>
|
||||||
|
<path class="st0" d="M437.3,194.4c0-2,0.4-3.8,1.2-5.3c0.8-1.6,1.9-2.8,3.2-3.7s3-1.3,4.8-1.3c2.8,0,5,1,6.7,2.9
|
||||||
|
s2.5,4.4,2.5,7.6v0.2c0,2-0.4,3.7-1.1,5.3s-1.8,2.8-3.2,3.7c-1.4,0.9-3,1.3-4.8,1.3c-2.7,0-5-1-6.7-2.9s-2.5-4.4-2.5-7.6v-0.2
|
||||||
|
H437.3z M440.8,194.8c0,2.2,0.5,4,1.6,5.4c1,1.4,2.4,2,4.2,2c1.8,0,3.1-0.7,4.2-2.1s1.6-3.3,1.6-5.8c0-2.2-0.5-4-1.6-5.4
|
||||||
|
s-2.5-2.1-4.2-2.1s-3.1,0.7-4.1,2C441.3,190.3,440.8,192.3,440.8,194.8z"/>
|
||||||
|
<path class="st0" d="M485.1,204.7h-3.5V176h3.5V204.7z"/>
|
||||||
|
<path class="st0" d="M511,194.4c0-2,0.4-3.8,1.2-5.3c0.8-1.5,1.9-2.8,3.2-3.7c1.4-0.9,3-1.3,4.8-1.3c2.8,0,5,1,6.7,2.9
|
||||||
|
s2.5,4.4,2.5,7.6v0.2c0,2-0.4,3.7-1.1,5.3s-1.8,2.8-3.2,3.7s-3,1.3-4.8,1.3c-2.7,0-5-1-6.7-2.9c-1.7-1.9-2.5-4.4-2.5-7.6v-0.2
|
||||||
|
H511z M514.4,194.8c0,2.2,0.5,4,1.6,5.4c1,1.4,2.4,2,4.2,2s3.2-0.7,4.2-2.1c1-1.4,1.5-3.3,1.5-5.8c0-2.2-0.5-4-1.6-5.4
|
||||||
|
s-2.5-2.1-4.2-2.1s-3.1,0.7-4.1,2C515,190.3,514.4,192.3,514.4,194.8z"/>
|
||||||
|
<path class="st0" d="M554.1,194.4c0-3.2,0.7-5.7,2.2-7.5s3.4-2.8,5.8-2.8c2.5,0,4.4,0.9,5.8,2.6l0.2-2.2h3.2v19.7
|
||||||
|
c0,2.6-0.8,4.7-2.3,6.2c-1.6,1.5-3.6,2.3-6.2,2.3c-1.5,0-2.9-0.3-4.3-0.9c-1.4-0.6-2.5-1.5-3.2-2.6l1.8-2.1
|
||||||
|
c1.5,1.8,3.3,2.7,5.4,2.7c1.7,0,3-0.5,3.9-1.4s1.4-2.3,1.4-4v-1.7c-1.4,1.6-3.3,2.4-5.7,2.4s-4.3-1-5.7-2.9
|
||||||
|
S554.1,197.7,554.1,194.4z M557.6,194.8c0,2.3,0.5,4.1,1.4,5.4c0.9,1.3,2.2,2,3.9,2c2.2,0,3.8-1,4.8-3V190
|
||||||
|
c-1.1-1.9-2.6-2.9-4.8-2.9c-1.7,0-3,0.7-3.9,2C558.1,190.3,557.6,192.2,557.6,194.8z"/>
|
||||||
|
<path class="st0" d="M604.3,199.6l4.7-15.1h3.7l-8.1,23.3c-1.3,3.4-3.3,5-6,5l-0.7-0.1l-1.3-0.2v-2.8l0.9,0.1
|
||||||
|
c1.2,0,2.1-0.2,2.7-0.7c0.7-0.5,1.2-1.3,1.6-2.6l0.8-2.1l-7.2-20h3.8L604.3,199.6z"/>
|
||||||
|
<path class="st0" d="M688.3,196c-0.3,2.9-1.4,5.1-3.2,6.7c-1.8,1.6-4.2,2.3-7.1,2.3c-3.2,0-5.8-1.2-7.7-3.5s-2.9-5.4-2.9-9.2
|
||||||
|
v-2.6c0-2.5,0.5-4.8,1.4-6.7s2.2-3.4,3.8-4.4s3.6-1.5,5.8-1.5c2.9,0,5.2,0.8,6.9,2.4c1.7,1.6,2.8,3.8,3,6.7h-3.6
|
||||||
|
c-0.3-2.2-1-3.7-2-4.7s-2.5-1.5-4.4-1.5c-2.3,0-4.1,0.8-5.4,2.5c-1.3,1.7-2,4.1-2,7.2v2.6c0,3,0.6,5.3,1.8,7s3,2.6,5.2,2.6
|
||||||
|
c2,0,3.5-0.5,4.6-1.4s1.8-2.5,2.1-4.7h3.7V196z"/>
|
||||||
|
<path class="st0" d="M713,194.4c0-2,0.4-3.8,1.2-5.3s1.9-2.8,3.2-3.7c1.4-0.9,3-1.3,4.8-1.3c2.8,0,5,1,6.7,2.9s2.5,4.4,2.5,7.6
|
||||||
|
v0.2c0,2-0.4,3.7-1.1,5.3s-1.8,2.8-3.2,3.7s-3,1.3-4.8,1.3c-2.7,0-5-1-6.7-2.9s-2.5-4.4-2.5-7.6v-0.2H713z M716.5,194.8
|
||||||
|
c0,2.2,0.5,4,1.6,5.4c1,1.4,2.4,2,4.2,2s3.2-0.7,4.2-2.1c1-1.4,1.5-3.3,1.5-5.8c0-2.2-0.5-4-1.6-5.4s-2.5-2.1-4.2-2.1
|
||||||
|
s-3.1,0.7-4.1,2C717,190.3,716.5,192.3,716.5,194.8z"/>
|
||||||
|
<path class="st0" d="M760.3,184.5l0.1,2.2c1.5-1.7,3.5-2.6,6-2.6c2.8,0,4.8,1.1,5.8,3.2c0.7-1,1.5-1.8,2.6-2.4
|
||||||
|
c1.1-0.6,2.4-0.9,3.8-0.9c4.4,0,6.7,2.3,6.8,7v13.5h-3.5v-13.3c0-1.4-0.3-2.5-1-3.2s-1.8-1.1-3.3-1.1c-1.3,0-2.3,0.4-3.2,1.1
|
||||||
|
c-0.8,0.8-1.3,1.8-1.5,3.1v13.4h-3.5v-13.2c0-2.9-1.4-4.4-4.3-4.4c-2.3,0-3.8,1-4.7,2.9v14.8h-3.5v-20.2h3.4V184.5z"/>
|
||||||
|
<path class="st0" d="M828.9,194.8c0,3.1-0.7,5.6-2.1,7.4c-1.4,1.9-3.3,2.8-5.7,2.8c-2.5,0-4.4-0.8-5.8-2.3v9.7h-3.5v-28h3.2
|
||||||
|
l0.2,2.2c1.4-1.7,3.4-2.6,5.9-2.6c2.4,0,4.3,0.9,5.8,2.7c1.4,1.8,2.1,4.4,2.1,7.6v0.5H828.9z M825.5,194.4
|
||||||
|
c0-2.3-0.5-4.1-1.5-5.4c-1-1.3-2.3-2-4-2c-2.1,0-3.7,0.9-4.7,2.8v9.7c1,1.8,2.6,2.8,4.7,2.8c1.7,0,3-0.7,4-2
|
||||||
|
S825.5,197,825.5,194.4z"/>
|
||||||
|
<path class="st0" d="M867,204.7c-0.2-0.4-0.4-1.1-0.5-2.1c-1.6,1.7-3.5,2.5-5.8,2.5c-2,0-3.6-0.6-4.9-1.7s-1.9-2.6-1.9-4.3
|
||||||
|
c0-2.1,0.8-3.7,2.4-4.9c1.6-1.2,3.9-1.7,6.8-1.7h3.4v-1.7c0-1.2-0.4-2.2-1.1-2.9c-0.7-0.7-1.8-1.1-3.2-1.1
|
||||||
|
c-1.2,0-2.3,0.3-3.1,0.9c-0.8,0.6-1.3,1.4-1.3,2.3h-3.5c0-1,0.4-2,1.1-2.9c0.7-0.9,1.7-1.7,2.9-2.2s2.6-0.8,4-0.8
|
||||||
|
c2.3,0,4.2,0.6,5.5,1.7c1.3,1.2,2,2.8,2.1,4.8v9.3c0,1.9,0.2,3.3,0.7,4.4v0.3H867V204.7z M861.3,202c1.1,0,2.1-0.3,3.1-0.8
|
||||||
|
c1-0.6,1.7-1.3,2.1-2.2v-4.1h-2.7c-4.2,0-6.4,1.2-6.4,3.7c0,1.1,0.4,1.9,1.1,2.5C859.2,201.7,860.2,202,861.3,202z"/>
|
||||||
|
<path class="st0" d="M899.9,184.5l0.1,2.5c1.5-1.9,3.6-2.9,6.1-2.9c4.3,0,6.4,2.4,6.5,7.2v13.4h-3.5v-13.4c0-1.5-0.3-2.5-1-3.2
|
||||||
|
s-1.7-1-3.1-1c-1.1,0-2.1,0.3-3,0.9s-1.5,1.4-2,2.4v14.4h-3.5v-20.2h3.4V184.5z"/>
|
||||||
|
<path class="st0" d="M945.6,199.6l4.7-15.1h3.7l-8.1,23.3c-1.3,3.4-3.3,5-6,5l-0.7-0.1l-1.3-0.2v-2.8l0.9,0.1
|
||||||
|
c1.2,0,2.1-0.2,2.7-0.7c0.7-0.5,1.2-1.3,1.6-2.6l0.8-2.1l-7.2-20h3.8L945.6,199.6z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<path class="st0" d="M221.6,148.7V44.1h29.5c9.1,0,17.1,2,24.1,6s12.4,9.7,16.2,17.2c3.8,7.4,5.7,15.9,5.8,25.6v6.7
|
||||||
|
c0,9.9-1.9,18.5-5.7,25.9c-3.8,7.4-9.2,13.1-16.3,17.1c-7.1,4-15.3,6-24.7,6.1L221.6,148.7L221.6,148.7z M235.4,55.4v82h14.5
|
||||||
|
c10.6,0,18.9-3.3,24.8-9.9c5.9-6.6,8.9-16,8.9-28.3v-6.1c0-11.9-2.8-21.1-8.4-27.7s-13.5-9.9-23.7-10L235.4,55.4L235.4,55.4z"/>
|
||||||
|
<path class="st0" d="M627.1,148.7h-13.9l-52.7-80.6v80.6h-13.9V44.1h13.9l52.8,81v-81H627v104.6H627.1z"/>
|
||||||
|
<path class="st0" d="M900.9,100.1l-12.8,13.3v35.3h-13.8V44.1h13.8v51.7l46.5-51.7h16.7l-41.2,46.2l44.4,58.4H938L900.9,100.1z"/>
|
||||||
|
<polygon class="st0" points="423.2,61.1 455.1,148.7 469.2,148.7 429.2,44.1 417.1,44.1 377.2,148.7 391.4,148.7 "/>
|
||||||
|
<polygon class="st0" points="750.6,61.1 782.6,148.7 796.7,148.7 756.7,44.1 744.6,44.1 704.7,148.7 718.9,148.7 "/>
|
||||||
|
</g>
|
||||||
|
<path class="st0" d="M132.1,140.4c0,0-26.1,25.2-48,46.5v28.9c-2.2,1.7,77.6-75.4,77.6-75.4L84.1,59v31L132.1,140.4z"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st0" points="83.7,157.5 6.5,75.1 7.5,74.2 84.7,156.6 "/>
|
||||||
|
<polygon class="st0" points="83.7,149.8 13.6,75.2 14.6,74.3 84.6,148.9 "/>
|
||||||
|
<polygon class="st0" points="83.7,142.1 20.7,75.4 21.7,74.4 84.6,141.1 "/>
|
||||||
|
<polygon class="st0" points="83.6,134.3 27.8,75.5 28.7,74.6 84.6,133.4 "/>
|
||||||
|
<polygon class="st0" points="83.6,126.6 34.9,75.6 35.8,74.7 84.6,125.7 "/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<polygon class="st0" points="8,75.5 7.1,74.5 83.7,0 84.6,0.9 "/>
|
||||||
|
<polygon class="st0" points="15.2,75.5 14.3,74.6 83.6,7 84.5,7.9 "/>
|
||||||
|
<polygon class="st0" points="22.4,75.6 21.4,74.7 83.6,14 84.5,14.9 "/>
|
||||||
|
<polygon class="st0" points="29.5,75.6 28.6,74.7 83.5,21 84.4,22 "/>
|
||||||
|
<polygon class="st0" points="36.7,75.7 35.7,74.8 83.5,28 84.4,29 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 7.7 KiB |
@@ -0,0 +1,46 @@
|
|||||||
|
import { forwardRef, type ButtonHTMLAttributes, type ReactNode } from 'react'
|
||||||
|
import { clx } from '@/helpers/utils'
|
||||||
|
|
||||||
|
type ButtonProps = {
|
||||||
|
variant?: 'primary' | 'secondary' | 'outline'
|
||||||
|
leftIcon?: ReactNode
|
||||||
|
rightIcon?: ReactNode
|
||||||
|
} & ButtonHTMLAttributes<HTMLButtonElement>
|
||||||
|
|
||||||
|
const variantClasses: Record<NonNullable<ButtonProps['variant']>, string> = {
|
||||||
|
primary: 'bg-black text-white hover:bg-black/90',
|
||||||
|
secondary: 'bg-white text-black border border-black hover:bg-black/5',
|
||||||
|
outline: 'border border-black text-black hover:bg-black/5'
|
||||||
|
}
|
||||||
|
|
||||||
|
const Button = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
|
||||||
|
const {
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
variant = 'primary',
|
||||||
|
leftIcon,
|
||||||
|
rightIcon,
|
||||||
|
...rest
|
||||||
|
} = props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
ref={ref}
|
||||||
|
className={clx(
|
||||||
|
'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-full px-4 py-2 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-black/10 disabled:pointer-events-none disabled:opacity-50',
|
||||||
|
variantClasses[variant],
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...rest}
|
||||||
|
>
|
||||||
|
{leftIcon ? <span className='flex items-center'>{leftIcon}</span> : null}
|
||||||
|
{children}
|
||||||
|
{rightIcon ? <span className='flex items-center'>{rightIcon}</span> : null}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
Button.displayName = 'Button'
|
||||||
|
|
||||||
|
export default Button
|
||||||
|
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { type FC } from 'react'
|
||||||
|
|
||||||
|
import { clx } from '@/helpers/utils'
|
||||||
|
|
||||||
|
type DividerProps = {
|
||||||
|
label?: string
|
||||||
|
className?: string
|
||||||
|
lineClassName?: string
|
||||||
|
labelClassName?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const Divider: FC<DividerProps> = ({ label, className, lineClassName, labelClassName }) => {
|
||||||
|
return (
|
||||||
|
<div className={clx('flex items-center gap-4', className)}>
|
||||||
|
<span className={clx('flex-1 border-t border-dashed border-neutral-200', lineClassName)} />
|
||||||
|
{label ? (
|
||||||
|
<span className={clx('text-xs text-neutral-500', labelClassName)}>
|
||||||
|
{label}
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
|
<span className={clx('flex-1 border-t border-dashed border-neutral-200', lineClassName)} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Divider
|
||||||
|
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { type FC } from 'react'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
errorText: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const Error: FC<Props> = (props: Props) => {
|
||||||
|
return (
|
||||||
|
<div className='mt-1.5 font-normal text-red-500 text-[11px]'>
|
||||||
|
{props.errorText}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Error
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
import { type FC, type InputHTMLAttributes, useEffect, useState } from 'react'
|
||||||
|
import { clx } from '../helpers/utils';
|
||||||
|
import { Eye, EyeSlash, SearchNormal } from 'iconsax-react';
|
||||||
|
import Error from './Error';
|
||||||
|
|
||||||
|
type Variant = "floating_outlined" | "primary" | "search";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
label?: string;
|
||||||
|
className?: string;
|
||||||
|
variant?: Variant;
|
||||||
|
error_text?: string;
|
||||||
|
onEnter?: () => void;
|
||||||
|
unit?: string;
|
||||||
|
seprator?: boolean;
|
||||||
|
isNotRequired?: boolean;
|
||||||
|
onChangeSearchFinal?: (value: string) => void;
|
||||||
|
} & InputHTMLAttributes<HTMLInputElement>
|
||||||
|
|
||||||
|
const formatNumber = (value: string | number): string => {
|
||||||
|
if (!value) return '';
|
||||||
|
const inputValue = String(value).replace(/,/g, '');
|
||||||
|
return inputValue.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||||
|
};
|
||||||
|
|
||||||
|
const Input: FC<Props> = (props: Props) => {
|
||||||
|
|
||||||
|
const [formattedValue, setFormattedValue] = useState<string>(
|
||||||
|
props.value ? formatNumber(props.value as string) : ''
|
||||||
|
);
|
||||||
|
|
||||||
|
const [showPassword, setShowPassword] = useState<boolean>(false)
|
||||||
|
const [search, setSearch] = useState<string>('')
|
||||||
|
|
||||||
|
const inputClass = clx(
|
||||||
|
'w-full h-10 text-black block px-4 text-xs rounded-xl border border-border',
|
||||||
|
props.readOnly && 'bg-gray-100 border-0 text-description',
|
||||||
|
props.variant === 'search' && 'bg-[#EEF0F7] border-0 ps-10',
|
||||||
|
props.className
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
if (props.seprator) {
|
||||||
|
const inputValue = event.target.value.replace(/,/g, ''); // حذف کاماها
|
||||||
|
const formatted = formatNumber(inputValue);
|
||||||
|
|
||||||
|
// بهروزرسانی مقدار قالببندیشده
|
||||||
|
setFormattedValue(formatted);
|
||||||
|
|
||||||
|
// ارسال مقدار خام به `onChange` والد
|
||||||
|
props.onChange?.({
|
||||||
|
...event,
|
||||||
|
target: {
|
||||||
|
...event.target,
|
||||||
|
value: inputValue,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
props.onChange?.(event);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (props.value) {
|
||||||
|
setFormattedValue(formatNumber(props.value as string));
|
||||||
|
} else {
|
||||||
|
setFormattedValue('');
|
||||||
|
}
|
||||||
|
}, [props.value])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (props.variant === 'search' && props.onChangeSearchFinal) {
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
props.onChangeSearchFinal?.(search)
|
||||||
|
}, 1000)
|
||||||
|
return () => clearTimeout(timeout)
|
||||||
|
}
|
||||||
|
}, [search, props])
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='w-full'>
|
||||||
|
<label className='text-sm'>
|
||||||
|
{props.label}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div className='w-full relative mt-1'>
|
||||||
|
<input {...props} onChange={(e) => {
|
||||||
|
setSearch(e.target.value)
|
||||||
|
handleInputChange(e)
|
||||||
|
}} value={props.seprator ? formattedValue : props.value} type={props.type === 'password' && showPassword ? 'text' : props.type === 'password' ? 'password' : undefined} className={inputClass} />
|
||||||
|
|
||||||
|
{
|
||||||
|
props.type === 'password' &&
|
||||||
|
<button
|
||||||
|
type='button'
|
||||||
|
onClick={() => setShowPassword((oldValue) => !oldValue)}
|
||||||
|
className='absolute top-0 bottom-0 left-3 my-auto flex h-5 w-5 cursor-pointer items-center justify-center text-black'
|
||||||
|
>
|
||||||
|
{
|
||||||
|
showPassword
|
||||||
|
? <EyeSlash color='#8C90A3' size={20} />
|
||||||
|
: <Eye color='#8C90A3' size={20} />
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
props.variant === 'search' &&
|
||||||
|
<SearchNormal size={20} color='#8C90A3' className='absolute top-0 w-5 bottom-0 my-auto right-3' />
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
props.error_text &&
|
||||||
|
<Error
|
||||||
|
errorText={props.error_text}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Input
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export const Paths = {
|
||||||
|
home: "/",
|
||||||
|
editor: "/editor",
|
||||||
|
};
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
export const formatDate = (dateString: string): string => {
|
||||||
|
const date = new Date(dateString);
|
||||||
|
return new Intl.DateTimeFormat("fa-IR", {
|
||||||
|
year: "numeric",
|
||||||
|
month: "long",
|
||||||
|
day: "numeric",
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
}).format(date);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const formatDateShort = (dateString: string): string => {
|
||||||
|
const date = new Date(dateString);
|
||||||
|
return new Intl.DateTimeFormat("fa-IR", {
|
||||||
|
year: "numeric",
|
||||||
|
month: "short",
|
||||||
|
day: "numeric",
|
||||||
|
}).format(date);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const formatPrice = (price: number): string => {
|
||||||
|
return new Intl.NumberFormat("fa-IR", {
|
||||||
|
style: "decimal",
|
||||||
|
minimumFractionDigits: 0,
|
||||||
|
maximumFractionDigits: 0,
|
||||||
|
}).format(price);
|
||||||
|
};
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
|
||||||
|
|
||||||
|
export type XOR<T, U> = T | U extends object
|
||||||
|
? (Without<T, U> & U) | (Without<U, T> & T)
|
||||||
|
: T | U;
|
||||||
|
|
||||||
|
export type ItemsSelectType = {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ErrorType = Error & {
|
||||||
|
response?: {
|
||||||
|
data?: {
|
||||||
|
error: {
|
||||||
|
message: string[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface IGeneralError {
|
||||||
|
status: number;
|
||||||
|
success: boolean;
|
||||||
|
error: {
|
||||||
|
message: string;
|
||||||
|
details: string[];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { twMerge } from "tailwind-merge";
|
||||||
|
import type { IGeneralError } from "./types";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Concatenates truthy classes into a space-separated string.
|
||||||
|
*
|
||||||
|
* @param classes - The classes to concatenate.
|
||||||
|
* @returns The concatenated classes.
|
||||||
|
*/
|
||||||
|
export const clx = (...classes: (string | boolean | undefined)[]): string => {
|
||||||
|
return twMerge(classes.filter(Boolean).join(" "));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const extractErrorMessage = (
|
||||||
|
error: Error | unknown,
|
||||||
|
fallbackMessage: string = "خطایی رخ داده است"
|
||||||
|
): string => {
|
||||||
|
try {
|
||||||
|
const axiosError = error as { response?: { data: IGeneralError } };
|
||||||
|
return axiosError?.response?.data?.error?.details?.[0] || fallbackMessage;
|
||||||
|
} catch {
|
||||||
|
return fallbackMessage;
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"sidebar": {
|
||||||
|
"menu": "منو",
|
||||||
|
"home_page": "صفحه اصلی"
|
||||||
|
},
|
||||||
|
"header": {
|
||||||
|
"search": "جستجو"
|
||||||
|
},
|
||||||
|
"view": {
|
||||||
|
"title": "نمایش کاتالوگ",
|
||||||
|
"download_pdf": "دانلود PDF",
|
||||||
|
"sidebar": {
|
||||||
|
"title": "صفحات کاتالوگ",
|
||||||
|
"search_placeholder": "جستجوی صفحه",
|
||||||
|
"items": {
|
||||||
|
"cover": "صفحه جلد",
|
||||||
|
"details": "جزئیات محصول",
|
||||||
|
"gallery": "گالری تصاویر"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"actions": {
|
||||||
|
"add_page": "افزودن صفحه جدید"
|
||||||
|
},
|
||||||
|
"main": {
|
||||||
|
"upload_hint": "برای جایگزینی فایل جدید، PDF خود را آپلود کنید.",
|
||||||
|
"empty_state": "برای مشاهده محتوا، یکی از صفحات سایدبار را انتخاب کنید."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import EditorSidebar from './components/EditorSidebar'
|
||||||
|
|
||||||
|
const Editor = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<EditorSidebar />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Editor
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
|
||||||
|
import Logo from "@/assets/images/logo.svg";
|
||||||
|
import { clx } from "@/helpers/utils";
|
||||||
|
import { useSharedStore } from "@/shared/store/sharedStore";
|
||||||
|
|
||||||
|
const EditorSidebar = () => {
|
||||||
|
const { openSidebar, setOpenSidebar } = useSharedStore();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{openSidebar ? (
|
||||||
|
<div className="fixed inset-0 z-10 bg-black/50" onClick={() => setOpenSidebar(false)} />
|
||||||
|
) : null}
|
||||||
|
<aside
|
||||||
|
className={clx(
|
||||||
|
"fixed right-0 top-0 bottom-0 flex translate-x-[420px] w-[350px] flex-col bg-white px-8 py-10 opacity-0 transition-all ease-in-out xl:visible xl:right-4 xl:top-4 xl:bottom-4 xl:translate-x-0 xl:rounded-[32px] xl:bg-white xl:px-8 xl:py-10 xl:opacity-100",
|
||||||
|
openSidebar && "z-40 translate-x-0 opacity-100",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="mb-10 flex items-center justify-center">
|
||||||
|
<img src={Logo} alt="لوگو دانک" className="h-9" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</aside>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EditorSidebar;
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
const Home = () => {
|
||||||
|
return (
|
||||||
|
<div>Home</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Home
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import SideBar from '@/shared/SideBar'
|
||||||
|
import Header from '@/shared/Header'
|
||||||
|
import { Route, Routes, useLocation } from 'react-router-dom'
|
||||||
|
import { Paths } from '@/config/Paths'
|
||||||
|
import { clx } from '@/helpers/utils'
|
||||||
|
import { useSharedStore } from '@/shared/store/sharedStore'
|
||||||
|
import Home from '@/pages/home/Home'
|
||||||
|
import Editor from '@/pages/editor/Editor'
|
||||||
|
|
||||||
|
const MainRouter = () => {
|
||||||
|
const { hasSubMenu } = useSharedStore()
|
||||||
|
const location = useLocation()
|
||||||
|
const hiddenSideBarRoutes = [Paths.editor]
|
||||||
|
const shouldRenderSideBar = !hiddenSideBarRoutes.includes(location.pathname)
|
||||||
|
const routeHasLocalSidebar = location.pathname === Paths.editor
|
||||||
|
const hasSidebarSpace = shouldRenderSideBar || routeHasLocalSidebar
|
||||||
|
const headerSidebarSize = routeHasLocalSidebar ? 'wide' : 'default'
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='flex min-h-full flex-col overflow-hidden p-4'>
|
||||||
|
{shouldRenderSideBar ? <SideBar /> : null}
|
||||||
|
<Header hasMainSidebar={hasSidebarSpace} sidebarSize={headerSidebarSize} />
|
||||||
|
<div className={clx(
|
||||||
|
'mt-[68px] flex flex-1 flex-col xl:mt-[81px]',
|
||||||
|
shouldRenderSideBar && 'xl:ms-[269px]',
|
||||||
|
shouldRenderSideBar && hasSubMenu && 'xl:ms-[305px]',
|
||||||
|
routeHasLocalSidebar && 'xl:mr-[374px]',
|
||||||
|
)}>
|
||||||
|
<div className='flex-1 flex flex-col overflow-auto w-full max-h-[calc(100vh-113px)]'>
|
||||||
|
<div className='flex-1 h-full flex'>
|
||||||
|
<Routes>
|
||||||
|
<Route path={Paths.home} element={<Home />} />
|
||||||
|
<Route path={Paths.editor} element={<Editor />} />
|
||||||
|
</Routes>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MainRouter
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
import { type FC, useEffect } from 'react'
|
||||||
|
import Input from '@/components/Input'
|
||||||
|
// import { ArrowDown2, CloseCircle, HambergerMenu, Logout, Wallet } from 'iconsax-react'
|
||||||
|
// import AvatarImage from '../assets/images/avatar_image.png'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { useLocation } from 'react-router-dom'
|
||||||
|
import { useSharedStore } from '@/shared/store/sharedStore'
|
||||||
|
import { HambergerMenu } from 'iconsax-react'
|
||||||
|
import { clx } from '@/helpers/utils'
|
||||||
|
|
||||||
|
type SidebarSize = 'default' | 'wide'
|
||||||
|
|
||||||
|
type HeaderProps = {
|
||||||
|
hasMainSidebar?: boolean
|
||||||
|
sidebarSize?: SidebarSize
|
||||||
|
}
|
||||||
|
|
||||||
|
const sidebarSizeClasses: Record<SidebarSize, { base: string; withSub: string }> = {
|
||||||
|
default: {
|
||||||
|
base: 'xl:right-[285px] xl:w-[calc(100%-305px)]',
|
||||||
|
withSub: 'xl:right-[320px] xl:w-[calc(100%-340px)]',
|
||||||
|
},
|
||||||
|
wide: {
|
||||||
|
base: 'xl:right-[390px] xl:w-[calc(100%-406px)]',
|
||||||
|
withSub: 'xl:right-[425px] xl:w-[calc(100%-441px)]',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const Header: FC<HeaderProps> = ({ hasMainSidebar = true, sidebarSize = 'default' }) => {
|
||||||
|
|
||||||
|
// const location = useLocation();
|
||||||
|
// const [popoverKey, setPopoverKey] = useState(0);
|
||||||
|
const { t } = useTranslation('global')
|
||||||
|
const { setOpenSidebar, openSidebar, hasSubMenu, setSearch } = useSharedStore()
|
||||||
|
const location = useLocation()
|
||||||
|
|
||||||
|
// useEffect(() => {
|
||||||
|
// setPopoverKey((prevKey) => prevKey + 1);
|
||||||
|
// }, [location.pathname]);
|
||||||
|
|
||||||
|
// const handleLogout = () => {
|
||||||
|
// removeToken()
|
||||||
|
// removeRefreshToken()
|
||||||
|
// window.location.href = Pages.auth.login
|
||||||
|
// }
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSearch('')
|
||||||
|
}, [location.pathname])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={clx(
|
||||||
|
'fixed z-10 right-4 left-4 top-4 xl:h-16 h-12 flex items-center px-6 bg-white justify-between rounded-[32px]',
|
||||||
|
hasMainSidebar && (
|
||||||
|
hasSubMenu
|
||||||
|
? sidebarSizeClasses[sidebarSize].withSub
|
||||||
|
: sidebarSizeClasses[sidebarSize].base
|
||||||
|
)
|
||||||
|
)}>
|
||||||
|
|
||||||
|
<div className='min-w-[270px] hidden xl:block'>
|
||||||
|
<Input
|
||||||
|
variant='search'
|
||||||
|
placeholder={t('header.search')}
|
||||||
|
onChangeSearchFinal={(value) => setSearch(value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div onClick={() => setOpenSidebar(!openSidebar)} className='xl:hidden block'>
|
||||||
|
<HambergerMenu size={24} color='black' />
|
||||||
|
</div>
|
||||||
|
{/* <img src={LogoImage} className='h-6 xl:hidden block absolute right-0 left-0 mx-auto' /> */}
|
||||||
|
<div className='flex xl:gap-6 gap-4 items-center'>
|
||||||
|
{/* <Link className='xl:hidden' to={Paths.wallet}>
|
||||||
|
<Wallet className='xl:size-[18px] size-[17px]' color='#da2129' />
|
||||||
|
</Link> */}
|
||||||
|
{/* <Notifications /> */}
|
||||||
|
{/* {
|
||||||
|
data && (
|
||||||
|
<Popover className="relative" key={popoverKey}>
|
||||||
|
<PopoverButton >
|
||||||
|
<div className='flex gap-2 items-center mt-2.5'>
|
||||||
|
<div className='size-6 rounded-full bg-description overflow-hidden'>
|
||||||
|
<img src={data?.data?.user?.profilePic ? data?.data?.user?.profilePic : AvatarImage} className='size-full object-cover' />
|
||||||
|
</div>
|
||||||
|
<div className='xl:flex hidden gap-1 items-center'>
|
||||||
|
<div className='text-xs'>
|
||||||
|
{data?.data?.user?.firstName + ' ' + data?.data?.user?.lastName}
|
||||||
|
</div>
|
||||||
|
<ArrowDown2 size={14} color='#8C90A3' />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</PopoverButton>
|
||||||
|
|
||||||
|
<PopoverPanel style={{ minHeight: window.innerWidth < 1140 ? window.innerHeight : undefined }} anchor="bottom" className="flex xl:ml-6 overflow-auto flex-col gap-3 bg-white boxShadow xl:mt-7 z-30 py-4 text-xs rounded-2.5 xl:w-[300px] -mt-[50px] w-full fixed xl:h-fit h-full shadow-md">
|
||||||
|
<div className='absolute xl:hidden top-6 left-6'>
|
||||||
|
<CloseCircle onClick={() => setPopoverKey((prevKey) => prevKey + 1)} size={20} color='#da2129' />
|
||||||
|
</div>
|
||||||
|
<Link to={Pages.profile} className='flex flex-col gap-2 items-center justify-center border-b border-[#EAEDF5] pb-4'>
|
||||||
|
<div className='size-14 rounded-full overflow-hidden'>
|
||||||
|
<img src={data?.data?.user?.profilePic ? data?.data?.user?.profilePic : AvatarImage} className='size-full object-cover' />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{data?.data?.user?.firstName + ' ' + data?.data?.user?.lastName}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='text-description'>
|
||||||
|
{data?.data?.user?.email}
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<div className='px-6 mt-2'>
|
||||||
|
<div onClick={() => handleLogout()} className='flex gap-2 items-center cursor-pointer'>
|
||||||
|
<Logout size={20} color='#da2129' />
|
||||||
|
<div>
|
||||||
|
{t('sidebar.logout')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</PopoverPanel>
|
||||||
|
</Popover>
|
||||||
|
)
|
||||||
|
} */}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Header
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
import { type FC, useEffect } from 'react'
|
||||||
|
import LogoImage from '../assets/images/logo.svg'
|
||||||
|
import LogoSmall from '../assets/images/logo-small.svg'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import {
|
||||||
|
Home2,
|
||||||
|
Logout,
|
||||||
|
} from 'iconsax-react'
|
||||||
|
import SideBarItem from './SideBarItem'
|
||||||
|
import { useLocation } from 'react-router-dom'
|
||||||
|
import { useSharedStore } from './store/sharedStore'
|
||||||
|
import { clx } from '../helpers/utils'
|
||||||
|
import { Paths } from '../config/Paths'
|
||||||
|
|
||||||
|
|
||||||
|
const SideBar: FC = () => {
|
||||||
|
|
||||||
|
const { t } = useTranslation('global')
|
||||||
|
const { openSidebar, setOpenSidebar, hasSubMenu, setSubMenuName, setSubtMenu, subMenuName } = useSharedStore()
|
||||||
|
const location = useLocation()
|
||||||
|
|
||||||
|
const isActive = (name: string) => {
|
||||||
|
return window.location.href.includes(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const split = location.pathname.split('/')
|
||||||
|
|
||||||
|
if (split[1] === 'dashboard' || split[1] === 'products') {
|
||||||
|
setSubMenuName(split[1])
|
||||||
|
setSubtMenu(true)
|
||||||
|
} else {
|
||||||
|
setSubMenuName('')
|
||||||
|
setSubtMenu(false)
|
||||||
|
}
|
||||||
|
}, [location.pathname, setSubMenuName, setSubtMenu])
|
||||||
|
|
||||||
|
const iconSizeSideBar = 20
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{
|
||||||
|
openSidebar && <div className='fixed top-0 left-0 right-0 bottom-0 bg-black/50 bg-opacity-50 z-10' onClick={() => setOpenSidebar(false)} />
|
||||||
|
}
|
||||||
|
<div
|
||||||
|
className={clx(
|
||||||
|
'fixed xl:flex translate-x-[400px] xl:translate-x-0 transition-all ease-in-out opacity-0 invisible xl:visible xl:opacity-100 xl:right-4 right-0 xl:top-4 top-0 xl:bottom-4 bottom-0 xl:rounded-[32px] w-[250px] bg-white flex-col py-12',
|
||||||
|
openSidebar && 'opacity-100 visible -translate-x-[0px] block z-40',
|
||||||
|
hasSubMenu && 'w-24 !rounded-tl-none !rounded-bl-none'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className='flex justify-center'>
|
||||||
|
{
|
||||||
|
!hasSubMenu ?
|
||||||
|
<img src={LogoImage} className='w-[140px]' />
|
||||||
|
:
|
||||||
|
<img src={LogoSmall} className='w-7' />
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='flex-1 flex flex-col h-full overflow-y-auto no-scrollbar'>
|
||||||
|
<div className={clx(
|
||||||
|
'mt-10 px-12 text-header text-sm font-normal',
|
||||||
|
hasSubMenu && 'px-2 text-center'
|
||||||
|
)}>
|
||||||
|
{t('sidebar.menu')}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='text-xs text-[#8C90A3]'>
|
||||||
|
<SideBarItem
|
||||||
|
icon={<Home2 variant={isActive(Paths.home) ? 'Bold' : 'Outline'} color={isActive(Paths.home) ? 'black' : '#8C90A3'} size={iconSizeSideBar} />}
|
||||||
|
title={t('sidebar.home_page')}
|
||||||
|
isActive={isActive(Paths.home)}
|
||||||
|
link={Paths.home}
|
||||||
|
activeName={Paths.home}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='flex-1 flex flex-col justify-end mt-14'>
|
||||||
|
<div className='text-xs text-[#8C90A3]'>
|
||||||
|
<SideBarItem
|
||||||
|
icon={<Logout variant={isActive('logout') ? 'Bold' : 'Outline'} color={isActive('logout') ? 'black' : '#8C90A3'} size={iconSizeSideBar} />}
|
||||||
|
title="خروج"
|
||||||
|
isActive={isActive('logout')}
|
||||||
|
link={`#`}
|
||||||
|
isLogout
|
||||||
|
activeName=''
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* منوی فرعی */}
|
||||||
|
{
|
||||||
|
hasSubMenu && (openSidebar || window.innerWidth > 1139) &&
|
||||||
|
<div className='fixed xl:right-[112px] right-[96px] bg-white z-20 xl:top-4 xl:bottom-4 top-0 bottom-0 rounded-tl-[32px] rounded-bl-[32px] w-[190px] border-r border-border'>
|
||||||
|
{
|
||||||
|
subMenuName === 'dashboard' ? null
|
||||||
|
// <DashboardSubMenu />
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default SideBar
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import { type FC, type ReactNode } from 'react'
|
||||||
|
import { Link } from 'react-router-dom'
|
||||||
|
import { clx } from '../helpers/utils'
|
||||||
|
import { useSharedStore } from './store/sharedStore'
|
||||||
|
// import { useGetAdminPermissions } from '../pages/users/hooks/useUserData'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
icon: ReactNode,
|
||||||
|
title: string,
|
||||||
|
isActive: boolean,
|
||||||
|
link: string,
|
||||||
|
isLogout?: boolean,
|
||||||
|
name?: string,
|
||||||
|
activeName?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const SideBarItem: FC<Props> = (props: Props) => {
|
||||||
|
|
||||||
|
const { hasSubMenu, setSubMenuName, setSubtMenu } = useSharedStore()
|
||||||
|
// const { data: adminPermissions } = useGetAdminPermissions()
|
||||||
|
const handleLogout = () => {
|
||||||
|
// removeToken()
|
||||||
|
// removeRefreshToken()
|
||||||
|
// window.location.href = Pages.auth.login
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
if (props.name) {
|
||||||
|
setSubMenuName(props.name)
|
||||||
|
setSubtMenu(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if (props.activeName === '' || props.activeName && adminPermissions?.data?.permissions?.some((permission: { name: string }) => permission.name === props.activeName)) {
|
||||||
|
if (true) {
|
||||||
|
return (
|
||||||
|
<Link onClick={props.isLogout ? handleLogout : handleClick} to={props.link} className={clx(
|
||||||
|
'flex text-xs gap-9 mt-4',
|
||||||
|
hasSubMenu && 'flex-col',
|
||||||
|
)}>
|
||||||
|
<div className={clx(
|
||||||
|
'w-1 bg-primary h-6',
|
||||||
|
!props.isActive && 'invisible',
|
||||||
|
hasSubMenu && 'hidden',
|
||||||
|
)}></div>
|
||||||
|
<div className={clx(
|
||||||
|
'flex gap-3 items-center',
|
||||||
|
hasSubMenu && 'flex-col text-[10px]',
|
||||||
|
)}>
|
||||||
|
{props.icon}
|
||||||
|
<div className={props.isActive ? 'text-primary' : ''}>
|
||||||
|
{props.title}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SideBarItem
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { create } from "zustand";
|
||||||
|
import { type SharedStoreType } from "../types/SharedTypes";
|
||||||
|
|
||||||
|
export const useSharedStore = create<SharedStoreType>((set) => ({
|
||||||
|
openSidebar: false,
|
||||||
|
setOpenSidebar: (value) => set({ openSidebar: value }),
|
||||||
|
hasSubMenu: false,
|
||||||
|
setSubtMenu: (value: boolean) => set({ hasSubMenu: value }),
|
||||||
|
subMenuName: "",
|
||||||
|
setSubMenuName: (value: string) => set({ subMenuName: value }),
|
||||||
|
search: "",
|
||||||
|
setSearch: (value: string) => set({ search: value }),
|
||||||
|
}));
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
export type SharedStoreType = {
|
||||||
|
openSidebar: boolean;
|
||||||
|
setOpenSidebar: (value: boolean) => void;
|
||||||
|
hasSubMenu: boolean;
|
||||||
|
setSubtMenu: (value: boolean) => void;
|
||||||
|
subMenuName: string;
|
||||||
|
setSubMenuName: (value: string) => void;
|
||||||
|
search: string;
|
||||||
|
setSearch: (value: string) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PagerType = {
|
||||||
|
page: number;
|
||||||
|
limit: number;
|
||||||
|
totalItems: number;
|
||||||
|
totalPages: number;
|
||||||
|
prevPage: boolean | null;
|
||||||
|
nextPage: string | null;
|
||||||
|
};
|
||||||
@@ -15,6 +15,10 @@
|
|||||||
"moduleDetection": "force",
|
"moduleDetection": "force",
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["src/*"]
|
||||||
|
},
|
||||||
|
|
||||||
/* Linting */
|
/* Linting */
|
||||||
"strict": true,
|
"strict": true,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { defineConfig } from "vite";
|
import { defineConfig } from "vite";
|
||||||
import react from "@vitejs/plugin-react";
|
import react from "@vitejs/plugin-react";
|
||||||
import tailwindcss from "@tailwindcss/vite";
|
import tailwindcss from "@tailwindcss/vite";
|
||||||
|
import { fileURLToPath, URL } from "node:url";
|
||||||
|
|
||||||
// https://vite.dev/config/
|
// https://vite.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
@@ -12,4 +13,9 @@ export default defineConfig({
|
|||||||
}),
|
}),
|
||||||
tailwindcss(),
|
tailwindcss(),
|
||||||
],
|
],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"@": fileURLToPath(new URL("./src", import.meta.url)),
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user