diff --git a/.gitignore b/.gitignore index 44300dc1f..1477cb127 100644 --- a/.gitignore +++ b/.gitignore @@ -1,36 +1,63 @@ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. -# dependencies -node_modules -.pnp +# Bağımlılıklar +/node_modules +/.pnp .pnp.js -# testing -coverage +# Test kapsama raporları +/coverage -# production -.next -.swc -_static -out -dist -build +# Üretim derlemesi +/build +/dist -# environment variables +# Çevre değişkenleri .env .env.local .env.development.local .env.test.local .env.production.local -# misc -.DS_Store -.vercel -.netlify -.unimportedrc.json -tsconfig.tsbuildinfo -.vscode - +# Günlük dosyaları npm-debug.log* yarn-debug.log* yarn-error.log* + +# IDE ve editör dosyaları +.idea/ +.vscode/ +*.swp +*.swo +.DS_Store +*.sublime-workspace +*.sublime-project + +# TypeScript derleme çıktıları +*.tsbuildinfo + +# Geçici dosyalar +.temp +.tmp +.cache + +# Sistem dosyaları +Thumbs.db +*.pem + +# Yerel SSL sertifikaları +*.cert +*.key + +# Yarn +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/sdks +!.yarn/versions +.pnp.* + +# Diğer +*.log +.vercel diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 000000000..13566b81b --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 000000000..03d9549ea --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 000000000..07115cdf1 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 000000000..edb1cba1a --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/prettier.xml b/.idea/prettier.xml new file mode 100644 index 000000000..b0c1c68fb --- /dev/null +++ b/.idea/prettier.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/react-dashboard.iml b/.idea/react-dashboard.iml new file mode 100644 index 000000000..d6ebd4805 --- /dev/null +++ b/.idea/react-dashboard.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 000000000..35eb1ddfb --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/index.html b/index.html index efb687cbc..db8ce74e0 100644 --- a/index.html +++ b/index.html @@ -5,7 +5,7 @@ - Minimal UI Kit + Transport diff --git a/package-lock.json b/package-lock.json index bcd1a82ae..5f5a38f88 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,22 +14,34 @@ "@fontsource-variable/dm-sans": "^5.0.7", "@fontsource/barlow": "^5.0.14", "@iconify/react": "^5.0.2", + "@mui/icons-material": "^6.3.1", "@mui/lab": "^5.0.0-alpha.173", "@mui/material": "^5.16.7", + "@types/yup": "^0.29.14", "apexcharts": "^3.52.0", + "axios": "^1.7.9", "dayjs": "^1.11.13", + "formik": "^2.4.6", "history": "^5.3.0", + "i18next": "^24.2.0", + "i18next-browser-languagedetector": "^8.0.2", + "i18next-http-backend": "^3.0.1", + "jwt-decode": "^4.0.0", "react": "^18.3.1", "react-apexcharts": "^1.4.1", "react-dom": "^18.3.1", "react-helmet-async": "^2.0.5", + "react-i18next": "^15.4.0", + "react-input-mask": "^2.0.4", "react-router-dom": "^6.26.1", - "simplebar-react": "^3.2.6" + "simplebar-react": "^3.2.6", + "yup": "^1.6.1" }, "devDependencies": { "@types/node": "^22.5.0", "@types/react": "^18.3.4", "@types/react-dom": "^18.3.0", + "@types/react-input-mask": "^3.0.6", "@typescript-eslint/eslint-plugin": "^7.18.0", "@typescript-eslint/parser": "^7.18.0", "@vitejs/plugin-react-swc": "^3.7.0", @@ -154,9 +166,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.4.tgz", - "integrity": "sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" @@ -1084,6 +1096,32 @@ "url": "https://opencollective.com/mui-org" } }, + "node_modules/@mui/icons-material": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.3.1.tgz", + "integrity": "sha512-nJmWj1PBlwS3t1PnoqcixIsftE+7xrW3Su7f0yrjPw4tVjYrgkhU0hrRp+OlURfZ3ptdSkoBkalee9Bhf1Erfw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@mui/material": "^6.3.1", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@mui/lab": { "version": "5.0.0-alpha.173", "resolved": "https://registry.npmjs.org/@mui/lab/-/lab-5.0.0-alpha.173.tgz", @@ -1384,9 +1422,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.0.tgz", - "integrity": "sha512-WTWD8PfoSAJ+qL87lE7votj3syLavxunWhzCnx3XFxFiI/BA/r3X7MUM8dVrH8rb2r4AiO8jJsr3ZjdaftmnfA==", + "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" ], @@ -1398,9 +1436,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.0.tgz", - "integrity": "sha512-a1sR2zSK1B4eYkiZu17ZUZhmUQcKjk2/j9Me2IDjk1GHW7LB5Z35LEzj9iJch6gtUfsnvZs1ZNyDW2oZSThrkA==", + "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" ], @@ -1412,9 +1450,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.0.tgz", - "integrity": "sha512-zOnKWLgDld/svhKO5PD9ozmL6roy5OQ5T4ThvdYZLpiOhEGY+dp2NwUmxK0Ld91LrbjrvtNAE0ERBwjqhZTRAA==", + "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" ], @@ -1426,9 +1464,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.0.tgz", - "integrity": "sha512-7doS8br0xAkg48SKE2QNtMSFPFUlRdw9+votl27MvT46vo44ATBmdZdGysOevNELmZlfd+NEa0UYOA8f01WSrg==", + "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" ], @@ -1439,10 +1477,38 @@ "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, + "license": "MIT", + "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, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.0.tgz", - "integrity": "sha512-pWJsfQjNWNGsoCq53KjMtwdJDmh/6NubwQcz52aEwLEuvx08bzcy6tOUuawAOncPnxz/3siRtd8hiQ32G1y8VA==", + "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" ], @@ -1454,9 +1520,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.0.tgz", - "integrity": "sha512-efRIANsz3UHZrnZXuEvxS9LoCOWMGD1rweciD6uJQIx2myN3a8Im1FafZBzh7zk1RJ6oKcR16dU3UPldaKd83w==", + "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" ], @@ -1468,9 +1534,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.0.tgz", - "integrity": "sha512-ZrPhydkTVhyeGTW94WJ8pnl1uroqVHM3j3hjdquwAcWnmivjAwOYjTEAuEDeJvGX7xv3Z9GAvrBkEzCgHq9U1w==", + "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" ], @@ -1482,9 +1548,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.0.tgz", - "integrity": "sha512-cfaupqd+UEFeURmqNP2eEvXqgbSox/LHOyN9/d2pSdV8xTrjdg3NgOFJCtc1vQ/jEke1qD0IejbBfxleBPHnPw==", + "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" ], @@ -1495,10 +1561,24 @@ "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, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.0.tgz", - "integrity": "sha512-ZKPan1/RvAhrUylwBXC9t7B2hXdpb/ufeu22pG2psV7RN8roOfGurEghw1ySmX/CmDDHNTDDjY3lo9hRlgtaHg==", + "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" ], @@ -1510,9 +1590,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.0.tgz", - "integrity": "sha512-H1eRaCwd5E8eS8leiS+o/NqMdljkcb1d6r2h4fKSsCXQilLKArq6WS7XBLDu80Yz+nMqHVFDquwcVrQmGr28rg==", + "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" ], @@ -1524,9 +1604,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.0.tgz", - "integrity": "sha512-zJ4hA+3b5tu8u7L58CCSI0A9N1vkfwPhWd/puGXwtZlsB5bTkwDNW/+JCU84+3QYmKpLi+XvHdmrlwUwDA6kqw==", + "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" ], @@ -1538,9 +1618,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.0.tgz", - "integrity": "sha512-e2hrvElFIh6kW/UNBQK/kzqMNY5mO+67YtEh9OA65RM5IJXYTWiXjX6fjIiPaqOkBthYF1EqgiZ6OXKcQsM0hg==", + "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" ], @@ -1552,9 +1632,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.0.tgz", - "integrity": "sha512-1vvmgDdUSebVGXWX2lIcgRebqfQSff0hMEkLJyakQ9JQUbLDkEaMsPTLOmyccyC6IJ/l3FZuJbmrBw/u0A0uCQ==", + "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" ], @@ -1566,9 +1646,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.0.tgz", - "integrity": "sha512-s5oFkZ/hFcrlAyBTONFY1TWndfyre1wOMwU+6KCpm/iatybvrRgmZVM+vCFwxmC5ZhdlgfE0N4XorsDpi7/4XQ==", + "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" ], @@ -1580,9 +1660,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.0.tgz", - "integrity": "sha512-G9+TEqRnAA6nbpqyUqgTiopmnfgnMkR3kMukFBDsiyy23LZvUCpiUwjTRx6ezYCjJODXrh52rBR9oXvm+Fp5wg==", + "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" ], @@ -1594,9 +1674,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.0.tgz", - "integrity": "sha512-2jsCDZwtQvRhejHLfZ1JY6w6kEuEtfF9nzYsZxzSlNVKDX+DpsDJ+Rbjkm74nvg2rdx0gwBS+IMdvwJuq3S9pQ==", + "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" ], @@ -1834,12 +1914,22 @@ } }, "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, "license": "MIT" }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.6.tgz", + "integrity": "sha512-lPByRJUer/iN/xa4qpyL0qmL11DqNW81iU/IG1S3uvRUq4oKagz8VCxZjiWkumgt66YT3vOdDgZ0o32sGKtCEw==", + "license": "MIT", + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -1904,6 +1994,16 @@ "@types/react": "*" } }, + "node_modules/@types/react-input-mask": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/react-input-mask/-/react-input-mask-3.0.6.tgz", + "integrity": "sha512-+5I18WKyG3eWIj7TVPWfK1VitI9mPpS9y6jE/BfmTCe+iL27NfBw/yzKRvCFp1DRBvlvvcsiZf05bub0YC1k8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/react-transition-group": { "version": "4.4.11", "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.11.tgz", @@ -1913,6 +2013,12 @@ "@types/react": "*" } }, + "node_modules/@types/yup": { + "version": "0.29.14", + "resolved": "https://registry.npmjs.org/@types/yup/-/yup-0.29.14.tgz", + "integrity": "sha512-Ynb/CjHhE/Xp/4bhHmQC4U1Ox+I2OpfRYF3dnNgQqn1cHa6LK3H1wJMNPT02tSVZA6FYuXE2ITORfbnb6zBCSA==", + "license": "MIT" + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "7.18.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", @@ -2430,6 +2536,12 @@ "dev": true, "license": "MIT" }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -2456,6 +2568,17 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/axobject-query": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz", @@ -2638,6 +2761,18 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "license": "MIT" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", @@ -2684,10 +2819,19 @@ "node": ">=10" } }, + "node_modules/cross-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.6.12" + } + }, "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, "license": "MIT", "dependencies": { @@ -2825,6 +2969,15 @@ "dev": true, "license": "MIT" }, + "node_modules/deepmerge": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz", + "integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -2861,6 +3014,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -4089,6 +4251,26 @@ "dev": true, "license": "ISC" }, + "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" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -4099,6 +4281,51 @@ "is-callable": "^1.1.3" } }, + "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==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/formik": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/formik/-/formik-2.4.6.tgz", + "integrity": "sha512-A+2EI7U7aG296q2TLGvNapDNTZp1khVt5Vk0Q/fyfSROss0V/V6+txt2aJnwEos44IxTCW/LYAi/zgWzlevj+g==", + "funding": [ + { + "type": "individual", + "url": "https://opencollective.com/formik" + } + ], + "license": "Apache-2.0", + "dependencies": { + "@types/hoist-non-react-statics": "^3.3.1", + "deepmerge": "^2.1.1", + "hoist-non-react-statics": "^3.3.0", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "react-fast-compare": "^2.0.1", + "tiny-warning": "^1.0.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/formik/node_modules/react-fast-compare": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz", + "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==", + "license": "MIT" + }, "node_modules/fs-extra": { "version": "11.1.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", @@ -4467,6 +4694,64 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, + "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": "24.2.0", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-24.2.0.tgz", + "integrity": "sha512-ArJJTS1lV6lgKH7yEf4EpgNZ7+THl7bsGxxougPYiXRTJ/Fe1j08/TBpV9QsXCIYVfdE/HWG/xLezJ5DOlfBOA==", + "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", + "dependencies": { + "@babel/runtime": "^7.23.2" + }, + "peerDependencies": { + "typescript": "^5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/i18next-browser-languagedetector": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.0.2.tgz", + "integrity": "sha512-shBvPmnIyZeD2VU5jVGIOWP7u9qNG3Lj7mpaiPFpbJ3LVfHZJvVzKR4v1Cb91wAOFpNw442N+LGPzHOHsten2g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.2" + } + }, + "node_modules/i18next-http-backend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-3.0.1.tgz", + "integrity": "sha512-XT2lYSkbAtDE55c6m7CtKxxrsfuRQO3rUfHzj8ZyRtY9CkIX3aRGwXGTkUhpGWce+J8n7sfu3J0f2wTzo7Lw0A==", + "license": "MIT", + "dependencies": { + "cross-fetch": "4.0.0" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -5077,6 +5362,15 @@ "node": ">=4.0" } }, + "node_modules/jwt-decode": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", + "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/language-subtag-registry": { "version": "0.3.23", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", @@ -5188,6 +5482,27 @@ "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==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -5221,9 +5536,9 @@ "license": "MIT" }, "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==", "dev": true, "funding": [ { @@ -5253,6 +5568,26 @@ "dev": true, "license": "MIT" }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -5545,9 +5880,9 @@ } }, "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==", "license": "ISC" }, "node_modules/picomatch": { @@ -5574,9 +5909,9 @@ } }, "node_modules/postcss": { - "version": "8.4.41", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", - "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", "dev": true, "funding": [ { @@ -5595,8 +5930,8 @@ "license": "MIT", "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" @@ -5658,6 +5993,18 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, + "node_modules/property-expr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz", + "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==", + "license": "MIT" + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -5747,6 +6094,42 @@ "react": "^16.6.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/react-i18next": { + "version": "15.4.0", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.4.0.tgz", + "integrity": "sha512-Py6UkX3zV08RTvL6ZANRoBh9sL/ne6rQq79XlkHEdd82cZr2H9usbWpUNVadJntIZP2pu3M2rL1CN+5rQYfYFw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.25.0", + "html-parse-stringify": "^3.0.1" + }, + "peerDependencies": { + "i18next": ">= 23.2.3", + "react": ">= 16.8.0" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/react-input-mask": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/react-input-mask/-/react-input-mask-2.0.4.tgz", + "integrity": "sha512-1hwzMr/aO9tXfiroiVCx5EtKohKwLk/NT8QlJXHQ4N+yJJFyUuMT+zfTpLBwX/lK3PkuMlievIffncpMZ3HGRQ==", + "license": "MIT", + "dependencies": { + "invariant": "^2.2.4", + "warning": "^4.0.2" + }, + "peerDependencies": { + "react": ">=0.14.0", + "react-dom": ">=0.14.0" + } + }, "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", @@ -5926,13 +6309,13 @@ } }, "node_modules/rollup": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.0.tgz", - "integrity": "sha512-vo+S/lfA2lMS7rZ2Qoubi6I5hwZwzXeUIctILZLbHI+laNtvhhOIon2S1JksA5UEDQ7l3vberd0fxK44lTYjbQ==", + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.29.1.tgz", + "integrity": "sha512-RaJ45M/kmJUzSWDs1Nnd5DdV4eerC98idtUOVr6FfKcgxqvjwHmxc5upLF9qZU9EpsVzzhleFahrT3shLuJzIw==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.5" + "@types/estree": "1.0.6" }, "bin": { "rollup": "dist/bin/rollup" @@ -5942,22 +6325,25 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.21.0", - "@rollup/rollup-android-arm64": "4.21.0", - "@rollup/rollup-darwin-arm64": "4.21.0", - "@rollup/rollup-darwin-x64": "4.21.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.21.0", - "@rollup/rollup-linux-arm-musleabihf": "4.21.0", - "@rollup/rollup-linux-arm64-gnu": "4.21.0", - "@rollup/rollup-linux-arm64-musl": "4.21.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.21.0", - "@rollup/rollup-linux-riscv64-gnu": "4.21.0", - "@rollup/rollup-linux-s390x-gnu": "4.21.0", - "@rollup/rollup-linux-x64-gnu": "4.21.0", - "@rollup/rollup-linux-x64-musl": "4.21.0", - "@rollup/rollup-win32-arm64-msvc": "4.21.0", - "@rollup/rollup-win32-ia32-msvc": "4.21.0", - "@rollup/rollup-win32-x64-msvc": "4.21.0", + "@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" } }, @@ -6169,9 +6555,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==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -6483,6 +6869,12 @@ "dev": true, "license": "MIT" }, + "node_modules/tiny-case": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz", + "integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==", + "license": "MIT" + }, "node_modules/tiny-invariant": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", @@ -6490,6 +6882,12 @@ "dev": true, "license": "MIT" }, + "node_modules/tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", + "license": "MIT" + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -6512,6 +6910,18 @@ "node": ">=8.0" } }, + "node_modules/toposort": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==", + "license": "MIT" + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, "node_modules/ts-api-utils": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", @@ -6542,7 +6952,6 @@ "version": "2.6.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", - "dev": true, "license": "0BSD" }, "node_modules/type-check": { @@ -6706,14 +7115,14 @@ } }, "node_modules/vite": { - "version": "5.4.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.2.tgz", - "integrity": "sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==", + "version": "5.4.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz", + "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.21.3", - "postcss": "^8.4.41", + "postcss": "^8.4.43", "rollup": "^4.20.0" }, "bin": { @@ -6908,6 +7317,15 @@ "node": ">=8" } }, + "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/vscode-jsonrpc": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz", @@ -7002,6 +7420,31 @@ "dev": true, "license": "MIT" }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -7129,6 +7572,30 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/yup": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/yup/-/yup-1.6.1.tgz", + "integrity": "sha512-JED8pB50qbA4FOkDol0bYF/p60qSEDQqBD0/qeIrUCG1KbPBIQ776fCUNb9ldbPcSTxA69g/47XTo4TqWiuXOA==", + "license": "MIT", + "dependencies": { + "property-expr": "^2.0.5", + "tiny-case": "^1.0.3", + "toposort": "^2.0.2", + "type-fest": "^2.19.0" + } + }, + "node_modules/yup/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/package.json b/package.json index 8842c7bea..e21ca2198 100644 --- a/package.json +++ b/package.json @@ -30,22 +30,34 @@ "@fontsource-variable/dm-sans": "^5.0.7", "@fontsource/barlow": "^5.0.14", "@iconify/react": "^5.0.2", + "@mui/icons-material": "^6.3.1", "@mui/lab": "^5.0.0-alpha.173", "@mui/material": "^5.16.7", + "@types/yup": "^0.29.14", "apexcharts": "^3.52.0", + "axios": "^1.7.9", "dayjs": "^1.11.13", + "formik": "^2.4.6", "history": "^5.3.0", + "i18next": "^24.2.0", + "i18next-browser-languagedetector": "^8.0.2", + "i18next-http-backend": "^3.0.1", + "jwt-decode": "^4.0.0", "react": "^18.3.1", "react-apexcharts": "^1.4.1", "react-dom": "^18.3.1", "react-helmet-async": "^2.0.5", + "react-i18next": "^15.4.0", + "react-input-mask": "^2.0.4", "react-router-dom": "^6.26.1", - "simplebar-react": "^3.2.6" + "simplebar-react": "^3.2.6", + "yup": "^1.6.1" }, "devDependencies": { "@types/node": "^22.5.0", "@types/react": "^18.3.4", "@types/react-dom": "^18.3.0", + "@types/react-input-mask": "^3.0.6", "@typescript-eslint/eslint-plugin": "^7.18.0", "@typescript-eslint/parser": "^7.18.0", "@vitejs/plugin-react-swc": "^3.7.0", diff --git a/public/assets/icons/flags/ic-flag-en.svg b/public/assets/icons/flags/ic-flag-en.svg index 485ad5c34..30bc3008c 100644 --- a/public/assets/icons/flags/ic-flag-en.svg +++ b/public/assets/icons/flags/ic-flag-en.svg @@ -1 +1,21 @@ - \ No newline at end of file + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/assets/icons/flags/ic-flag-fr.svg b/public/assets/icons/flags/ic-flag-fr.svg deleted file mode 100644 index 2ae63a139..000000000 --- a/public/assets/icons/flags/ic-flag-fr.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/assets/icons/flags/ic-flag-tr.svg b/public/assets/icons/flags/ic-flag-tr.svg new file mode 100644 index 000000000..6eb0e145e --- /dev/null +++ b/public/assets/icons/flags/ic-flag-tr.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/assets/icons/navbar/ic-business.svg b/public/assets/icons/navbar/ic-business.svg new file mode 100644 index 000000000..04eb9028b --- /dev/null +++ b/public/assets/icons/navbar/ic-business.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/public/locales/en/auth.json b/public/locales/en/auth.json new file mode 100644 index 000000000..f7fd9e5b7 --- /dev/null +++ b/public/locales/en/auth.json @@ -0,0 +1,36 @@ +{ + "signIn": { + "title": "Sign In", + "welcome": "Welcome", + "getStarted": "Get Started", + "forgotPassword": "Forgot password?", + "labels": { + "email": "Email address", + "password": "Password" + }, + "placeholders": { + "email": "your@email.com", + "password": "••••••" + }, + "buttons": { + "signIn": "Sign In", + "signingIn": "Signing in..." + }, + "errors": { + "invalidEmail": "Please enter a valid email address.", + "passwordLength": "Password must be at least 6 characters long." + }, + "helmet": "Sign In | Admin Panel" + }, + "resetPassword": { + "title": "Reset Password", + "description": "Enter your account's email address, and we'll send you a link to reset your password.", + "emailLabel": "Email address", + "emailPlaceholder": "your@email.com", + "cancel": "Cancel", + "continue": "Continue", + "loading": "Loading...", + "successMessage": "Password reset link sent successfully!", + "emailErrorMessage": "Please enter a valid email address." + } +} \ No newline at end of file diff --git a/public/locales/en/common.json b/public/locales/en/common.json new file mode 100644 index 000000000..f32e119f7 --- /dev/null +++ b/public/locales/en/common.json @@ -0,0 +1,37 @@ +{ + "welcome": "Welcome", + "getStarted": "Get Started", + "months": { + "short": { + "jan": "Jan", + "feb": "Feb", + "mar": "Mar", + "apr": "Apr", + "may": "May", + "jun": "Jun", + "jul": "Jul", + "aug": "Aug", + "sep": "Sep", + "oct": "Oct", + "nov": "Nov", + "dec": "Dec" + }, + "long": { + "january": "January", + "february": "February", + "march": "March", + "april": "April", + "may": "May", + "june": "June", + "july": "July", + "august": "August", + "september": "September", + "october": "October", + "november": "November", + "december": "December" + } + }, + "spinner": { + "pleaseWait": "Please Wait..." + } +} \ No newline at end of file diff --git a/public/locales/en/company.json b/public/locales/en/company.json new file mode 100644 index 000000000..1ca2bb64c --- /dev/null +++ b/public/locales/en/company.json @@ -0,0 +1,82 @@ +{ + "company": { + "title": "Companies", + "newCompany": "New Company", + "addCompany": "Add Company", + "companyName": "Company Name", + "phone": "Phone", + "email": "Email", + "status": "Status", + "active": "Active", + "passive": "Passive", + "taxNumber": "Tax Number", + "website": "Website", + "address": "Address", + "description": "Description", + "cancel": "Cancel", + "save": "Save", + "edit": "Edit", + "delete": "Delete", + "companyDetails": "Company Details", + "foundedYear": "Founded Year", + "employeeCount": "Employee Count", + "certifications": "Certifications", + "operationAreas": "Operation Areas", + "sections": { + "basicInfo": "Basic Information", + "companyDetails": "Company Details", + "insurance": "Insurance Information", + "bankAccounts": "Bank Accounts", + "description": "Description", + "contacts": "Contact Persons" + }, + "insurance": { + "provider": "Insurance Provider", + "policyNumber": "Policy Number", + "coverage": "Coverage", + "expiryDate": "Expiry Date" + }, + "bank": { + "name": "Bank", + "branch": "Branch", + "accountNumber": "Account Number", + "iban": "IBAN", + "currency": "Currency" + }, + "contacts": { + "new": "New Contact", + "mainContact": "Main Contact", + "contactMethod": { + "phone": "Phone", + "email": "Email", + "whatsapp": "WhatsApp" + } + }, + "tabs": { + "general": "General Information", + "contacts": "Contact Persons", + "contracts": "Contracts", + "performance": "Performance", + "pricing": "Pricing", + "analytics": "Analytics" + }, + "validation": { + "companyNameRequired": "Company name is required", + "companyNameMin": "Company name must be at least 2 characters", + "companyNameMax": "Company name cannot exceed 100 characters", + "taxNumberRequired": "Tax number is required", + "taxNumberFormat": "Tax number must be 10 digits", + "phoneRequired": "Phone number is required", + "phoneFormat": "Please enter a valid phone number (10-11 digits)", + "emailRequired": "Email address is required", + "addressRequired": "Address is required", + "addressMin": "Address must be at least 10 characters", + "addressMax": "Address cannot exceed 500 characters", + "descriptionMax": "Description cannot exceed 1000 characters", + "statusRequired": "Status selection is required", + "email": "Please enter a valid email address", + "phone": "Please enter a valid phone number", + "url": "Please enter a valid website URL" + } + } +} \ No newline at end of file diff --git a/public/locales/en/error.json b/public/locales/en/error.json new file mode 100644 index 000000000..7fc17582e --- /dev/null +++ b/public/locales/en/error.json @@ -0,0 +1,6 @@ +{ + "notFound": { + "title": "404 page not found!", + "helmet": "404 page not found! | Error - {appName}" + } +} \ No newline at end of file diff --git a/public/locales/en/overview.json b/public/locales/en/overview.json new file mode 100644 index 000000000..ddb9f0df3 --- /dev/null +++ b/public/locales/en/overview.json @@ -0,0 +1,57 @@ +{ + "welcome": "Hi, Welcome back 👋", + "widgets": { + "weeklySales": "Weekly Sales", + "newUsers": "New Users", + "purchaseOrders": "Purchase Orders", + "messages": "Messages" + }, + "charts": { + "currentVisits": { + "title": "Current Visits", + "regions": { + "america": "America", + "asia": "Asia", + "europe": "Europe", + "africa": "Africa" + } + }, + "websiteVisits": { + "title": "Website Visits", + "subheader": "(+43%) than last year", + "teams": { + "teamA": "Team A", + "teamB": "Team B" + } + }, + "conversionRates": { + "title": "Conversion Rates", + "subheader": "(+43%) than last year" + }, + "currentSubject": { + "title": "Current Subject", + "subjects": { + "english": "English", + "history": "History", + "physics": "Physics", + "geography": "Geography", + "chinese": "Chinese", + "math": "Math" + } + } + }, + "sections": { + "news": { + "title": "News" + }, + "orderTimeline": { + "title": "Order Timeline" + }, + "trafficBySite": { + "title": "Traffic by Site" + }, + "tasks": { + "title": "Tasks" + } + } +} \ No newline at end of file diff --git a/public/locales/en/products.json b/public/locales/en/products.json new file mode 100644 index 000000000..eb199d435 --- /dev/null +++ b/public/locales/en/products.json @@ -0,0 +1,6 @@ +{ + "list": { + "title": "Products", + "helmet": "Products | Admin Panel" + } +} \ No newline at end of file diff --git a/public/locales/en/profile.json b/public/locales/en/profile.json new file mode 100644 index 000000000..1eef9240e --- /dev/null +++ b/public/locales/en/profile.json @@ -0,0 +1,43 @@ +{ + "title": "Profile", + "subtitle": "Update your profile information and manage security settings", + "tabs": { + "general": "General Information", + "security": "Security", + "notifications": "Notifications" + }, + "name": "First Name", + "lastName": "Last Name", + "email": "Email", + "phone": "Phone Number", + "phoneFormat": "(555) 555 55 55", + "about": "About Me", + "aboutPlaceholder": "Write a short bio about yourself...", + "save": "Save", + "reset": "Reset", + "success": "Your profile has been updated successfully", + "error": { + "update": "Failed to update your profile" + }, + "loading": "Loading...", + "security": { + "title": "Change Password", + "currentPassword": "Current Password", + "newPassword": "New Password", + "confirmPassword": "Confirm New Password", + "changePassword": "Change Password", + "success": "Your password has been changed successfully", + "error": "Failed to change your password" + }, + "notifications": { + "title": "Notification Preferences", + "email": "Email Notifications", + "emailDesc": "Receive email notifications for important updates and information", + "push": "Push Notifications", + "pushDesc": "Receive push notifications through the application", + "sms": "SMS Notifications", + "smsDesc": "Receive SMS notifications for important updates", + "success": "Your notification preferences have been updated", + "error": "Failed to update your notification preferences" + } +} \ No newline at end of file diff --git a/public/locales/en/validation.json b/public/locales/en/validation.json new file mode 100644 index 000000000..aa5416bd2 --- /dev/null +++ b/public/locales/en/validation.json @@ -0,0 +1,35 @@ +{ + "required": { + "firstName": "First name is required", + "lastName": "Last name is required", + "email": "Email is required", + "phone": "Phone number is required", + "password": "Password is required", + "currentPassword": "Current password is required", + "newPassword": "New password is required", + "confirmPassword": "Confirm password is required" + }, + "invalid": { + "email": "Please enter a valid email address", + "phone": "Please enter a valid phone number", + "password": "Please enter a valid password" + }, + "length": { + "name": { + "min": "Must be at least 2 characters", + "max": "Must be at most 50 characters" + }, + "about": { + "max": "Must be at most 500 characters" + } + }, + "password": { + "uppercase": "Must contain at least one uppercase letter", + "lowercase": "Must contain at least one lowercase letter", + "number": "Must contain at least one number", + "special": "Must contain at least one special character", + "match": "Passwords do not match", + "same": "New password cannot be the same as current password", + "min": "Must be at least {{length}} characters" + } +} \ No newline at end of file diff --git a/public/locales/tr/auth.json b/public/locales/tr/auth.json new file mode 100644 index 000000000..653ff74c6 --- /dev/null +++ b/public/locales/tr/auth.json @@ -0,0 +1,36 @@ +{ + "signIn": { + "title": "Giriş Yap", + "welcome": "Hoş geldiniz", + "getStarted": "Başlayın", + "forgotPassword": "Şifremi Unuttum", + "labels": { + "email": "E-posta adresi", + "password": "Şifre" + }, + "placeholders": { + "email": "ornek@email.com", + "password": "••••••" + }, + "buttons": { + "signIn": "Giriş Yap", + "signingIn": "Giriş Yapılıyor..." + }, + "errors": { + "invalidEmail": "Lütfen geçerli bir e-posta adresi girin.", + "passwordLength": "Şifre en az 6 karakter olmalıdır." + }, + "helmet": "Giriş Yap | Yönetim Paneli" + }, + "resetPassword": { + "title": "Şifreyi sıfırla", + "description": "Hesabınızın e-posta adresini girin, size şifrenizi sıfırlamak için bir bağlantı göndereceğiz.", + "emailLabel": "E-posta adresi", + "emailPlaceholder": "sizin@email.com", + "cancel": "İptal", + "continue": "Devam et", + "loading": "Yükleniyor...", + "successMessage": "Şifre sıfırlama bağlantısı başarıyla gönderildi!", + "emailErrorMessage": "Lütfen geçerli bir e-posta adresi girin." + } +} \ No newline at end of file diff --git a/public/locales/tr/common.json b/public/locales/tr/common.json new file mode 100644 index 000000000..d00832cad --- /dev/null +++ b/public/locales/tr/common.json @@ -0,0 +1,8 @@ +{ + "back": "Geri Dön", + "cancel": "İptal", + "confirm": "Tamam", + "unsavedChanges": "Kaydedilmemiş Değişiklikler", + "unsavedChangesMessage": "Kaydedilmemiş değişiklikleriniz var. Çıkmak istediğinizden emin misiniz?", + "pleaseWait": "Lütfen bekleyin..." +} \ No newline at end of file diff --git a/public/locales/tr/company.json b/public/locales/tr/company.json new file mode 100644 index 000000000..3fde0e1e3 --- /dev/null +++ b/public/locales/tr/company.json @@ -0,0 +1,82 @@ +{ + "company": { + "title": "İş Ortakları", + "newCompany": "Yeni Şirket", + "addCompany": "Şirket Ekle", + "companyName": "Şirket Adı", + "phone": "Telefon", + "email": "E-posta", + "status": "Durum", + "active": "Aktif", + "passive": "Pasif", + "taxNumber": "Vergi Numarası", + "website": "Web Sitesi", + "address": "Adres", + "description": "Açıklama", + "cancel": "İptal", + "save": "Kaydet", + "edit": "Düzenle", + "delete": "Sil", + "companyDetails": "Şirket Detayları", + "foundedYear": "Kuruluş Yılı", + "employeeCount": "Çalışan Sayısı", + "certifications": "Sertifikalar", + "operationAreas": "Faaliyet Alanları", + "sections": { + "basicInfo": "Temel Bilgiler", + "companyDetails": "Şirket Detayları", + "insurance": "Sigorta Bilgileri", + "bankAccounts": "Banka Hesapları", + "description": "Açıklama", + "contacts": "İletişim Kişileri" + }, + "insurance": { + "provider": "Sigorta Şirketi", + "policyNumber": "Poliçe Numarası", + "coverage": "Kapsam", + "expiryDate": "Bitiş Tarihi" + }, + "bank": { + "name": "Banka", + "branch": "Şube", + "accountNumber": "Hesap Numarası", + "iban": "IBAN", + "currency": "Para Birimi" + }, + "contacts": { + "new": "Yeni Kişi", + "mainContact": "Ana İletişim", + "contactMethod": { + "phone": "Telefon", + "email": "E-posta", + "whatsapp": "WhatsApp" + } + }, + "tabs": { + "general": "Genel Bilgiler", + "contacts": "İletişim Kişileri", + "contracts": "Sözleşmeler", + "performance": "Performans", + "pricing": "Fiyatlandırma", + "analytics": "Analitik" + }, + "validation": { + "companyNameRequired": "Şirket adı zorunludur", + "companyNameMin": "Şirket adı en az 2 karakter olmalıdır", + "companyNameMax": "Şirket adı en fazla 100 karakter olabilir", + "taxNumberRequired": "Vergi numarası zorunludur", + "taxNumberFormat": "Vergi numarası 10 haneli olmalıdır", + "phoneRequired": "Telefon numarası zorunludur", + "phoneFormat": "Geçerli bir telefon numarası giriniz (10-11 haneli)", + "emailRequired": "E-posta adresi zorunludur", + "addressRequired": "Adres zorunludur", + "addressMin": "Adres en az 10 karakter olmalıdır", + "addressMax": "Adres en fazla 500 karakter olabilir", + "descriptionMax": "Açıklama en fazla 1000 karakter olabilir", + "statusRequired": "Durum seçimi zorunludur", + "email": "Geçerli bir e-posta adresi giriniz", + "phone": "Geçerli bir telefon numarası giriniz", + "url": "Geçerli bir web sitesi adresi giriniz" + } + } +} \ No newline at end of file diff --git a/public/locales/tr/error.json b/public/locales/tr/error.json new file mode 100644 index 000000000..e395cf7d8 --- /dev/null +++ b/public/locales/tr/error.json @@ -0,0 +1,6 @@ +{ + "notFound": { + "title": "404 sayfa bulunamadı!", + "helmet": "404 sayfa bulunamadı! | Hata - {appName}" + } +} \ No newline at end of file diff --git a/public/locales/tr/overview.json b/public/locales/tr/overview.json new file mode 100644 index 000000000..29e42f738 --- /dev/null +++ b/public/locales/tr/overview.json @@ -0,0 +1,57 @@ +{ + "welcome": "Merhaba, Tekrar Hoş Geldiniz 👋", + "widgets": { + "weeklySales": "Haftalık Satışlar", + "newUsers": "Yeni Kullanıcılar", + "purchaseOrders": "Satın Alma Siparişleri", + "messages": "Mesajlar" + }, + "charts": { + "currentVisits": { + "title": "Mevcut Ziyaretler", + "regions": { + "america": "Amerika", + "asia": "Asya", + "europe": "Avrupa", + "africa": "Afrika" + } + }, + "websiteVisits": { + "title": "Website Ziyaretleri", + "subheader": "Geçen yıla göre (+%43)", + "teams": { + "teamA": "Takım A", + "teamB": "Takım B" + } + }, + "conversionRates": { + "title": "Dönüşüm Oranları", + "subheader": "Geçen yıla göre (+%43)" + }, + "currentSubject": { + "title": "Mevcut Konu", + "subjects": { + "english": "İngilizce", + "history": "Tarih", + "physics": "Fizik", + "geography": "Coğrafya", + "chinese": "Çince", + "math": "Matematik" + } + } + }, + "sections": { + "news": { + "title": "Haberler" + }, + "orderTimeline": { + "title": "Sipariş Zaman Çizelgesi" + }, + "trafficBySite": { + "title": "Sitelere Göre Trafik" + }, + "tasks": { + "title": "Görevler" + } + } +} \ No newline at end of file diff --git a/public/locales/tr/products.json b/public/locales/tr/products.json new file mode 100644 index 000000000..40d2938f4 --- /dev/null +++ b/public/locales/tr/products.json @@ -0,0 +1,6 @@ +{ + "list": { + "title": "Ürünler", + "helmet": "Ürünler | Yönetim Paneli" + } +} \ No newline at end of file diff --git a/public/locales/tr/profile.json b/public/locales/tr/profile.json new file mode 100644 index 000000000..caaa9f36a --- /dev/null +++ b/public/locales/tr/profile.json @@ -0,0 +1,53 @@ +{ + "title": "Profil", + "subtitle": "Profil bilgilerinizi güncelleyin ve güvenlik ayarlarınızı yönetin", + "tabs": { + "general": "Genel Bilgiler", + "security": "Güvenlik", + "notifications": "Bildirimler" + }, + "name": "Ad", + "lastName": "Soyad", + "email": "E-posta", + "phone": "Telefon Numarası", + "phoneFormat": "(___) ___ __ __", + "about": "Hakkımda", + "aboutPlaceholder": "Kendiniz hakkında kısa bir bilgi yazın...", + "save": "Kaydet", + "reset": "Sıfırla", + "success": "Profil bilgileriniz başarıyla güncellendi", + "error": { + "update": "Profil bilgileriniz güncellenirken bir hata oluştu" + }, + "loading": "Yükleniyor...", + "security": { + "title": "Şifre Değiştir", + "currentPassword": "Mevcut Şifre", + "newPassword": "Yeni Şifre", + "confirmPassword": "Yeni Şifre (Tekrar)", + "changePassword": "Şifreyi Değiştir", + "passwordRules": "Şifre aşağıdaki kurallara uygun olmalıdır", + "success": "Şifreniz başarıyla değiştirildi", + "error": "Şifreniz değiştirilirken bir hata oluştu" + }, + "notifications": { + "title": "Bildirim Tercihleri", + "manage": "Yönet", + "email": { + "title": "E-posta Bildirimleri", + "description": "Önemli güncellemeler ve bilgilendirmeler için e-posta bildirimleri alın" + }, + "push": { + "title": "Anlık Bildirimler", + "description": "Uygulama üzerinden anlık bildirimler alın" + }, + "monthly": { + "title": "Aylık Rapor", + "description": "Her ay sonunda aktivite özetinizi alın" + }, + "news": { + "title": "Haberler ve Güncellemeler", + "description": "Yeni özellikler ve güncellemelerden haberdar olun" + } + } +} \ No newline at end of file diff --git a/public/locales/tr/validation.json b/public/locales/tr/validation.json new file mode 100644 index 000000000..5700d125e --- /dev/null +++ b/public/locales/tr/validation.json @@ -0,0 +1,35 @@ +{ + "required": { + "firstName": "Ad alanı zorunludur", + "lastName": "Soyad alanı zorunludur", + "email": "E-posta alanı zorunludur", + "phone": "Telefon numarası zorunludur", + "password": "Şifre alanı zorunludur", + "currentPassword": "Mevcut şifre alanı zorunludur", + "newPassword": "Yeni şifre alanı zorunludur", + "confirmPassword": "Şifre tekrar alanı zorunludur" + }, + "invalid": { + "email": "Geçerli bir e-posta adresi giriniz", + "phone": "Geçerli bir telefon numarası giriniz", + "password": "Geçerli bir şifre giriniz" + }, + "length": { + "name": { + "min": "En az 2 karakter olmalıdır", + "max": "En fazla 50 karakter olmalıdır" + }, + "about": { + "max": "En fazla 500 karakter olmalıdır" + } + }, + "password": { + "uppercase": "En az bir büyük harf içermelidir", + "lowercase": "En az bir küçük harf içermelidir", + "number": "En az bir rakam içermelidir", + "special": "En az bir özel karakter içermelidir", + "match": "Şifreler eşleşmiyor", + "same": "Yeni şifre mevcut şifre ile aynı olamaz", + "min": "En az {{length}} karakter olmalıdır" + } +} \ No newline at end of file diff --git a/src/_mock/_data.ts b/src/_mock/_data.ts index d6dadc0eb..0e22139ff 100644 --- a/src/_mock/_data.ts +++ b/src/_mock/_data.ts @@ -99,21 +99,21 @@ export const _products = [...Array(24)].map((_, index) => { // ---------------------------------------------------------------------- export const _langs = [ + { + value: 'fr', + label: 'Turkish', + icon: '/assets/icons/flags/ic-flag-tr.svg', + }, { value: 'en', label: 'English', icon: '/assets/icons/flags/ic-flag-en.svg', }, - { - value: 'de', - label: 'German', - icon: '/assets/icons/flags/ic-flag-de.svg', - }, - { - value: 'fr', - label: 'French', - icon: '/assets/icons/flags/ic-flag-fr.svg', - }, + // { + // value: 'de', + // label: 'German', + // icon: '/assets/icons/flags/ic-flag-de.svg', + // } ]; // ---------------------------------------------------------------------- diff --git a/src/app.tsx b/src/app.tsx index 68bbf1784..80df5516d 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -1,44 +1,19 @@ import 'src/global.css'; -import Fab from '@mui/material/Fab'; - import { Router } from 'src/routes/sections'; import { useScrollToTop } from 'src/hooks/use-scroll-to-top'; import { ThemeProvider } from 'src/theme/theme-provider'; -import { Iconify } from 'src/components/iconify'; - -// ---------------------------------------------------------------------- +import { LoadingScreen } from './components/loading-screen/loading-screen'; export default function App() { useScrollToTop(); - - const githubButton = ( - - - - ); - return ( - {githubButton} + ); } diff --git a/src/components/back-button.tsx b/src/components/back-button.tsx new file mode 100644 index 000000000..1b8af4ac6 --- /dev/null +++ b/src/components/back-button.tsx @@ -0,0 +1,21 @@ +import { Button } from '@mui/material'; +import { useNavigate } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; +import ArrowBackIcon from '@mui/icons-material/ArrowBack'; + +export function BackButton() { + const navigate = useNavigate(); + const { t } = useTranslation(); + + return ( + + ); +} \ No newline at end of file diff --git a/src/components/color-utils/color-picker.tsx b/src/components/color-utils/color-picker.tsx index 78d79db0f..92e8016fc 100644 --- a/src/components/color-utils/color-picker.tsx +++ b/src/components/color-utils/color-picker.tsx @@ -89,7 +89,13 @@ export const ColorPicker = forwardRef ({ + sx={(theme: { + palette: { getContrastText: (arg0: string) => any }; + transitions: { + create: (arg0: string, arg1: { duration: any }) => any; + duration: { shortest: any }; + }; + }) => ({ color: theme.palette.getContrastText(color), transition: theme.transitions.create('all', { duration: theme.transitions.duration.shortest, diff --git a/src/components/iconify/index.ts b/src/components/iconify/index.ts index 111840cf0..86b67def1 100644 --- a/src/components/iconify/index.ts +++ b/src/components/iconify/index.ts @@ -1,7 +1,4 @@ export * from './classes'; - -export * from './iconify'; - export * from './flag-icon'; - export type * from './types'; +export { Iconify } from './iconify'; diff --git a/src/components/loading-screen/loading-screen.tsx b/src/components/loading-screen/loading-screen.tsx new file mode 100644 index 000000000..48128f7a5 --- /dev/null +++ b/src/components/loading-screen/loading-screen.tsx @@ -0,0 +1,38 @@ +import {useTranslation} from "react-i18next"; + +import { Box, Backdrop, Typography, CircularProgress } from '@mui/material'; + +import { useLoading } from 'src/contexts/loading-context'; + +export function LoadingScreen() { + const { t } = useTranslation(); + const { isLoading } = useLoading(); + + if (!isLoading) return null; + + return ( + + + + + {t('common:spinner.pleaseWait')} + + + + ); +} \ No newline at end of file diff --git a/src/components/scrollbar/index.ts b/src/components/scrollbar/index.ts index a483df68b..871a0a108 100644 --- a/src/components/scrollbar/index.ts +++ b/src/components/scrollbar/index.ts @@ -1,5 +1,3 @@ export * from './classes'; - -export * from './scrollbar'; - export type * from './types'; +export { Scrollbar } from './scrollbar'; diff --git a/src/components/settings/context/settings-context.tsx b/src/components/settings/context/settings-context.tsx new file mode 100644 index 000000000..2e4e20b3c --- /dev/null +++ b/src/components/settings/context/settings-context.tsx @@ -0,0 +1,15 @@ +import { useContext, createContext } from 'react'; + +export interface SettingsContextProps { + themeStretch: boolean; +} + +export const SettingsContext = createContext({} as SettingsContextProps); + +export const useSettingsContext = () => { + const context = useContext(SettingsContext); + + if (!context) throw new Error('useSettingsContext must be used inside SettingsProvider'); + + return context; +}; \ No newline at end of file diff --git a/src/config/axios-instance.ts b/src/config/axios-instance.ts new file mode 100644 index 000000000..57c3b5ce7 --- /dev/null +++ b/src/config/axios-instance.ts @@ -0,0 +1,30 @@ +import axios from 'axios'; + +// Axios instance oluştur +const axiosInstance = axios.create({ + baseURL: 'http://localhost:8888', // .env'den baseURL al + timeout: 10000, // 10 saniye zaman aşımı + headers: { + 'Content-Type': 'application/json', + }, +}); + +// Request interceptor: Token ekleme +axiosInstance.interceptors.request.use((config) => { + const token: string | null = localStorage.getItem('token'); + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } + return config; +}); + +// Response interceptor: Hata yönetimi +axiosInstance.interceptors.response.use( + (response) => response, + (error) => { + console.error('API Error:', error.response || error.message); + return Promise.reject(error?.response?.data || 'Something went wrong'); + } +); + +export default axiosInstance; diff --git a/src/contexts/Providers.tsx b/src/contexts/Providers.tsx new file mode 100644 index 000000000..b1ad09886 --- /dev/null +++ b/src/contexts/Providers.tsx @@ -0,0 +1,34 @@ +import type { ReactNode } from 'react'; + +import { BrowserRouter } from 'react-router-dom'; +import { HelmetProvider } from 'react-helmet-async'; + +import { AuthProvider } from './auth-context'; +import { UserProvider } from './user-context'; +import { LoadingProvider } from './loading-context'; +import { RouterGuard } from '../routes/components/router-guard'; +import { UnsavedChangesProvider } from './unsaved-changes-context'; + +type ProvidersProps = { + children: ReactNode; +}; + +export function Providers({ children }: ProvidersProps) { + return ( + + + + + + + + {children} + + + + + + + + ); +} \ No newline at end of file diff --git a/src/contexts/auth-context.tsx b/src/contexts/auth-context.tsx new file mode 100644 index 000000000..074846855 --- /dev/null +++ b/src/contexts/auth-context.tsx @@ -0,0 +1,113 @@ +import type { ReactNode } from 'react'; + +import { jwtDecode } from 'jwt-decode'; +import React, { useMemo, useState, useEffect, useContext, createContext } from 'react'; + +interface DecodedToken { + sub: string; + roles: string[]; + exp: number; // Token expiration timestamp +} + +interface User { + email: string; + roles: string[]; +} + +interface AuthContextProps { + isAuthenticated: boolean; + user: User | null; + isLoading: boolean; + login: (token: string) => void; + logout: () => void; +} + +export const AuthContext = createContext({ + isAuthenticated: false, + user: null, + isLoading: true, + login: () => {}, + logout: () => {}, +}); + +interface AuthProviderProps { + children: ReactNode; +} + +export const AuthProvider: React.FC = ({ children }) => { + const [isAuthenticated, setIsAuthenticated] = useState(false); + const [user, setUser] = useState(null); + const [isLoading, setIsLoading] = useState(true); + + // Token geçerliliğini kontrol eden yardımcı fonksiyon + const isTokenValid = (token: string): boolean => { + try { + const { exp } = jwtDecode(token); + return Date.now() < exp * 1000; // Token geçerli mi kontrol et + } catch (error) { + console.error('Invalid token:', error); + return false; + } + }; + + // Login işlemi + const login = (token: string) => { + console.log('Login fonksiyonu çağrıldı', token); // Debug için log + localStorage.setItem('token', token); + const decodedToken = jwtDecode(token); + setIsAuthenticated(true); + setUser({ email: decodedToken.sub, roles: decodedToken.roles || [] }); // sub alanını kullanıyoruz + }; + + // Logout işlemi + const logout = () => { + localStorage.removeItem('token'); + setIsAuthenticated(false); + setUser(null); + }; + + useEffect(() => { + const checkToken = () => { + const token = localStorage.getItem('token'); + if (token && isTokenValid(token)) { + const decodedToken = jwtDecode(token); + setIsAuthenticated(true); + setUser({ email: decodedToken.sub, roles: decodedToken.roles }); + } else { + setIsAuthenticated(false); + setUser(null); + } + setIsLoading(false); // Yükleme tamamlandı + }; + + // İlk yüklemede token kontrolü yap + checkToken(); + + // LocalStorage değişikliklerini dinle + const handleStorageChange = () => { + checkToken(); + }; + + window.addEventListener('storage', handleStorageChange); + + // Cleanup listener + return () => { + window.removeEventListener('storage', handleStorageChange); + }; + }, []); + + const contextValue = useMemo( + () => ({ isAuthenticated, user, isLoading, login, logout }), + [isAuthenticated, user, isLoading] + ); + + return {children}; +}; + +export const useAuth = () => { + const context = useContext(AuthContext); + if (!context) { + throw new Error('useAuth must be used within an AuthProvider'); + } + return context; +}; diff --git a/src/contexts/loading-context.tsx b/src/contexts/loading-context.tsx new file mode 100644 index 000000000..62a177cec --- /dev/null +++ b/src/contexts/loading-context.tsx @@ -0,0 +1,47 @@ +import { createContext, useContext, useState, useCallback, useMemo, ReactNode } from 'react'; + +type LoadingContextType = { + isLoading: boolean; + showLoading: () => void; + hideLoading: () => void; +}; + +const LoadingContext = createContext(null); + +type LoadingProviderProps = { + children: ReactNode; +}; + +export function LoadingProvider({ children }: LoadingProviderProps) { + const [isLoading, setIsLoading] = useState(false); + + const showLoading = useCallback(() => { + setIsLoading(true); + }, []); + + const hideLoading = useCallback(() => { + setIsLoading(false); + }, []); + + const contextValue = useMemo(() => ({ + isLoading, + showLoading, + hideLoading, + }), [isLoading, showLoading, hideLoading]); + + return ( + + {children} + + ); +} + +export const useLoading = () => { + const context = useContext(LoadingContext); + + if (!context) { + throw new Error('useLoading must be used within LoadingProvider'); + } + + return context; +}; \ No newline at end of file diff --git a/src/contexts/unsaved-changes-context.tsx b/src/contexts/unsaved-changes-context.tsx new file mode 100644 index 000000000..a611dcc5d --- /dev/null +++ b/src/contexts/unsaved-changes-context.tsx @@ -0,0 +1,53 @@ +import type { ReactNode} from 'react'; + +import { useMemo, useState, useContext, useCallback, createContext } from 'react'; + +type UnsavedChangesContextType = { + hasUnsavedChanges: boolean; + setHasUnsavedChanges: (value: boolean) => void; + showPrompt: () => Promise; +}; + +const UnsavedChangesContext = createContext(null); + +type UnsavedChangesProviderProps = { + children: ReactNode; +}; + +export function UnsavedChangesProvider({ children }: UnsavedChangesProviderProps) { + const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); + + const showPrompt = useCallback(() => new Promise((resolve) => { + if (!hasUnsavedChanges) { + resolve(true); + return; + } + + const userWantsToLeave = window.confirm( + 'Kaydedilmemiş değişiklikleriniz var. Çıkmak istediğinizden emin misiniz?' + ); + resolve(userWantsToLeave); + }), [hasUnsavedChanges]); + + const value = useMemo(() => ({ + hasUnsavedChanges, + setHasUnsavedChanges, + showPrompt, + }), [hasUnsavedChanges, showPrompt]); + + return ( + + {children} + + ); +} + +export const useUnsavedChanges = () => { + const context = useContext(UnsavedChangesContext); + + if (!context) { + throw new Error('useUnsavedChanges must be used within UnsavedChangesProvider'); + } + + return context; +}; \ No newline at end of file diff --git a/src/contexts/user-context.tsx b/src/contexts/user-context.tsx new file mode 100644 index 000000000..1beb52329 --- /dev/null +++ b/src/contexts/user-context.tsx @@ -0,0 +1,138 @@ +import type { ReactNode } from 'react'; + +import { useMemo, useState, useEffect, useContext, useCallback, createContext } from 'react'; + +import { useAuth } from './auth-context'; +import { userService } from '../services/user/user-service'; + +export interface User { + id: string; + name: string; + firstName: string; + lastName: string; + email: string; + phone: string; + avatar?: string; + avatarUrl?: string; + role: 'admin' | 'user'; + notifications: { + email: boolean; + push: boolean; + sms: boolean; + }; +} + +interface UserContextType { + user: User | null; + isLoading: boolean; + error: string | null; + updateUser: (data: Partial) => Promise; + updatePassword: (currentPassword: string, newPassword: string) => Promise; + updateAvatar: (file: File) => Promise; + updateNotifications: (notifications: User['notifications']) => Promise; + refreshUser: () => Promise; +} + +const UserContext = createContext(undefined); + +export function UserProvider({ children }: { children: ReactNode }) { + const { user: authUser, isAuthenticated } = useAuth(); + const [user, setUser] = useState(null); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(null); + + const fetchUser = useCallback(async () => { + try { + setIsLoading(true); + setError(null); + + if (authUser?.email) { + const userData = await userService.getUserProfileByEmail(authUser.email); + setUser(userData); + } + } catch (err) { + setError('Kullanıcı bilgileri alınırken bir hata oluştu'); + console.error('Kullanıcı bilgileri alınamadı:', err); + } finally { + setIsLoading(false); + } + }, [authUser?.email]); + + const updateUser = useCallback(async (data: Partial) => { + try { + setError(null); + await userService.updateUserProfile(data); + await fetchUser(); + } catch (err) { + setError('Profil güncellenirken bir hata oluştu'); + throw err; + } + }, [fetchUser]); + + const updatePassword = useCallback(async (currentPassword: string, newPassword: string) => { + try { + await userService.updatePassword(currentPassword, newPassword); + } catch (err) { + console.error('Failed to update password:', err); + throw err; + } + }, []); + + const updateAvatar = useCallback(async (file: File) => { + try { + const avatarUrl = await userService.uploadProfilePhoto(file); + setUser((prev) => (prev ? { ...prev, avatarUrl } : null)); + } catch (err) { + console.error('Failed to update avatar:', err); + throw err; + } + }, []); + + const updateNotifications = useCallback(async (notifications: User['notifications']) => { + try { + await userService.updateNotifications(notifications); + setUser((prev) => (prev ? { ...prev, notifications } : null)); + } catch (err) { + console.error('Failed to update notifications:', err); + throw err; + } + }, []); + + useEffect(() => { + setIsLoading(true); + if (isAuthenticated && authUser?.email) { + fetchUser().catch((err) => { + console.error('Failed to fetch user:', err); + setIsLoading(false); + }); + } else { + setUser(null); + setIsLoading(false); + } + }, [isAuthenticated, authUser?.email, fetchUser]); + + const contextValue = useMemo(() => ({ + user, + isLoading, + error, + updateUser, + updatePassword, + updateAvatar, + updateNotifications, + refreshUser: fetchUser, + }), [user, isLoading, error, updateUser, updatePassword, updateAvatar, updateNotifications, fetchUser]); + + return ( + + {children} + + ); +} + +export function useUser() { + const context = useContext(UserContext); + if (context === undefined) { + throw new Error('useUser must be used within a UserProvider'); + } + return context; +} \ No newline at end of file diff --git a/src/declarations.d.ts b/src/declarations.d.ts new file mode 100644 index 000000000..34dfcc409 --- /dev/null +++ b/src/declarations.d.ts @@ -0,0 +1,9 @@ +declare module 'i18next-http-backend' { + const HttpApi: any; + export default HttpApi; +} + +declare module 'i18next-browser-languagedetector' { + const LanguageDetector: any; + export default LanguageDetector; +} \ No newline at end of file diff --git a/src/hooks/use-boolean.ts b/src/hooks/use-boolean.ts new file mode 100644 index 000000000..c67e152c2 --- /dev/null +++ b/src/hooks/use-boolean.ts @@ -0,0 +1,33 @@ +import React, { useState, useCallback } from 'react'; + +export interface ReturnType { + value: boolean; + onTrue: () => void; + onFalse: () => void; + onToggle: () => void; + setValue: React.Dispatch>; +} + +export function useBoolean(defaultValue?: boolean): ReturnType { + const [value, setValue] = useState(!!defaultValue); + + const onTrue = useCallback(() => { + setValue(true); + }, []); + + const onFalse = useCallback(() => { + setValue(false); + }, []); + + const onToggle = useCallback(() => { + setValue((prev) => !prev); + }, []); + + return { + value, + onTrue, + onFalse, + onToggle, + setValue, + }; +} \ No newline at end of file diff --git a/src/i18n.ts b/src/i18n.ts new file mode 100644 index 000000000..135585852 --- /dev/null +++ b/src/i18n.ts @@ -0,0 +1,27 @@ +import i18n from 'i18next'; +import HttpApi from 'i18next-http-backend'; +import { initReactI18next } from 'react-i18next'; +import LanguageDetector from 'i18next-browser-languagedetector'; + +i18n + .use(HttpApi) + .use(LanguageDetector) + .use(initReactI18next) + .init({ + fallbackLng: 'tr', + ns: ['common', 'auth', 'products', 'error', 'validation', 'company','profile'], + defaultNS: 'common', + debug: true, + interpolation: { + escapeValue: false, + }, + backend: { + loadPath: '/locales/{{lng}}/{{ns}}.json', + }, + detection: { + order: ['localStorage', 'navigator'], + caches: ['localStorage'], + }, + }); + +export default i18n; \ No newline at end of file diff --git a/src/layouts/auth/layout.tsx b/src/layouts/auth/layout.tsx index 49bcda255..8b82e1fe6 100644 --- a/src/layouts/auth/layout.tsx +++ b/src/layouts/auth/layout.tsx @@ -1,10 +1,7 @@ import type { Theme, SxProps, Breakpoint } from '@mui/material/styles'; -import Link from '@mui/material/Link'; import Alert from '@mui/material/Alert'; -import { RouterLink } from 'src/routes/components'; - import { stylesMode } from 'src/theme/styles'; import { Logo } from 'src/components/logo'; @@ -40,7 +37,6 @@ export function AuthLayout({ sx, children, header }: AuthLayoutProps) { }} sx={{ position: { [layoutQuery]: 'fixed' }, - ...header?.sx, }} slots={{ @@ -50,16 +46,6 @@ export function AuthLayout({ sx, children, header }: AuthLayoutProps) { ), leftArea: , - rightArea: ( - - Need help? - - ), }} /> } diff --git a/src/layouts/components/account-popover.tsx b/src/layouts/components/account-popover.tsx index 995efce0a..b0351f0ba 100644 --- a/src/layouts/components/account-popover.tsx +++ b/src/layouts/components/account-popover.tsx @@ -1,6 +1,6 @@ import type { IconButtonProps } from '@mui/material/IconButton'; -import { useState, useCallback } from 'react'; +import React, {useState, useContext, useCallback} from 'react'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; @@ -16,6 +16,8 @@ import { useRouter, usePathname } from 'src/routes/hooks'; import { _myAccount } from 'src/_mock'; +import {AuthContext} from "../../contexts/auth-context"; + // ---------------------------------------------------------------------- export type AccountPopoverProps = IconButtonProps & { @@ -29,6 +31,7 @@ export type AccountPopoverProps = IconButtonProps & { export function AccountPopover({ data = [], sx, ...other }: AccountPopoverProps) { const router = useRouter(); + const { logout } = useContext(AuthContext); const pathname = usePathname(); @@ -129,7 +132,7 @@ export function AccountPopover({ data = [], sx, ...other }: AccountPopoverProps) - diff --git a/src/layouts/components/index.ts b/src/layouts/components/index.ts new file mode 100644 index 000000000..cc78b190c --- /dev/null +++ b/src/layouts/components/index.ts @@ -0,0 +1 @@ +export { LanguagePopover } from './language-popover'; \ No newline at end of file diff --git a/src/layouts/components/language-popover.tsx b/src/layouts/components/language-popover.tsx index d8e654136..b37df8606 100644 --- a/src/layouts/components/language-popover.tsx +++ b/src/layouts/components/language-popover.tsx @@ -1,6 +1,7 @@ import type { IconButtonProps } from '@mui/material/IconButton'; -import { useState, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import React, { useState, useCallback } from 'react'; import Box from '@mui/material/Box'; import Popover from '@mui/material/Popover'; @@ -10,16 +11,24 @@ import MenuItem, { menuItemClasses } from '@mui/material/MenuItem'; // ---------------------------------------------------------------------- -export type LanguagePopoverProps = IconButtonProps & { - data?: { - value: string; - label: string; - icon: string; - }[]; -}; +export type LanguagePopoverProps = IconButtonProps; -export function LanguagePopover({ data = [], sx, ...other }: LanguagePopoverProps) { - const [locale, setLocale] = useState(data[0].value); +const LANGUAGES = [ + { + value: 'en', + label: 'English', + icon: '/assets/icons/flags/ic-flag-en.svg', + }, + { + value: 'tr', + label: 'Türkçe', + icon: '/assets/icons/flags/ic-flag-tr.svg', + }, +]; + +export function LanguagePopover({ sx, ...other }: LanguagePopoverProps) { + const { i18n } = useTranslation(); + const [locale, setLocale] = useState(i18n.language || LANGUAGES[0].value); const [openPopover, setOpenPopover] = useState(null); @@ -32,14 +41,15 @@ export function LanguagePopover({ data = [], sx, ...other }: LanguagePopoverProp }, []); const handleChangeLang = useCallback( - (newLang: string) => { - setLocale(newLang); - handleClosePopover(); + async (newLang: string) => { + setLocale(newLang); + await i18n.changeLanguage(newLang); + handleClosePopover(); }, - [handleClosePopover] + [handleClosePopover, i18n] ); - const currentLang = data.find((lang) => lang.value === locale); + const currentLang = LANGUAGES.find((lang) => lang.value === locale); const renderFlag = (label?: string, icon?: string) => ( - {data?.map((option) => ( + {LANGUAGES.map((option) => ( - ({ - ...textGradient( - `to right, ${theme.vars.palette.secondary.main}, ${theme.vars.palette.warning.main}` - ), - })} - > - More features? - - - {`From only `} - - $69 - - - - - - - - ); -} diff --git a/src/layouts/components/notifications-popover.tsx b/src/layouts/components/notifications-popover.tsx index 68f0f81a2..f5389fe46 100644 --- a/src/layouts/components/notifications-popover.tsx +++ b/src/layouts/components/notifications-popover.tsx @@ -1,6 +1,6 @@ import type { IconButtonProps } from '@mui/material/IconButton'; -import { useState, useCallback } from 'react'; +import React, { useState, useCallback } from 'react'; import Box from '@mui/material/Box'; import List from '@mui/material/List'; diff --git a/src/layouts/components/workspaces-popover.tsx b/src/layouts/components/workspaces-popover.tsx index 7d86f7ec4..2779fefc3 100644 --- a/src/layouts/components/workspaces-popover.tsx +++ b/src/layouts/components/workspaces-popover.tsx @@ -1,6 +1,6 @@ import type { ButtonBaseProps } from '@mui/material/ButtonBase'; -import { useState, useCallback } from 'react'; +import React, { useState, useCallback } from 'react'; import Box from '@mui/material/Box'; import Popover from '@mui/material/Popover'; diff --git a/src/layouts/config-nav-dashboard.tsx b/src/layouts/config-nav-dashboard.tsx index 2acb80796..71737904a 100644 --- a/src/layouts/config-nav-dashboard.tsx +++ b/src/layouts/config-nav-dashboard.tsx @@ -18,6 +18,11 @@ export const navData = [ path: '/user', icon: icon('ic-user'), }, + { + title: 'Companies', + path: '/company', + icon: icon('ic-business'), + }, { title: 'Product', path: '/products', diff --git a/src/layouts/core/header-section.tsx b/src/layouts/core/header-section.tsx index 5b8525bfb..84429c3fe 100644 --- a/src/layouts/core/header-section.tsx +++ b/src/layouts/core/header-section.tsx @@ -3,6 +3,8 @@ import type { AppBarProps } from '@mui/material/AppBar'; import type { ToolbarProps } from '@mui/material/Toolbar'; import type { ContainerProps } from '@mui/material/Container'; +import React from "react"; + import Box from '@mui/material/Box'; import AppBar from '@mui/material/AppBar'; import Toolbar from '@mui/material/Toolbar'; diff --git a/src/layouts/core/layout-section.tsx b/src/layouts/core/layout-section.tsx index 000daa4ef..c688bd02f 100644 --- a/src/layouts/core/layout-section.tsx +++ b/src/layouts/core/layout-section.tsx @@ -1,5 +1,7 @@ import type { Theme, SxProps, CSSObject } from '@mui/material/styles'; +import React from "react"; + import Box from '@mui/material/Box'; import { useTheme } from '@mui/material/styles'; import GlobalStyles from '@mui/material/GlobalStyles'; diff --git a/src/layouts/dashboard/dashboard-content.tsx b/src/layouts/dashboard/dashboard-content.tsx new file mode 100644 index 000000000..fcef7d645 --- /dev/null +++ b/src/layouts/dashboard/dashboard-content.tsx @@ -0,0 +1,30 @@ +import Box from '@mui/material/Box'; +import { Theme } from '@mui/material/styles'; + +interface DashboardContentProps { + children: React.ReactNode; + maxWidth?: string | number | false; +} + +export function DashboardContent({ children, maxWidth }: DashboardContentProps) { + return ( + + {children} + + ); +} \ No newline at end of file diff --git a/src/layouts/dashboard/layout.tsx b/src/layouts/dashboard/layout.tsx index 281305821..97fc5ef3f 100644 --- a/src/layouts/dashboard/layout.tsx +++ b/src/layouts/dashboard/layout.tsx @@ -1,18 +1,19 @@ import type { Theme, SxProps, Breakpoint } from '@mui/material/styles'; -import { useState } from 'react'; +import React, { useState } from 'react'; import Box from '@mui/material/Box'; import Alert from '@mui/material/Alert'; import { useTheme } from '@mui/material/styles'; -import { _langs, _notifications } from 'src/_mock'; +import { _notifications } from 'src/_mock'; import { Iconify } from 'src/components/iconify'; import { Main } from './main'; import { layoutClasses } from '../classes'; import { NavMobile, NavDesktop } from './nav'; +import { LanguagePopover } from '../components'; import { navData } from '../config-nav-dashboard'; import { Searchbar } from '../components/searchbar'; import { _workspaces } from '../config-nav-workspace'; @@ -20,7 +21,6 @@ import { MenuButton } from '../components/menu-button'; import { LayoutSection } from '../core/layout-section'; import { HeaderSection } from '../core/header-section'; import { AccountPopover } from '../components/account-popover'; -import { LanguagePopover } from '../components/language-popover'; import { NotificationsPopover } from '../components/notifications-popover'; // ---------------------------------------------------------------------- @@ -81,7 +81,7 @@ export function DashboardLayout({ sx, children, header }: DashboardLayoutProps) rightArea: ( - + , }, { diff --git a/src/layouts/dashboard/nav.tsx b/src/layouts/dashboard/nav.tsx index 3b19cb940..acd16f70b 100644 --- a/src/layouts/dashboard/nav.tsx +++ b/src/layouts/dashboard/nav.tsx @@ -16,7 +16,6 @@ import { varAlpha } from 'src/theme/styles'; import { Logo } from 'src/components/logo'; import { Scrollbar } from 'src/components/scrollbar'; -import { NavUpgrade } from '../components/nav-upgrade'; import { WorkspacesPopover } from '../components/workspaces-popover'; import type { WorkspacesPopoverProps } from '../components/workspaces-popover'; @@ -38,9 +37,37 @@ export type NavContentProps = { sx?: SxProps; }; +const defaultNavItems = [ + { + path: '/', + title: 'Ana Sayfa', + icon: , + }, + { + path: '/company', + title: 'İş Ortakları', + icon: , + }, + { + path: '/user', + title: 'Kullanıcılar', + icon: , + }, + { + path: '/products', + title: 'Ürünler', + icon: , + }, + { + path: '/blog', + title: 'Blog', + icon: , + }, +]; + export function NavDesktop({ sx, - data, + data = defaultNavItems, slots, workspaces, layoutQuery, @@ -175,8 +202,6 @@ export function NavContent({ data, slots, workspaces, sx }: NavContentProps) { {slots?.bottomArea} - - ); } diff --git a/src/main.tsx b/src/main.tsx index 890a4c1b3..76a6dc751 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,22 +1,16 @@ +import { StrictMode } from 'react'; import ReactDOM from 'react-dom/client'; -import { Suspense, StrictMode } from 'react'; -import { BrowserRouter } from 'react-router-dom'; -import { HelmetProvider } from 'react-helmet-async'; +import './i18n'; import App from './app'; - -// ---------------------------------------------------------------------- +import {Providers} from "./contexts/Providers"; const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); root.render( - - - - - - - - - -); + + + + + +); \ No newline at end of file diff --git a/src/pages/company/company-detail/company-detail.tsx b/src/pages/company/company-detail/company-detail.tsx new file mode 100644 index 000000000..0e0262244 --- /dev/null +++ b/src/pages/company/company-detail/company-detail.tsx @@ -0,0 +1,19 @@ +import { Helmet } from 'react-helmet-async'; + +import {CONFIG} from "../../../config-global"; +import { + CompanyDetail +} from "../../../sections/company/company-detail."; + + +export default function CompanyDetailPage() { + return ( + <> + + {`Şirket Detayı - ${CONFIG.appName}`} + + + + + ); +} \ No newline at end of file diff --git a/src/pages/company/company-list/company-list.tsx b/src/pages/company/company-list/company-list.tsx new file mode 100644 index 000000000..a473c8679 --- /dev/null +++ b/src/pages/company/company-list/company-list.tsx @@ -0,0 +1,17 @@ +import { Helmet } from 'react-helmet-async'; + +import { CONFIG } from 'src/config-global'; + +import { CompanyList } from 'src/sections/company/company-list'; + +export default function CompanyListPage() { + return ( + <> + + {`Şirketler - ${CONFIG.appName}`} + + + + + ); +} \ No newline at end of file diff --git a/src/pages/page-not-found.tsx b/src/pages/page-not-found.tsx index afffe01ae..1b434211c 100644 --- a/src/pages/page-not-found.tsx +++ b/src/pages/page-not-found.tsx @@ -1,4 +1,5 @@ import { Helmet } from 'react-helmet-async'; +import { useTranslation } from 'react-i18next'; import { CONFIG } from 'src/config-global'; @@ -7,10 +8,12 @@ import { NotFoundView } from 'src/sections/error'; // ---------------------------------------------------------------------- export default function Page() { + const { t } = useTranslation('error'); + return ( <> - {`404 page not found! | Error - ${CONFIG.appName}`} + {t('notFound.helmet', { appName: CONFIG.appName })} diff --git a/src/pages/products.tsx b/src/pages/products.tsx index f5eb29049..1026d1cda 100644 --- a/src/pages/products.tsx +++ b/src/pages/products.tsx @@ -1,16 +1,17 @@ import { Helmet } from 'react-helmet-async'; - -import { CONFIG } from 'src/config-global'; +import { useTranslation } from 'react-i18next'; import { ProductsView } from 'src/sections/product/view'; // ---------------------------------------------------------------------- -export default function Page() { +export default function ProductsPage() { + const { t } = useTranslation('products'); + return ( <> - {`Products - ${CONFIG.appName}`} + {t('list.helmet')} diff --git a/src/pages/profile.tsx b/src/pages/profile.tsx new file mode 100644 index 000000000..6fe1a553e --- /dev/null +++ b/src/pages/profile.tsx @@ -0,0 +1,15 @@ +import { Helmet } from 'react-helmet-async'; + +import { ProfileView } from 'src/sections/profile/view'; + +export default function ProfilePage() { + return ( + <> + + Profil | Lojistik Yönetim Sistemi + + + + + ); +} \ No newline at end of file diff --git a/src/pages/sign-in.tsx b/src/pages/sign-in.tsx index 20f76057f..04ae52fd7 100644 --- a/src/pages/sign-in.tsx +++ b/src/pages/sign-in.tsx @@ -1,16 +1,17 @@ import { Helmet } from 'react-helmet-async'; - -import { CONFIG } from 'src/config-global'; +import { useTranslation } from 'react-i18next'; import { SignInView } from 'src/sections/auth'; // ---------------------------------------------------------------------- -export default function Page() { +export default function SignInPage() { + const { t } = useTranslation('auth'); + return ( <> - {`Sign in - ${CONFIG.appName}`} + {t('signIn.helmet')} diff --git a/src/routes/components/router-guard.tsx b/src/routes/components/router-guard.tsx new file mode 100644 index 000000000..f14dd4f18 --- /dev/null +++ b/src/routes/components/router-guard.tsx @@ -0,0 +1,54 @@ +import type { ReactNode } from 'react'; + +import { useRef, useEffect } from 'react'; +import { useNavigate, useLocation } from 'react-router-dom'; + +import { useUnsavedChanges } from 'src/contexts/unsaved-changes-context'; + +type RouterGuardProps = { + children: ReactNode; +}; + +export function RouterGuard({ children }: RouterGuardProps) { + const navigate = useNavigate(); + const location = useLocation(); + const { hasUnsavedChanges, showPrompt, setHasUnsavedChanges } = useUnsavedChanges(); + const lastLocation = useRef(location); + + // Sayfa yenileme ve kapatma kontrolü + useEffect(() => { + const handleBeforeUnload = (e: BeforeUnloadEvent) => { + if (hasUnsavedChanges) { + e.preventDefault(); + e.returnValue = ''; + } + }; + + window.addEventListener('beforeunload', handleBeforeUnload); + return () => window.removeEventListener('beforeunload', handleBeforeUnload); + }, [hasUnsavedChanges]); + + // Route değişikliklerini izle + useEffect(() => { + if (!hasUnsavedChanges) { + lastLocation.current = location; + return; + } + + if (location !== lastLocation.current) { + const handleRouteChange = async () => { + const canNavigate = await showPrompt(); + if (!canNavigate) { + navigate(lastLocation.current.pathname, { replace: true }); + } else { + lastLocation.current = location; + setHasUnsavedChanges(false); + } + }; + + handleRouteChange(); + } + }, [location, hasUnsavedChanges, navigate, showPrompt, setHasUnsavedChanges]); + + return children; +} \ No newline at end of file diff --git a/src/routes/hooks/index.ts b/src/routes/hooks/index.ts index 03b735171..73350e966 100644 --- a/src/routes/hooks/index.ts +++ b/src/routes/hooks/index.ts @@ -1,3 +1,3 @@ -export { useRouter } from './use-router'; +export * from './use-router'; export { usePathname } from './use-pathname'; diff --git a/src/routes/hooks/use-router.ts b/src/routes/hooks/use-router.ts index 2925a778a..f6772aea5 100644 --- a/src/routes/hooks/use-router.ts +++ b/src/routes/hooks/use-router.ts @@ -1,21 +1,11 @@ -import { useMemo } from 'react'; import { useNavigate } from 'react-router-dom'; -// ---------------------------------------------------------------------- - export function useRouter() { const navigate = useNavigate(); - const router = useMemo( - () => ({ - back: () => navigate(-1), - forward: () => navigate(1), - refresh: () => navigate(0), - push: (href: string) => navigate(href), - replace: (href: string) => navigate(href, { replace: true }), - }), - [navigate] - ); - - return router; + return { + push: (path: string) => navigate(path), + replace: (path: string) => navigate(path, { replace: true }), + back: () => navigate(-1), + }; } diff --git a/src/routes/paths.ts b/src/routes/paths.ts new file mode 100644 index 000000000..697a894d3 --- /dev/null +++ b/src/routes/paths.ts @@ -0,0 +1,33 @@ +// ---------------------------------------------------------------------- + +function path(root: string, sublink: string): string { + return `${root}${sublink}`; +} + +const ROOTS = { + AUTH: '/auth', + DASHBOARD: '/dashboard', +}; + +export const paths = { + auth: { + login: path(ROOTS.AUTH, '/login'), + register: path(ROOTS.AUTH, '/register'), + forgotPassword: path(ROOTS.AUTH, '/forgot-password'), + verify: path(ROOTS.AUTH, '/verify'), + }, + dashboard: { + root: ROOTS.DASHBOARD, + overview: path(ROOTS.DASHBOARD, '/overview'), + user: path(ROOTS.DASHBOARD, '/user'), + products: path(ROOTS.DASHBOARD, '/products'), + blog: path(ROOTS.DASHBOARD, '/blog'), + partners: { + root: path(ROOTS.DASHBOARD, '/company'), + list: path(ROOTS.DASHBOARD, '/company'), + new: path(ROOTS.DASHBOARD, '/company/new'), + edit: (id: string) => path(ROOTS.DASHBOARD, `/company/${id}/edit`), + view: (id: string) => path(ROOTS.DASHBOARD, `/company/${id}`), + }, + }, +} as const; \ No newline at end of file diff --git a/src/routes/private-route.tsx b/src/routes/private-route.tsx new file mode 100644 index 000000000..f7514bc1e --- /dev/null +++ b/src/routes/private-route.tsx @@ -0,0 +1,45 @@ +import React, { useContext } from 'react'; +import { Navigate } from 'react-router-dom'; + +import Box from '@mui/material/Box'; +import LinearProgress, { linearProgressClasses } from '@mui/material/LinearProgress'; + +import { AuthContext } from 'src/contexts/auth-context'; + +import { varAlpha } from '../theme/styles'; + +const PrivateRoute = ({ children }: { children: React.ReactNode }) => { + const { isAuthenticated, isLoading } = useContext(AuthContext); + + if (isLoading) { + return ( + + varAlpha(theme.vars.palette.text.primaryChannel, 0.16), + [`& .${linearProgressClasses.bar}`]: { bgcolor: 'text.primary' }, + }} + /> + + ); + } + + + return <>{children}; +}; + +export default PrivateRoute; diff --git a/src/routes/sections.tsx b/src/routes/sections.tsx index 34a673b10..d6162b275 100644 --- a/src/routes/sections.tsx +++ b/src/routes/sections.tsx @@ -1,4 +1,4 @@ -import { lazy, Suspense } from 'react'; +import React, {lazy, Suspense} from 'react'; import { Outlet, Navigate, useRoutes } from 'react-router-dom'; import Box from '@mui/material/Box'; @@ -8,16 +8,18 @@ import { varAlpha } from 'src/theme/styles'; import { AuthLayout } from 'src/layouts/auth'; import { DashboardLayout } from 'src/layouts/dashboard'; -// ---------------------------------------------------------------------- +import PrivateRoute from './private-route'; -export const HomePage = lazy(() => import('src/pages/home')); -export const BlogPage = lazy(() => import('src/pages/blog')); -export const UserPage = lazy(() => import('src/pages/user')); -export const SignInPage = lazy(() => import('src/pages/sign-in')); -export const ProductsPage = lazy(() => import('src/pages/products')); -export const Page404 = lazy(() => import('src/pages/page-not-found')); - -// ---------------------------------------------------------------------- +// Lazy load pages +const HomePage = lazy(() => import('src/pages/home')); +const BlogPage = lazy(() => import('src/pages/blog')); +const UserPage = lazy(() => import('src/pages/user')); +const SignInPage = lazy(() => import('src/pages/sign-in')); +const ProductsPage = lazy(() => import('src/pages/products')); +const Page404 = lazy(() => import('src/pages/page-not-found')); +const ProfilePage = lazy(() => import('src/pages/profile')); +const CompanyListPage = lazy(() => import('src/pages/company/company-list/company-list')); +const CompanyDetailPage = lazy(() => import('src/pages/company/company-detail/company-detail')); const renderFallback = ( @@ -36,17 +38,22 @@ export function Router() { return useRoutes([ { element: ( - - - - - + + + + + + + ), children: [ { element: , index: true }, { path: 'user', element: }, { path: 'products', element: }, { path: 'blog', element: }, + { path: 'profile', element: }, + { path: 'company', element: }, + { path: 'company/:id', element: }, ], }, { diff --git a/src/sections/auth/forget-password.tsx b/src/sections/auth/forget-password.tsx new file mode 100644 index 000000000..289e934f6 --- /dev/null +++ b/src/sections/auth/forget-password.tsx @@ -0,0 +1,130 @@ +import * as React from 'react'; +import { useState, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; + +import Alert from '@mui/material/Alert'; +import Button from '@mui/material/Button'; +import Dialog from '@mui/material/Dialog'; +import Snackbar from '@mui/material/Snackbar'; +import TextField from '@mui/material/TextField'; +import DialogTitle from '@mui/material/DialogTitle'; +import DialogActions from '@mui/material/DialogActions'; +import DialogContent from '@mui/material/DialogContent'; +import DialogContentText from '@mui/material/DialogContentText'; + +interface ForgotPasswordProps { + open: boolean; + handleClose: () => void; +} + +export default function ForgotPassword({ open, handleClose }: ForgotPasswordProps) { + const { t } = useTranslation(['auth', 'common']); + const [emailError, setEmailError] = useState(false); + const [emailErrorMessage, setEmailErrorMessage] = useState(''); + const [loading, setLoading] = useState(false); + const [successMessage, setSuccessMessage] = useState(false); + + const validateEmail = useCallback(() => { + const email = document.getElementById('forgetPasswordEmail') as HTMLInputElement; + + let isValid = true; + + if (!email.value || !/\S+@\S+\.\S+/.test(email.value)) { + setEmailError(true); + setEmailErrorMessage(t('resetPassword.emailErrorMessage')); + isValid = false; + } else { + setEmailError(false); + setEmailErrorMessage(''); + } + + return isValid; + }, [t]); + + const handleSubmit = useCallback(async (event: React.FormEvent) => { + event.preventDefault(); + if (!validateEmail()) { + return; + } + const email = (document.getElementById('forgetPasswordEmail') as HTMLInputElement).value; + setLoading(true); + + try { + // Simulated API call + await new Promise((resolve) => setTimeout(resolve, 2000)); + setSuccessMessage(true); // Show success message + handleClose(); + } catch (error) { + console.error('Password reset failed:', error); + } finally { + setLoading(false); + } + }, [handleClose, validateEmail]); + + const handleSuccessClose = () => { + setSuccessMessage(false); + }; + + const handleDialogClose = () => { + setEmailError(false); + setEmailErrorMessage(''); + handleClose(); + }; + + return ( + <> + + {t('resetPassword.title')} + + + {t('resetPassword.description')} + + + + + + + + + + + {t('resetPassword.successMessage')} + + + + ); +} \ No newline at end of file diff --git a/src/sections/auth/index.ts b/src/sections/auth/index.ts index d9ccca24c..9f2179e10 100644 --- a/src/sections/auth/index.ts +++ b/src/sections/auth/index.ts @@ -1 +1 @@ -export * from './sign-in-view'; +export { SignInView } from './sign-in-view'; diff --git a/src/sections/auth/sign-in-view.tsx b/src/sections/auth/sign-in-view.tsx index 52f619215..17dc5c437 100644 --- a/src/sections/auth/sign-in-view.tsx +++ b/src/sections/auth/sign-in-view.tsx @@ -1,8 +1,8 @@ -import { useState, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import React, { useState, useContext, useCallback } from 'react'; import Box from '@mui/material/Box'; import Link from '@mui/material/Link'; -import Divider from '@mui/material/Divider'; import TextField from '@mui/material/TextField'; import IconButton from '@mui/material/IconButton'; import Typography from '@mui/material/Typography'; @@ -11,39 +11,125 @@ import InputAdornment from '@mui/material/InputAdornment'; import { useRouter } from 'src/routes/hooks'; +import { LanguagePopover } from 'src/layouts/components/language-popover'; + import { Iconify } from 'src/components/iconify'; +import ForgotPassword from './forget-password'; +import { AuthContext } from '../../contexts/auth-context'; +import { authService } from '../../services/auth/auth-service'; + // ---------------------------------------------------------------------- export function SignInView() { const router = useRouter(); - + const { t } = useTranslation(['auth', 'common']); const [showPassword, setShowPassword] = useState(false); + const { login } = useContext(AuthContext); + const [emailError, setEmailError] = useState(false); + const [emailErrorMessage, setEmailErrorMessage] = useState(''); + const [passwordError, setPasswordError] = useState(false); + const [passwordErrorMessage, setPasswordErrorMessage] = useState(''); + const [loading, setLoading] = useState(false); + const [loginError, setLoginError] = useState(null); + const [forgotPasswordOpen, setForgotPasswordOpen] = useState(false); + + const validateInputs = useCallback(() => { + const email = document.getElementById('email') as HTMLInputElement; + const password = document.getElementById('password') as HTMLInputElement; + + let isValid = true; + + if (!email.value || !/\S+@\S+\.\S+/.test(email.value)) { + setEmailError(true); + setEmailErrorMessage(t('auth:signIn.errors.invalidEmail')); + isValid = false; + } else { + setEmailError(false); + setEmailErrorMessage(''); + } + + if (!password.value || password.value.length < 6) { + setPasswordError(true); + setPasswordErrorMessage(t('auth:signIn.errors.passwordLength')); + isValid = false; + } else { + setPasswordError(false); + setPasswordErrorMessage(''); + } + + return isValid; + }, [t]); + + const handleSubmit = useCallback(async () => { + if (!validateInputs()) { + return; + } + + const email = (document.getElementById('email') as HTMLInputElement).value; + const password = (document.getElementById('password') as HTMLInputElement).value; + + setLoading(true); + setLoginError(null); + + try { + const response = await authService.login({ email, password }); + login(response.token); + router.push('/'); + } catch (error: any) { + console.error('Login failed:', error); + setLoginError(error?.message || 'Login failed. Please try again.'); + } finally { + setLoading(false); + } + }, [login, router, validateInputs]); + + const handleForgotPasswordClick = () => { + setForgotPasswordOpen(true); + }; - const handleSignIn = useCallback(() => { - router.push('/'); - }, [router]); + const handleForgotPasswordClose = () => { + setForgotPasswordOpen(false); + }; + // @ts-ignore const renderForm = ( - - Forgot password? + + {t('auth:signIn.forgotPassword')} ), }} - sx={{ mb: 3 }} + sx={{ mb: 1.5 }} /> - + {loginError && ( + + {loginError} + + )} - Sign in + {loading ? t('auth:signIn.buttons.signingIn') : t('auth:signIn.buttons.signIn')} ); return ( <> + + + + - Sign in + {t('auth:signIn.title')} - Don’t have an account? + {t('common:welcome')} - Get started + {t('common:getStarted')} {renderForm} - - - OR - - - - - - - - - - - - - - + ); } diff --git a/src/sections/company/company-contacts.tsx b/src/sections/company/company-contacts.tsx new file mode 100644 index 000000000..29b7d6283 --- /dev/null +++ b/src/sections/company/company-contacts.tsx @@ -0,0 +1,337 @@ +import * as Yup from 'yup'; +import { useState } from 'react'; +import { useFormik } from 'formik'; +import { useTranslation } from 'react-i18next'; + +import { + Box, + Card, + Grid, + Table, + Alert, + Stack, + Button, + Dialog, + Divider, + TableRow, + Snackbar, + TableBody, + TableCell, + TableHead, + TextField, + IconButton, + Typography, + DialogTitle, + DialogContent, + DialogActions, + TableContainer, +} from '@mui/material'; + +import { Label } from 'src/components/label'; +import { Iconify } from 'src/components/iconify'; +import { Scrollbar } from 'src/components/scrollbar'; + +interface Contact { + id: number; + name: string; + position: string; + department: string; + phone: string; + email: string; +} + +interface CompanyContactsProps { + companyId?: string; +} + +const validationSchema = Yup.object({ + name: Yup.string().required('Ad Soyad zorunludur'), + position: Yup.string().required('Pozisyon zorunludur'), + department: Yup.string().required('Departman zorunludur'), + phone: Yup.string().required('Telefon zorunludur'), + email: Yup.string().email('Geçerli bir e-posta adresi giriniz').required('E-posta zorunludur'), +}); + +export function CompanyContacts({ companyId }: CompanyContactsProps) { + const { t } = useTranslation(); + const [contacts, setContacts] = useState([ + { + id: 1, + name: 'Ahmet Yılmaz', + position: 'Operasyon Müdürü', + department: 'Operasyon', + phone: '0532 555 44 33', + email: 'ahmet.yilmaz@abclojistik.com', + }, + ]); + const [openDialog, setOpenDialog] = useState(false); + const [editingId, setEditingId] = useState(null); + const [snackbar, setSnackbar] = useState<{ + open: boolean; + message: string; + severity: 'success' | 'error'; + }>({ open: false, message: '', severity: 'success' }); + + const formik = useFormik({ + initialValues: { + name: '', + position: '', + department: '', + phone: '', + email: '', + }, + validationSchema, + onSubmit: async (values: any, { resetForm }: { resetForm: () => void }) => { + try { + if (editingId) { + setContacts(contacts.map(contact => + contact.id === editingId + ? { ...contact, ...values } + : contact + )); + setSnackbar({ + open: true, + message: t('Kişi başarıyla güncellendi'), + severity: 'success', + }); + } else { + setContacts([...contacts, { id: contacts.length + 1, ...values }]); + setSnackbar({ + open: true, + message: t('Kişi başarıyla eklendi'), + severity: 'success', + }); + } + handleCloseDialog(); + resetForm(); + } catch (error) { + setSnackbar({ + open: true, + message: t('Bir hata oluştu'), + severity: 'error', + }); + } + }, + }); + + const handleOpenDialog = (contact?: Contact) => { + if (contact) { + setEditingId(contact.id); + formik.setValues({ + name: contact.name, + position: contact.position, + department: contact.department, + phone: contact.phone, + email: contact.email, + }); + } else { + setEditingId(null); + formik.resetForm(); + } + setOpenDialog(true); + }; + + const handleCloseDialog = () => { + setOpenDialog(false); + setEditingId(null); + formik.resetForm(); + }; + + const handleDelete = (id: number) => { + setContacts(contacts.filter(contact => contact.id !== id)); + setSnackbar({ + open: true, + message: t('Kişi başarıyla silindi'), + severity: 'success', + }); + }; + + const handleCloseSnackbar = () => { + setSnackbar({ ...snackbar, open: false }); + }; + + return ( + <> + + + + {t('İletişim Kişileri')} + + + + + + + + + + + + {t('Ad Soyad')} + {t('Departman')} + {t('İletişim')} + {t('İşlemler')} + + + + {contacts.map((contact) => ( + + + + + + + + {contact.name} + + + + + {contact.department} + + + {contact.phone} + + + {contact.email} + + + + handleOpenDialog(contact)}> + + + handleDelete(contact.id)}> + + + + + ))} + +
+
+
+
+ + + + {editingId ? t('Kişi Düzenle') : t('Yeni Kişi Ekle')} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {snackbar.message} + + + + ); +} \ No newline at end of file diff --git a/src/sections/company/company-detail..tsx b/src/sections/company/company-detail..tsx new file mode 100644 index 000000000..a6fcbeb4b --- /dev/null +++ b/src/sections/company/company-detail..tsx @@ -0,0 +1,114 @@ +import React, { useState } from 'react'; +import { useParams } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; + +import { Box, Tab, Card, Tabs, Stack, Button, Typography } from '@mui/material'; + +import { DashboardContent } from 'src/layouts/dashboard'; + +import { CompanyInfo } from './company-info'; +import { Iconify } from '../../components/iconify'; +import { useBoolean } from '../../hooks/use-boolean'; +import { CompanyContacts } from './company-contacts'; +import { Scrollbar } from '../../components/scrollbar'; +import { BackButton } from '../../components/back-button'; + +const TABS = [ + { + value: 'general', + label: 'company.tabs.general', + icon: , + }, + { + value: 'contacts', + label: 'company.tabs.contacts', + icon: , + }, + { + value: 'contracts', + label: 'company.tabs.contracts', + icon: , + }, + { + value: 'performance', + label: 'company.tabs.performance', + icon: , + }, + { + value: 'pricing', + label: 'company.tabs.pricing', + icon: , + }, + { + value: 'analytics', + label: 'company.tabs.analytics', + icon: , + }, +]; + +export function CompanyDetail() { + const { t } = useTranslation('company'); + const { id } = useParams(); + const confirm = useBoolean(); + + const [currentTab, setCurrentTab] = useState('general'); + + const handleChangeTab = (_event: React.SyntheticEvent, newValue: string) => { + setCurrentTab(newValue); + }; + + const handleDelete = () => { + confirm.onTrue(); + }; + + return ( + + + {t('company.companyDetails')} + + + + + + + + `inset 0 -2px 0 0 ${theme.palette.divider}`, + }} + > + {TABS.map((tab) => ( + + ))} + + + + + {currentTab === 'general' && } + {currentTab === 'contacts' && } + {currentTab === 'contracts' && Contracts} + {currentTab === 'performance' && Performance} + {currentTab === 'pricing' && Pricing} + {currentTab === 'analytics' && Analytics} + + + + + ); +} diff --git a/src/sections/company/company-info.tsx b/src/sections/company/company-info.tsx new file mode 100644 index 000000000..73d86e94f --- /dev/null +++ b/src/sections/company/company-info.tsx @@ -0,0 +1,329 @@ +import * as Yup from 'yup'; +import { useFormik } from 'formik'; +import React, { useState } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { + Box, + Grid, + Card, + Stack, + Alert, + Button, + Divider, + Snackbar, + TextField, + Typography, +} from '@mui/material'; + +import { Iconify } from 'src/components/iconify'; + +interface CompanyData { + id: string; + name: string; + address: string; + phone: string; + email: string; + taxNumber: string; + website: string; + description: string; +} + +interface InfoFieldProps { + icon: string; + label: string; + name: keyof CompanyData; + value: string; + error?: string; + touched?: boolean; + isEditing: boolean; + multiline?: boolean; + onChange: (e: React.ChangeEvent) => void; + onBlur: (e: React.FocusEvent) => void; +} + +const InfoField: React.FC = ({ + icon, + label, + name, + value, + error, + touched, + isEditing, + multiline = false, + onChange, + onBlur, +}) => { + const { t } = useTranslation(); + + return ( + + {isEditing ? ( + + + {label} + + + + ) : ( + + + + + + + {label} + + + {value || '-'} + + + + )} + + ); +}; + +const validationSchema = Yup.object({ + name: Yup.string().required('Şirket adı zorunludur'), + taxNumber: Yup.string().required('Vergi numarası zorunludur'), + phone: Yup.string().required('Telefon zorunludur'), + email: Yup.string().email('Geçerli bir e-posta adresi giriniz').required('E-posta zorunludur'), + website: Yup.string().url('Geçerli bir website adresi giriniz'), + address: Yup.string().required('Adres zorunludur'), + description: Yup.string(), +}); + +export function CompanyInfo({ id }: { id?: string }) { + const { t } = useTranslation(); + const [isEditing, setIsEditing] = useState(false); + const [snackbar, setSnackbar] = useState<{ + open: boolean; + message: string; + severity: 'success' | 'error'; + }>({ open: false, message: '', severity: 'success' }); + + const formik = useFormik({ + initialValues: { + id: id || '', + name: 'ABC Lojistik Ltd. Şti.', + address: 'Ankara Cad. No:123 İstanbul', + phone: '0212 555 44 33', + email: 'info@abclojistik.com', + taxNumber: '1234567890', + website: 'www.abclojistik.com', + description: 'Uluslararası lojistik hizmetleri', + }, + validationSchema, + onSubmit: async (values) => { + try { + // TODO: API call to save data + console.log('Saving...', values); + setSnackbar({ + open: true, + message: t('Şirket bilgileri başarıyla güncellendi'), + severity: 'success', + }); + setIsEditing(false); + } catch (error) { + setSnackbar({ + open: true, + message: t('Bir hata oluştu'), + severity: 'error', + }); + } + }, + }); + + const handleEdit = () => { + setIsEditing(true); + }; + + const handleCancel = () => { + formik.resetForm(); + setIsEditing(false); + }; + + const handleCloseSnackbar = () => { + setSnackbar({ ...snackbar, open: false }); + }; + + return ( + + + + {t('Şirket Bilgileri')} + + {!isEditing ? ( + + ) : ( + + + + + )} + + + + + + + + + + + + + + + + + + + + + + + + {snackbar.message} + + + + ); +} \ No newline at end of file diff --git a/src/sections/company/company-list.tsx b/src/sections/company/company-list.tsx new file mode 100644 index 000000000..64991d04a --- /dev/null +++ b/src/sections/company/company-list.tsx @@ -0,0 +1,519 @@ +import * as Yup from 'yup'; +import { useFormik } from 'formik'; +import { useTranslation } from 'react-i18next'; +import React, { useState, useCallback } from 'react'; + +import { + Box, + Card, + Grid, + Table, + Stack, + Button, + Dialog, + Divider, + MenuItem, + TableBody, + TextField, + Typography, + DialogTitle, + DialogContent, + DialogActions, + TableContainer, + InputAdornment, + TablePagination, +} from '@mui/material'; + +import { DashboardContent } from 'src/layouts/dashboard'; + +import { Iconify } from 'src/components/iconify'; +import { Scrollbar } from 'src/components/scrollbar'; + +import { CompanyTableRow } from './company-table-row'; +import { CompanyTableHead } from './company-table-head'; +import { CompanyTableToolbar } from './company-table-toolbar'; + +const COMPANY_LIST = [ + { + id: '1', + name: 'ABC Lojistik Ltd. Şti.', + phone: '0212 555 44 33', + email: 'info@abclojistik.com', + status: 'active' as const, + verified: true, + }, + { + id: '2', + name: 'XYZ Taşımacılık A.Ş.', + phone: '0312 666 55 44', + email: 'info@xyztasima.com', + status: 'passive' as const, + verified: false, + }, +]; + +const PHONE_REGEX = /^[0-9]{10,11}$/; // 10 veya 11 haneli telefon numarası +const TAX_NUMBER_REGEX = /^[0-9]{10}$/; // 10 haneli vergi numarası +const WEBSITE_REGEX = /^(https?:\/\/)?([\da-z.-]+)\.([a-z.]{2,6})([/\w .-]*)*\/?$/; + +const validationSchema = Yup.object({ + name: Yup.string() + .min(2, 'company.validation.companyNameMin') + .max(100, 'company.validation.companyNameMax') + .required('company.validation.companyNameRequired'), + phone: Yup.string() + .matches(PHONE_REGEX, 'company.validation.phoneFormat') + .required('company.validation.phoneRequired'), + email: Yup.string() + .email('company.validation.email') + .required('company.validation.emailRequired'), + status: Yup.string() + .oneOf(['active', 'passive']) + .required('company.validation.statusRequired'), + taxNumber: Yup.string() + .matches(TAX_NUMBER_REGEX, 'company.validation.taxNumberFormat') + .required('company.validation.taxNumberRequired'), + website: Yup.string() + .matches(WEBSITE_REGEX, 'company.validation.url') + .nullable(), + address: Yup.string() + .min(10, 'company.validation.addressMin') + .max(500, 'company.validation.addressMax') + .required('company.validation.addressRequired'), + description: Yup.string() + .max(1000, 'company.validation.descriptionMax'), +}); + +export function CompanyList() { + const { t } = useTranslation('company'); + const [page, setPage] = useState(0); + const [order, setOrder] = useState<'asc' | 'desc'>('asc'); + const [selected, setSelected] = useState([]); + const [orderBy, setOrderBy] = useState('name'); + const [filterName, setFilterName] = useState(''); + const [rowsPerPage, setRowsPerPage] = useState(5); + const [openDialog, setOpenDialog] = useState(false); + + const TABLE_HEAD = [ + { id: 'name', label: t('company.companyName'), align: 'left' }, + { id: 'phone', label: t('company.phone'), align: 'left' }, + { id: 'email', label: t('company.email'), align: 'left' }, + { id: 'status', label: t('company.status'), align: 'left' }, + { id: 'actions', label: '', align: 'right' }, + ]; + + const formik = useFormik({ + initialValues: { + name: '', + phone: '', + email: '', + status: 'active' as const, + taxNumber: '', + website: '', + address: '', + description: '', + }, + validationSchema, + onSubmit: async (values, { resetForm }) => { + // TODO: API call to save data + console.log('Saving...', values); + setOpenDialog(false); + resetForm(); + }, + }); + + const handleSort = useCallback((id: string) => { + const isAsc = orderBy === id && order === 'asc'; + if (id !== '') { + setOrder(isAsc ? 'desc' : 'asc'); + setOrderBy(id); + } + }, [order, orderBy]); + + const handleSelectAllRows = useCallback((checked: boolean) => { + if (checked) { + const newSelected = COMPANY_LIST.map((n) => n.id); + setSelected(newSelected); + return; + } + setSelected([]); + }, []); + + const handleFilterByName = useCallback((event: React.ChangeEvent) => { + setPage(0); + setFilterName(event.target.value); + }, []); + + const handleSelectRow = useCallback((id: string) => { + const selectedIndex = selected.indexOf(id); + let newSelected: string[] = []; + + if (selectedIndex === -1) { + newSelected = newSelected.concat(selected, id); + } else if (selectedIndex === 0) { + newSelected = newSelected.concat(selected.slice(1)); + } else if (selectedIndex === selected.length - 1) { + newSelected = newSelected.concat(selected.slice(0, -1)); + } else if (selectedIndex > 0) { + newSelected = newSelected.concat( + selected.slice(0, selectedIndex), + selected.slice(selectedIndex + 1) + ); + } + setSelected(newSelected); + }, [selected]); + + const handleOpenDialog = () => { + setOpenDialog(true); + }; + + const handleCloseDialog = () => { + setOpenDialog(false); + formik.resetForm(); + }; + + const filteredCompanies = COMPANY_LIST.filter((company) => + company.name.toLowerCase().includes(filterName.toLowerCase()) + ); + + return ( + + + {t('company.title')} + + + + + + + + + + + + + {filteredCompanies + .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) + .map((row) => ( + handleSelectRow(row.id)} + /> + ))} + +
+
+
+ + + setPage(newPage)} + rowsPerPageOptions={[5, 10, 25]} + onRowsPerPageChange={(e) => { + setPage(0); + setRowsPerPage(Number(e.target.value)); + }} + /> + +
+ + + + + + {t('company.newCompany')} + + + + + + + + + + + + + ), + }} + /> + + + + { + // Sadece rakam girişine izin ver + const value = e.target.value.replace(/[^0-9]/g, ''); + formik.setFieldValue('taxNumber', value); + }} + onBlur={formik.handleBlur} + error={formik.touched.taxNumber && Boolean(formik.errors.taxNumber)} + helperText={formik.touched.taxNumber && t(formik.errors.taxNumber as string)} + inputProps={{ + maxLength: 10, + }} + InputProps={{ + startAdornment: ( + + + + ), + }} + /> + + + + + + + ), + }} + /> + + + + { + // Sadece rakam girişine izin ver + const value = e.target.value.replace(/[^0-9]/g, ''); + formik.setFieldValue('phone', value); + }} + onBlur={formik.handleBlur} + error={formik.touched.phone && Boolean(formik.errors.phone)} + helperText={formik.touched.phone && t(formik.errors.phone as string)} + inputProps={{ + maxLength: 11, + }} + InputProps={{ + startAdornment: ( + + + + ), + }} + /> + + + + + + + ), + }} + /> + + + + + + + ), + }} + > + + + + {t('company.active')} + + + + + + {t('company.passive')} + + + + + + + + + + ), + }} + /> + + + + + + + ), + }} + /> + + + + + + + + + + + + +
+ ); +} \ No newline at end of file diff --git a/src/sections/company/company-table-head.tsx b/src/sections/company/company-table-head.tsx new file mode 100644 index 000000000..1691b9f09 --- /dev/null +++ b/src/sections/company/company-table-head.tsx @@ -0,0 +1,67 @@ +import React from "react"; + +import Box from '@mui/material/Box'; +import TableRow from '@mui/material/TableRow'; +import Checkbox from '@mui/material/Checkbox'; +import TableHead from '@mui/material/TableHead'; +import TableCell from '@mui/material/TableCell'; +import TableSortLabel from '@mui/material/TableSortLabel'; + +type CompanyTableHeadProps = { + orderBy: string; + rowCount: number; + numSelected: number; + order: 'asc' | 'desc'; + onSort: (id: string) => void; + headLabel: Record[]; + onSelectAllRows: (checked: boolean) => void; +}; + +export function CompanyTableHead({ + order, + onSort, + orderBy, + rowCount, + headLabel, + numSelected, + onSelectAllRows, +}: CompanyTableHeadProps) { + return ( + + + + 0 && numSelected < rowCount} + checked={rowCount > 0 && numSelected === rowCount} + onChange={(event: React.ChangeEvent) => + onSelectAllRows(event.target.checked) + } + /> + + + {headLabel.map((headCell) => ( + + onSort(headCell.id)} + > + {headCell.label} + {orderBy === headCell.id ? ( + + {order === 'desc' ? 'sıralama azalan' : 'sıralama artan'} + + ) : null} + + + ))} + + + ); +} \ No newline at end of file diff --git a/src/sections/company/company-table-row.tsx b/src/sections/company/company-table-row.tsx new file mode 100644 index 000000000..b5d1125f8 --- /dev/null +++ b/src/sections/company/company-table-row.tsx @@ -0,0 +1,93 @@ +import React from 'react'; +import { useNavigate } from 'react-router-dom'; + +import Box from '@mui/material/Box'; +import { Chip } from '@mui/material'; +import TableRow from '@mui/material/TableRow'; +import Checkbox from '@mui/material/Checkbox'; +import TableCell from '@mui/material/TableCell'; +import IconButton from '@mui/material/IconButton'; + +import { Iconify } from 'src/components/iconify'; + +export type CompanyProps = { + id: string; + name: string; + phone: string; + email: string; + status: 'active' | 'passive'; + verified: boolean; +}; + +type CompanyTableRowProps = { + row: CompanyProps; + selected: boolean; + onSelectRow: () => void; +}; + +export function CompanyTableRow({ row, selected, onSelectRow }: CompanyTableRowProps) { + const navigate = useNavigate(); + + const handleNavigateToDetail = () => { + navigate(`/company/${row.id}`); + }; + + return ( + + e.stopPropagation()}> + + + + + + + + {row.name} + + + {row.phone} + + {row.email} + + + + + + e.stopPropagation()}> + navigate(`/company/${row.id}`)} + > + + + + + + + + ); +} \ No newline at end of file diff --git a/src/sections/company/company-table-toolbar.tsx b/src/sections/company/company-table-toolbar.tsx new file mode 100644 index 000000000..e12976d93 --- /dev/null +++ b/src/sections/company/company-table-toolbar.tsx @@ -0,0 +1,66 @@ +import React from "react"; + +import Tooltip from '@mui/material/Tooltip'; +import Toolbar from '@mui/material/Toolbar'; +import Typography from '@mui/material/Typography'; +import IconButton from '@mui/material/IconButton'; +import OutlinedInput from '@mui/material/OutlinedInput'; +import InputAdornment from '@mui/material/InputAdornment'; + +import { Iconify } from 'src/components/iconify'; + +type CompanyTableToolbarProps = { + numSelected: number; + filterName: string; + onFilterName: (event: React.ChangeEvent) => void; +}; + +export function CompanyTableToolbar({ numSelected, filterName, onFilterName }: CompanyTableToolbarProps) { + return ( + theme.spacing(0, 1, 0, 3), + ...(numSelected > 0 && { + color: 'primary.main', + bgcolor: 'primary.lighter', + }), + }} + > + {numSelected > 0 ? ( + + {numSelected} seçildi + + ) : ( + + + + } + sx={{ maxWidth: 320 }} + /> + )} + + {numSelected > 0 ? ( + + + + + + ) : ( + + + + + + )} + + ); +} \ No newline at end of file diff --git a/src/sections/overview/view/overview-analytics-view.tsx b/src/sections/overview/view/overview-analytics-view.tsx index 2b6b6da4f..e0d0c2405 100644 --- a/src/sections/overview/view/overview-analytics-view.tsx +++ b/src/sections/overview/view/overview-analytics-view.tsx @@ -1,5 +1,6 @@ import Grid from '@mui/material/Unstable_Grid2'; import Typography from '@mui/material/Typography'; +import { useTranslation } from 'react-i18next'; import { _tasks, _posts, _timeline } from 'src/_mock'; import { DashboardContent } from 'src/layouts/dashboard'; @@ -17,21 +18,32 @@ import { AnalyticsConversionRates } from '../analytics-conversion-rates'; // ---------------------------------------------------------------------- export function OverviewAnalyticsView() { + const { t } = useTranslation(['overview', 'common']); + return ( - Hi, Welcome back 👋 + {t('overview:welcome')} } chart={{ - categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug'], + categories: [ + t('common:months.short.jan'), + t('common:months.short.feb'), + t('common:months.short.mar'), + t('common:months.short.apr'), + t('common:months.short.may'), + t('common:months.short.jun'), + t('common:months.short.jul'), + t('common:months.short.aug') + ], series: [22, 8, 35, 50, 82, 84, 77, 12], }} /> @@ -39,13 +51,22 @@ export function OverviewAnalyticsView() { } chart={{ - categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug'], + categories: [ + t('common:months.short.jan'), + t('common:months.short.feb'), + t('common:months.short.mar'), + t('common:months.short.apr'), + t('common:months.short.may'), + t('common:months.short.jun'), + t('common:months.short.jul'), + t('common:months.short.aug') + ], series: [56, 47, 40, 62, 73, 30, 23, 54], }} /> @@ -53,13 +74,22 @@ export function OverviewAnalyticsView() { } chart={{ - categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug'], + categories: [ + t('common:months.short.jan'), + t('common:months.short.feb'), + t('common:months.short.mar'), + t('common:months.short.apr'), + t('common:months.short.may'), + t('common:months.short.jun'), + t('common:months.short.jul'), + t('common:months.short.aug') + ], series: [40, 70, 50, 28, 70, 75, 7, 64], }} /> @@ -67,13 +97,22 @@ export function OverviewAnalyticsView() { } chart={{ - categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug'], + categories: [ + t('common:months.short.jan'), + t('common:months.short.feb'), + t('common:months.short.mar'), + t('common:months.short.apr'), + t('common:months.short.may'), + t('common:months.short.jun'), + t('common:months.short.jul'), + t('common:months.short.aug') + ], series: [56, 30, 23, 54, 47, 40, 62, 73], }} /> @@ -81,13 +120,13 @@ export function OverviewAnalyticsView() { @@ -95,13 +134,23 @@ export function OverviewAnalyticsView() { diff --git a/src/sections/profile/components/notification-card.tsx b/src/sections/profile/components/notification-card.tsx new file mode 100644 index 000000000..680b3d0a7 --- /dev/null +++ b/src/sections/profile/components/notification-card.tsx @@ -0,0 +1,50 @@ +import { useTranslation } from 'react-i18next'; + +import { useTheme } from '@mui/material/styles'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import { Box, Card, Stack, Switch, Typography } from '@mui/material'; + +export type NotificationType = 'email' | 'push' | 'monthly' | 'news'; + +interface NotificationCardProps { + type: NotificationType; + checked: boolean; + onChange: (checked: boolean) => void; +} + +export function NotificationCard({ type, checked, onChange }: NotificationCardProps) { + const { t } = useTranslation(); + const theme = useTheme(); + + return ( + + + + + {t(`profile:notifications.${type}.title`)} + + + {t(`profile:notifications.${type}.description`)} + + + onChange(e.target.checked)} + color="primary" + /> + } + label="" + /> + + + ); +} \ No newline at end of file diff --git a/src/sections/profile/components/password-rules.tsx b/src/sections/profile/components/password-rules.tsx new file mode 100644 index 000000000..69e456425 --- /dev/null +++ b/src/sections/profile/components/password-rules.tsx @@ -0,0 +1,63 @@ +import { Box, Stack, Typography } from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import { PASSWORD_RULES } from 'src/utils/validators'; +import { Iconify } from 'src/components/iconify'; + +// Şifre kuralları kontrolü için yardımcı fonksiyon +const checkPasswordRule = (password: string, rule: (pass: string) => boolean): boolean => { + if (!password) return false; + return rule(password); +}; + +interface PasswordRulesProps { + password: string; +} + +export function PasswordRules({ password }: PasswordRulesProps) { + const { t } = useTranslation(); + + const rules = [ + { + check: (pass: string) => pass.length >= PASSWORD_RULES.minLength, + text: t('validation:password.min', { length: PASSWORD_RULES.minLength }) + }, + { + check: (pass: string) => /[A-Z]/.test(pass), + text: t('validation:password.uppercase') + }, + { + check: (pass: string) => /[a-z]/.test(pass), + text: t('validation:password.lowercase') + }, + { + check: (pass: string) => /\d/.test(pass), + text: t('validation:password.number') + }, + { + check: (pass: string) => /[!@#$%^&*(),.?":{}|<>]/.test(pass), + text: t('validation:password.special') + } + ]; + + return ( + + + {t('profile:security.passwordRules')}: + + + {rules.map((rule, index) => ( + + {checkPasswordRule(password, rule.check) ? ( + + ) : ( + + )} + + {rule.text} + + + ))} + + + ); +} \ No newline at end of file diff --git a/src/sections/profile/components/profile-avatar.tsx b/src/sections/profile/components/profile-avatar.tsx new file mode 100644 index 000000000..035a0049f --- /dev/null +++ b/src/sections/profile/components/profile-avatar.tsx @@ -0,0 +1,80 @@ +import React, { useState } from 'react'; + +import { useTheme } from '@mui/material/styles'; +import { Box, Badge, Avatar, IconButton } from '@mui/material'; + +import { userService } from 'src/services/user/user-service'; + +import { Iconify } from 'src/components/iconify'; + +interface ProfileAvatarProps { + avatarUrl?: string; + name: string; +} + +export function ProfileAvatar({ avatarUrl, name }: ProfileAvatarProps) { + const theme = useTheme(); + const [uploadedPhoto, setUploadedPhoto] = useState(null); + + const handlePhotoUpload = async (event: React.ChangeEvent) => { + const file = event.target.files?.[0]; + if (file) { + try { + const response = await userService.uploadProfilePhoto(file); + setUploadedPhoto("response.filePath"); + } catch (error) { + console.error('Error uploading file:', error); + } + } + }; + + return ( + + + + {/* eslint-disable-next-line jsx-a11y/label-has-associated-control */} + + + } + > + + + + ); +} diff --git a/src/sections/profile/hooks/use-password-form.ts b/src/sections/profile/hooks/use-password-form.ts new file mode 100644 index 000000000..dbf532854 --- /dev/null +++ b/src/sections/profile/hooks/use-password-form.ts @@ -0,0 +1,57 @@ +import type React from 'react'; + +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { validatePasswordChange } from 'src/utils/validators'; + +interface PasswordFormErrors { + currentPassword?: string; + newPassword?: string; + confirmPassword?: string; +} + +export const usePasswordForm = () => { + const { t } = useTranslation(); + const [currentPassword, setCurrentPassword] = useState(''); + const [newPassword, setNewPassword] = useState(''); + const [confirmPassword, setConfirmPassword] = useState(''); + const [isSaving, setIsSaving] = useState(false); + const [successMessage, setSuccessMessage] = useState(''); + const [formErrors, setFormErrors] = useState({}); + + const validateForm = (): boolean => { + const errors = validatePasswordChange(currentPassword, newPassword, confirmPassword, t); + setFormErrors(errors); + return !Object.values(errors).some(Boolean); + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + if (!validateForm()) return; + + try { + setIsSaving(true); + // Şifre değiştirme API çağrısı burada yapılacak + setSuccessMessage(t('profile:security.success')); + } catch (err) { + console.error(t('profile:security.error'), err); + } finally { + setIsSaving(false); + } + }; + + return { + currentPassword, + setCurrentPassword, + newPassword, + setNewPassword, + confirmPassword, + setConfirmPassword, + isSaving, + successMessage, + formErrors, + setFormErrors, + handleSubmit, + }; +}; \ No newline at end of file diff --git a/src/sections/profile/hooks/use-profile-form.ts b/src/sections/profile/hooks/use-profile-form.ts new file mode 100644 index 000000000..1c90c51dd --- /dev/null +++ b/src/sections/profile/hooks/use-profile-form.ts @@ -0,0 +1,107 @@ +import * as Yup from 'yup'; +import { useEffect } from 'react'; +import { useFormik } from 'formik'; +import { useTranslation } from 'react-i18next'; + +import { useUser } from 'src/contexts/user-context'; +import { useLoading } from "src/contexts/loading-context"; +import { useUnsavedChanges } from "src/contexts/unsaved-changes-context"; + +export const useProfileForm = () => { + const { t } = useTranslation(); + const { user, updateUser } = useUser(); + const { showLoading, hideLoading } = useLoading(); + const { setHasUnsavedChanges } = useUnsavedChanges(); + + const validationSchema = Yup.object().shape({ + firstName: Yup.string() + .required(t('validation:required.firstName')) + .min(2, t('validation:length.name.min')) + .max(50, t('validation:length.name.max')), + lastName: Yup.string() + .required(t('validation:required.lastName')) + .min(2, t('validation:length.name.min')) + .max(50, t('validation:length.name.max')), + email: Yup.string() + .required(t('validation:required.email')) + .email(t('validation:invalid.email')), + phone: Yup.string() + .required(t('validation:required.phone')) + .matches(/^\(\d{3}\) \d{3} \d{2} \d{2}$/, t('validation:invalid.phone')), + about: Yup.string() + .max(500, t('validation:length.about.max')), + }); + + const formik = useFormik({ + initialValues: { + firstName: user?.firstName || '', + lastName: user?.lastName || '', + email: user?.email || '', + phone: user?.phone || '', + about: '', + countryCode: '+90', + }, + enableReinitialize: true, + validationSchema, + onSubmit: async (values, { setSubmitting, setStatus }) => { + showLoading(); + + try { + setStatus(null); + setSubmitting(true); + + const updatedFields: Partial = {}; + + if (values.firstName !== user?.firstName) updatedFields.firstName = values.firstName; + if (values.lastName !== user?.lastName) updatedFields.lastName = values.lastName; + if (values.email !== user?.email) updatedFields.email = values.email; + if (values.phone !== user?.phone) updatedFields.phone = values.phone; + + await updateUser(updatedFields); + setStatus({ success: t('profile:success') }); + setHasUnsavedChanges(false); + } catch (error) { + console.error('Form submission error:', error); + setStatus({ error: t('profile:error.update') }); + } finally { + hideLoading(); + setSubmitting(false); + } + }, + }); + + // Form değişikliklerini takip et + useEffect(() => { + const hasChanges = Object.keys(formik.values).some((key) => { + const value = formik.values[key as keyof typeof formik.values]; + const initialValue = formik.initialValues[key as keyof typeof formik.initialValues]; + return value !== initialValue; + }); + + setHasUnsavedChanges(hasChanges); + }, [formik.values, formik.initialValues, setHasUnsavedChanges, formik]); + + const formatPhoneNumber = (value: string) => { + const numbersOnly = value.replace(/\D/g, ''); + let formatted = ''; + if (numbersOnly.length > 0) formatted += '('; + if (numbersOnly.length > 0) formatted += numbersOnly.substring(0, 3); + if (numbersOnly.length > 3) formatted += ') '; + if (numbersOnly.length > 3) formatted += numbersOnly.substring(3, 6); + if (numbersOnly.length > 6) formatted += ' '; + if (numbersOnly.length > 6) formatted += numbersOnly.substring(6, 8); + if (numbersOnly.length > 8) formatted += ' '; + if (numbersOnly.length > 8) formatted += numbersOnly.substring(8, 10); + return formatted; + }; + + const handlePhoneChange = (e: React.ChangeEvent) => { + const formatted = formatPhoneNumber(e.target.value); + formik.setFieldValue('phone', formatted); + }; + + return { + ...formik, + handlePhoneChange, + }; +}; \ No newline at end of file diff --git a/src/sections/profile/tabs/general-tab.tsx b/src/sections/profile/tabs/general-tab.tsx new file mode 100644 index 000000000..aa3f1b957 --- /dev/null +++ b/src/sections/profile/tabs/general-tab.tsx @@ -0,0 +1,233 @@ +import { useTranslation } from 'react-i18next'; + +import { LoadingButton } from '@mui/lab'; +import { + Box, + Alert, + Stack, + Button, + Select, + Divider, + MenuItem, + TextField, + InputAdornment, + +} from '@mui/material'; + +import { Iconify } from 'src/components/iconify'; + +import { useProfileForm } from '../hooks/use-profile-form'; + +const COUNTRY_CODES = [ + { code: '+90', country: 'TR', flag: '🇹🇷' }, + { code: '+1', country: 'US', flag: '🇺🇸' }, + { code: '+44', country: 'GB', flag: '🇬🇧' }, + { code: '+49', country: 'DE', flag: '🇩🇪' }, + { code: '+33', country: 'FR', flag: '🇫🇷' }, + { code: '+39', country: 'IT', flag: '🇮🇹' }, + { code: '+34', country: 'ES', flag: '🇪🇸' }, + { code: '+31', country: 'NL', flag: '🇳🇱' }, +] as const; + +export function GeneralTab() { + const { t } = useTranslation(['profile', 'validation', 'common']); + const { + values, + errors, + touched, + isSubmitting, + status, + handleSubmit, + handleChange, + handleBlur, + setFieldValue, + resetForm, + handlePhoneChange, + } = useProfileForm(); + + return ( +
+ + {status?.error && ( + + {t('error.update')} + + )} + + {status?.success && ( + + {t('success')} + + )} + + + + ), + }} + /> + + + ), + }} + /> + + + + ), + }} + /> + + + + + + + + + ), + }} + /> + + + ), + }} + /> + + + + + } + sx={{ + bgcolor: 'primary.main', + color: 'primary.contrastText', + '&:hover': { bgcolor: 'primary.dark' }, + px: 3, + }} + > + {t('save')} + + + +
+ ); +} diff --git a/src/sections/profile/tabs/notifications-tab.tsx b/src/sections/profile/tabs/notifications-tab.tsx new file mode 100644 index 000000000..afc863a6d --- /dev/null +++ b/src/sections/profile/tabs/notifications-tab.tsx @@ -0,0 +1,44 @@ +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { Box, Stack, Typography } from '@mui/material'; + +import { NotificationCard } from '../components/notification-card'; + +import type { NotificationType } from '../components/notification-card'; + +export function NotificationsTab() { + const { t } = useTranslation(); + const [notifications, setNotifications] = useState>({ + email: true, + push: true, + monthly: false, + news: true, + }); + + const handleNotificationChange = (type: NotificationType, checked: boolean) => { + setNotifications(prev => ({ + ...prev, + [type]: checked, + })); + }; + + return ( + + + {t('profile:notifications.title')} + + + + {(['email', 'push', 'monthly', 'news'] as NotificationType[]).map((type) => ( + handleNotificationChange(type, checked)} + /> + ))} + + + ); +} \ No newline at end of file diff --git a/src/sections/profile/tabs/security-tab.tsx b/src/sections/profile/tabs/security-tab.tsx new file mode 100644 index 000000000..c6d318e20 --- /dev/null +++ b/src/sections/profile/tabs/security-tab.tsx @@ -0,0 +1,104 @@ +import { useTranslation } from 'react-i18next'; + +import { LoadingButton } from '@mui/lab'; +import { Box, Alert, Stack, TextField, Typography } from '@mui/material'; + +import { Iconify } from 'src/components/iconify'; + +import { usePasswordForm } from '../hooks/use-password-form'; +import { PasswordRules } from '../components/password-rules'; + +export function SecurityTab() { + const { t } = useTranslation(); + const { + currentPassword, + setCurrentPassword, + newPassword, + setNewPassword, + confirmPassword, + setConfirmPassword, + isSaving, + successMessage, + formErrors, + setFormErrors, + handleSubmit, + } = usePasswordForm(); + + return ( + + + {t('profile:security.title')} + + +
+ + { + setCurrentPassword(e.target.value); + setFormErrors(prev => ({ ...prev, currentPassword: undefined })); + }} + error={!!formErrors.currentPassword} + helperText={formErrors.currentPassword} + InputProps={{ + startAdornment: , + }} + /> + + + + { + setNewPassword(e.target.value); + setFormErrors(prev => ({ ...prev, newPassword: undefined, confirmPassword: undefined })); + }} + error={!!formErrors.newPassword} + helperText={formErrors.newPassword} + InputProps={{ + startAdornment: , + }} + /> + + { + setConfirmPassword(e.target.value); + setFormErrors(prev => ({ ...prev, confirmPassword: undefined })); + }} + error={!!formErrors.confirmPassword} + helperText={formErrors.confirmPassword} + InputProps={{ + startAdornment: , + }} + /> + + {successMessage && ( + + {successMessage} + + )} + + } + > + {t('profile:security.changePassword')} + + +
+
+ ); +} \ No newline at end of file diff --git a/src/sections/profile/view/index.ts b/src/sections/profile/view/index.ts new file mode 100644 index 000000000..afa040dd7 --- /dev/null +++ b/src/sections/profile/view/index.ts @@ -0,0 +1 @@ +export * from './profile-view'; \ No newline at end of file diff --git a/src/sections/profile/view/profile-view.tsx b/src/sections/profile/view/profile-view.tsx new file mode 100644 index 000000000..af5d4b323 --- /dev/null +++ b/src/sections/profile/view/profile-view.tsx @@ -0,0 +1,115 @@ +import React, { useState } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { Box, Tab, Card, Tabs, Stack, Divider, Container, Typography } from '@mui/material'; + +import { useUser } from 'src/contexts/user-context'; +import { DashboardContent } from 'src/layouts/dashboard'; + +import { GeneralTab } from '../tabs/general-tab'; +import { SecurityTab } from '../tabs/security-tab'; +import { ProfileAvatar } from '../components/profile-avatar'; +import { NotificationsTab } from '../tabs/notifications-tab'; + +interface TabPanelProps { + children?: React.ReactNode; + index: number; + value: number; +} + +function TabPanel(props: TabPanelProps) { + const { children, value, index, ...other } = props; + + return ( + + ); +} + +export function ProfileView() { + const { t } = useTranslation(); + const { user } = useUser(); + const [activeTab, setActiveTab] = useState(0); + + const handleTabChange = (_event: React.SyntheticEvent, newValue: number) => { + setActiveTab(newValue); + }; + + return ( + + + + {t('profile:title')} + + + {t('profile:subtitle')} + + + + + + `linear-gradient(135deg, ${theme.palette.primary.light} 0%, ${theme.palette.primary.main} 100%)`, + }} + > + + + + {`${user?.firstName ?? ''} ${user?.lastName ?? ''}`} + + {user?.email} + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} \ No newline at end of file diff --git a/src/services/auth/auth-service.ts b/src/services/auth/auth-service.ts new file mode 100644 index 000000000..1f8d80a07 --- /dev/null +++ b/src/services/auth/auth-service.ts @@ -0,0 +1,18 @@ +import axiosInstance from '../../config/axios-instance'; + +import type { LoginRequest, LoginResponse } from './auth-service.type'; + +const BASE_URL = '/auth'; + +export const authService = { + // Login + login: async (data: LoginRequest): Promise => { + const response = await axiosInstance.post(`${BASE_URL}/login`, data); + return response.data; + }, + + // Logout + logout: () => { + localStorage.removeItem('token'); + }, +}; diff --git a/src/services/auth/auth-service.type.ts b/src/services/auth/auth-service.type.ts new file mode 100644 index 000000000..26bfa5fc3 --- /dev/null +++ b/src/services/auth/auth-service.type.ts @@ -0,0 +1,9 @@ +export interface LoginRequest { + email: string; + password: string; +} + +export interface LoginResponse { + token: string; +} + diff --git a/src/services/company/company-service.ts b/src/services/company/company-service.ts new file mode 100644 index 000000000..e69de29bb diff --git a/src/services/company/company-service.type.ts b/src/services/company/company-service.type.ts new file mode 100644 index 000000000..d5edf0cc0 --- /dev/null +++ b/src/services/company/company-service.type.ts @@ -0,0 +1,156 @@ +export type PartnerType = 'CARRIER' | 'WAREHOUSE' | 'CUSTOMS' | 'SUPPLIER' | 'CUSTOMER'; + +export interface BankAccount { + bankName: string; + branch: string; + accountNumber: string; + iban: string; + currency: string; +} + +export interface InsuranceInfo { + provider: string; + policyNumber: string; + coverage: string; + expiryDate: Date; +} + +export interface CompanyDetails { + id: string; + name: string; + type: PartnerType; + taxNumber: string; + taxOffice?: string; + phone: string; + email: string; + website?: string; + address: string; + status: 'active' | 'passive'; + foundedYear?: number; + employeeCount?: number; + certifications?: string[]; + operationAreas?: string[]; + description?: string; + insuranceInfo?: InsuranceInfo; + bankAccounts?: BankAccount[]; +} + +export interface ContactAvailability { + workHours: string; + timezone: string; + preferredContactMethod: 'PHONE' | 'EMAIL' | 'WHATSAPP'; +} + +export interface ContactPerson { + id: string; + companyId: string; + name: string; + position: string; + department: string; + phone: string; + mobile?: string; + email: string; + isMainContact: boolean; + availability: ContactAvailability; +} + +export type Contract = { + id: string; + companyId: string; + type: 'SERVICE' | 'PARTNERSHIP' | 'FRAMEWORK'; + startDate: Date; + endDate: Date; + status: 'DRAFT' | 'ACTIVE' | 'EXPIRED' | 'TERMINATED'; + terms: { + paymentTerms: string; + deliveryTerms: string; + specialConditions: string[]; + }; + attachments: { + id: string; + name: string; + type: string; + url: string; + }[]; + history: { + date: Date; + action: string; + user: string; + notes: string; + }[]; +}; + +export type Performance = { + partnerId: string; + metrics: { + onTimeDelivery: number; + qualityScore: number; + responseTime: number; + incidentCount: number; + }; + reviews: { + date: Date; + score: number; + feedback: string; + reviewer: string; + }[]; + incidents: { + date: Date; + type: string; + description: string; + status: 'OPEN' | 'RESOLVED' | 'CLOSED'; + resolution?: string; + }[]; +}; + +export type Pricing = { + partnerId: string; + rateCards: { + id: string; + name: string; + validFrom: Date; + validTo: Date; + currency: string; + services: { + serviceType: string; + unitPrice: number; + unit: string; + conditions: string[]; + }[]; + }[]; + specialOffers: { + id: string; + description: string; + validFrom: Date; + validTo: Date; + discountRate: number; + conditions: string[]; + }[]; +}; + +export type Analytics = { + partnerId: string; + financials: { + totalRevenue: number; + outstandingBalance: number; + paymentHistory: { + date: Date; + amount: number; + type: 'INVOICE' | 'PAYMENT'; + status: 'PENDING' | 'COMPLETED' | 'OVERDUE'; + }[]; + }; + operationalMetrics: { + serviceUtilization: number; + incidentResolutionTime: number; + customerSatisfaction: number; + }; + trends: { + period: string; + metrics: { + name: string; + value: number; + change: number; + }[]; + }[]; +}; \ No newline at end of file diff --git a/src/services/user/user-service.ts b/src/services/user/user-service.ts new file mode 100644 index 000000000..316213cc7 --- /dev/null +++ b/src/services/user/user-service.ts @@ -0,0 +1,39 @@ +import type { User } from 'src/contexts/user-context'; +import axiosInstance from '../../config/axios-instance'; + +const BASE_URL = '/user'; + +export const userService = { + getUserProfileByEmail: async (email: string): Promise => { + const response = await axiosInstance.get(`${BASE_URL}/profile/${email}`); + return response.data; + }, + + updateUserProfile: async (data: Partial): Promise => { + const response = await axiosInstance.put(`${BASE_URL}/profile`, data); + return response.data; + }, + + updatePassword: async (currentPassword: string, newPassword: string): Promise => { + await axiosInstance.put(`${BASE_URL}/password`, { + currentPassword, + newPassword, + }); + }, + + uploadProfilePhoto: async (file: File): Promise => { + const formData = new FormData(); + formData.append('photo', file); + + const response = await axiosInstance.put(`${BASE_URL}/photo`, formData, { + headers: { + 'Content-Type': 'multipart/form-data', + }, + }); + return response.data.avatarUrl; + }, + + updateNotifications: async (notifications: User['notifications']): Promise => { + await axiosInstance.put(`${BASE_URL}/notifications`, { notifications }); + }, +}; diff --git a/src/services/user/user-service.type.ts b/src/services/user/user-service.type.ts new file mode 100644 index 000000000..520e2f5a2 --- /dev/null +++ b/src/services/user/user-service.type.ts @@ -0,0 +1,25 @@ +export interface UpdateUserProfileRequest { + firstName?: string; + lastName?: string; + email?: string; +} + +export interface UpdateUserProfileResponse { + success: boolean; + message: string; +} + +export interface UserProfileResponse { + id: string; + email: string; + firstName: string; + lastName: string; + avatarUrl?: string; +} + +export interface UploadProfilePhotoResponse { + success: boolean; + message: string; + url: string; + filePath: string; +} diff --git a/src/utils/validators.ts b/src/utils/validators.ts new file mode 100644 index 000000000..523f014ef --- /dev/null +++ b/src/utils/validators.ts @@ -0,0 +1,81 @@ +import type { TFunction } from 'i18next'; + +export const PASSWORD_RULES = { + minLength: 8, +}; + +export const validateName = (value: string, t: TFunction): string | undefined => { + if (!value) { + return t('validation.required'); + } + if (value.length < 2) { + return t('validation.nameMinLength'); + } + if (value.length > 50) { + return t('validation.nameMaxLength'); + } + return undefined; +}; + +export const validateEmail = (value: string, t: TFunction): string | undefined => { + if (!value) { + return t('validation.required'); + } + const emailRegex = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i; + if (!emailRegex.test(value)) { + return t('validation.invalidEmail'); + } + return undefined; +}; + +export const validatePhone = (value: string, t: TFunction): string | undefined => { + if (!value) { + return t('validation.required'); + } + const phoneRegex = /^\(\d{3}\) \d{3} \d{2} \d{2}$/; + if (!phoneRegex.test(value)) { + return t('validation.invalidPhone'); + } + return undefined; +}; + +export const validatePasswordChange = ( + currentPassword: string, + newPassword: string, + confirmPassword: string, + t: TFunction +): { currentPassword?: string; newPassword?: string; confirmPassword?: string } => { + const errors: { currentPassword?: string; newPassword?: string; confirmPassword?: string } = {}; + + if (!currentPassword) { + errors.currentPassword = t('validation.required'); + } + + if (!newPassword) { + errors.newPassword = t('validation.required'); + } else { + if (newPassword.length < PASSWORD_RULES.minLength) { + errors.newPassword = t('validation.passwordMinLength', { length: PASSWORD_RULES.minLength }); + } + if (!/[A-Z]/.test(newPassword)) { + errors.newPassword = t('validation.passwordRequireUppercase'); + } + if (!/[a-z]/.test(newPassword)) { + errors.newPassword = t('validation.passwordRequireLowercase'); + } + if (!/\d/.test(newPassword)) { + errors.newPassword = t('validation.passwordRequireNumber'); + } + if (!/[!@#$%^&*(),.?":{}|<>]/.test(newPassword)) { + errors.newPassword = t('validation.passwordRequireSpecialChar'); + } + } + + if (!confirmPassword) { + errors.confirmPassword = t('validation.required'); + } else if (confirmPassword !== newPassword) { + errors.confirmPassword = t('validation.passwordMismatch'); + } + + return errors; +}; \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index eb21c020b..4dad99eab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -60,10 +60,10 @@ dependencies: "@babel/types" "^7.25.4" -"@babel/runtime@^7.12.5", "@babel/runtime@^7.18.3", "@babel/runtime@^7.23.9", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.7": - version "7.25.4" - resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.4.tgz" - integrity sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w== +"@babel/runtime@^7.12.5", "@babel/runtime@^7.18.3", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.9", "@babel/runtime@^7.25.0", "@babel/runtime@^7.26.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.7": + version "7.26.0" + resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz" + integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw== dependencies: regenerator-runtime "^0.14.0" @@ -143,7 +143,7 @@ resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz" integrity sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ== -"@emotion/react@^11.0.0-rc.0", "@emotion/react@^11.13.3", "@emotion/react@^11.4.1", "@emotion/react@^11.5.0": +"@emotion/react@^11.13.3": version "11.13.3" resolved "https://registry.npmjs.org/@emotion/react/-/react-11.13.3.tgz" integrity sha512-lIsdU6JNrmYfJ5EbUCf4xW1ovy5wKQ2CkPRM4xogziOxH1nXxBSjpC9YqbFAP7circxMfYp+6x676BqWcEiixg== @@ -173,7 +173,7 @@ resolved "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz" integrity sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg== -"@emotion/styled@^11.13.0", "@emotion/styled@^11.3.0": +"@emotion/styled@^11.13.0": version "11.13.0" resolved "https://registry.npmjs.org/@emotion/styled/-/styled-11.13.0.tgz" integrity sha512-tkzkY7nQhW/zC4hztlwucpT8QEZ6eUzpXDRhww/Eej4tFfO0FxQYWRyg/c5CCXa4d/f174kqeXYjuQRnhzf6dA== @@ -205,10 +205,10 @@ resolved "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz" integrity sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg== -"@esbuild/darwin-arm64@0.21.5": +"@esbuild/win32-x64@0.21.5": version "0.21.5" - resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz" - integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ== + resolved "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz" + integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw== "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" @@ -360,6 +360,13 @@ resolved "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.16.7.tgz" integrity sha512-RtsCt4Geed2/v74sbihWzzRs+HsIQCfclHeORh5Ynu2fS4icIKozcSubwuG7vtzq2uW3fOR1zITSP84TNt2GoQ== +"@mui/icons-material@^6.3.1": + version "6.3.1" + resolved "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.3.1.tgz" + integrity sha512-nJmWj1PBlwS3t1PnoqcixIsftE+7xrW3Su7f0yrjPw4tVjYrgkhU0hrRp+OlURfZ3ptdSkoBkalee9Bhf1Erfw== + dependencies: + "@babel/runtime" "^7.26.0" + "@mui/lab@^5.0.0-alpha.173": version "5.0.0-alpha.173" resolved "https://registry.npmjs.org/@mui/lab/-/lab-5.0.0-alpha.173.tgz" @@ -373,7 +380,7 @@ clsx "^2.1.0" prop-types "^15.8.1" -"@mui/material@^5.16.7", "@mui/material@>=5.15.0": +"@mui/material@^5.16.7": version "5.16.7" resolved "https://registry.npmjs.org/@mui/material/-/material-5.16.7.tgz" integrity sha512-cwwVQxBhK60OIOqZOVLFt55t01zmarKJiJUWbk0+8s/Ix5IaUzAShqlJchxsIQ4mSrWqgcKCCXKtIlG5H+/Jmg== @@ -477,15 +484,15 @@ resolved "https://registry.npmjs.org/@remix-run/router/-/router-1.19.1.tgz" integrity sha512-S45oynt/WH19bHbIXjtli6QmwNYvaz+vtnubvNpNDvUOoA/OWh6j1OikIP3G+v5GHdxyC6EXoChG3HgYGEUfcg== -"@rollup/rollup-darwin-arm64@4.21.0": - version "4.21.0" - resolved "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.0.tgz" - integrity sha512-zOnKWLgDld/svhKO5PD9ozmL6roy5OQ5T4ThvdYZLpiOhEGY+dp2NwUmxK0Ld91LrbjrvtNAE0ERBwjqhZTRAA== +"@rollup/rollup-win32-x64-msvc@4.29.1": + 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== -"@swc/core-darwin-arm64@1.7.14": +"@swc/core-win32-x64-msvc@1.7.14": version "1.7.14" - resolved "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.7.14.tgz" - integrity sha512-V0OUXjOH+hdGxDYG8NkQzy25mKOpcNKFpqtZEzLe5V/CpLJPnpg1+pMz70m14s9ZFda9OxsjlvPbg1FLUwhgIQ== + resolved "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.7.14.tgz" + integrity sha512-NNrprQCK6d28mG436jVo2TD+vACHseUECacEBGZ9Ef0qfOIWS1XIt2MisQKG0Oea2VvLFl6tF/V4Lnx/H0Sn3Q== "@swc/core@^1.5.7": version "1.7.14" @@ -518,10 +525,18 @@ dependencies: "@swc/counter" "^0.1.3" -"@types/estree@1.0.5": - version "1.0.5" - resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz" - integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== +"@types/estree@1.0.6": + version "1.0.6" + resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz" + integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== + +"@types/hoist-non-react-statics@^3.3.1": + version "3.3.6" + resolved "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.6.tgz" + integrity sha512-lPByRJUer/iN/xa4qpyL0qmL11DqNW81iU/IG1S3uvRUq4oKagz8VCxZjiWkumgt66YT3vOdDgZ0o32sGKtCEw== + dependencies: + "@types/react" "*" + hoist-non-react-statics "^3.3.0" "@types/json5@^0.0.29": version "0.0.29" @@ -540,7 +555,7 @@ resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.7.tgz" integrity sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA== -"@types/node@^18.0.0 || >=20.0.0", "@types/node@^22.5.0": +"@types/node@^22.5.0": version "22.5.0" resolved "https://registry.npmjs.org/@types/node/-/node-22.5.0.tgz" integrity sha512-DkFrJOe+rfdHTqqMg0bSNlGlQ85hSoh2TPzZyhHsXnMtligRWpxUySiyw8FY14ITt24HVCiQPWxS3KO/QlGmWg== @@ -564,6 +579,13 @@ dependencies: "@types/react" "*" +"@types/react-input-mask@^3.0.6": + version "3.0.6" + resolved "https://registry.npmjs.org/@types/react-input-mask/-/react-input-mask-3.0.6.tgz" + integrity sha512-+5I18WKyG3eWIj7TVPWfK1VitI9mPpS9y6jE/BfmTCe+iL27NfBw/yzKRvCFp1DRBvlvvcsiZf05bub0YC1k8A== + dependencies: + "@types/react" "*" + "@types/react-transition-group@^4.4.10": version "4.4.11" resolved "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.11.tgz" @@ -571,7 +593,7 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^17.0.0 || ^18.0.0", "@types/react@^18.3.4": +"@types/react@*", "@types/react@^18.3.4": version "18.3.4" resolved "https://registry.npmjs.org/@types/react/-/react-18.3.4.tgz" integrity sha512-J7W30FTdfCxDDjmfRM+/JqLHBIyl7xUIp9kwK637FGmY7+mkSFSe6L4jpZzhj5QMfLssSDP4/i75AKkrdC7/Jw== @@ -579,7 +601,12 @@ "@types/prop-types" "*" csstype "^3.0.2" -"@typescript-eslint/eslint-plugin@^7.0.0", "@typescript-eslint/eslint-plugin@^7.18.0", "@typescript-eslint/eslint-plugin@6 - 7": +"@types/yup@^0.29.14": + version "0.29.14" + resolved "https://registry.npmjs.org/@types/yup/-/yup-0.29.14.tgz" + integrity sha512-Ynb/CjHhE/Xp/4bhHmQC4U1Ox+I2OpfRYF3dnNgQqn1cHa6LK3H1wJMNPT02tSVZA6FYuXE2ITORfbnb6zBCSA== + +"@typescript-eslint/eslint-plugin@^7.18.0": version "7.18.0" resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz" integrity sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw== @@ -594,7 +621,7 @@ natural-compare "^1.4.0" ts-api-utils "^1.3.0" -"@typescript-eslint/parser@^7.0.0", "@typescript-eslint/parser@^7.18.0": +"@typescript-eslint/parser@^7.18.0": version "7.18.0" resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz" integrity sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg== @@ -682,7 +709,7 @@ acorn-jsx@^5.3.2: resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8.9.0: +acorn@^8.9.0: version "8.10.0" resolved "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz" integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== @@ -731,7 +758,7 @@ anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" -apexcharts@^3.41.0, apexcharts@^3.52.0: +apexcharts@^3.52.0: version "3.52.0" resolved "https://registry.npmjs.org/apexcharts/-/apexcharts-3.52.0.tgz" integrity sha512-7dg0ADKs8AA89iYMZMe2sFDG0XK5PfqllKV9N+i3hKHm3vEtdhwz8AlXGm+/b0nJ6jKiaXsqci5LfVxNhtB+dA== @@ -854,6 +881,11 @@ ast-types-flow@^0.0.8: resolved "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz" integrity sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ== +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + available-typed-arrays@^1.0.7: version "1.0.7" resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz" @@ -866,6 +898,15 @@ axe-core@^4.9.1: resolved "https://registry.npmjs.org/axe-core/-/axe-core-4.10.0.tgz" integrity sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g== +axios@^1.7.9: + 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" + axobject-query@~3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz" @@ -999,6 +1040,13 @@ color-name@1.1.3: resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== +combined-stream@^1.0.8: + 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" + commander@^8.0.0: version "8.3.0" resolved "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz" @@ -1030,10 +1078,17 @@ cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" +cross-fetch@4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz" + integrity sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g== + dependencies: + node-fetch "^2.6.12" + cross-spawn@^7.0.2: - 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== dependencies: path-key "^3.1.0" shebang-command "^2.0.0" @@ -1123,6 +1178,11 @@ deep-is@^0.1.3: resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== +deepmerge@^2.1.1: + version "2.2.1" + resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz" + integrity sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA== + define-data-property@^1.0.1, define-data-property@^1.1.4: version "1.1.4" resolved "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz" @@ -1141,6 +1201,11 @@ define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1: has-property-descriptors "^1.0.0" object-keys "^1.1.1" +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" @@ -1386,7 +1451,7 @@ eslint-config-airbnb@^19.0.4: object.assign "^4.1.2" object.entries "^1.1.5" -eslint-config-prettier@*, eslint-config-prettier@^9.1.0: +eslint-config-prettier@^9.1.0: version "9.1.0" resolved "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz" integrity sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw== @@ -1420,7 +1485,7 @@ eslint-module-utils@^2.7.4, eslint-module-utils@^2.8.0: dependencies: debug "^3.2.7" -eslint-plugin-import@*, eslint-plugin-import@^2.25.2, eslint-plugin-import@^2.25.3, eslint-plugin-import@^2.29.1: +eslint-plugin-import@^2.29.1: version "2.29.1" resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz" integrity sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw== @@ -1443,7 +1508,7 @@ eslint-plugin-import@*, eslint-plugin-import@^2.25.2, eslint-plugin-import@^2.25 semver "^6.3.1" tsconfig-paths "^3.15.0" -eslint-plugin-jsx-a11y@^6.5.1, eslint-plugin-jsx-a11y@^6.9.0: +eslint-plugin-jsx-a11y@^6.9.0: version "6.9.0" resolved "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.9.0.tgz" integrity sha512-nOFOCaJG2pYqORjK19lqPqxMO/JpvdCZdPtNdxY3kvom3jTvkAbOvQvD8wuD0G8BYR0IGAGYDlzqWJOh/ybn2g== @@ -1482,12 +1547,12 @@ eslint-plugin-prettier@^5.2.1: prettier-linter-helpers "^1.0.0" synckit "^0.9.1" -eslint-plugin-react-hooks@^4.3.0, eslint-plugin-react-hooks@^4.6.2: +eslint-plugin-react-hooks@^4.6.2: version "4.6.2" resolved "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz" integrity sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ== -eslint-plugin-react@^7.28.0, eslint-plugin-react@^7.35.0: +eslint-plugin-react@^7.35.0: version "7.35.0" resolved "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.35.0.tgz" integrity sha512-v501SSMOWv8gerHkk+IIQBkcGRGrO2nfybfj5pLxuJNFTPxxA3PSryhXTK+9pNbtkggheDdsC0E9Q8CuPk6JKA== @@ -1536,7 +1601,7 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4 resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -eslint@*, "eslint@^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8", "eslint@^3 || ^4 || ^5 || ^6 || ^7 || ^8", "eslint@^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7", "eslint@^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0", "eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^7.32.0 || ^8.2.0", eslint@^8.56.0, eslint@^8.57.0, eslint@>=7, eslint@>=7.0.0, eslint@>=8.0.0, eslint@8: +eslint@^8.57.0: version "8.57.0" resolved "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz" integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== @@ -1691,6 +1756,11 @@ flatted@^3.1.0: resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== +follow-redirects@^1.15.6: + version "1.15.9" + resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz" + integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== + for-each@^0.3.3: version "0.3.3" resolved "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz" @@ -1698,6 +1768,29 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" +form-data@^4.0.0: + 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" + +formik@^2.4.6: + version "2.4.6" + resolved "https://registry.npmjs.org/formik/-/formik-2.4.6.tgz" + integrity sha512-A+2EI7U7aG296q2TLGvNapDNTZp1khVt5Vk0Q/fyfSROss0V/V6+txt2aJnwEos44IxTCW/LYAi/zgWzlevj+g== + dependencies: + "@types/hoist-non-react-statics" "^3.3.1" + deepmerge "^2.1.1" + hoist-non-react-statics "^3.3.0" + lodash "^4.17.21" + lodash-es "^4.17.21" + react-fast-compare "^2.0.1" + tiny-warning "^1.0.2" + tslib "^2.0.0" + fs-extra@^11.1.0: version "11.1.1" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz" @@ -1712,11 +1805,6 @@ fs.realpath@^1.0.0: resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@~2.3.2, fsevents@~2.3.3: - version "2.3.3" - resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz" - integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== - function-bind@^1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" @@ -1898,13 +1986,41 @@ history@^5.3.0: dependencies: "@babel/runtime" "^7.7.6" -hoist-non-react-statics@^3.3.1: +hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1: version "3.3.2" resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== dependencies: react-is "^16.7.0" +html-parse-stringify@^3.0.1: + 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== + dependencies: + void-elements "3.1.0" + +i18next-browser-languagedetector@^8.0.2: + version "8.0.2" + resolved "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.0.2.tgz" + integrity sha512-shBvPmnIyZeD2VU5jVGIOWP7u9qNG3Lj7mpaiPFpbJ3LVfHZJvVzKR4v1Cb91wAOFpNw442N+LGPzHOHsten2g== + dependencies: + "@babel/runtime" "^7.23.2" + +i18next-http-backend@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-3.0.1.tgz" + integrity sha512-XT2lYSkbAtDE55c6m7CtKxxrsfuRQO3rUfHzj8ZyRtY9CkIX3aRGwXGTkUhpGWce+J8n7sfu3J0f2wTzo7Lw0A== + dependencies: + cross-fetch "4.0.0" + +i18next@^24.2.0: + version "24.2.0" + resolved "https://registry.npmjs.org/i18next/-/i18next-24.2.0.tgz" + integrity sha512-ArJJTS1lV6lgKH7yEf4EpgNZ7+THl7bsGxxougPYiXRTJ/Fe1j08/TBpV9QsXCIYVfdE/HWG/xLezJ5DOlfBOA== + dependencies: + "@babel/runtime" "^7.23.2" + ignore@^5.2.0, ignore@^5.3.1: version "5.3.2" resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz" @@ -2221,6 +2337,11 @@ jsonfile@^6.0.1: object.assign "^4.1.4" object.values "^1.1.6" +jwt-decode@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz" + integrity sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA== + language-subtag-registry@^0.3.20: version "0.3.23" resolved "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz" @@ -2288,6 +2409,18 @@ micromatch@^4.0.4: braces "^3.0.3" picomatch "^2.3.1" +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + 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" + minimatch@^3.0.4: version "3.1.2" resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" @@ -2334,9 +2467,9 @@ ms@^2.1.1, ms@2.1.2: integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== nanoid@^3.3.7: - 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== natural-compare-lite@^1.4.0: version "1.4.0" @@ -2348,6 +2481,13 @@ natural-compare@^1.4.0: resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== +node-fetch@^2.6.12: + version "2.7.0" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== + dependencies: + whatwg-url "^5.0.0" + normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" @@ -2438,7 +2578,7 @@ once@^1.3.0: dependencies: wrappy "1" -optionator@^0.9.1, optionator@^0.9.3: +optionator@^0.9.3: version "0.9.3" resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz" integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== @@ -2506,10 +2646,10 @@ path-type@^4.0.0: resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -picocolors@^1.0.0, picocolors@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz" - integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== +picocolors@^1.0.0, picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: version "2.3.1" @@ -2521,14 +2661,14 @@ possible-typed-array-names@^1.0.0: resolved "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz" integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== -postcss@^8.4.41: - version "8.4.41" - resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz" - integrity sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ== +postcss@^8.4.43: + version "8.4.49" + resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz" + integrity sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA== 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" prelude-ls@^1.2.1: version "1.2.1" @@ -2542,7 +2682,7 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier@^3.3.3, prettier@>=3.0.0: +prettier@^3.3.3: version "3.3.3" resolved "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz" integrity sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew== @@ -2556,6 +2696,16 @@ prop-types@^15.6.2, prop-types@^15.8.1: object-assign "^4.1.1" react-is "^16.13.1" +property-expr@^2.0.5: + version "2.0.6" + resolved "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz" + integrity sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA== + +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + punycode@^2.1.0: version "2.3.1" resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" @@ -2573,7 +2723,7 @@ react-apexcharts@^1.4.1: dependencies: prop-types "^15.8.1" -"react-dom@^17.0.0 || ^18.0.0", react-dom@^18.3.1, react-dom@>=16.6.0, react-dom@>=16.8, react-dom@>=16.8.0: +react-dom@^18.3.1: version "18.3.1" resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz" integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw== @@ -2581,6 +2731,11 @@ react-apexcharts@^1.4.1: loose-envify "^1.1.0" scheduler "^0.23.2" +react-fast-compare@^2.0.1: + version "2.0.4" + resolved "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz" + integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw== + react-fast-compare@^3.2.2: version "3.2.2" resolved "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz" @@ -2595,6 +2750,22 @@ react-helmet-async@^2.0.5: react-fast-compare "^3.2.2" shallowequal "^1.1.0" +react-i18next@^15.4.0: + version "15.4.0" + resolved "https://registry.npmjs.org/react-i18next/-/react-i18next-15.4.0.tgz" + integrity sha512-Py6UkX3zV08RTvL6ZANRoBh9sL/ne6rQq79XlkHEdd82cZr2H9usbWpUNVadJntIZP2pu3M2rL1CN+5rQYfYFw== + dependencies: + "@babel/runtime" "^7.25.0" + html-parse-stringify "^3.0.1" + +react-input-mask@^2.0.4: + version "2.0.4" + resolved "https://registry.npmjs.org/react-input-mask/-/react-input-mask-2.0.4.tgz" + integrity sha512-1hwzMr/aO9tXfiroiVCx5EtKohKwLk/NT8QlJXHQ4N+yJJFyUuMT+zfTpLBwX/lK3PkuMlievIffncpMZ3HGRQ== + dependencies: + invariant "^2.2.4" + warning "^4.0.2" + react-is@^16.13.1: version "16.13.1" resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" @@ -2635,7 +2806,7 @@ react-transition-group@^4.4.5: loose-envify "^1.4.0" prop-types "^15.6.2" -"react@^16.6.0 || ^17.0.0 || ^18.0.0", "react@^17.0.0 || ^18.0.0", react@^18.3.1, react@>=0.13, react@>=16, react@>=16.6.0, react@>=16.8, react@>=16.8.0: +react@^18.3.1: version "18.3.1" resolved "https://registry.npmjs.org/react/-/react-18.3.1.tgz" integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ== @@ -2718,28 +2889,31 @@ rimraf@^3.0.2: glob "^7.1.3" rollup@^4.20.0: - version "4.21.0" - resolved "https://registry.npmjs.org/rollup/-/rollup-4.21.0.tgz" - integrity sha512-vo+S/lfA2lMS7rZ2Qoubi6I5hwZwzXeUIctILZLbHI+laNtvhhOIon2S1JksA5UEDQ7l3vberd0fxK44lTYjbQ== + version "4.29.1" + resolved "https://registry.npmjs.org/rollup/-/rollup-4.29.1.tgz" + integrity sha512-RaJ45M/kmJUzSWDs1Nnd5DdV4eerC98idtUOVr6FfKcgxqvjwHmxc5upLF9qZU9EpsVzzhleFahrT3shLuJzIw== dependencies: - "@types/estree" "1.0.5" + "@types/estree" "1.0.6" optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.21.0" - "@rollup/rollup-android-arm64" "4.21.0" - "@rollup/rollup-darwin-arm64" "4.21.0" - "@rollup/rollup-darwin-x64" "4.21.0" - "@rollup/rollup-linux-arm-gnueabihf" "4.21.0" - "@rollup/rollup-linux-arm-musleabihf" "4.21.0" - "@rollup/rollup-linux-arm64-gnu" "4.21.0" - "@rollup/rollup-linux-arm64-musl" "4.21.0" - "@rollup/rollup-linux-powerpc64le-gnu" "4.21.0" - "@rollup/rollup-linux-riscv64-gnu" "4.21.0" - "@rollup/rollup-linux-s390x-gnu" "4.21.0" - "@rollup/rollup-linux-x64-gnu" "4.21.0" - "@rollup/rollup-linux-x64-musl" "4.21.0" - "@rollup/rollup-win32-arm64-msvc" "4.21.0" - "@rollup/rollup-win32-ia32-msvc" "4.21.0" - "@rollup/rollup-win32-x64-msvc" "4.21.0" + "@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" run-parallel@^1.1.9: @@ -2860,10 +3034,10 @@ slash@^3.0.0: resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -source-map-js@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz" - integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== +source-map-js@^1.2.1: + 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== source-map@^0.5.7: version "0.5.7" @@ -3053,11 +3227,21 @@ text-table@^0.2.0: resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== +tiny-case@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz" + integrity sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q== + tiny-invariant@^1.1.0: version "1.3.1" resolved "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz" integrity sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw== +tiny-warning@^1.0.2: + version "1.0.3" + resolved "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz" + integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== + to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz" @@ -3070,6 +3254,16 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +toposort@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz" + integrity sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg== + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + ts-api-utils@^1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz" @@ -3085,7 +3279,7 @@ tsconfig-paths@^3.15.0: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^2.6.2: +tslib@^2.0.0, tslib@^2.6.2: version "2.6.3" resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz" integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== @@ -3107,6 +3301,11 @@ type-fest@^0.21.3: resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== +type-fest@^2.19.0: + version "2.19.0" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz" + integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== + typed-array-buffer@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz" @@ -3151,7 +3350,7 @@ typed-array-length@^1.0.6: is-typed-array "^1.1.13" possible-typed-array-names "^1.0.0" -typescript@*, typescript@^5.5.4, typescript@>=4.2.0: +typescript@^5.5.4: version "5.5.4" resolved "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz" integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q== @@ -3203,17 +3402,22 @@ vite-plugin-checker@^0.7.2: vscode-languageserver-textdocument "^1.0.1" vscode-uri "^3.0.2" -"vite@^4 || ^5", vite@^5.4.2, vite@>=2.0.0: - version "5.4.2" - resolved "https://registry.npmjs.org/vite/-/vite-5.4.2.tgz" - integrity sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA== +vite@^5.4.2: + version "5.4.11" + resolved "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz" + integrity sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q== dependencies: esbuild "^0.21.3" - postcss "^8.4.41" + postcss "^8.4.43" rollup "^4.20.0" optionalDependencies: fsevents "~2.3.3" +void-elements@3.1.0: + 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== + vscode-jsonrpc@6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz" @@ -3258,6 +3462,26 @@ vscode-uri@^3.0.2: resolved "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz" integrity sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw== +warning@^4.0.2: + version "4.0.3" + resolved "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz" + integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== + dependencies: + loose-envify "^1.0.0" + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + which-boxed-primitive@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz" @@ -3329,3 +3553,13 @@ yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +yup@^1.6.1: + version "1.6.1" + resolved "https://registry.npmjs.org/yup/-/yup-1.6.1.tgz" + integrity sha512-JED8pB50qbA4FOkDol0bYF/p60qSEDQqBD0/qeIrUCG1KbPBIQ776fCUNb9ldbPcSTxA69g/47XTo4TqWiuXOA== + dependencies: + property-expr "^2.0.5" + tiny-case "^1.0.3" + toposort "^2.0.2" + type-fest "^2.19.0"