From fd054bc68824306b4ebac50ad77ab701ff7d112c Mon Sep 17 00:00:00 2001 From: gnezim Date: Sun, 5 Apr 2026 20:59:02 +0300 Subject: [PATCH] feat: create TimeSelector time range component --- .../src/app/components/time-selector/index.ts | 2 + .../time-selector/time-selector.scss | 42 +++++++ .../time-selector/time-selector.tsx | 112 ++++++++++++++++++ 3 files changed, 156 insertions(+) create mode 100644 apps/react/src/app/components/time-selector/index.ts create mode 100644 apps/react/src/app/components/time-selector/time-selector.scss create mode 100644 apps/react/src/app/components/time-selector/time-selector.tsx diff --git a/apps/react/src/app/components/time-selector/index.ts b/apps/react/src/app/components/time-selector/index.ts new file mode 100644 index 000000000..4026dee10 --- /dev/null +++ b/apps/react/src/app/components/time-selector/index.ts @@ -0,0 +1,2 @@ +export { TimeSelector } from './time-selector' +export type { TimeSelectorProps } from './time-selector' diff --git a/apps/react/src/app/components/time-selector/time-selector.scss b/apps/react/src/app/components/time-selector/time-selector.scss new file mode 100644 index 000000000..fbf39014f --- /dev/null +++ b/apps/react/src/app/components/time-selector/time-selector.scss @@ -0,0 +1,42 @@ +.time-selector { + display: flex; + gap: 20px; + margin: 16px 0; +} + +.time-selector__group { + flex: 1; + display: flex; + flex-direction: column; + gap: 8px; +} + +.time-selector__label { + font-size: 12px; + font-weight: 500; + color: #666; + text-transform: uppercase; +} + +.time-selector__inputs { + display: flex; + align-items: center; + gap: 8px; +} + +.time-selector__input { + width: 60px; + text-align: center; + + :global { + .p-inputnumber-input { + padding: 8px 4px; + font-size: 14px; + } + } +} + +.time-selector__separator { + font-weight: 600; + color: #666; +} diff --git a/apps/react/src/app/components/time-selector/time-selector.tsx b/apps/react/src/app/components/time-selector/time-selector.tsx new file mode 100644 index 000000000..644adbba8 --- /dev/null +++ b/apps/react/src/app/components/time-selector/time-selector.tsx @@ -0,0 +1,112 @@ +import React from 'react' +import { InputNumber } from 'primereact/inputnumber' +import { useTranslation } from 'react-i18next' +import './time-selector.scss' + +export interface TimeSelectorProps { + startTime?: string + endTime?: string + onStartTimeChange?: (time: string) => void + onEndTimeChange?: (time: string) => void + 'data-testid'?: string +} + +export const TimeSelector: React.FC = ({ + startTime = '00:00', + endTime = '23:59', + onStartTimeChange, + onEndTimeChange, + 'data-testid': dataTestId, +}) => { + const { t } = useTranslation() + + const parseTime = (timeStr: string): { hours: number; minutes: number } => { + const [h, m] = timeStr.split(':').map(Number) + return { hours: h || 0, minutes: m || 0 } + } + + const formatTime = (hours: number, minutes: number): string => { + return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}` + } + + const handleStartHoursChange = (value: number | null | undefined) => { + if (onStartTimeChange && value !== null && value !== undefined) { + const { minutes } = parseTime(startTime) + onStartTimeChange(formatTime(value, minutes)) + } + } + + const handleStartMinutesChange = (value: number | null | undefined) => { + if (onStartTimeChange && value !== null && value !== undefined) { + const { hours } = parseTime(startTime) + onStartTimeChange(formatTime(hours, value)) + } + } + + const handleEndHoursChange = (value: number | null | undefined) => { + if (onEndTimeChange && value !== null && value !== undefined) { + const { minutes } = parseTime(endTime) + onEndTimeChange(formatTime(value, minutes)) + } + } + + const handleEndMinutesChange = (value: number | null | undefined) => { + if (onEndTimeChange && value !== null && value !== undefined) { + const { hours } = parseTime(endTime) + onEndTimeChange(formatTime(hours, value)) + } + } + + const startParsed = parseTime(startTime) + const endParsed = parseTime(endTime) + + return ( +
+
+ +
+ handleStartHoursChange(e.value)} + min={0} + max={23} + className="time-selector__input" + data-testid="time-selector-start-hours" + /> + : + handleStartMinutesChange(e.value)} + min={0} + max={59} + className="time-selector__input" + data-testid="time-selector-start-minutes" + /> +
+
+ +
+ +
+ handleEndHoursChange(e.value)} + min={0} + max={23} + className="time-selector__input" + data-testid="time-selector-end-hours" + /> + : + handleEndMinutesChange(e.value)} + min={0} + max={59} + className="time-selector__input" + data-testid="time-selector-end-minutes" + /> +
+
+
+ ) +}