diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 0f436a95..ab11d1d5 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "3x-ui-frontend", - "version": "0.0.3", + "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "3x-ui-frontend", - "version": "0.0.3", + "version": "0.1.0", "dependencies": { "@ant-design/icons": "^6.2.3", "@codemirror/lang-json": "^6.0.2", @@ -17,6 +17,7 @@ "dayjs": "^1.11.20", "i18next": "^26.2.0", "otpauth": "^9.5.1", + "persian-calendar-suite": "^1.5.5", "qs": "^6.15.2", "react": "^19.2.6", "react-dom": "^19.2.6", @@ -82,18 +83,6 @@ "react-dom": ">=18" } }, - "node_modules/@ant-design/cssinjs/node_modules/@emotion/hash": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", - "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==", - "license": "MIT" - }, - "node_modules/@ant-design/cssinjs/node_modules/@emotion/unitless": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", - "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==", - "license": "MIT" - }, "node_modules/@ant-design/fast-color": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@ant-design/fast-color/-/fast-color-3.0.1.tgz", @@ -530,6 +519,18 @@ "tslib": "^2.4.0" } }, + "node_modules/@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==", + "license": "MIT" + }, + "node_modules/@emotion/unitless": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==", + "license": "MIT" + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", @@ -2271,21 +2272,6 @@ "react-dom": ">=18.0.0" } }, - "node_modules/antd/node_modules/compute-scroll-into-view": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.1.tgz", - "integrity": "sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==", - "license": "MIT" - }, - "node_modules/antd/node_modules/scroll-into-view-if-needed": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz", - "integrity": "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==", - "license": "MIT", - "dependencies": { - "compute-scroll-into-view": "^3.0.2" - } - }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -2460,6 +2446,12 @@ "node": ">= 0.8" } }, + "node_modules/compute-scroll-into-view": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.1.tgz", + "integrity": "sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==", + "license": "MIT" + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -2558,9 +2550,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.360", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.360.tgz", - "integrity": "sha512-GkcBt6YYAw9SxFWn+xVar4cLVGlXVuswwtRLBozi2zp0GjXs4ZnOrqV4zbXzg35n7w81hCkyJNYicgXlVHAmBA==", + "version": "1.5.361", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.361.tgz", + "integrity": "sha512-Q6Hts7N9FnJc5LeGRINFvLhCI9xZmNtTDe5ZbcVezQz7cU4a8Aua3GH1b8J2XY8Al9PF+OCwYqhgsOOheMdvkA==", "dev": true, "license": "ISC" }, @@ -3666,9 +3658,9 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.45", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.45.tgz", - "integrity": "sha512-iIbHXV9eBB2nB0wa7oTsrrXq+qQt+9SIlx9AX3T96YgobtEQfis5n6TJ6vV+3QP8DwdriEAcGhARaFCu37peBg==", + "version": "2.0.46", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.46.tgz", + "integrity": "sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==", "dev": true, "license": "MIT", "engines": { @@ -3769,6 +3761,15 @@ "node": ">=8" } }, + "node_modules/persian-calendar-suite": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/persian-calendar-suite/-/persian-calendar-suite-1.5.5.tgz", + "integrity": "sha512-KJSzN9q7MZKhfkm97X/j+nD6L0AQ5coUq/B7PpIklXAvRjkALwiV+KmYG0pfr546EQxO9l4fBwE7R1HPI3yT7w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -3956,6 +3957,15 @@ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", "license": "MIT" }, + "node_modules/scroll-into-view-if-needed": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz", + "integrity": "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==", + "license": "MIT", + "dependencies": { + "compute-scroll-into-view": "^3.0.2" + } + }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 70cae164..c36aa065 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "3x-ui-frontend", "private": true, - "version": "0.0.3", + "version": "0.1.0", "type": "module", "description": "3x-ui panel frontend (React 19 + Ant Design 6 + Vite 8).", "engines": { @@ -25,6 +25,7 @@ "dayjs": "^1.11.20", "i18next": "^26.2.0", "otpauth": "^9.5.1", + "persian-calendar-suite": "^1.5.5", "qs": "^6.15.2", "react": "^19.2.6", "react-dom": "^19.2.6", diff --git a/frontend/src/components/DateTimePicker.css b/frontend/src/components/DateTimePicker.css new file mode 100644 index 00000000..b745afb4 --- /dev/null +++ b/frontend/src/components/DateTimePicker.css @@ -0,0 +1,35 @@ +.jdp-wrap { + width: 100%; +} + +.jdp-wrap > * { + width: 100%; +} + +.jdp-wrap input { + direction: ltr; + text-align: left; + unicode-bidi: plaintext; +} + +.jdp-dark .jdp-wrap input, +.jdp-dark input { + color: rgba(255, 255, 255, 0.88) !important; + background-color: #23252b !important; +} + +.jdp-ultra .jdp-wrap input, +.jdp-ultra input { + color: rgba(255, 255, 255, 0.88) !important; + background-color: #101013 !important; +} + +.jdp-dark input::placeholder, +.jdp-ultra input::placeholder { + color: rgba(255, 255, 255, 0.30) !important; +} + +.jdp-disabled { + pointer-events: none; + opacity: 0.6; +} diff --git a/frontend/src/components/DateTimePicker.tsx b/frontend/src/components/DateTimePicker.tsx index b5ff13f2..bdd521b6 100644 --- a/frontend/src/components/DateTimePicker.tsx +++ b/frontend/src/components/DateTimePicker.tsx @@ -1,5 +1,12 @@ +import { useMemo } from 'react'; import { DatePicker } from 'antd'; +import dayjs from 'dayjs'; import type { Dayjs } from 'dayjs'; +import { PersianDateTimePicker } from 'persian-calendar-suite'; + +import { useDatepicker } from '@/hooks/useDatepicker'; +import { useTheme } from '@/hooks/useTheme'; +import './DateTimePicker.css'; interface DateTimePickerProps { value: Dayjs | null; @@ -10,6 +17,33 @@ interface DateTimePickerProps { disabled?: boolean; } +const LIGHT_THEME = { + primaryColor: '#1677ff', + backgroundColor: '#ffffff', + borderColor: '#d9d9d9', + hoverColor: 'rgba(22, 119, 255, 0.10)', + selectedTextColor: '#ffffff', + textColor: 'rgba(0, 0, 0, 0.88)', +}; + +const DARK_THEME = { + primaryColor: '#1677ff', + backgroundColor: '#23252b', + borderColor: 'rgba(255, 255, 255, 0.12)', + hoverColor: 'rgba(22, 119, 255, 0.18)', + selectedTextColor: '#ffffff', + textColor: 'rgba(255, 255, 255, 0.88)', +}; + +const ULTRA_DARK_THEME = { + primaryColor: '#1677ff', + backgroundColor: '#101013', + borderColor: 'rgba(255, 255, 255, 0.08)', + hoverColor: 'rgba(22, 119, 255, 0.16)', + selectedTextColor: '#ffffff', + textColor: 'rgba(255, 255, 255, 0.88)', +}; + export default function DateTimePicker({ value, onChange, @@ -18,6 +52,38 @@ export default function DateTimePicker({ placeholder = '', disabled = false, }: DateTimePickerProps) { + const { datepicker } = useDatepicker(); + const { isDark, isUltra } = useTheme(); + + const persianTheme = useMemo(() => { + if (isUltra) return ULTRA_DARK_THEME; + if (isDark) return DARK_THEME; + return LIGHT_THEME; + }, [isDark, isUltra]); + + if (datepicker === 'jalalian') { + return ( +