From b978875a0b2f0f9a5602e58ef53d7e863adc7526 Mon Sep 17 00:00:00 2001 From: neizbejnoezlo <137374284+neizbejnoezlo@users.noreply.github.com> Date: Wed, 29 Apr 2026 09:07:36 +0700 Subject: [PATCH] =?UTF-8?q?=D0=A1=D1=82=D1=80=D0=B0=D0=BD=D0=B8=D1=86?= =?UTF-8?q?=D0=B0=20=D1=83=D1=81=D1=82=D1=80=D0=BE=D0=B9=D1=81=D1=82=D0=B2?= =?UTF-8?q?=D0=B0,=20=D0=BA=D0=B0=D1=80=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mdm-front/package-lock.json | 848 +----------------- mdm-front/package.json | 4 +- mdm-front/public/devices/армафон3.3+.webp | Bin 0 -> 30714 bytes mdm-front/src/app/router/router.tsx | 6 +- mdm-front/src/assets/icons/Block.tsx | 7 + mdm-front/src/assets/icons/Bluetooth.tsx | 7 + mdm-front/src/assets/icons/Camera.tsx | 7 + mdm-front/src/assets/icons/Gps.tsx | 9 + mdm-front/src/assets/icons/Kiosk.tsx | 7 + mdm-front/src/assets/icons/Message.tsx | 7 + mdm-front/src/assets/icons/Reboot.tsx | 7 + mdm-front/src/assets/icons/Sim.tsx | 8 + mdm-front/src/assets/icons/Volume.tsx | 14 + mdm-front/src/assets/icons/Wifi.tsx | 7 + mdm-front/src/index.scss | 8 +- mdm-front/src/main.tsx | 3 +- .../src/pages/DevicePage/DevicePage.scss | 581 ++++++++++++ mdm-front/src/pages/DevicePage/DevicePage.tsx | 60 ++ .../DeviceActionsCard/DeviceActionsCard.tsx | 34 + .../DeviceMainCard/DeviceMainCard.tsx | 74 ++ .../DeviceMapCard/DeviceMapCard.scss | 87 ++ .../DeviceMapCard/DeviceMapCard.tsx | 135 +++ .../DevicePermissionsCard.tsx | 48 + .../DeviceStatsCards/DeviceStatsCards.tsx | 59 ++ mdm-front/src/pages/DevicePage/types.ts | 68 ++ .../src/pages/DevicesPage/DevicesPage.scss | 11 + .../src/pages/DevicesPage/DevicesPage.tsx | 5 +- .../DevicesDateRangePicker.scss | 246 +++++ .../DevicesDateRangePicker.tsx | 223 +++++ .../DevicesFiltersPanel.scss | 49 +- .../DevicesFiltersPanel.tsx | 133 +-- .../src/pages/DevicesPage/devices.mock.json | 126 ++- mdm-front/src/widgets/Navbar/Navbar.tsx | 14 +- mdm-front/src/widgets/Sidebar/Sidebar.scss | 2 +- 34 files changed, 1888 insertions(+), 1016 deletions(-) create mode 100644 mdm-front/public/devices/армафон3.3+.webp create mode 100644 mdm-front/src/assets/icons/Block.tsx create mode 100644 mdm-front/src/assets/icons/Bluetooth.tsx create mode 100644 mdm-front/src/assets/icons/Camera.tsx create mode 100644 mdm-front/src/assets/icons/Gps.tsx create mode 100644 mdm-front/src/assets/icons/Kiosk.tsx create mode 100644 mdm-front/src/assets/icons/Message.tsx create mode 100644 mdm-front/src/assets/icons/Reboot.tsx create mode 100644 mdm-front/src/assets/icons/Sim.tsx create mode 100644 mdm-front/src/assets/icons/Volume.tsx create mode 100644 mdm-front/src/assets/icons/Wifi.tsx create mode 100644 mdm-front/src/pages/DevicePage/DevicePage.scss create mode 100644 mdm-front/src/pages/DevicePage/DevicePage.tsx create mode 100644 mdm-front/src/pages/DevicePage/components/DeviceActionsCard/DeviceActionsCard.tsx create mode 100644 mdm-front/src/pages/DevicePage/components/DeviceMainCard/DeviceMainCard.tsx create mode 100644 mdm-front/src/pages/DevicePage/components/DeviceMapCard/DeviceMapCard.scss create mode 100644 mdm-front/src/pages/DevicePage/components/DeviceMapCard/DeviceMapCard.tsx create mode 100644 mdm-front/src/pages/DevicePage/components/DevicePermissionsCard/DevicePermissionsCard.tsx create mode 100644 mdm-front/src/pages/DevicePage/components/DeviceStatsCards/DeviceStatsCards.tsx create mode 100644 mdm-front/src/pages/DevicePage/types.ts create mode 100644 mdm-front/src/pages/DevicesPage/components/DevicesDateRangePicker/DevicesDateRangePicker.scss create mode 100644 mdm-front/src/pages/DevicesPage/components/DevicesDateRangePicker/DevicesDateRangePicker.tsx 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 0000000000000000000000000000000000000000..c526803a29f16a728bae682cd9a09f15190d6fdf GIT binary patch literal 30714 zcmV)3K+C^UNk&HUcK`raMM6+kP&il$0000G0001w0RZd)06|PpNTUV-00E#zYuoX- zdV*R(-A*U@(jMEk?NMfqZQHi(+O}=mw%b84`M&2z@;)<@^klvF(H9XDK#+Ci8b&Z6 z3~tbv3NYT@~zf~gj0K% z%vKdS3$&`r$(EAY$|l)jwv^0c&p!lQH=DKck}Dh;<_@oA&Aj=AmKhazyMncI;H;^^_q8sW~o3msgYxzYbb*jmA*E-hp2i^@;i}T5KtnUY$T@AIK zT){ej$XQjY_wqW{`-9q5!TI1;*8LyWm0h`=wg0{wMQvs>02drBaDB@V{L!g8rvd|| z7pl552!B(YRDpuZi&DMCF#Ih=?Zr6Q)i&uY69*hldYggRJ!vQsb{>QO+JDl%GgMqmVF&i0DxyJd)IMD-L2uB+*Ye z7(*#9S+97 zGx-LQrxF1LqN7a&+#zt%ZH&OL1VlX{Fxj2TLG3RZ#1;S&%~BB^H+TgE%A2TyI~jr_ z9jXp&LEr;Y8;n4c3k4xIAy5Q|LO2w|A^fi^B?1EkVL=E4#s7E+!=XfAfS^baf`b8q z+XNv9So|;;n12@-AlTMK)t)T~aBG#foFTZyqjFBbKsTqtL2n_z;zvpN0Gs;H#^w2EjV271?0HQbGX^$k?*EfWG8B#eO`4lG#w1gQ-On4AZPB0=aM3dR4RFhCF% zgg{UX41+BQgasisAy5K`Vu1mN5C{qd1_Tlu27}23f)E6l-xjoI3j%{35)4M0sRAIV zY@u@Xvmmgzj*9%;UGe?np)nl1 zF*F=uBA{4wD`;%(45lr@ZZa| z*to0Rl(wEbm+Pq+){y)(SP>O(!X#I zH{rYbulY~=U+TYq{)B&`e_{Wr`Tz0(|J&39qd(Grz5mAko9ch*-^TyT@b~mDbYF75 zIIlO_z7YS{{~PpxQe>VNb7xc#a3 z!TTTkfBm2Az6JlFe{=th{ww_l{{Qqp|Nr{^;s5aek?-^N+y3AEKZD=a-}rx@ENXIJ zf^KA3C~^mj2{WZDP3Z-=Lm z7qgtggo)Twn2Bak^epo^5N+|&vj`oI#s%BTY~#bG277|qd1dTms~u>YLUZSrsL47& z@>UjtSo_$oknEOjAWe9-wq&O7oT_p)5R6t)7_L18|b*^D!9n^IPR^j#6m6>u^7jpqUQ^Sx4XY_i~|>QXym zi1DSl|k56 z{66ISkehw#CZQNzbKRyv9LmmWWmW-Xy}R3-{d}WhR#fR*#FAJ<nrypfS5d~ZC>{b4Rij?7y3QJ(GZ@{8p7%!3c}l45;|NlMDF_A?*foElXsn`-%hfP1qoMoxm!&j zFYwoi!d02O(5=Fc+K+3;RO)Fv!9s*Pp$n#W=y~$8|Bv-)sTZx4ilrNxhCALoi!zzr zF&;hW&frq@q{qh3KPD>Kc+q7p^Ch&&=>13c-CetoMf2R9yWqB z(%)?&2k(^43VNd0x@IE5j|Kc9&A|!D<_#jtiV9s-xx=0R0!rM(BLOtL_{S3-Cuz{d zH(jH)QraP6!$_npG_pH?ewvkw)TxfBI^eq86bJX!`37{e4alNfnOx%{;tF7d1ZXF_ zFj;M`#v*Y#rksEA9ark0e}jRCtz|wsHH8c%E2CNX?RN`<(+tuP#|BeTSQz=YYiF1 zSFG+YrBW;uD?GI83lV}TFt^dnsf5#DVvvw-1-2yK#@FF!688>o$!v6!?XOV{n`#6R zO{d}UH|q~9HziMSnd8d%;`|Dq=UPW)m&m9^mF`?`9v%}_Ts31-^;s|60u5>}f#&s> z#suQcC?>Y^mJ4W*27v*P#yW`s1AX2yw<0H6tj@jhS3s%TCHV%f!=wN5qk*l2UH!R?nR|OXtko#(Q$QcShA!oN?;wv)DzTl*uVG{; z_icgk!4`x{9{MFkt#)Ezn$eo}THBv?V3}%k?=&toYA?sykvkRli-!rc`L%(y8VFJJA1T(z~xgr(E+_xQ_tIsF;@)|H-+4d{5oSPfV4Y#Z^oT{WzkOPs!6}}O>u|us2y(!o23O@@!bn{c-}7ntfE-gKsO{9N z9KZ%a$~X&!fK)$hVILmd!?7)0vR`@|AhTuRp`O~AHv|h=jLF>E&j~^#w^n+MK5zbh zTpxKn=dRnE`uN?3@k9PPIkG?l_aO@~$zggRg*n~wq)mXu3xWs%KR0@$tf#$wi{bz|acDLQo8*zGoo~87U5P_$jILZ?P(g3Z6L@K8)cL2F0G6y0t zB~XyYy@Xew$rfNKs4FOI5W~E;p@ImGcoz){y|D7oL=FGsS3DeW0zJfe2Fl*9j=JE> zyKF0CCzGS^ZapclmM8)2=0LA87`&L(=L=PJJ4I|&+Yw6z_G{#05&<_?QrIH&@Ndu2 zTMP6F73IOVv%?yhlYfIiAwF0j-YXS{8$Q_4z09w#q?A1e1bp+39 z3c83m4v}I9yae(>Sb_j&bk?!aQ7Dg|P{iym(J7`vk-ScpAyaTU!Vo~OazNMM7JH55 z&K#}MAn1bx)k^OF?o zs&XYgerFtjyI{y=&nV>k7|Q;*fr6l0aponrFm6xvX&-WXW1iDCv}!x~wUyG9d-mcC zAM=-Zg|$*z55sk}r+BTI$ddF+y-DKz1*tas!{v99PsOI$B)QI z##SlYm;SX$j_7oSO8n;v9a3BBbuB^I_K*0gH~+uT%=s$%Vm8=OUs8Tu(-LF%qJSWBAM$Ob*1ZX5 zneN4lCI)gKc9Z-dp-^xxV?P0b=!*r>>6bO?-_;PX7XxIM0{Ur=xa9190FYKorU$En zzoSk;_&1>Ax@PAdCMp+#q8xx2HN%fmK2i?9xvs-(_(!8iu;Qy>vH zO&&3+Ei}FZKLhOZ4p|8-AB4{8zAhI*2(8)7G+!tqM&t%mUa}RshSC0IFW?kl8SR`H zU4jw&N1z4c6nFmTx78K`fry@b6ath-g|6eW5B#!|=6X zb<_xCZiBY?emjA(142%E({`z&tDV-~>M`k$5^9w-l!zKSPHSn+? z?U8;J6w1jm1`qyhEcHfVYFMf5lTG;$_BQXE-5MaQ{hIO6Ha zxRJ-t%3HwiY_I4W$>nLt;Z&r@P@s5Sjy9X?UqfI!O#aun&U5BLNxkym!x?S(1Enw@ zWhLC5_+No*{}g|6{EeL*a&a|9MHWpX#GVZ`jw0b!bC`i zS%~{Ha7m1@6~s1m(+J3qYLBeD>1O~&=)~bAJ5G}FuL>~uaFpxM7O%#qS`&UmO9F=g zr)g(QX1KY=oF=ME=D~j0s1?6`=ptPRCQ&c!6dW<x;}A8X8Yf%fK0||#SeWC058Ec9 z*8EAHaP#l{ytl5pnZ^0I6Jfe*63gD(|KgcDU-)y$t+;~@4yy_yU5zaS+-!UUhK)Z8 zhrDZ$=Vha=#d$@%66!*Lq+?|1);U~r{VC%!)bAk)601xO9c-IBvmq1@b$Sc-#`k00 z4J?V-wc@jfPeZeF9qo*I>jXbJH44?Sg|5`MlrBOLiv}_fO$?Oo#2%lrPg7fq`e>H!){g1#Q_yFd3*mBkH65puTy~dFvqpk;FC8aIJj7x4Sz3}V zOsJ0JVr;#J1b2%y9=iYu1^F-WZEJq^4WX-S6+C{(f|&#e;nAA7GSNsS)yoU!%Upg^ zX=w*I7wf^96c6>M0@o_UCS!HBB&r(mD0>zaBv0j4&Pr7*wj~&Z?KOZ(z=}paxE&yY z`65#73!fSkpzYN$#UL3HBOZQ3l~kYH>TT*I#glh&5Ye;E`TfY-`|u7BmSgxIzjtM` z&fL%ZpB{5CgHW6Oe!45A@v?=nGvMsso`7=5nglnnb;R1WXGrka7;L}7=W%!WUBS?Y zZ4xJZ*PMmivc{{KvIm&efXqzW{#Lh6jl02dQE>`~^Z2d?qr~egwZZ|k8(3%V`jR!c|hpWxf4HU)o3H7on zzDeZbui)0xm+)(%2qK(CB{|x}678{aDZ4I*8|X-q@p+AN`X^T=BtdnH_Mjr~^4b>i zg5-&X0M+^ud#;~Sug8snIN&0QPgO48BU&Wc(K0L6K;Mzn>Fl#$M>FQpor}v!m$0+) zvvUM@v$t>v%xc;KUY`!zNdW%(x_hkQV8KO3xMjAK2`5PY+;;LA1t%_h0JnPe%a+l5 z%$i?^0E_GPJ2F~`1O$VwzMtEUB&JAVyC253aFYYA&oet__oF34)KGzzH#q3>@?Lo@eZ zMmpW0g&@^%+ivHy-Nn-c1YG=se5VPycJiqLCE*_20&e2rn9Pl*W<0%cwf##Zg%wUT zIj>~b&_BQZp8T)^3x9V3z1U)s6xbEYDEvLOWx1>LaeQUq$Zfq@@>zYUqs*{OIdbg2 zKV(@ZaoON;upzA6wHoK0kxM{(g7*OSmZ5|LV~1!x7?0l_G`N!@icW ze?47jsufpK7@8B$EQL0#><0JLL<;UC_G=> zZ@Aa{{Qp>BBJXBCtrzb(XSSwTsn?|5P2i-yvixxe$fk&uNZ{&Mctb1u19lztIkg=0^!) zjjgY_vD#F_-gXVuqA%i+!eXatF|`_;42YHvL&dr;{w3+&zvEc!fBb^xT|UNYN#nse zAn`UlUccRWLL1*Km;f=TcfEJ7+0>m>HLq87D5@Lz{(RZ*HG0cVerAupK4`B^WO=wj-yt)B-*dn~Dfz6Y78HLB%6^pn< z3`+QCu(G55i7JCIbzNQ|5o)%8i(NT4-M(w95toA&Q~x>Q_=&%uKZ%JW&pR2nm1~v{ zTYvT8pD39rHc%SgOxMmy&pmA>Jzj3_HtV@{AwmBn=RCyR?drkz%(sg6@TsU0W~eBrc8SPMGt40R^;^Y#@sUz z^ZZY?z1Oox2lj`j?OiWemwEY_as+D1mO@bOsUH#lrQ=$Tbn$;h+D2&N>^$HLY9sk3 zloSfpQxJBbz?&qP5)$>cU3TKWlLuXkznUnBQ!C(E%a%G;4R>zVA#O|?^rY4^1vR+g zCHmO*HJH`i&GSILz>@4%1047Vi2{2-4rkZ*M19ZVnQvLFmtD99=DN%8U`5(ke$phR zFT)yQ8PZROW)dL62kM$^tp23;BQ~?9Npk&q@F0ji1BcvxFey2`b@9qyDIc1_e3}u1 z-p_YQ9!Cf>uQd(kbt^${1{NB!dZXV`5~0N zE+uE3=sNEQ3jf&`O@8@l$mu!h6Btcr=GC;yx@QNej`+}n1xOshglCjekW6HH+*NRG zK3hsdXqMlwm@3B~w`L%zq_Gav^07{NF69lA7}i%-p<+lcMw0C;n=amxgja)CB>O}k za9IrWf96)yZg;>(M77yvikU&+cGtnVVcEL9r}ji1AmLvyS0{iR4R)aP1T#^X+lf`( z#FUWc6+QGax)v>p3D(pgYWhGefkh6eO1$( z*S^aO>>GVfvlb1UA2YQ;O;6Gdw3=hh)6|7SNJ0WI2AT7%futdiw#VB&9=t~%@nmJ; zgob%TSqPMGZz4&_;0G9klpaTN5T*a|906@)|=iLEdm6+H!~2HL}g*LGZblr_lE_NHXEUt_L`G)#29BL49DnJ*vE|dA>Z_U+=E4i#jW~p zRRZj?<}n95CMjHtjZL1Pb7W6;%BhE2SM5!R7kMVr2PR8_?NSKr%($y4{vc+o; z9!SLJfY2OGEEXoZ%`vNa&f)7C7F#LbQif#ZmtjBgg!DgvQUv_V8ig*0F^=rn4$MOq zb1n@?49l{FTvnu-+Ic*X+7yIq=a27FDl9trtJN>7M01lOrI>SpPajGqC5ED65&RR1 z0^TPME*Sf58_k&$7!#TdQrLzL%hb-4;QAy@ytrEG;uA_Nsv;Qui@2xYBi%oI>8*Y%oOH10%*#>#|p=efN|D-oZS}$n4UReVtKCaE_n)5#rifNvE7id79rEs&^k) zF0!lx+8qlQSQ|B&yyYB{kB(R%nAJw+CGGJAVkOw>Mo`&l)}dCMWL9s`c5nt>ujl~8 zVt+@YMd?NFU&{jDW6+C)1CpH8ef+27++bt@dJBDaNU`HEQ%h6|Px<~b$T0#@f)^Lr zQz0DFJgMq&nnfHtvJkPUp6`^pDzoJ(LqiXAcA`n|%T|e+_7cVEj#-2?-t=OFZr5$G z3_DKN6Uya62vvi<^3KmH>rp@lDfbaWl+(rl{N*b%n0Yug=v<2$%Y(ZxsMlzf<8!E6JLCSm_2Ec z-`Rlym?}trgWggr^eN-NVhp>&GXzkZs*w&%A@D0hL-4p@Fz*`oROlh%9X*rPB>)}|*Yp(hQY#e`z zHzfq(y&X{oaVV*0Rs>tXX}v_@g8WS99j7!iVdOT33w`zF5_Ukn^$DvUGtR%4CHuPA z^f?~K-KpnNA66o8)n6*MccF9)l>|Iye zk^p;g8{qS1Z`&*4Y}`A4GqbUw$$}A~?wb8?Ye`sE zR3nSok6JS2hS9yc`iTbC<6i1RNlHXRyk|p8;SEO7rGJX7LdhL7kuPQ^c>k_7$MBP! zizK<1TZ_U%yW~Hkjd`z{-}5b20zFZN zMro*x0~3*EHuOgM77U1Ko2_t3-CF_yWq~ z3u*fbwFx`rRamU_p$}M!3+~VX=;xHI;v3BY+0WGX6n%vqQq2#Pf*W-e5?fn%KwYas zCoJM9Zh%6{_jV*BUpg#U~#>!Fj_iXf&A4y}@x*y2`4W zJ@5ykaDs~D^BNS0SbaWq;W)zCPY=Xl)9M*6uwNh5MFF;(mn9PJzK^Q;yS-%W-OJso}^@3J=mxb8Pe#@#Waym=%7f->!i~J6i{`o4_2K5@aIEqjgx_al~V z8u?80MU5|e>Ts7x=Efonc<)JNI#5vnKUIpiyf^^G$(3IlsBlT@@E%*>|LfiB**x0w zygfkA+V1~_mvqL4OFb+{f|a!`+e)2Jn!h5-g>!QUG5-;Rb@&PO>fGM_yy? zhiAeQx61*UYkHYVJCndPJ$46c__DBM2D!^5SV^sd%d||n3iLq>b_&^5Q#zRX0SNj8 zasu-Si&RW6={b#|s~xog|4^uo3%VHVIze;#Hm4GGH0F5YJyc|5X!7wnxPqbdv@-sf z_cvm2QI1JMtA%v8ak}Sv6(xHB2I4drI{7FNYoTjdhX6;Ju4JbJG}X<0G}6*=79V*p ztil2!Z3<2tKP9C)3s!tnG8yz7MWFd+coV&~vlqQUhPK;y^>mHDSM*bS8TspnQPi8T zzvZ&Xx-?ZhhtJdi`W>cNiPhHWyY}7McGK!sJTNx1@Eh`oqp?EF+OCa83zg4FMT=?* z_q9r%M*UW}VfHV(w$hlrQ*n9rAkw$vz;RTkdNE@C$x_u*KOxA^ylq@N z(^EDGrz+_E{a94fsa7tWMHzku2T8S=tKHW=yg1G+d|qv@B@}S>jD!!S(2^vDw|07X z>jg5bU-`7Se?RCR4pe`Q#*UNjXjMa0U|BvV%2f#%M(3}>&vo6|Ed4n{DaodBf^dve zv;Ux!pm_$+v&uo-hqL*8`gkGCCG+sLu4*VTG-GMb*_TdT9H!)4D2}|)q`Ev35ZjdD zrJe~%SNKN8hsrm+`$DG{>pGQN3$tcft4%;5Pl1QT>i5lxiCX>N!>*omWKy5}2Lpct zNx)?a+oTYD(-0Bma?SFojsWb)Ex|!Bu>}`yWb&zK_K;tJkX=;skab*pF68`gBQx-o(pXvwx;fHSuHkFc>Jeuw_kqyu;<*_TA zY3#f8zz8b{WrylknK-1vD@+f+Ji>rz4KMT2)BZUrp(K)e+9;&XQId6IMgEzbVrfHx z5YB3v`d+DuYY_~zc=Ax_WR%tr*?xEv4~XxMSP(#RakOPOV~Mr6M) zX~N*aDQNpJ|kK^#w+HW{Cm#E(( zmnET+5w!muANl`>f8;M?Elv@Z1}dSfW|04$fL$n|!ezL!b-cn9o79+E9E7ZW24D$`O&ROHP-qOwaNqXrBNZ0D-6b~ z|INA{Y{=p!8f$$#>gz5S@$Gf>01NlwENhq7uuUZt`24HZfisXoxas&MHICeRL^MVOv7-j!CA; z7AMDR0C!ykGkj_cqIL(0qeY5+S8$aE%Qxs4ovbGhV<3=2G};s<7_80WET{>H0p?C0 zG`07Xo;NJ(J0S<)vP7WwNsqU>>|7diFr5Zd8#G79ndI^Wna0u_wvIEtZQ!91@my~Q zVo9?~MGmQSzi?@I^Lrc8#Q+J>q-0^gxVDV`m(nC3*hJ$;@w?l>oVJJ?KT1xBZUmkH zSKKI8Z5R7Eqcyu9`D8K_pK-hn2cB0A_3)7vdF9Ayn;ESesPiS?Mapl!6PRe(h6SXK2X$|C3CbM*4PyD7 z=)w~i?vad_9uwSoFr!Xd8F(1=uz3z`ar`+LfLZGBS-;co*6UvhtPL?p$RWE-?4ODgI7#@v6pgiyv^i^glhrbk%_jiR6 zhFO1BI+Qf&eyx>}nxh7{_^mh1t_Et6B4X;W3Q8(5j!BRxujM%6Qm|{l-+`Z=)YBuM zT~l7h0}H@GJ0@mw-}vtnYEs>V3x`Nxzn9df)+)G++}T!C4w^e|osc9rCi0o%aj+-O zafPhfe}RQxrHnA{i{ixAK{Z{G1aQ_>1cx>Ve`}K z&>EREZ@!d1Pc#t`^FZzBGbDt?!F&Y@cdaX2`u2adV@SWXTO_vU%8SzA`i76e^n=O{ zvn;QG*Ez(hctmz`j5ZD!*4d3*(Z~6uLLy?o^tV2*4?GeGk^Qu2=p*Z$zMmV-%vPWe z&ZUt#-1MUAO>yB5cQ>*mGxkCBM4=Ha2);pcR-86nKcg1cUSjJ2=Vrp-vH!W|PZ;Z*j-8WL^q#GsMCHw)NSd$WJq zyhoh+TLWIjK@$9q z)KMwmMu0pRJcZu2HgzXTdV24AlL5MXC+>lGU#TuqOhlOiJtygp z{Nw|Gu8jtjcEuBkoBTN4;Rf`e%i}tbry^kD2$d%37^T2x!4!9j`d{@)!U_W*GUwo?R$lok4&6t1pEnqENKvHnVWc5L!K9u>yncKd=b zE@m3K&BVDOoNz`$AqZ9})$nx)1dC0gj*7i2bpD3X8*D9>gLe)?LlLiC5wi!9Cw6i+ zNUU@I;cU6Ta+R{^?;tIG+GEooRCG(iQ^2vSLgNELhs@Dx;h3R0l>Fv#Vs($%`3wh* z%1XME-j!=}1NWq4>Mom(`ApNT^S_JgpN=#}4&}z^8CY;*1B5R@88J|MM`9+~Y8%&F z>(CjeL0#Fzz1gz5vt-RtXc?8;;rKI~`9DyP-NJMqUOJR=^jWE{tuy4&tg+;&Yn?na zP>lQb%?or#gyk=OHpbnc9^K@(Lkx|g>8L=)*HFK`=L_E31PgqQm9$F8I7X=&Soc&g zksUT4cC@w+epTOrBV11&?F;{gyVl_}H#W}a=_5V13D|zMj%n=htb%*lh@@&6+&NBj zFlK};Gk!>9(6CFWK9xn>($daz-unL9^qXmbrv%PCOVYhuHiY{iy5`8pI#*nif>C;R zK}b)WBW2^|Dpcz-fKE$f$~W;UB0sQ__1~!tVFNId2oAg`v>~| zckzA3J<3ztxD94X&Zpkvy?Nb9qUh`qfm6KLceg!$q&!-b{}}KN#=N6UjW4w)8J>8F z&@MP<&5o7tZKLEd~tV>T7ASiR>(Pr*?cvp8m~`%+%j zn{Z3$r+*TAUzc!^qT9-lo4cuEM@pSJcUw)i-53&vuDZtq_2;=vTtfH@2>E37{v`O> z8gx=LDH-~F;rp-piu2jTh^{o`L;tm~VFCxexgss`dtcxou{vUGH*CC)o#^IckB9yDk9wwdURr2k|4w7`LQsgtmp&Z)`oZ#s8%o83vUIb>4JS;8tzN5#+foHowRRyKVpTgo!VSXWcV0tIks z4&)yjDC#rtC_;4)hgx^?qvCdMt^$%8|5u9~Foavu>cgxnCRPH87AVYFakL|l3Kz=5 zR#nn?c)SE@D@D&OiF1un?qy^WOc=B-qr0l$vx0aF!K77zil&)ns$>PBN!4>DhlS|! zro_Iu#BE3vC=vq4@a^Ih1=w>`f#ifkd?b z*$!)Vx4J$P`wlRBeA)~No%HJQz}_rtQ0Egfg(}VOPVibHhDw-n-=$KQWIo7S(CbJx z^~*w0IDnd)dPYz7ywbw`ChDm=Mzob#tr+_!)nxP;uF7jE%W=Rqcz^N>>ZB7cg?d{8 zpzqV#rJ;i=D+ZKj4em8|FtpKd7LnPkuf=`9<~#p*9{l{|>U-OTOT#a~ zLA2pfrWA!PY>DaO>1EkXV)>B<^5d2e)(kTX$1a+5pS7XX4DSG(pbwE#TD047(y(pA z{rPgUUir?@MiGh5_+*~9V7?(D#q{{99;RMGmR?PtA)EXjrV#+(B^TO0rGK8Y;sXm= z5f8C`IN$xN0>={L1)MtTi^AfD^Hh*|BF*`WN+e4s%`*As%ofvSmw22SvgIDjN-Do?4KKn%Slh}`7sLUu^pE}dm4S+%v=Fk@32tiXTBO$$-K&rsyl z<=_h;akFOptW~55-t!WX%7gyOc+1RE@DekUZn=W=Vwi_ta29;l2-|a_u?o>Mp6B%oQ9a$-=3S>D&A!VJB;gYBA8$~WD)O|{ z1#7OCz>{o_s{(VZ(4S6|dU?zeN!vz@jW}BUnCXQ?%D(Ay(1#ylybj@&2C=;P%1?7b zl=elW&Pj=`Q+Xs^R6$IsPiQd2X$RgB$PES?O6m^$0-eo1*u(Wwh#<4M+>u8Aa9V{S z-w;Q)D6ifETI&sN6Fxg+D-e-sC~?VIEuWW4X*Hx8(;=>l={E1trKoTk@l5i5x7vT1xLACPwn0FN!YA;2 zvKPNzcbUDo@hCeg2Dt(ZSI=A4Wws3(e-9qm^_=&jAD| z)&;@1DZgRM${td45jG50%{bcnBuYrUamivK51Ue0gz+#qZ&xT4-P`_eGGGPR7~s$F zWpRA=u4!7%IAGZ)^Pha|+`oXt$_CA%OpmRC$`HdwWr#l~zK5rK3LiY`qcLPK{Lz+b zVxUS3g141jKiPFHGgO5pU$KcgWAuduTIQX7z1qsSwa`ZG`i4Ly;{5;}ujmcze5CAI zpN^X=beTd;myhEXJSabnIOtOQYWHTT7y*y#ACTVV1Off)+ULH+lUhlI{Gq8xxH8d% zpGZUuD5>`gU2I^H#~tdd*xcaXh!0K-J)^pkrTGv7D=3TK>t`u5kb-10bHH$-kJ-)* z1)SGIliCx*(wKdf?5~V+gVLmukNQHO8V1jVc0-;;W2m7|mh0{Zxo^tT<{QG(xn5vz z74bT_RH^V8wmSvi#@S;<)PVXd;Y`se{tzN!SoJ$qFP^@#DP?K|!nnloucC|l9$qWa z)yi!54OvBxsQ-G~Hl9J<#+hrx4y#JrF@^Be7}eNh(XRTEe6~7G$;GS0{``TI+3tW4 zWGaLMim3Q9A(yqDUc^)*soM+bHF-aE^hhBc8F_$26bZ^iR>tK=L#HAO_6l{YM98jw z>nR6IWxnh~%Re$3^@9(w4duy_Y493ra$lQNEYh~4TV+;Q2PR%tW^FLOab;O9Pz|+f z5Y;0gVstofMs9gsE^<>sVlec~_Omh;B%Jy$|AXc3co(E+#dhtrtFUmx_)wH7I8N#* zHO#_9`nbngAXCzi;$nC#KgbQ}O6MJ!F-HT$f;Ss4A3z;hcDi=~j$t`+^JiCjin8(? zrllDRu&l4*pDVrGqLf#C+8G&FiON)o4NqRb1O`=UqE4nl)n`DDTd+;MlXjPtd)%vr z^j+$dfm1Or4j!lh#y2_0RQ?_!iy@GsX_rQOHteh6*=671Kb{+bhtHc_l?ckxL0~2* z{zp!S$MpJZZe;Y2!*YrCX71@7*hdX%+HuI>oIa(Z6A=8x0BsgfVf@mQswqUetoHw> z)G=CD^O43QF&zm&v=bxA7Grc7kr3)v;U!_N=aQ={rf$Kp-=eW)m8``7U;`gCf6w0O z=kL=*YP5Zk5@^hVku;K{ydBzUQKQ$a7tX(2mx-@JUQ;!(*|kJ5}YtJ&+Tcwe>T(q5yEaZ|y+__{kT zp#j4;E9jSzH>2m@bI(cZ{5jz1kK4>oJW@9hzWHP1OXu^m&_&kSvV@EP`&*G`o+w6y zW^6Z1F9eCEjdbh)WAg$-TBM?nDipQUuCX=L9s*C!BiuTmmj48j4zq5){p|M>UomHc zKm1ZDSRmbwIx$WDSuTGDV%0!H!TpfQW9UzEo|Nq!$3gqGXUFu`kCT;Now|Q&$D|&r zE9>K3!)`1pfaQu?sF$Uw}0@eJ$h|d(LCOW8mgHEPwe*1(wY+>pC#`eprp858tAKASM z;x4z9uK+=sHM-)dthA0%e;Db^7TIG<$FM}}y2Y)%VHk-uIusnpW#r%p-LRSXayaJ747A8L*qp^wL+`RE=klcStu#D_v$t_ z_3u_7xAsw9fYB|^a4NY|<(V>$;{$IO38O7B%n}*>cBK93yx3<%ApcZ66YWr5;#QYI zK})g65I;f{BjR=v?6Q6OMLE4h7Z?5NVvuf9X|ig2%rjUb5DC0W-U-=CH_O}vnxw`3 z${xo92_iHk$?4$gEMdbHhe~lRi)to+@X(jDBt!6;+!aP%3c&9rO1Rt>?kgB*JV%%_ zEI057D$_NeGCUUOu-iqE2YP5KAqM3glEKPtdH&;ls2~f0a%`&2hB#jkW#z4KGr!dX zw5Rj7u?;q%zWWe| zNL}pZ|H{24ewXJ!IS6Vo&&%W=6XFQ=^vckvQS3Pz{h4;elAf!>qpM|J*zV&DJ2dbt z^5d(Zo>B#ICSfaVu(`JCz(&FMY&>&j?!gavB26C{dZnKpoPK5aX?S+ON+RqR8@<@p zlA9nhl~1dGeQ8i&QeNb45@M;WsBEBjm#)BSK8`Ff08ZZeF6W`Ubn^l9I><)kb6{qw zjN9|ilU#;}8ElNB4=lgNr?P$B6{GMa%tJ#LFXVoCOHZ7GUWLTTo8}!=4h7WPpd>pC8>C55uN3|1(A5@}FrT z3neGy`WD8&dtTLJ#xgGbpN22kvF0iA-1Do0{Mgg{iNGO3;&=3fND5Z+9W=35lJC`x zy|?Qg4NzI(kl{XI^Yi4MP&tUHWlYFGYVNU@-m02=TOXoaymr-<0@ilFLTb8<4+^#) zSBKGI7bC{?4L}3XID5#aQPFyg3(}+vmHrq{N|SwcBwZ9U#*?28FxHfxRzFb!SUUwr7z69F4Z*izbY$91(qZBAX1JiSXMnOurvMT`* zC2(VFzgYV4q4HzXR_&c`&B_$s-Jt6xS_@>dq?$@-;SumZRWibTp%qCRUg@hWs=Aws zyoW8OdEG)h7la%We|jFG3>Mbwg_+wVd|cV@M$b?Gj}R0PDtReA7`4Bfd>V&x=^86 z75AggGo%CXrQl<21SA!SQn}Xqoa88ODf_5=(Knu9L2LtnFuDrYOp#x*HyOa27gDk1 z^#CSZqn2w+(y z2+j2IG0Est&8odB-;RBLXwu137X8HSGJDCHI88p=3oT;M2q$?qh}ylNf}Z$0dNNEg zlL?E6RCV73(K_)$dwDbLWM$%&T&`21!nRVTgYbZ|L5x8jvbS+Ii5_;2EOd zv(UcZ%O zZ&cZK!-pXwwr5K{d?)LAn^|SiT3=E&elvQdXW@GMfD2a@Vve}y2u;ma3|4@J6y!XW zVx<7%A(PC!hpAvoEG}g*R=&4#p9gJuc_~uQ*r1d7ZEo5islaA5M(RbM190O}-&ajV zBSKP&T>9L$JzpYE9I}N{NPwasC@fKFT`VGZzI0t+;JZ45*M+3fwVR`1m|WF_BhM8b z>p%&snPdAR66+$MQ63ID5|L+tdC$00p(HS@3Z7N1nEwTGyB;JpqLHUc9e9zZ9X~>0Bh-z#SImnyS-THIi~G$25`ylsHCe|CN6{_@#E<2c7fLVHMl7;-heO+U z%HHeO)S8O8?|UkpbSpzUPu+8c>_8v2b|7J)rak@#gkZq}q=k2Y|{mN`y;I&T3e5e-MsDOU7GEzFZgo$}LUv z_W83YSym*sc(g4(x1MRC!7jOb<`ATWmWODuOK84ryU9}0+xKp;I7`sWsRP^v+R8w*AExMS?+rP8)zi}UFsEMsE!&)M;760ROD3Qc9sTjH|CbjEyinx;*awO z0|kw)!9`q>x?IgYStG6g-*S{buyJf~1{mk*<-Vlbd?RR#72#)Vy5l`GShIE!N`WyJ zmCeT|OOYB{8yh&Ez4tcUT4FfhEhRM^+>jt25eoYDh@M*vw_P)X(h17+Gp?Gn?& zH?@*5dnfBuNTOYm$OML6a37hb0Awv1l(@;+2M&6JHDuPU(v|xLzN~gj3-@L-($VGAOQs5ajJ{hhdhKzj4;#90ozx#FTbk@* zv*CyO`tZEZ4M9y{*V4B&r;UGMtcCa;HCi7=Um?mtLKTwuT0-LkgK4u!HdCPvn+7fU zi05TJPyG7WGfptQxIyP@IsW^HFmxJSIWmw<5Xb$OF?N z`gbwvopDb~@T~_k4S2m}Ocx^R%pD{Y?x-8N3(`}NF5);$kDi~dL`c|D!$3ElIA^f5 zbOxtdL06xun-I{)6GV1ii4Z@zs_GmA`h@P^hm?62~u1MzC#^f8cPP6=J3^laPbQ|N#h95?s3F?T3r z&QRZ61a}Zc9~&OksU$sQQuUYkdaH`{fV^TN#_fC=TYV%tti1xHfjQNok)oV7Co7>w zr+Z_&jCh!`e)kZdupiQhdDbp|DfIOuA8QOM5#DCmYx{{^&W7Vc$^TVa7P=qvm|JgP5%8!KP5MoGU|oCeN`62UL#^_eCfGR6Oc z7LY=tqbYH;wGEdziBZHT#9Q8^fmoXje^s_+Bt@X)#vL+3#hOOu%XYNvQ+Bvkjq-jx zOM2qM_YDt}T5*H)y0oR&`l!LCWJ7u-J_2aHEnqC-j4)$Q%%2A51NfwDV!pQ9DI$HB z1r{i`n(>4 zdH(c-D6_kYLu0Z2IE%+2@=` zi!wp)HcFrIjJ*$Qhcc=1Bm-e41zSbPHiCIVvjN$K6YBZp@W>!X@F4+nHgx|A%l4tx zi;v9DcOf67h#_fGZ21s*d&yua1*`4w@7oav zz(%&+V2^Kpa+LJQ&>a++9KAp`PSX0ANk1xvbW~@YEt1WzKcy^+B!o#4 zN01*EI+jsmNr;q%jZt1#=c>S~N3tC;?>~&7kNFsgg~*l7>c;FY4K{|q3%s$RpgJBV zqAdz?;WxJ(&CML4054=^c}W)YD;FvGz1EQ9N6Bpg{WX`EGpLah2#!fF zJ~s~kL>o?53U5Hihh6oBFbZP5chZpBU9^S;tA_&Wf*}~a{{AtgC{N93-KA=wUtMo3 zUxZy-`5$O6UHk>vU{65Vg2K0%M}^KiM5b^y@rDvMT8hRCh&Uk9K+7R_7vN_@|^^;o$Z=Q z))HAtrEb#GWJ4!mO>)e0An4`5rPhM!RIrZ}xp+ia3M``mRD(xdOxUT@m@!*P$5@MVm8B{Pwgo36$K$alzd8pqw) zZ_@4s4Cr1l=+Yk+l?f`ARBm$}N@OG>(9TZ&?VP@6cb_mz$gO zRLc`EN0=tr4(NDkvvy(6{99u{b+tLgky)!zm?tnxs1v3@B9FGu>c+?YQi~2<*?*Qy0be(rDNk@(=(P z6azjAqiD=9`>BoIH!`53CTp&vm?jn|CLyLh@LB-hB#B-R0$`VAvnFsm{ix;+RzbNZ z+(}9V+YWKk3jAnSu<3H)L)S5WOsGVISVmrKwJx7E4xH1NYsnztvu_RT3qvO*jq=@U zt>W&^+d?^@1Ojia@UL}hRt2`txZ-n+3 zr&)lA__`c!?Dz!<$SrOl$YC@ni(A}gp9CdU>Pk<6hQB3pdo_;h>H1OS`KdNE6@n=3lQN$uNlKV$^Q9Y6zXg^r?t9cCA!ZqN` zs{~wFJ3JF%K-}nB1elRKB&ViFu^yPrp%G5o5SBs^1!_H3C)mKR@jHP?JOa&UdvKUp zvmx3_8|O7n7xodqN6RZDmV3hhDwC(2dD=2!72ng9v+Yw6`OYEV43|liYyU@zVDn1} z<9Pn1lf+DW#@Kq4zZp-jN@y+k`G0$X%vAoOIi9LBO0$JXns4)A^qB?3WP-X-ifMnvfK4h( zEK8}=mLHSuH;nS*<-Z_{6{G&DL@s3--n2vb+1VTYPH;?}Gm6s=Jf^+h@?>a8jYeCl zQWTHCUY~N+EUt1Y-y9gJLWgBH@xzJ6+c0xt(N6;3Gxk@Og?s>8Gac0h zh5fxOA+Ob2sS3aD)@Bj2Ev`jXpuHtQh%2U9M^dw1O!0I~Ox2C~o}sUkXQKRhxg;1z z2DVPts(a$PE74)}GS`J)P&-sg**~L|##|SB_qJe9^~>HFp#6i9jm(7K3GF;!`N5SV z-(hrs4ACpGlp`!Xx)e%ud!l9i>R2@=I8IZ8rf68@5!$~&mOOt|%$_=gnLQDn*M>*D z`a!&(jAW13#ElTiEE3os_M+NwsV0o{uZl$M5r%mFdQpUrT!TsU_LT2V=vdEB{5hEt zPASC1PDwf!!>1_=$Ne1Wl7jl2H6&Gj_E(-2VU0kxzq$cadARmgc3Q}v#7zON%dGm9 zu!nz&M^NDq*KNx7evLb|zaA0D4&YICELS%P@-yM_nM!`49IvdW>771p$H5HUg~%FK zu6>Ng`)#R1=gr)V*4u>P8&lwl2L_?9QbRPPn!W+C@zOQ^%^B&5Cu`hO1FqLPHh&t` zw3Ns?9wdg-xL<79fFVlNi~w;9p5hmSj9_E6*hveEzv8T;H9U*ku&mXu?u-_Mo7S%z zhd*pBavUW9jp4_4Ii2>&&+EhdOzsa<+M20~m~m;#wP(m_IBJe<)S0aejnB`)XCG&c zBTIfA-u-vRgo82g45ECA;iSoVm06=V@WYvBEwi+>$JrDbI!i&p)NH$iqbgQ3+)Hr(SipUH!Pf@njutEdhtg;|- zrHyxmxeW6=B9En{2@(#z2M5O8-9mOG6U0Q^@Uc|2ZOCjapgM|=**#~!b~v|_&g^)S z?)4(pZ6V37r|BZD2d|J<>apNiJkn|ZL)SoOle@4Kp=j9?5#lpy!?9CEP?o#}Fdr}W z$F8no$_m?n`+kzt8H;S?gFV@G@lfM+wC^lTyJ|AK!`?6*ww1DMj#TYn^K)HuFmCBg zcg-AyXxoP3x_LTmSEi`~ob;$OZHHD$pnLTh>E^F3|-E5Moj5_*sZsCYJ32|owx9alA!XVg_R?nmiiL2>6VV64n zWWcOAu@f5>PW;U{&jzZ&j9e%M#toJuP9tt}SE2jbMvs24ISu-gXd5pvXo#3T?P%_q z5LH;K60!|0=AkwcQvv!0dolL2AP#4t3_p&5=QdLN3z}4yY5;EO<)AZb{|e_RA_8Ue zBAs`bpy!tM0Hki=Xy)OdaDSgVdm!gglGb_TIf}%v`4p5CFVDa;MP{BRccpZz-vg+h z4)tEwLBVaFq3N~v(eXuX=KJW#d0IK`<_xytq}d^iX>1~_DAN}2*Lt;b^nD3QAE=yF zU7rh*6W_@=t|^^sEHc|yqaEs5r)KIX5Zgm#EV+MgbWd-*l7V?R)TkCVmJHC4XM9Yb z?ZI{&0H#6C`5-o&6h_~tOHl9&Rri}KiGMBwQ*6AMFjOAApu)yo)-NDLV?RB>lg7X= zmePO!#K)nBA>hpPe=lr%nM}1(w2X?oO8lajwFJ;(Xq-fj6F?#6w~ySG{w(&Ok-cMg z(A5lT0|MpT7`ruJV-J`Vtzcyzjr{8c@&R2f6XT}7#e6v@&lKyyy3`$W;=vHBIT5^xE~lwKu9(M{`_eTdsnnjvu$v5f_VOd>g+w zmr1ObDX}BVWchBCd}#II%hnBe9hiGZVM#!yUbc2h{4G(epV^G~KY-KQ2@xxaf?Mq) z?2$4miA<@w!dk!wyhXcuII@%F!>4sw`N%^4atbK4L;G&FlQtgD-}3>y;grsE6=ZyR z&Z`}-EdwPf;b<(I;lVo&s9*X$tzwmL_KHCdDVN&%98 zsVD3;5~byzg-s`q5AQ{>D~clb0jYNt34LHR59MtiI-8cV{+s}_CPmq_32@%*9UikP z!q=m|Q_pyA3$glr=W=3Fbry{N9TQ>Jm>*d@RUVowIT8yryguaBIePT(d~AR?UG;lUke(z2mYMrZLz1Qp&Aga1Guqn`fK zlV0lSesSPwi$kr6R|i1UPir0ogv@bzky>P&VyRLF2-n5@r8G}7me!d40L5lBp0{t$ z;#w+IENf)3}El8f*l z>DMdva}6d3iu_d+F-uH=AJjr;S>DMo9l_OKX}~~l?^WR@au{FKZ*D1Z5&`-Xm@7?Zgrd+6aE46qiZN@-?y}C;yAlH7#Srg%ZzsBS^K0T@1>8C29=xndw|r zFswT|rfscL>NNjUWZIyY5QpS;RPix^)xbco>)(trwO9q(Sbyaf9E~TrMmKQeUWJKZ zqPX6-|9kaI~s}ZBdXYyNg&7jU(5cXlz^-D4_mwU+lc#J(CFlW(d6aFpcan1`_ zy`^Ik6q-Yijcsf>*H(B!a4snk#e~J(j^ZPj*%l zkM;pCj3O%_IDjf>_;QI-=_f@sYKsBIi-H&bsCyfA8i3=N@iD&^oY#Md;Yd;~N_Ej< zX3x0jYStdH+Xnj~S@KMFk1NrYF|~{ZWyMq*!zf=`1SO*C*iLE!O`f<<_SSbE3{dZd zq{5XS^n+dyC#LbkUwE{%&>D=`B@e+MjVO-SF_5wqQRes zx4myDKLda|SNpjl$?J0yE)S{QC^BvtMsAnCb550PCGK9#=kXE;9)Ur|EceyUm<~Ja<^WsY`$Vgq zt;fFK;eg!1tF1wiTAUFA0b8YuYxcb=+gNRDF;ugE-McLUmz7if6p!N0#+fzJ?kQV# zd)4LO`yj>86Piwu96U=H2BazZ9P;c^L5n+z1g%N`v}P;dpz8 zqP@P_*nOG>XGcgHE{`CW@*PYY0zyZ9{kC1rw&i#r8&H~hT6rHaB z6>VNoz=rRc|DW@~7b{;Tgc3HA=()bd4YoMj-P6EU&C=b)zQDccGxo!2g9bF*{J4?K zU9$Im^SQI(wv&BLGD`rMui`((nBMObXi0SzJ`Y&Vhm;s_mVYQcW&{2kA1n!$jd*g= z9w{iVaD6CBDE{tGm7t}2BpUpofY*4E14?zLWHqDZ8RW>8sbV`AR*go8DE|6bq-;iteAelO+K#&B!A&8 zpa=}7;?8l44Wq*EG8WUs3zVhxUnELk{mEDb-@U3=8a9X~U+0jCG951(_g1bC4})3W z&MI|9u8jPsWofC2!NAMuORkzo+Kc6wJCC$Mznj5i>rfM=KVDzUe4ZI#i3Vmu(sVv< z9f5bzcdGfs6CToiuq#AxTrQZ)3$w+Y@rh8WOb5w_d!sLqzxbUHjtkuZiWqS6 zO;zYcTDQb!H8AA!IX1a^dE5#W;dmvdu$<4rb2U4jP$R@K_e$sD6*%@8+>usgH+(>O z9eyRW``T+TbvjEM?cVJ0CDWcdvy0*!C-?JU!Vp1#Pvb^2)Hvx8t?}7UP!#FC^>!96OYF(ml4_?W!T@7~6)~f#K;niOrO-n1(4DimHbPk6 zJup9_=jDR(IKnsp3Cb=xTdF#wtn@cC!xF8eb*e zgK6RY!^M=z@T1e_WR{b!pxEqo2;Wg(bt_t2;g_jg>(5aW%Hvo22!5r z7$PAzZCJ3otQhS2sm?QXqf znN{bIWUFPHtF)hby_!)K>~og(F6Ymf9Ppj^^*48J`)F=iD%0pV70mMaO9?T8n6o0n zzl8K$?{W+7RIug$>zEuW(TIcz)9flLEjqNG{k7ufRnOx~*x=Hn=fD&`%%5JGF~<$@ z6j2~I`Ei=2NiVvpG4v$AITRxf{SaQ_eD^0Ym*@L&!N$Mv0{r-7vwp)H0EMER>_5yK zRrKeE>3%C`@+*{Jo%>b}id3>lzavF)_-vwzrs*lysbsB*5LhttSOGLR#t9sZuML%_ zE28Oz3UKI_2VRwQCUGGP%^L8gKb((Jj_E%4P=!<*1%+`2^h`j4=L30qZ81!=svzwm z9G1q>^wQeS*K=&b;|L;63whSsX`QclTv5q07>X`amx|&jTrA)DsDEzVezRw#7|Z@g z`^Z(Z;R04q6D2xsC_$j6z`G6ApE=QArz=61!-wpOhd>U*+wtcTR6 z6=0`UYWzjnOEhtiTzUWy(UCUj!TL78xh8RrLPGJdrUVLkG0`;?l4y~>2>x(&b&`Qw z@cs~HC$~w6;PlWzLcQ_K_*ou)-eCYQ#CSHupws&kJ8V#a_!9r^5=s4W;bTLZ`{L&{-9z&D}%Wr*p&m{DmLy5Z0Jljqb8;c zN0u5}H=yOBJadZ`uqJcOsoSTT>= z0!uIU=-Q-@WpS;qPV1HkD^j*ytDv=eyf5y8-;ZfQ6Yxz3Yi z98ERH!pj(O{uzc^N#95A4xWfOMoGI<=-K_jDEq z{xUVPcPcVl3TFSpcSp88+IU>2(0{XRx}|@fr!|pYl`rxrn1O%ivlff~BoSMhIwebA z-SB31?b)%d>2~-)82}z4mr$CEz|lkCZn-#E$gP1w0T;Mal>^_^*x|;$_aZ0V+7(m* z>LnkJ?EMMw#~&;>NPSncH<6?XfPO5x%#q#!Hc!34@1wa7drUJ#)uDM}P)vxnuif0p z01JfK7WptxWeb`}_#n=XUAqJUY+*kw@P|+>L*Jbd9>yQ-4Mby- zXS`J(94)|Y2A!VR_&>NZ{ms|? zj=qVXBN26&Hxo!xr-QTeD$r`(g$DX4=hNcf?9tqbM_F%_F!p$F`EHD0N*XwRQf$W_ zY@z=Pr3GQ`{a_&VZH}_$FkSBtoT1A#ynQ4x;*9VgDHt)9c?3@6f%PrN_iiD_&%9Xy zdT!u`6-okDHAje23Gu4qZ+Rk8-=2lK$7HDCOMd8N;~sTq?=q=<;~8MG9fUb>A2^xg z5VC1tG5<{4%lALsl)ma`>6pE$OBH2{j}2o8X_P3jbwO&o%zU46Lq1ZdCeOy^yw{2N z@#=BSlKzgrL{3uL1Bb{)@h>K17JEep_5l}BC1Hn14l@x2djy)KiTvLB3t{YR?Za7j zcbLg04_TC(a$-7LwGSC+{SMkHx*$yDb(CAdRRF{3=qqKxcDm8<3ZAT2q}Uw$AVkd! zcEZ7OUwNOiV4O_4pny?0AqaQDr+Eoz*@w4?(S43#G93S35m>9=0WFd0-VMx(2E_EN zX=X*$5!~~{>RdJDb_Tt$!F{kr9k0o7k@3Fx-@1rD0&b6;TR}R$LBqoEqU!_$_z6NJ z6=wIt;#@$PBwM!c+Vx^|RM$Ld;}%Q=sFBEG6OT+G+9*kI%It$SMc6;G(mV4I*D$g& z3+(Claz1b)!M@k$65{MMLoFrGfw{5gR*7YlYGxWN|nD@fD#+zk; z_WJjuTZ6_Z-7o|lK6Dd`)&uG03=@{y+%W44Ket8+n?>==GBXod6msq(O`THpFBe$J z!!?I~3au}t&$tzYNsG2A@gH=7ly3QV`xSnux~e8nUsuIFCn1B&tbACX96k~cB3VQT z=qnuk^LK~;kr*N3EFOm_m<9i3a#yhiJSJ8`37jzBO)a9a^gku@z**3+~=w=sk z4N9vlA8>b#KbAtfaF4!yX35FY?Juj%N7_@ZiIpG5H3fpSEw&OUcv;f+F=Ch0=6Jq_xu!s9}wdEIJ2M}ZqeG#*JCbMb_$4=CYzLBRen3!;i zxaO~%$n)v=Iu9?lfc-w08Lr#60_y@T)Lbhu@GVKW>M&zE&ti89;!#h782;18gHHQ^ zl5Wm|Qk{SdT2w$EQe6l&XW$cP`Uac)x|BAfVTpx%5{Gz0yNuRjd*_Z2K6;9em9~8j zVI)zsFl6P6@Tc+eyX$f$Av-Vxe#+D;+uHgDNUgj`^lh7=!yuPJgc3;hN{}ylNuQ3$ z-ctYe>|*IeWxwsEw1k6zV!-S2UCeBF?Ujr9*@|dAQE&-CMi7e!isunj*MpJ4KIfJy@ZCp?N z@@PDR%|X-25|tAN1MXX@7$uCclNeXvVKRMh!;hz9&Ol$z$z5LXLv6 zS!q{o)ptnJa?8OsH zQ)a42QlkM*?dd;wvoTiLjqjkNJmwi>pvzi7>!LrFV;?LROnETzVhg6S_p0kaP{M!y z%&wgW!(rW_-!bB+2SAExkRJ8E4FQ zdysxVJ9sdF!o!--H<$bKzp_5@I(eF9D9T^Q#$_!WyGxJuguMjikG-F`iJ^y=JW#{E zyCb-PrzSMCk$6mtb;mj|edGS75F}MyZy=8_cm6)sF9>E_vKrTb#iEQ5d2}*+eNV^d zpN9UwjCQ!F@-|S^gj8LM$*uBPrD<8sb}th_7UJ=<--vj807bK$d5tmkvZYLLTtBN>l|EyQLLq|%Bje_BV9)%|^m&RG zj?|z%hV@-7vD=y^9P>1OwXC=i@J0-;vlKE(E!nP#b8(*PUm`}^B_ui|z=iXy(*}xc z1Xj#$9O_=hLs5&{Be}x=|J*`8{&SCu8pfG2+6 z{YIdIXdknJo(vnW*oS6YfouCcB^;WGrR#YTlqu0?iJw_z7(9?1!&{fb^NH_I#RzY{gu<+sk-=ASs+wXBn<)Et22T{ zgStfK0=zc)#WJFtMZp>xCz%IP+|OnX}U1p@yhu#4b)avQ-I*T zhfE%?`)w0{jQ1y^4R%QDoSwNK6d6k;IM4=Y zx~oWVI{f(C8XCN7H7Ih=6e8j$l?7tsQ>W;uG?^}N2L%m&jJUp^3?BL-1-=On@oq2X z!#9lEurYE@%*nvmyZbG8?!Ym=z0}zmjfU}Ngm83n=tSJ0)0Xtx7{A|goFT~n!D$!- x6D|^(xYrTVBHTEZl~y_BLO3++7LZ%?&>ldH6HYt3{Fa8RRTEP&E#(`4008_q^3?zU literal 0 HcmV?d00001 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 (