diff --git a/mdm-front/package-lock.json b/mdm-front/package-lock.json index a2d1cf2..e795067 100644 --- a/mdm-front/package-lock.json +++ b/mdm-front/package-lock.json @@ -9,7 +9,6 @@ "version": "0.0.0", "dependencies": { "@apollo/client": "^4.1.9", - "@heroui/react": "^3.0.3", "@hookform/resolvers": "^5.2.2", "@internationalized/date": "^3.12.1", "@radix-ui/react-accordion": "^1.2.12", @@ -22,7 +21,7 @@ "leaflet": "^1.9.4", "lucide-react": "^1.9.0", "react": "^19.2.5", - "react-datepicker": "^9.1.0", + "react-day-picker": "^9.14.0", "react-dom": "^19.2.5", "react-hook-form": "^7.73.1", "react-leaflet": "^5.0.0", @@ -36,7 +35,6 @@ "@types/leaflet": "^1.9.21", "@types/node": "^24.12.2", "@types/react": "^19.2.14", - "@types/react-datepicker": "^6.2.0", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.1", "eslint": "^10.2.1", @@ -48,50 +46,6 @@ "vite": "^8.0.10" } }, - "node_modules/@adobe/react-spectrum": { - "version": "3.47.0", - "resolved": "https://registry.npmjs.org/@adobe/react-spectrum/-/react-spectrum-3.47.0.tgz", - "integrity": "sha512-EDQuMzz0kUeiMUUlxoeLFQyyxOXaAC7qlBw2PYOUfFLYd87xcV7VVV0JxiYx8zGk1IIY3UgQHgXrS1fv7CgezQ==", - "license": "Apache-2.0", - "dependencies": { - "@internationalized/date": "^3.12.1", - "@react-types/shared": "^3.34.0", - "@spectrum-icons/ui": "^3.7.0", - "@spectrum-icons/workflow": "^4.3.0", - "@swc/helpers": "^0.5.0", - "client-only": "^0.0.1", - "clsx": "^2.0.0", - "react-aria": "3.48.0", - "react-aria-components": "1.17.0", - "react-stately": "3.46.0", - "react-transition-group": "^4.4.5", - "use-sync-external-store": "^1.6.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@adobe/react-spectrum-ui": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@adobe/react-spectrum-ui/-/react-spectrum-ui-1.2.1.tgz", - "integrity": "sha512-wcrbEE2O/9WnEn6avBnaVRRx88S5PLFsPLr4wffzlbMfXeQsy+RMQwaJd3cbzrn18/j04Isit7f7Emfn0dhrJA==", - "license": "Apache-2.0", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/@adobe/react-spectrum-workflow": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@adobe/react-spectrum-workflow/-/react-spectrum-workflow-2.3.5.tgz", - "integrity": "sha512-b53VIPwPWKb/T5gzE3qs+QlGP5gVrw/LnWV3xMksDU+CRl3rzOKUwxIGiZO8ICyYh1WiyqY4myGlPU/nAynBUg==", - "license": "Apache-2.0", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0" - } - }, "node_modules/@apollo/client": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@apollo/client/-/client-4.1.9.tgz", @@ -321,15 +275,6 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/runtime": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", - "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/template": { "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", @@ -378,6 +323,12 @@ "node": ">=6.9.0" } }, + "node_modules/@date-fns/tz": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@date-fns/tz/-/tz-1.4.1.tgz", + "integrity": "sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA==", + "license": "MIT" + }, "node_modules/@emnapi/core": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", @@ -540,110 +491,6 @@ "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@floating-ui/core": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz", - "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==", - "license": "MIT", - "dependencies": { - "@floating-ui/utils": "^0.2.11" - } - }, - "node_modules/@floating-ui/dom": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz", - "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==", - "license": "MIT", - "dependencies": { - "@floating-ui/core": "^1.7.5", - "@floating-ui/utils": "^0.2.11" - } - }, - "node_modules/@floating-ui/react": { - "version": "0.27.19", - "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.27.19.tgz", - "integrity": "sha512-31B8h5mm8YxotlE7/AU/PhNAl8eWxAmjL/v2QOxroDNkTFLk3Uu82u63N3b6TXa4EGJeeZLVcd/9AlNlVqzeog==", - "license": "MIT", - "dependencies": { - "@floating-ui/react-dom": "^2.1.8", - "@floating-ui/utils": "^0.2.11", - "tabbable": "^6.0.0" - }, - "peerDependencies": { - "react": ">=17.0.0", - "react-dom": ">=17.0.0" - } - }, - "node_modules/@floating-ui/react-dom": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.8.tgz", - "integrity": "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==", - "license": "MIT", - "dependencies": { - "@floating-ui/dom": "^1.7.6" - }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, - "node_modules/@floating-ui/utils": { - "version": "0.2.11", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz", - "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", - "license": "MIT" - }, - "node_modules/@formatjs/ecma402-abstract": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.6.tgz", - "integrity": "sha512-HJnTFeRM2kVFVr5gr5kH1XP6K0JcJtE7Lzvtr3FS/so5f1kpsqqqxy5JF+FRaO6H2qmcMfAUIox7AJteieRtVw==", - "license": "MIT", - "dependencies": { - "@formatjs/fast-memoize": "2.2.7", - "@formatjs/intl-localematcher": "0.6.2", - "decimal.js": "^10.4.3", - "tslib": "^2.8.0" - } - }, - "node_modules/@formatjs/fast-memoize": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.7.tgz", - "integrity": "sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==", - "license": "MIT", - "dependencies": { - "tslib": "^2.8.0" - } - }, - "node_modules/@formatjs/icu-messageformat-parser": { - "version": "2.11.4", - "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.4.tgz", - "integrity": "sha512-7kR78cRrPNB4fjGFZg3Rmj5aah8rQj9KPzuLsmcSn4ipLXQvC04keycTI1F7kJYDwIXtT2+7IDEto842CfZBtw==", - "license": "MIT", - "dependencies": { - "@formatjs/ecma402-abstract": "2.3.6", - "@formatjs/icu-skeleton-parser": "1.8.16", - "tslib": "^2.8.0" - } - }, - "node_modules/@formatjs/icu-skeleton-parser": { - "version": "1.8.16", - "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.16.tgz", - "integrity": "sha512-H13E9Xl+PxBd8D5/6TVUluSpxGNvFSlN/b3coUp0e0JpuWXXnQDiavIpY3NnvSp4xhEMoXyyBvVfdFX8jglOHQ==", - "license": "MIT", - "dependencies": { - "@formatjs/ecma402-abstract": "2.3.6", - "tslib": "^2.8.0" - } - }, - "node_modules/@formatjs/intl-localematcher": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.6.2.tgz", - "integrity": "sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA==", - "license": "MIT", - "dependencies": { - "tslib": "^2.8.0" - } - }, "node_modules/@graphql-typed-document-node/core": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", @@ -653,44 +500,6 @@ "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, - "node_modules/@heroui/react": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@heroui/react/-/react-3.0.3.tgz", - "integrity": "sha512-UJPOxg3IbS5KtI2GLP7S56KrSBn/r4xQyntCVpnPzgoT7JuciB4dq7IhckuDNHcrdIPO45JrKHGJz2hiYxaRGg==", - "license": "MIT", - "dependencies": { - "@heroui/styles": "3.0.3", - "@radix-ui/react-avatar": "1.1.11", - "@react-aria/i18n": "3.13.0", - "@react-aria/ssr": "3.10.0", - "@react-aria/utils": "3.34.0", - "@react-stately/utils": "3.12.0", - "@react-types/color": "3.2.0", - "@react-types/shared": "3.34.0", - "input-otp": "1.4.2", - "react-aria-components": "1.17.0", - "tailwind-merge": "3.4.0", - "tailwind-variants": "3.2.2" - }, - "peerDependencies": { - "react": ">=19.0.0", - "react-dom": ">=19.0.0", - "tailwindcss": ">=4.0.0" - } - }, - "node_modules/@heroui/styles": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@heroui/styles/-/styles-3.0.3.tgz", - "integrity": "sha512-DtN5QWfLVxy6DSDNgtUuHVU/h4G+dfoDHHxGVZRW6plCEh1Rc5Yc7YW7b8JBiYCknSRx/fqLXpas3Fe5Q5XJSw==", - "license": "MIT", - "dependencies": { - "tailwind-variants": "3.2.2", - "tw-animate-css": "1.4.0" - }, - "peerDependencies": { - "tailwindcss": ">=4.0.0" - } - }, "node_modules/@hookform/resolvers": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.2.2.tgz", @@ -778,34 +587,6 @@ "@swc/helpers": "^0.5.0" } }, - "node_modules/@internationalized/message": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@internationalized/message/-/message-3.1.9.tgz", - "integrity": "sha512-x03MSVTaB/4JHtW1VAYaY/2cCuBrHbWM6ZvlgpKdnSdW28tZbqpR673RJrVJyXWRw1bpgYN89Tz7ohX5tgNgPA==", - "license": "Apache-2.0", - "dependencies": { - "@swc/helpers": "^0.5.0", - "intl-messageformat": "^10.1.0" - } - }, - "node_modules/@internationalized/number": { - "version": "3.6.6", - "resolved": "https://registry.npmjs.org/@internationalized/number/-/number-3.6.6.tgz", - "integrity": "sha512-iFgmQaXHE0vytNfpLZWOC2mEJCBRzcUxt53Xf/yCXG93lRvqas237i3r7X4RKMwO3txiyZD4mQjKAByFv6UGSQ==", - "license": "Apache-2.0", - "dependencies": { - "@swc/helpers": "^0.5.0" - } - }, - "node_modules/@internationalized/string": { - "version": "3.2.8", - "resolved": "https://registry.npmjs.org/@internationalized/string/-/string-3.2.8.tgz", - "integrity": "sha512-NdbMQUSfXLYIQol5VyMtinm9pZDciiMfN7RtmSuSB78io1hqwJ0naYfxyW6vgxWBkzWymQa/3uLDlbfmshtCaA==", - "license": "Apache-2.0", - "dependencies": { - "@swc/helpers": "^0.5.0" - } - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -1236,89 +1017,6 @@ } } }, - "node_modules/@radix-ui/react-avatar": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.11.tgz", - "integrity": "sha512-0Qk603AHGV28BOBO34p7IgD5m+V5Sg/YovfayABkoDDBM5d3NCx0Mp4gGrjzLGes1jV5eNOE1r3itqOR33VC6Q==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-context": "1.1.3", - "@radix-ui/react-primitive": "2.1.4", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-is-hydrated": "0.1.0", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-avatar/node_modules/@radix-ui/react-context": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.3.tgz", - "integrity": "sha512-ieIFACdMpYfMEjF0rEf5KLvfVyIkOz6PDGyNnP+u+4xQ6jny3VCgA4OgXOwNx2aUkxn8zx9fiVcM8CfFYv9Lxw==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-avatar/node_modules/@radix-ui/react-primitive": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz", - "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-slot": "1.2.4" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-avatar/node_modules/@radix-ui/react-slot": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz", - "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-collapsible": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.12.tgz", @@ -1503,21 +1201,6 @@ } } }, - "node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", - "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-use-controllable-state": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", @@ -1555,24 +1238,6 @@ } } }, - "node_modules/@radix-ui/react-use-is-hydrated": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.0.tgz", - "integrity": "sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==", - "license": "MIT", - "dependencies": { - "use-sync-external-store": "^1.5.0" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-use-layout-effect": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", @@ -1588,69 +1253,6 @@ } } }, - "node_modules/@react-aria/color": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@react-aria/color/-/color-3.2.0.tgz", - "integrity": "sha512-Qw1TySxXnGlE4L7kzsi8v86U1yFs9FtonqsbySFzLPzsMV1Oar+rtkYHI5vwNSyNNF6TBJJikJNocS9Fi8xXwA==", - "license": "Apache-2.0", - "dependencies": { - "@swc/helpers": "^0.5.0", - "react-aria": "3.48.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@react-aria/i18n": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@react-aria/i18n/-/i18n-3.13.0.tgz", - "integrity": "sha512-APjw4EwmvlnIyDxixSWfjHvOFFkW2rVTyKZ4l9FV0v7hOerh+FWLE6mF1XnnX3pgz3yARkKWwhSR9xYcRK6tpg==", - "license": "Apache-2.0", - "dependencies": { - "@internationalized/date": "^3.12.1", - "@internationalized/message": "^3.1.9", - "@internationalized/string": "^3.2.8", - "@swc/helpers": "^0.5.0", - "react-aria": "3.48.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@react-aria/ssr": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.10.0.tgz", - "integrity": "sha512-mnelvACtfNWWKFCT1YHebxJRmfBmmANGwHQhCFPByMVTx1L8RumcaLxChYkE87g2KPuP5xX2il/oRn1DytW+qQ==", - "license": "Apache-2.0", - "dependencies": { - "@swc/helpers": "^0.5.0", - "react-aria": "3.48.0" - }, - "engines": { - "node": ">= 12" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@react-aria/utils": { - "version": "3.34.0", - "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.34.0.tgz", - "integrity": "sha512-ZM1ZXIqpwGTJjjL6o3JhlZkEaBpQdxuOCqLEvwEwooaj5GsYI3E9UfOl5vy3UW6bYiEEWl9pNBntrb9CR9kItQ==", - "license": "Apache-2.0", - "dependencies": { - "@swc/helpers": "^0.5.0", - "react-aria": "3.48.0", - "react-stately": "3.46.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, "node_modules/@react-leaflet/core": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-3.0.0.tgz", @@ -1662,89 +1264,6 @@ "react-dom": "^19.0.0" } }, - "node_modules/@react-spectrum/color": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@react-spectrum/color/-/color-3.2.0.tgz", - "integrity": "sha512-Xg/U8+l1CQdvPRF4Zrv7AvtqsjuYUNkMxJMG0cIug9RKtIfEoyh7VR4Xg3FNd4Y/AwKXNJZZN4l94qz4WlK23Q==", - "license": "Apache-2.0", - "dependencies": { - "@adobe/react-spectrum": "3.47.0", - "@swc/helpers": "^0.5.0", - "react-stately": "3.46.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@react-spectrum/provider": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/@react-spectrum/provider/-/provider-3.11.0.tgz", - "integrity": "sha512-W2Gxbj8AcG5OR2K5Ua3K8qQqxdsiytEiz+2rhr6oQyBM8VafEgDcNPYSOTtfjrQM3snl2Uhp8LzwN0jwQe/6nQ==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@adobe/react-spectrum": "3.47.0", - "@swc/helpers": "^0.5.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@react-stately/color": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@react-stately/color/-/color-3.10.0.tgz", - "integrity": "sha512-P4tlvOYFA8hl/NXiMyPxfM+7rXV01hnwlvGCwbZqUK1aRv0Ry0yGCj2AbSzhYHx7i4J4+CVUJUYozNLzhm+6Sw==", - "license": "Apache-2.0", - "dependencies": { - "@swc/helpers": "^0.5.0", - "react-stately": "3.46.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@react-stately/utils": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.12.0.tgz", - "integrity": "sha512-7q+iHz9cENvro1dVKgdTxNh1i1mtWuLUI6UHp10TAgpxM9DyRDvmuN35zLXYCmMDgx3WLY2xkwqoez8xd+CdxQ==", - "license": "Apache-2.0", - "dependencies": { - "@swc/helpers": "^0.5.0", - "react-stately": "3.46.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@react-types/color": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@react-types/color/-/color-3.2.0.tgz", - "integrity": "sha512-beV3vz80nzZ1EuYUM7296Kyi3AHcMrbQw0qub/9yzHWVTKKc5sy/e4dCMKcWL/ArkeAyc7jDOiui190RQ4l0Fw==", - "license": "Apache-2.0", - "dependencies": { - "@react-aria/color": "^3.2.0", - "@react-spectrum/color": "^3.2.0", - "@react-stately/color": "^3.10.0" - }, - "peerDependencies": { - "@react-spectrum/provider": "^3.0.0", - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@react-types/shared": { - "version": "3.34.0", - "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.34.0.tgz", - "integrity": "sha512-gp6xo/s2lX54AlTjOiqwDnxA7UW79BNvI9dB9pr3LZTzRKCd1ZA+ZbgKw/ReIiWuvvVw/8QFJpnqeeFyLocMcQ==", - "license": "Apache-2.0", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, "node_modules/@rolldown/binding-android-arm64": { "version": "1.0.0-rc.17", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.17.tgz", @@ -2027,37 +1546,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@spectrum-icons/ui": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@spectrum-icons/ui/-/ui-3.7.0.tgz", - "integrity": "sha512-86iQSDfJb3Ama1WSJ/mEiFy4DJT7e/v4pSmEuX4aKKMzbNYft+O40N18S2POUnmblrb7MQneLC/pgIp1SDBwEQ==", - "license": "Apache-2.0", - "dependencies": { - "@adobe/react-spectrum-ui": "1.2.1", - "@babel/runtime": "^7.24.4", - "@swc/helpers": "^0.5.0" - }, - "peerDependencies": { - "@adobe/react-spectrum": "^3.47.0", - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@spectrum-icons/workflow": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@spectrum-icons/workflow/-/workflow-4.3.0.tgz", - "integrity": "sha512-ILuhgWh9jMXaEVMRuOYgTAjMc22cKyvCtUDyZmc8OEMfOYuejj+Gcp5t6DhaCfE0M9rORtVxCrRgsO2WyEgfUw==", - "license": "Apache-2.0", - "dependencies": { - "@adobe/react-spectrum-workflow": "2.3.5", - "@swc/helpers": "^0.5.0" - }, - "peerDependencies": { - "@adobe/react-spectrum": "^3.47.0", - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, "node_modules/@standard-schema/utils": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", @@ -2073,6 +1561,15 @@ "tslib": "^2.8.0" } }, + "node_modules/@tabby_ai/hijri-converter": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@tabby_ai/hijri-converter/-/hijri-converter-1.0.5.tgz", + "integrity": "sha512-r5bClKrcIusDoo049dSL8CawnHR6mRdDwhlQuIgZRNty68q0x8k3Lf1BtPAMxRf/GgnHBnIO4ujd3+GQdLWzxQ==", + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/@tanstack/react-table": { "version": "8.21.3", "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.21.3.tgz", @@ -2175,45 +1672,6 @@ "csstype": "^3.2.2" } }, - "node_modules/@types/react-datepicker": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@types/react-datepicker/-/react-datepicker-6.2.0.tgz", - "integrity": "sha512-+JtO4Fm97WLkJTH8j8/v3Ldh7JCNRwjMYjRaKh4KHH0M3jJoXtwiD3JBCsdlg3tsFIw9eQSqyAPeVDN2H2oM9Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@floating-ui/react": "^0.26.2", - "@types/react": "*", - "date-fns": "^3.3.1" - } - }, - "node_modules/@types/react-datepicker/node_modules/@floating-ui/react": { - "version": "0.26.28", - "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.28.tgz", - "integrity": "sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@floating-ui/react-dom": "^2.1.2", - "@floating-ui/utils": "^0.2.8", - "tabbable": "^6.0.0" - }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, - "node_modules/@types/react-datepicker/node_modules/date-fns": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", - "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/kossnocorp" - } - }, "node_modules/@types/react-dom": { "version": "19.2.3", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", @@ -2581,18 +2039,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/aria-hidden": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", - "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/balanced-match": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", @@ -2699,21 +2145,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/client-only": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", - "license": "MIT" - }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -2753,6 +2184,7 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "devOptional": true, "license": "MIT" }, "node_modules/date-fns": { @@ -2765,6 +2197,12 @@ "url": "https://github.com/sponsors/kossnocorp" } }, + "node_modules/date-fns-jalali": { + "version": "4.1.0-0", + "resolved": "https://registry.npmjs.org/date-fns-jalali/-/date-fns-jalali-4.1.0-0.tgz", + "integrity": "sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg==", + "license": "MIT" + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -2783,12 +2221,6 @@ } } }, - "node_modules/decimal.js": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", - "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", - "license": "MIT" - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -2806,16 +2238,6 @@ "node": ">=8" } }, - "node_modules/dom-helpers": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" - } - }, "node_modules/echarts": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/echarts/-/echarts-6.0.0.tgz", @@ -3292,28 +2714,6 @@ "node": ">=0.8.19" } }, - "node_modules/input-otp": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/input-otp/-/input-otp-1.4.2.tgz", - "integrity": "sha512-l3jWwYNvrEa6NTCt7BECfCm48GvwuZzkoeG3gBL2w4CHeOXW3eKFmf9UNYkNfYc3mxMrthMnxjIE07MT0zLBQA==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" - } - }, - "node_modules/intl-messageformat": { - "version": "10.7.18", - "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.18.tgz", - "integrity": "sha512-m3Ofv/X/tV8Y3tHXLohcuVuhWKo7BBq62cqY15etqmLxg2DZ34AGGgQDeR+SCta2+zICb1NX83af0GJmbQ1++g==", - "license": "BSD-3-Clause", - "dependencies": { - "@formatjs/ecma402-abstract": "2.3.6", - "@formatjs/fast-memoize": "2.2.7", - "@formatjs/icu-messageformat-parser": "2.11.4", - "tslib": "^2.8.0" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -3348,6 +2748,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, "license": "MIT" }, "node_modules/jsesc": { @@ -3716,18 +3117,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "license": "MIT", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -3825,15 +3214,6 @@ "dev": true, "license": "MIT" }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/optimism": { "version": "0.18.1", "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.18.1.tgz", @@ -3975,17 +3355,6 @@ "node": ">= 0.8.0" } }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -4005,64 +3374,26 @@ "node": ">=0.10.0" } }, - "node_modules/react-aria": { - "version": "3.48.0", - "resolved": "https://registry.npmjs.org/react-aria/-/react-aria-3.48.0.tgz", - "integrity": "sha512-jQjd4rBEIMqecBaAKYJbVGK6EqIHLa5znVQ7jwFyK5vCyljoj6KhgtiahmcIPsG5vG5vEDLw+ba+bEWn6A2P4w==", - "license": "Apache-2.0", - "dependencies": { - "@internationalized/date": "^3.12.1", - "@internationalized/number": "^3.6.6", - "@internationalized/string": "^3.2.8", - "@react-types/shared": "^3.34.0", - "@swc/helpers": "^0.5.0", - "aria-hidden": "^1.2.3", - "clsx": "^2.0.0", - "react-stately": "3.46.0", - "use-sync-external-store": "^1.6.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/react-aria-components": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/react-aria-components/-/react-aria-components-1.17.0.tgz", - "integrity": "sha512-0EyisMgvsFJ2aML3crDYv2tW5vT2Ryf8PGzY/g63JjDdCbLshlwazhS8JNtPF1vkTkungJJ6sVJbKyX+YKSoFA==", - "license": "Apache-2.0", - "dependencies": { - "@internationalized/date": "^3.12.1", - "@react-types/shared": "^3.34.0", - "@swc/helpers": "^0.5.0", - "client-only": "^0.0.1", - "react-aria": "3.48.0", - "react-stately": "3.46.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/react-datepicker": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-9.1.0.tgz", - "integrity": "sha512-lOp+m5bc+ttgtB5MHEjwiVu4nlp4CvJLS/PG1OiOe5pmg9kV73pEqO8H0Geqvg2E8gjqTaL9eRhSe+ZpeKP3nA==", + "node_modules/react-day-picker": { + "version": "9.14.0", + "resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-9.14.0.tgz", + "integrity": "sha512-tBaoDWjPwe0M5pGrum4H0SR6Lyk+BO9oHnp9JbKpGKW2mlraNPgP9BMfsg5pWpwrssARmeqk7YBl2oXutZTaHA==", "license": "MIT", "dependencies": { - "@floating-ui/react": "^0.27.15", - "clsx": "^2.1.1", - "date-fns": "^4.1.0" + "@date-fns/tz": "^1.4.1", + "@tabby_ai/hijri-converter": "1.0.5", + "date-fns": "^4.1.0", + "date-fns-jalali": "4.1.0-0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/gpbl" }, "peerDependencies": { - "date-fns-tz": "^3.0.0", - "react": "^16.9.0 || ^17 || ^18 || ^19 || ^19.0.0-rc", - "react-dom": "^16.9.0 || ^17 || ^18 || ^19 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "date-fns-tz": { - "optional": true - } + "react": ">=16.8.0" } }, "node_modules/react-dom": { @@ -4093,12 +3424,6 @@ "react": "^16.8.0 || ^17 || ^18 || ^19" } }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" - }, "node_modules/react-leaflet": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-5.0.0.tgz", @@ -4151,39 +3476,6 @@ "react-dom": ">=18" } }, - "node_modules/react-stately": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/react-stately/-/react-stately-3.46.0.tgz", - "integrity": "sha512-OdxhWvHgs2L4OJGIs7hnuTr5WjjMM6enhNEAMRqiekhF8+ITvA2LRwNftOZwcogaoCslGYq5S2VQTQwnm0GbCA==", - "license": "Apache-2.0", - "dependencies": { - "@internationalized/date": "^3.12.1", - "@internationalized/number": "^3.6.6", - "@internationalized/string": "^3.2.8", - "@react-types/shared": "^3.34.0", - "@swc/helpers": "^0.5.0", - "use-sync-external-store": "^1.6.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", - "license": "BSD-3-Clause", - "dependencies": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": ">=16.6.0", - "react-dom": ">=16.6.0" - } - }, "node_modules/readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", @@ -4328,48 +3620,6 @@ "node": ">=0.10.0" } }, - "node_modules/tabbable": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.4.0.tgz", - "integrity": "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==", - "license": "MIT" - }, - "node_modules/tailwind-merge": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz", - "integrity": "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/dcastil" - } - }, - "node_modules/tailwind-variants": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/tailwind-variants/-/tailwind-variants-3.2.2.tgz", - "integrity": "sha512-Mi4kHeMTLvKlM98XPnK+7HoBPmf4gygdFmqQPaDivc3DpYS6aIY6KiG/PgThrGvii5YZJqRsPz0aPyhoFzmZgg==", - "license": "MIT", - "engines": { - "node": ">=16.x", - "pnpm": ">=7.x" - }, - "peerDependencies": { - "tailwind-merge": ">=3.0.0", - "tailwindcss": "*" - }, - "peerDependenciesMeta": { - "tailwind-merge": { - "optional": true - } - } - }, - "node_modules/tailwindcss": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.4.tgz", - "integrity": "sha512-HhKppgO81FQof5m6TEnuBWCZGgfRAWbaeOaGT00KOy/Pf/j6oUihdvBpA7ltCeAvZpFhW3j0PTclkxsd4IXYDA==", - "license": "MIT", - "peer": true - }, "node_modules/tinyglobby": { "version": "0.2.16", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", @@ -4406,15 +3656,6 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, - "node_modules/tw-animate-css": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.4.0.tgz", - "integrity": "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Wombosvideo" - } - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -4514,15 +3755,6 @@ "punycode": "^2.1.0" } }, - "node_modules/use-sync-external-store": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", - "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, "node_modules/vite": { "version": "8.0.10", "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.10.tgz", diff --git a/mdm-front/package.json b/mdm-front/package.json index 9e56ca4..bea0de5 100644 --- a/mdm-front/package.json +++ b/mdm-front/package.json @@ -11,7 +11,6 @@ }, "dependencies": { "@apollo/client": "^4.1.9", - "@heroui/react": "^3.0.3", "@hookform/resolvers": "^5.2.2", "@internationalized/date": "^3.12.1", "@radix-ui/react-accordion": "^1.2.12", @@ -24,7 +23,7 @@ "leaflet": "^1.9.4", "lucide-react": "^1.9.0", "react": "^19.2.5", - "react-datepicker": "^9.1.0", + "react-day-picker": "^9.14.0", "react-dom": "^19.2.5", "react-hook-form": "^7.73.1", "react-leaflet": "^5.0.0", @@ -38,7 +37,6 @@ "@types/leaflet": "^1.9.21", "@types/node": "^24.12.2", "@types/react": "^19.2.14", - "@types/react-datepicker": "^6.2.0", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.1", "eslint": "^10.2.1", diff --git a/mdm-front/public/devices/армафон3.3+.webp b/mdm-front/public/devices/армафон3.3+.webp new file mode 100644 index 0000000..c526803 Binary files /dev/null and b/mdm-front/public/devices/армафон3.3+.webp differ diff --git a/mdm-front/src/app/router/router.tsx b/mdm-front/src/app/router/router.tsx index 7d60087..5016963 100644 --- a/mdm-front/src/app/router/router.tsx +++ b/mdm-front/src/app/router/router.tsx @@ -3,7 +3,7 @@ import { createBrowserRouter, Navigate } from 'react-router-dom' import { AppLayout } from '../layouts/AppLayout' import { DevicesPage } from '../../pages/DevicesPage/DevicesPage' -//import { DevicePage } from '../../pages/DevicePage/DevicePage' +import { DevicePage } from '../../pages/DevicePage/DevicePage' import { MapPage } from '../../pages/MapPage/MapPage' import { EmployeesPage } from '../../pages/EmployeesPage/EmployeesPage' @@ -19,11 +19,11 @@ export const router = createBrowserRouter([ { path: 'devices', element: , - },/* + }, { path: 'devices/:deviceId', element: , - }, */ + }, { path: 'map', element: , diff --git a/mdm-front/src/assets/icons/Block.tsx b/mdm-front/src/assets/icons/Block.tsx new file mode 100644 index 0000000..6391b7a --- /dev/null +++ b/mdm-front/src/assets/icons/Block.tsx @@ -0,0 +1,7 @@ +export function BlockIcon() { + return ( + + + + ) +} \ No newline at end of file diff --git a/mdm-front/src/assets/icons/Bluetooth.tsx b/mdm-front/src/assets/icons/Bluetooth.tsx new file mode 100644 index 0000000..ae023e0 --- /dev/null +++ b/mdm-front/src/assets/icons/Bluetooth.tsx @@ -0,0 +1,7 @@ +export function BluetoothIcon() { + return ( + + + + ) +} \ No newline at end of file diff --git a/mdm-front/src/assets/icons/Camera.tsx b/mdm-front/src/assets/icons/Camera.tsx new file mode 100644 index 0000000..0a8b8b5 --- /dev/null +++ b/mdm-front/src/assets/icons/Camera.tsx @@ -0,0 +1,7 @@ +export function CameraIcon() { + return ( + + + + ) +} \ No newline at end of file diff --git a/mdm-front/src/assets/icons/Gps.tsx b/mdm-front/src/assets/icons/Gps.tsx new file mode 100644 index 0000000..af5b0fd --- /dev/null +++ b/mdm-front/src/assets/icons/Gps.tsx @@ -0,0 +1,9 @@ +export function GpsIcon() { + return ( + + + + + + ) +} \ No newline at end of file diff --git a/mdm-front/src/assets/icons/Kiosk.tsx b/mdm-front/src/assets/icons/Kiosk.tsx new file mode 100644 index 0000000..69ce92b --- /dev/null +++ b/mdm-front/src/assets/icons/Kiosk.tsx @@ -0,0 +1,7 @@ +export function KioskIcon() { + return ( + + + + ) +} \ No newline at end of file diff --git a/mdm-front/src/assets/icons/Message.tsx b/mdm-front/src/assets/icons/Message.tsx new file mode 100644 index 0000000..2ee397f --- /dev/null +++ b/mdm-front/src/assets/icons/Message.tsx @@ -0,0 +1,7 @@ +export function MessageIcon() { + return ( + + + + ) +} \ No newline at end of file diff --git a/mdm-front/src/assets/icons/Reboot.tsx b/mdm-front/src/assets/icons/Reboot.tsx new file mode 100644 index 0000000..58a06fb --- /dev/null +++ b/mdm-front/src/assets/icons/Reboot.tsx @@ -0,0 +1,7 @@ +export function RebootIcon() { + return ( + + + + ) +} \ No newline at end of file diff --git a/mdm-front/src/assets/icons/Sim.tsx b/mdm-front/src/assets/icons/Sim.tsx new file mode 100644 index 0000000..a731aa1 --- /dev/null +++ b/mdm-front/src/assets/icons/Sim.tsx @@ -0,0 +1,8 @@ +export function SimIcon() { + return ( + + + + + ) +} \ No newline at end of file diff --git a/mdm-front/src/assets/icons/Volume.tsx b/mdm-front/src/assets/icons/Volume.tsx new file mode 100644 index 0000000..e724e74 --- /dev/null +++ b/mdm-front/src/assets/icons/Volume.tsx @@ -0,0 +1,14 @@ +export function VolumeIcon() { + return ( + + + + + + + + + + + ) +} \ No newline at end of file diff --git a/mdm-front/src/assets/icons/Wifi.tsx b/mdm-front/src/assets/icons/Wifi.tsx new file mode 100644 index 0000000..04eee34 --- /dev/null +++ b/mdm-front/src/assets/icons/Wifi.tsx @@ -0,0 +1,7 @@ +export function WifiIcon() { + return ( + + + + ) +} \ No newline at end of file diff --git a/mdm-front/src/index.scss b/mdm-front/src/index.scss index 79da82c..00f60cb 100644 --- a/mdm-front/src/index.scss +++ b/mdm-front/src/index.scss @@ -2,7 +2,7 @@ @font-face { font-family: 'Montserrat'; - src: url('../assets/fonts/montserrat/Montserrat-VariableFont_wght.ttf') format('truetype'); + src: url('./assets/fonts/Montserrat-VariableFont_wght.ttf') format('truetype'); font-weight: 100 900; font-style: normal; font-display: swap; @@ -75,6 +75,12 @@ body { margin: 0; } +button{ + font-family: inherit; +} +a{ + text-decoration: none; +} h1, h2 { diff --git a/mdm-front/src/main.tsx b/mdm-front/src/main.tsx index 86fbc7c..f896905 100644 --- a/mdm-front/src/main.tsx +++ b/mdm-front/src/main.tsx @@ -3,7 +3,8 @@ import { createRoot } from 'react-dom/client' import './index.scss' import { RouterProvider } from 'react-router-dom' import { router } from './app/router/router.tsx' -import 'react-datepicker/dist/react-datepicker.css' +import 'react-day-picker/style.css' +import 'leaflet/dist/leaflet.css' createRoot(document.getElementById('root')!).render( diff --git a/mdm-front/src/pages/DevicePage/DevicePage.scss b/mdm-front/src/pages/DevicePage/DevicePage.scss new file mode 100644 index 0000000..0600307 --- /dev/null +++ b/mdm-front/src/pages/DevicePage/DevicePage.scss @@ -0,0 +1,581 @@ +@use '../../shared/styles/variables' as *; + +.device-page { + display: flex; + flex-direction: column; + gap: 18px; +} + +.device-breadcrumbs { + display: flex; + align-items: center; + gap: 6px; + + color: $gray50; + font-size: 16px; + font-weight: 500; + padding: 11px 0; + + button, a { + padding: 0; + border: none; + background: transparent; + color: #738098; + font: inherit; + cursor: pointer; + transition: .2s ease; + + &:hover { + color: $blue; + } + } + + span:last-child { + color: #30394b; + } +} + +.device-page__grid { + display: grid; + grid-template-columns: 420px 260px minmax(360px, 1fr); + gap: 20px; + align-items: start; +} + +.device-card { + border-radius: 20px; + background: #ffffff; + padding: 20px; +} + +.device-card--main { + grid-column: span 2; +} + +.device-main { + display: flex; + flex-direction: row; + gap: 20px; +} + +.device-main__image { + height: calc(50% - 40px); + width: 30%; + padding: 20px; + border-radius: 14px; + background: #f1f4f8; + + display: flex; + align-items: center; + justify-content: center; + + background-color: $color-bg; + + img { + max-width: 100%; + max-height: 100%; + height: 100%; + width: auto; + object-fit: contain; + } +} + +.device-main__info { + display: flex; + flex-direction: column; + flex: 1; + text-align: left; + + h2 { + margin: 0 0 6px; + color: black; + font-size: 30px; + font-weight: 600; + line-height: 1.1; + } + + p { + margin: 0; + color: $gray50; + font-size: 17px; + font-weight: 500; + line-height: 1.3; + } +} + +.device-main__divider { + height: 1px; + margin: 12px 0 14px; + background: $gray20; +} + +.device-main__statuses { + display: flex; + flex-direction: column; + gap: 4px; +} + +.device-status { + display: inline-flex; + align-items: center; + gap: 9px; + + color: black; + font-size: 18px; + font-weight: 500; + + svg { + padding: 4px; + width: 24px; + height: 24px; + border-radius: 8px; + background: $color-bg; + } + + &.is-success svg { + color: $green; + background: #eaf8ee; + } + + &.is-danger svg { + color: $red; + background: #ffe8e8; + } + + &.is-muted svg { + color: $gray50; + } +} + +.device-main__bottom { + margin-top: 18px; +} + +.device-registered { + width: 100%; + padding: 0; + + display: inline-flex; + align-items: center; + gap: 10px; + + color: black; + font-size: 18px; + font-weight: 500; + + .device-registered-info { + display: flex; + flex: 1; + padding: 10px 0; + height: inherit; + justify-content: space-between; + border-bottom: 1px solid $gray20; + } + + svg { + padding: 4px; + width: 24px; + height: 24px; + border-radius: 8px; + color: $gray50; + background: $color-bg; + } + + b { + color: $gray50; + font-weight: 500; + } +} + +.device-card__actions { + margin-top: 20px; + + display: flex; + align-items: center; + justify-content: space-between; + gap: 20px; +} + +.device-history-btn { + + padding: 12px; + + border: none; + border-radius: 12px; + background: #dfe6ff; + + color: $blue; + font-size: 18px; + font-weight: 500; + cursor: pointer; + transition: .2s ease; + line-height: 1; + + &:hover { + background-color: $blue; + color: white; + } +} + +.device-delete-btn { + padding: 12px; + + border: none; + border-radius: 12px; + background: #ffdede; + + display: inline-flex; + align-items: center; + justify-content: center; + color: $red; + cursor: pointer; + transition: .2s ease; + + svg { + width: 18px; + height: 18px; + } + + &:hover { + background-color: $red; + color: white; + } +} + +.device-card__header { + margin-bottom: 14px; + + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + + p { + margin: 0; + color: #738098; + font-size: 15px; + font-weight: 500; + } +} + +.device-card__title { + display: flex; + align-items: center; + gap: 10px; + + h3 { + margin: 0; + color: #30394b; + font-size: 18px; + font-weight: 600; + } + + svg { + padding: 5px; + width: 30px; + height: 30px; + border-radius: 9px; + background: #eef1f6; + color: #30394b; + } +} + +.device-map { + display: flex; + flex-direction: column; + grid-column: span 1; + min-height: 272px; + height: calc(100% - 40px); +} + +.device-actions { + display: flex; + flex-direction: column; + justify-content: space-between; + height: calc(100% - 40px); + + h3 { + line-height: 1; + margin: 0 0 12px; + padding-bottom: 12px; + border-bottom: 1px solid #e3e8f0; + text-align: left; + color: #30394b; + font-size: 18px; + font-weight: 600; + } + + .actions-button-container { + display: flex; + flex-direction: column; + } + + button { + width: 100%; + padding: 12px; + border: none; + border-radius: 12px; + background: $color-bg; + + display: flex; + align-items: center; + gap: 8px; + + color: $gray50; + font-size: 18px; + font-weight: 550; + cursor: pointer; + transition: .2s ease; + + svg { + height: 20px; + width: 20px; + } + + &:hover { + background-color: $gray20; + } + + &+button { + margin-top: 8px; + } + } +} + +.device-permissions { + padding: 10px 20px; + display: flex; + flex: 1; + flex-direction: column; + justify-content: center; + height: calc(100% - 20px); +} + +.device-permission { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + + color: #30394b; + font-size: 18px; + font-weight: 500; + + &:nth-child(1) { + .device-permission__label { + border-top: none; + } + } + + svg { + padding: 4px; + width: 24px; + height: 24px; + border-radius: 8px; + color: $blue; + background: #eef1f6; + } +} + +.device-permission__label { + display: flex; + align-items: center; + justify-content: space-between; + flex: 1; + gap: 10px; + padding: 10px 0; + border-top: 1px solid $gray20; +} + +.device-permission__switch { + width: 36px; + height: 20px; + padding: 2px; + border-radius: 999px; + background: #d6dce8; + + display: inline-flex; + align-items: center; + + span { + width: 16px; + height: 16px; + border-radius: 50%; + background: #ffffff; + transition: transform 0.2s ease; + } + + &.is-enabled { + background: $blue; + + span { + transform: translateX(16px); + } + } +} + +.device-card-stats { + display: flex; + flex-direction: column; + gap: 20px; +} + +.device-battery { + h3 { + text-align: left; + line-height: 1; + margin: 0 0 12px; + padding-bottom: 12px; + border-bottom: 1px solid $gray20; + + color: #30394b; + font-size: 18px; + font-weight: 600; + } + + .device-stats-card { + display: flex; + flex-direction: row; + align-items: center; + gap: 8px; + + &:nth-child(2) { + .device-stats-text { + border-top: 1px solid $gray20; + border-bottom: 1px solid $gray20; + padding: 8px 0; + } + } + + svg{ + width: 24px; + height: 24px; + padding: 4px; + border-radius: 8px; + } + } + + .device-stats-text { + display: flex; + flex: 1; + flex-direction: row; + align-items: center; + justify-content: space-between; + } +} + +.device-battery__content { + display: grid; + grid-template-columns: 120px 1fr; + gap: 20px; + align-items: center; +} + +.device-battery__circle { + width: 96px; + height: 96px; + border-radius: 50%; + background: + radial-gradient(circle at center, #ffffff 58%, transparent 60%), + conic-gradient($blue 0 65%, #d6dce8 65% 100%); + + display: flex; + align-items: center; + justify-content: center; + + span { + color: #111827; + font-size: 24px; + font-weight: 600; + } +} + +.device-stats { + display: flex; + flex-direction: column; + gap: 10px; + + div { + display: grid; + grid-template-columns: 30px 1fr auto; + align-items: center; + gap: 10px; + + color: #30394b; + font-size: 16px; + font-weight: 500; + } + + svg { + padding: 5px; + width: 30px; + height: 30px; + border-radius: 9px; + color: #31b24a; + background: #eaf8ee; + } + + b { + color: #30394b; + font-weight: 600; + } +} + +.device-impacts { + display: flex; + flex-direction: row; + align-items: center; + gap: 10px; + + color: #30394b; + font-size: 18px; + font-weight: 500; + + .device-stats-text{ + display: flex; + flex: 1; + flex-direction: row; + align-items: center; + justify-content: space-between; + gap: 20px; + } + + svg { + padding: 4px; + width: 24px; + height: 24px; + border-radius: 9px; + color: $green; + background: #eaf8ee; + } + + b { + font-weight: 600; + } +} + +.device-page__empty { + min-height: 260px; + border-radius: 18px; + background: #ffffff; + + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 16px; + + h2 { + margin: 0; + } + + button { + height: 40px; + padding: 0 16px; + border: none; + border-radius: 12px; + background: $blue; + color: #ffffff; + cursor: pointer; + } +} \ No newline at end of file diff --git a/mdm-front/src/pages/DevicePage/DevicePage.tsx b/mdm-front/src/pages/DevicePage/DevicePage.tsx new file mode 100644 index 0000000..c44abee --- /dev/null +++ b/mdm-front/src/pages/DevicePage/DevicePage.tsx @@ -0,0 +1,60 @@ +import { useMemo } from 'react' +import { useNavigate, useParams } from 'react-router-dom' +import { Link } from 'react-router-dom' + +import devices from '../DevicesPage/devices.mock.json' +import type { Device } from './types' + +import { DeviceMainCard } from './components/DeviceMainCard/DeviceMainCard' +import { DeviceMapCard } from './components/DeviceMapCard/DeviceMapCard' +import { DeviceActionsCard } from './components/DeviceActionsCard/DeviceActionsCard' +import { DevicePermissionsCard } from './components/DevicePermissionsCard/DevicePermissionsCard' +import { DeviceStatsCards } from './components/DeviceStatsCards/DeviceStatsCards' + +import './DevicePage.scss' + +const typedDevices = devices as Device[] + +export function DevicePage() { + const { deviceId } = useParams() + const navigate = useNavigate() + + const device = useMemo(() => { + return typedDevices.find((item) => String(item.id) === deviceId) + }, [deviceId]) + + if (!device) { + return ( +
+
+

Устройство не найдено

+ + +
+
+ ) + } + + return ( +
+
+ + Все устройства + + + / + {device.factoryNumber} +
+ +
+ + + + + +
+
+ ) +} \ No newline at end of file diff --git a/mdm-front/src/pages/DevicePage/components/DeviceActionsCard/DeviceActionsCard.tsx b/mdm-front/src/pages/DevicePage/components/DeviceActionsCard/DeviceActionsCard.tsx new file mode 100644 index 0000000..ec316d1 --- /dev/null +++ b/mdm-front/src/pages/DevicePage/components/DeviceActionsCard/DeviceActionsCard.tsx @@ -0,0 +1,34 @@ +import { BlockIcon } from '../../../../assets/icons/Block' +import { KioskIcon } from '../../../../assets/icons/Kiosk' +import { RebootIcon } from '../../../../assets/icons/Reboot' +import { MessageIcon } from '../../../../assets/icons/Message' + +export function DeviceActionsCard() { + return ( +
+

Действия

+ +
+ + + + + + + +
+
+ ) +} \ No newline at end of file diff --git a/mdm-front/src/pages/DevicePage/components/DeviceMainCard/DeviceMainCard.tsx b/mdm-front/src/pages/DevicePage/components/DeviceMainCard/DeviceMainCard.tsx new file mode 100644 index 0000000..d535722 --- /dev/null +++ b/mdm-front/src/pages/DevicePage/components/DeviceMainCard/DeviceMainCard.tsx @@ -0,0 +1,74 @@ +import { ShieldCheck, Signal, Smartphone, Trash2 } from 'lucide-react' +import type { Device } from '../../types' +import { conditionText, connectionText, getStatusClass } from '../../types' + +type DeviceMainCardProps = { + device: Device +} + +export function DeviceMainCard({ device }: DeviceMainCardProps) { + return ( +
+
+
+ {device.image ? ( + {device.model + ) : ( + + )} +
+ +
+

{device.model ?? '-'}

+ +

{device.factoryNumber}

+

+ {device.imei} + {device.serialNumber ? ` · ${device.serialNumber}` : ''} +

+ +
+ +
+
+ + {conditionText[device.condition]} +
+ +
+ + {device.connectionText ?? connectionText[device.connection]} +
+ + {device.workTime && ( +
+ + В работе: {device.workTime} +
+ )} +
+
+
+ +
+
+ +
+ Зарегистрирован + {device.registeredAt ?? '10:00 20.04.2026'} +
+
+ +
+ + + +
+
+
+ ) +} \ No newline at end of file diff --git a/mdm-front/src/pages/DevicePage/components/DeviceMapCard/DeviceMapCard.scss b/mdm-front/src/pages/DevicePage/components/DeviceMapCard/DeviceMapCard.scss new file mode 100644 index 0000000..6224a48 --- /dev/null +++ b/mdm-front/src/pages/DevicePage/components/DeviceMapCard/DeviceMapCard.scss @@ -0,0 +1,87 @@ +@use '../../../../shared/styles/variables' as *; + + +.device-map__container { + display: flex; + flex: 1; + position: relative; + border-radius: 14px; + overflow: hidden; + background: #eef1f6; +} + +.device-map__leaflet { + width: 100%; + height: 100%; + z-index: 1; +} + +.device-map__coords { + position: absolute; + left: 10px; + bottom: 10px; + z-index: 2; + + min-height: 28px; + padding: 0 10px; + + border-radius: 999px; + background: rgba(255, 255, 255, 0.92); + + display: inline-flex; + align-items: center; + gap: 6px; + + color: #30394b; + font-size: 12px; + font-weight: 600; + + box-shadow: 0 8px 22px rgba(15, 23, 42, 0.12); + + svg { + color: $blue; + } +} + +.device-map-marker { + background: transparent; + border: none; +} + +.device-map-marker__pin { + width: 34px; + height: 34px; + border-radius: 50%; + background: rgba(3, 29, 154, 0.18); + + display: flex; + align-items: center; + justify-content: center; +} + +.device-map-marker__inner { + width: 16px; + height: 16px; + border-radius: 50%; + background: $blue; + border: 3px solid #ffffff; + box-shadow: 0 8px 18px rgba(3, 29, 154, 0.35); +} + +.device-map-popup { + display: flex; + flex-direction: column; + gap: 3px; + + b { + color: #151a24; + font-size: 13px; + font-weight: 700; + } + + span { + color: #738098; + font-size: 12px; + font-weight: 500; + } +} diff --git a/mdm-front/src/pages/DevicePage/components/DeviceMapCard/DeviceMapCard.tsx b/mdm-front/src/pages/DevicePage/components/DeviceMapCard/DeviceMapCard.tsx new file mode 100644 index 0000000..44bdb20 --- /dev/null +++ b/mdm-front/src/pages/DevicePage/components/DeviceMapCard/DeviceMapCard.tsx @@ -0,0 +1,135 @@ +import { useEffect, useMemo } from 'react' +import { + CircleMarker, + MapContainer, + Marker, + Polyline, + Popup, + TileLayer, + useMap, +} from 'react-leaflet' +import L from 'leaflet' +import { Map } from 'lucide-react' +import './DeviceMapCard.scss' + +import type { Device } from '../../types' + +type DeviceMapCardProps = { + device: Device +} + +const defaultLocation = { + lat: 54.7558, + lng: 87.1099, +} + +const deviceMarkerIcon = L.divIcon({ + className: 'device-map-marker', + html: ` +
+
+
+ `, + iconSize: [34, 34], + iconAnchor: [17, 17], + popupAnchor: [0, -18], +}) + +function MapResizeWatcher() { + const map = useMap() + + useEffect(() => { + const timeoutId = window.setTimeout(() => { + map.invalidateSize() + }, 100) + + return () => window.clearTimeout(timeoutId) + }, [map]) + + return null +} + +export function DeviceMapCard({ device }: DeviceMapCardProps) { + const location = device.location ?? defaultLocation + + const currentPosition = useMemo<[number, number]>(() => { + return [location.lat, location.lng] + }, [location.lat, location.lng]) + + const routePositions = useMemo<[number, number][]>(() => { + return device.route?.map((point) => [point.lat, point.lng]) ?? [] + }, [device.route]) + + return ( +
+
+
+ +

Устройство на карте

+
+ +

+ Последнее местоположение:{' '} + {device.lastLocationAt ?? '12:56 23.04.2026'} +

+
+ +
+ + + + + + {routePositions.length > 1 && ( + + )} + + {device.route?.map((point, index) => ( + + +
+ Точка маршрута + {point.time && {point.time}} +
+
+
+ ))} + + + +
+ {device.model ?? 'Устройство'} + {device.factoryNumber} + {device.imei} + {device.lastLocationAt ?? 'Время не указано'} +
+
+
+
+
+
+ ) +} \ No newline at end of file diff --git a/mdm-front/src/pages/DevicePage/components/DevicePermissionsCard/DevicePermissionsCard.tsx b/mdm-front/src/pages/DevicePage/components/DevicePermissionsCard/DevicePermissionsCard.tsx new file mode 100644 index 0000000..13c240b --- /dev/null +++ b/mdm-front/src/pages/DevicePage/components/DevicePermissionsCard/DevicePermissionsCard.tsx @@ -0,0 +1,48 @@ +import type { ReactNode } from 'react' +import type { Device } from '../../types' + +import { WifiIcon } from '../../../../assets/icons/Wifi' +import { BluetoothIcon } from '../../../../assets/icons/Bluetooth' +import { GpsIcon } from '../../../../assets/icons/Gps' +import { CameraIcon } from '../../../../assets/icons/Camera' +import { SimIcon } from '../../../../assets/icons/Sim' +import { VolumeIcon } from '../../../../assets/icons/Volume' + +type DevicePermissionsCardProps = { + device: Device +} + +export function DevicePermissionsCard({ device }: DevicePermissionsCardProps) { + return ( +
+ } label="Wi-Fi" enabled={device.permissions?.wifi ?? true} /> + } label="Bluetooth" enabled={device.permissions?.bluetooth ?? true} /> + } label="GPS" enabled={device.permissions?.gps ?? true} /> + } label="Камера" enabled={device.permissions?.camera ?? true} /> + } label="SIM-карта" enabled={device.permissions?.sim ?? true} /> + } label="Динамик" enabled={device.permissions?.speaker ?? true} /> +
+ ) +} + +type PermissionItemProps = { + icon: ReactNode + label: string + enabled: boolean +} + +function PermissionItem({ icon, label, enabled }: PermissionItemProps) { + return ( +
+ {icon} + +
+ {label} + + + + +
+
+ ) +} \ No newline at end of file diff --git a/mdm-front/src/pages/DevicePage/components/DeviceStatsCards/DeviceStatsCards.tsx b/mdm-front/src/pages/DevicePage/components/DeviceStatsCards/DeviceStatsCards.tsx new file mode 100644 index 0000000..924b58a --- /dev/null +++ b/mdm-front/src/pages/DevicePage/components/DeviceStatsCards/DeviceStatsCards.tsx @@ -0,0 +1,59 @@ +import { Battery, RotateCcw, ShieldCheck, Smartphone } from 'lucide-react' +import type { Device } from '../../types' + +type DeviceStatsCardsProps = { + device: Device +} + +export function DeviceStatsCards({ device }: DeviceStatsCardsProps) { + return ( +
+
+

Аккумулятор

+ +
+
+ {device.battery ?? '-'}% +
+ +
+
+ + +
+ Максимальная емкость + {device.batteryMaxCapacity ?? '-'}% +
+
+ +
+ + +
+ Циклов перезарядки + {device.chargeCycles ?? '-'} +
+
+ +
+ + +
+ Общее время работы + {device.totalWorkTime ?? '-'} +
+
+
+
+
+ +
+ +
+ Средних ударов + {device.mediumImpacts ?? '-'} +
+
+
+ ) +} \ No newline at end of file diff --git a/mdm-front/src/pages/DevicePage/types.ts b/mdm-front/src/pages/DevicePage/types.ts new file mode 100644 index 0000000..eef96e8 --- /dev/null +++ b/mdm-front/src/pages/DevicePage/types.ts @@ -0,0 +1,68 @@ +export type DeviceCondition = 'ok' | 'inspection' +export type DeviceConnection = 'online' | 'offline' | 'offlineDanger' + +export type Device = { + id: number + factoryNumber: string + model?: string + imei: string + serialNumber?: string + workTime: string | null + employee: string | null + condition: DeviceCondition + connection: DeviceConnection + connectionText: string + registeredAt?: string + lastLocationAt?: string + battery?: number + batteryMaxCapacity?: number + chargeCycles?: string + totalWorkTime?: string + mediumImpacts?: string + image?: string + location?: { + lat: number + lng: number + } + route?: { + lat: number + lng: number + time?: string + }[] + permissions?: { + wifi: boolean + bluetooth: boolean + gps: boolean + camera: boolean + sim: boolean + speaker: boolean + } + statusIcons: { + gps: boolean + wifi: boolean + bluetooth: boolean + lock: boolean + camera: boolean + sim: boolean + sound: boolean + kiosk: boolean + } +} + +export const conditionText: Record = { + ok: 'Исправно', + inspection: 'Требует осмотра', +} + +export const connectionText: Record = { + online: 'В сети', + offline: 'Не в сети', + offlineDanger: 'Долго не в сети', +} + +export function getStatusClass(status: DeviceCondition | DeviceConnection) { + if (status === 'ok' || status === 'online') return 'is-success' + if (status === 'inspection' || status === 'offlineDanger') return 'is-danger' + + return 'is-muted' +} \ No newline at end of file diff --git a/mdm-front/src/pages/DevicesPage/DevicesPage.scss b/mdm-front/src/pages/DevicesPage/DevicesPage.scss index d61766d..67ff3da 100644 --- a/mdm-front/src/pages/DevicesPage/DevicesPage.scss +++ b/mdm-front/src/pages/DevicesPage/DevicesPage.scss @@ -31,6 +31,17 @@ border-collapse: collapse; table-layout: fixed; + &__row{ + transition: .2s ease; + cursor: pointer; + &:hover{ + background-color: $gray20; + .devices-map-btn{ + background-color: white; + } + } + } + th { //height: 36px; padding: 14px 20px; diff --git a/mdm-front/src/pages/DevicesPage/DevicesPage.tsx b/mdm-front/src/pages/DevicesPage/DevicesPage.tsx index 734a693..db30ca9 100644 --- a/mdm-front/src/pages/DevicesPage/DevicesPage.tsx +++ b/mdm-front/src/pages/DevicesPage/DevicesPage.tsx @@ -1,4 +1,5 @@ import { useState } from 'react' +import { useNavigate } from 'react-router-dom' import { Bluetooth, Camera, @@ -59,6 +60,8 @@ export function DevicesPage() { const [isFiltersOpen, setIsFiltersOpen] = useState(true) + const navigate = useNavigate() + return (
@@ -84,7 +87,7 @@ export function DevicesPage() { {typedDevices.map((device) => ( - + navigate(`/devices/${device.id}`)}> {device.id} diff --git a/mdm-front/src/pages/DevicesPage/components/DevicesDateRangePicker/DevicesDateRangePicker.scss b/mdm-front/src/pages/DevicesPage/components/DevicesDateRangePicker/DevicesDateRangePicker.scss new file mode 100644 index 0000000..d5620c3 --- /dev/null +++ b/mdm-front/src/pages/DevicesPage/components/DevicesDateRangePicker/DevicesDateRangePicker.scss @@ -0,0 +1,246 @@ +@use '../../../../shared/styles/variables' as *; + +.devices-date-range { + position: relative; + width: 100%; +} + +.devices-date-range__trigger { + width: 100%; + min-height: 34px; + padding: 0 10px; + + border: none; + border-radius: 999px; + background: #eef1f6; + + display: flex; + align-items: center; + gap: 8px; + + color: #4f5b73; + font-size: 14px; + font-weight: 500; + text-align: left; + cursor: pointer; + + span { + min-width: 0; + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + &.is-open { + box-shadow: 0 0 0 2px rgba(3, 29, 154, 0.15); + } +} + +.devices-date-range__clear, +.devices-date-range__chevron { + flex: 0 0 auto; + color: $blue; +} + +.devices-date-range__popover { + position: absolute; + z-index: 60; + top: calc(100% + 8px); + left: -13px; + + width: 100%; + padding: 12px; + + border-radius: 18px; + background: #ffffff; + border: 1.5px solid $gray20; +} + +.devices-date-range__calendar { + --rdp-accent-color: $blue; + --rdp-accent-background-color: #e8edff; + --rdp-day_button-border-radius: 12px; + --rdp-day_button-height: 34px; + --rdp-day_button-width: 34px; + --rdp-day-height: 36px; + --rdp-day-width: 36px; + + margin: 0; + font-family: inherit; + + .rdp-months { + max-width: 100%; + } + + .rdp-month { + width: 100%; + } + + .rdp-caption_label { + font-size: 14px; + font-weight: 700; + color: #151a24; + } + + .rdp-nav { + gap: 4px; + } + + .rdp-button_previous, + .rdp-button_next { + width: 28px; + height: 28px; + color: $blue; + border-radius: 8px; + + &:hover { + background: #eef1f6; + } + } + + .rdp-weekday { + color: #8c96aa; + font-size: 12px; + font-weight: 600; + } + + .rdp-day_button { + color: #30394b; + font-size: 12px; + font-weight: 600; + } + + .rdp-day_button:hover { + background: #eef1f6; + } + + .rdp-selected .rdp-day_button { + background: $blue; + color: #ffffff; + } + + .rdp-range_middle .rdp-day_button { + background: #e8edff; + color: $blue; + border-radius: 0; + } + + .rdp-range_start .rdp-day_button, + .rdp-range_end .rdp-day_button { + background: $blue; + color: #ffffff; + border-radius: 12px; + } + + .rdp-outside { + opacity: 0.35; + } +} + +.devices-date-range__time { + margin-top: 12px; + + display: grid; + grid-template-columns: 1fr 1fr; + gap: 8px; + + label { + display: flex; + flex-direction: column; + gap: 5px; + + span { + color: #738098; + font-size: 11px; + font-weight: 600; + } + + input { + height: 32px; + padding: 0 10px; + + border: none; + outline: none; + border-radius: 10px; + background: #eef1f6; + + color: #30394b; + font-size: 13px; + font-weight: 600; + font-family: inherit; + } + } +} + +.devices-date-range__presets { + margin-top: 10px; + + display: flex; + gap: 6px; + + button { + height: 28px; + padding: 0 10px; + + border: none; + border-radius: 999px; + background: #eef1f6; + + color: #4f5b73; + font-size: 12px; + font-weight: 600; + cursor: pointer; + + &:hover { + background: #e8edff; + color: $blue; + } + } +} + +.devices-date-range__footer { + margin-top: 12px; + padding-top: 10px; + border-top: 1px solid #e3e8f0; + + display: flex; + flex-direction: column; + gap: 10px; + + p { + margin: 0; + color: #738098; + font-size: 12px; + font-weight: 500; + } +} + +.devices-date-range__actions { + display: flex; + justify-content: flex-end; + gap: 8px; +} + +.devices-date-range__reset, +.devices-date-range__apply { + height: 32px; + padding: 0 12px; + + border: none; + border-radius: 10px; + + font-size: 12px; + font-weight: 700; + cursor: pointer; +} + +.devices-date-range__reset { + background: #eef1f6; + color: #4f5b73; +} + +.devices-date-range__apply { + background: $blue; + color: #ffffff; +} \ No newline at end of file diff --git a/mdm-front/src/pages/DevicesPage/components/DevicesDateRangePicker/DevicesDateRangePicker.tsx b/mdm-front/src/pages/DevicesPage/components/DevicesDateRangePicker/DevicesDateRangePicker.tsx new file mode 100644 index 0000000..ca97b44 --- /dev/null +++ b/mdm-front/src/pages/DevicesPage/components/DevicesDateRangePicker/DevicesDateRangePicker.tsx @@ -0,0 +1,223 @@ +import { useEffect, useRef, useState } from 'react' +import { DayPicker } from 'react-day-picker' +import type { DateRange } from 'react-day-picker' +import { ru } from 'date-fns/locale' +import { format } from 'date-fns' +import { CalendarDays, ChevronDown, X } from 'lucide-react' + +import 'react-day-picker/style.css' +import './DevicesDateRangePicker.scss' + +export type DevicesDateRangePickerValue = { + from: Date | null + to: Date | null + fromTime: string + toTime: string +} + +type DevicesDateRangePickerProps = { + value: DevicesDateRangePickerValue + onChange: (value: DevicesDateRangePickerValue) => void +} + +function formatDate(date: Date | null) { + if (!date) return '' + + return format(date, 'dd.MM.yyyy', { + locale: ru, + }) +} + +function getLabel(value: DevicesDateRangePickerValue) { + if (!value.from && !value.to) { + return 'Выберите период' + } + + if (value.from && !value.to) { + return `${formatDate(value.from)}, ${value.fromTime} — ...` + } + + return `${formatDate(value.from)}, ${value.fromTime} — ${formatDate(value.to)}, ${value.toTime}` +} + +export function DevicesDateRangePicker({ + value, + onChange, +}: DevicesDateRangePickerProps) { + const [isOpen, setIsOpen] = useState(false) + const rootRef = useRef(null) + + useEffect(() => { + function handleClickOutside(event: MouseEvent) { + if (!rootRef.current) return + + if (!rootRef.current.contains(event.target as Node)) { + setIsOpen(false) + } + } + + document.addEventListener('mousedown', handleClickOutside) + + return () => { + document.removeEventListener('mousedown', handleClickOutside) + } + }, []) + + const selectedRange: DateRange | undefined = + value.from || value.to + ? { + from: value.from ?? undefined, + to: value.to ?? undefined, + } + : undefined + + function handleSelect(range: DateRange | undefined) { + onChange({ + ...value, + from: range?.from ?? null, + to: range?.to ?? null, + }) + } + + function handleReset() { + onChange({ + from: null, + to: null, + fromTime: '07:00', + toTime: '16:00', + }) + } + + function handleToday() { + const today = new Date() + + onChange({ + from: today, + to: today, + fromTime: '00:00', + toTime: '23:59', + }) + } + + function handleWeek() { + const today = new Date() + const start = new Date() + + start.setDate(today.getDate() - 7) + + onChange({ + from: start, + to: today, + fromTime: '00:00', + toTime: '23:59', + }) + } + + return ( +
+ + + {isOpen && ( +
+ + +
+ + + +
+ +
+ + + +
+ +
+

+ {value.from && value.to + ? `Выбрано: ${formatDate(value.from)} — ${formatDate(value.to)}` + : 'Период не выбран'} +

+ +
+ + + +
+
+
+ )} +
+ ) +} \ No newline at end of file diff --git a/mdm-front/src/pages/DevicesPage/components/DevicesFiltersPanel/DevicesFiltersPanel.scss b/mdm-front/src/pages/DevicesPage/components/DevicesFiltersPanel/DevicesFiltersPanel.scss index 75b44b1..4cdbce2 100644 --- a/mdm-front/src/pages/DevicesPage/components/DevicesFiltersPanel/DevicesFiltersPanel.scss +++ b/mdm-front/src/pages/DevicesPage/components/DevicesFiltersPanel/DevicesFiltersPanel.scss @@ -35,7 +35,7 @@ .devices-filter-item { border-radius: 14px; background: #ffffff; - overflow: hidden; + overflow: visible; } .devices-filter-item__header { @@ -43,9 +43,9 @@ } .devices-filter-item__trigger { - width: 100%; + width: calc(100% - 32px); min-height: 44px; - padding: 0 14px; + margin: 0 16px; border: none; border-bottom: 1px solid transparent; @@ -56,14 +56,14 @@ justify-content: space-between; gap: 12px; - color: #30394b; - font-size: 15px; + color: black; + font-size: 18px; font-weight: 500; cursor: pointer; &[data-state='open'] { - color: #031d9a; - border-bottom-color: #e3e8f0; + //color: $blue; + border-bottom-color: $gray20; } } @@ -77,7 +77,7 @@ } .devices-filter-item__content { - overflow: hidden; + //overflow: hidden; &[data-state='open'] { animation: filterSlideDown 0.2s ease; @@ -89,7 +89,7 @@ } .devices-filter-item__inner { - padding: 12px 14px 10px; + padding: 12px 16px 16px; } .devices-period { @@ -101,7 +101,7 @@ .devices-period__divider { width: 12px; height: 1px; - background: #8c96aa; + background: $gray50; flex: 0 0 12px; } @@ -115,14 +115,14 @@ border-radius: 999px; background: #eef1f6; - color: #4f5b73; - font-size: 12px; + color: $gray50; + font-size: 16px; font-weight: 400; cursor: pointer; } .devices-filter-reset { - margin: 8px 0 0 auto; + margin: 12px 0 0 auto; padding: 0; display: block; @@ -130,8 +130,8 @@ border: none; background: transparent; - color: #031d9a; - font-size: 12px; + color: $blue; + font-size: 14px; font-weight: 400; text-decoration: underline; cursor: pointer; @@ -145,18 +145,24 @@ min-height: 28px; - color: #30394b; + color: $gray50; font-size: 13px; & + & { - margin-top: 8px; + margin-top: 12px; } } .devices-filter-row__label { - color: #30394b; - font-size: 13px; - font-weight: 400; + color: $gray50; + font-size: 16px; + font-weight: 500; + text-align: left; +} + +.devices-checkbox-list{ + display: flex; + flex-direction: column; } .devices-radio, @@ -166,7 +172,8 @@ gap: 6px; color: #30394b; - font-size: 13px; + font-size: 16px; + font-weight: 500; cursor: pointer; user-select: none; diff --git a/mdm-front/src/pages/DevicesPage/components/DevicesFiltersPanel/DevicesFiltersPanel.tsx b/mdm-front/src/pages/DevicesPage/components/DevicesFiltersPanel/DevicesFiltersPanel.tsx index 2bf267e..ce76032 100644 --- a/mdm-front/src/pages/DevicesPage/components/DevicesFiltersPanel/DevicesFiltersPanel.tsx +++ b/mdm-front/src/pages/DevicesPage/components/DevicesFiltersPanel/DevicesFiltersPanel.tsx @@ -1,13 +1,11 @@ import { useState } from 'react' -import DatePicker, { registerLocale } from 'react-datepicker' -import { ru } from 'date-fns/locale/ru' import * as Accordion from '@radix-ui/react-accordion' import { ChevronDown } from 'lucide-react' import './Datepicker.scss' +import { DevicesDateRangePicker, type DevicesDateRangePickerValue } from '../DevicesDateRangePicker/DevicesDateRangePicker' import './DevicesFiltersPanel.scss' -registerLocale('ru', ru) type DevicesFiltersPanelProps = { isOpen: boolean @@ -15,13 +13,12 @@ type DevicesFiltersPanelProps = { export function DevicesFiltersPanel({ isOpen }: DevicesFiltersPanelProps) { - const [startDate, setStartDate] = useState( - new Date(2026, 3, 20, 7, 0), - ) - - const [endDate, setEndDate] = useState( - new Date(2026, 3, 22, 16, 0), - ) + const [workPeriod, setWorkPeriod] = useState({ + from: new Date(2026, 3, 20), + to: new Date(2026, 3, 22), + fromTime: '07:00', + toTime: '16:00', + }) return (