From 63f241bce1a27e79544e7fd51181a3e1b30f24c7 Mon Sep 17 00:00:00 2001 From: Drish-xD Date: Wed, 14 Aug 2024 00:13:26 +0530 Subject: [PATCH 1/3] chore: add dropdown navigation to date-picker --- src/Constants/index.ts | 3 ++ .../session/[type]/Steps/Timeline/index.tsx | 8 ++-- src/components/ui/calendar.tsx | 39 ++++++++++++++++++- src/components/ui/date-time.tsx | 6 ++- 4 files changed, 51 insertions(+), 5 deletions(-) diff --git a/src/Constants/index.ts b/src/Constants/index.ts index 822ff7b..4163eaf 100644 --- a/src/Constants/index.ts +++ b/src/Constants/index.ts @@ -10,6 +10,9 @@ export const PAGE_SIZE_OPTIONS = [10, 25, 50, 100, 200]; // TABLE POLLING CONSTANTS export const POLLING_INTERVAL = 30000; // 30 seconds +// Date Picker Allowed Range +export const ALLOWED_YEARS = 5; + /** * Keys to be deleted before duplicating a session * from preventing the fields from being duplicated diff --git a/src/app/session/[type]/Steps/Timeline/index.tsx b/src/app/session/[type]/Steps/Timeline/index.tsx index 44847e3..e532512 100644 --- a/src/app/session/[type]/Steps/Timeline/index.tsx +++ b/src/app/session/[type]/Steps/Timeline/index.tsx @@ -1,6 +1,6 @@ 'use client'; -import { ActiveDaysOptions } from '@/Constants'; +import { ActiveDaysOptions, ALLOWED_YEARS } from '@/Constants'; import { FormBuilder } from '@/components/FormBuilder'; import { useFormContext } from '@/hooks/useFormContext'; import { FieldSchema, Session, timelineFields, timelineSchema } from '@/types'; @@ -17,13 +17,15 @@ const TimelineForm: FC = () => { type: 'datetime', label: 'Start Date And Time', placeholder: 'Select start date and time', - disableRange: (date: Date) => date < startOfToday() || date > addYears(startOfToday(), 1), + disableRange: (date: Date) => + date < startOfToday() || date > addYears(startOfToday(), ALLOWED_YEARS), }, endDate: { type: 'datetime', label: 'End Date And Time', placeholder: 'Select end date and time', - disableRange: (date: Date) => date < startOfToday() || date > addYears(startOfToday(), 1), + disableRange: (date: Date) => + date < startOfToday() || date > addYears(startOfToday(), ALLOWED_YEARS), }, testTakers: { type: 'number', diff --git a/src/components/ui/calendar.tsx b/src/components/ui/calendar.tsx index 2d512ae..05ffdd3 100644 --- a/src/components/ui/calendar.tsx +++ b/src/components/ui/calendar.tsx @@ -6,6 +6,7 @@ import { DayPicker } from 'react-day-picker'; import { buttonVariants } from '@/components/ui/button'; import { cn } from '@/lib/utils'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './select'; export type CalendarProps = React.ComponentProps; @@ -18,7 +19,8 @@ function Calendar({ className, classNames, showOutsideDays = true, ...props }: C months: 'flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0', month: 'space-y-4', caption: 'flex justify-center pt-1 relative items-center', - caption_label: 'text-sm font-medium', + caption_label: 'hidden', + caption_dropdowns: 'flex items-center justify-center gap-2', nav: 'space-x-1 flex items-center', nav_button: cn( buttonVariants({ variant: 'outline' }), @@ -49,6 +51,41 @@ function Calendar({ className, classNames, showOutsideDays = true, ...props }: C components={{ IconLeft: ({ ...props }) => , IconRight: ({ ...props }) => , + Dropdown: ({ name, value, children, onChange }) => { + type OptionElement = React.ReactElement>; + const options = React.Children.toArray(children) as OptionElement[]; + + return ( + + ); + }, }} {...props} /> diff --git a/src/components/ui/date-time.tsx b/src/components/ui/date-time.tsx index 24d0349..c8db3da 100644 --- a/src/components/ui/date-time.tsx +++ b/src/components/ui/date-time.tsx @@ -1,9 +1,10 @@ import { Button } from '@/components/ui/button'; import { FormControl, FormLabel } from '@/components/ui/form'; import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; +import { ALLOWED_YEARS } from '@/Constants'; import { cn } from '@/lib/utils'; import { MyDateTimeProps } from '@/types'; -import { format } from 'date-fns'; +import { addYears, format, startOfToday } from 'date-fns'; import { CalendarIcon } from 'lucide-react'; import { ElementRef, forwardRef } from 'react'; import { ControllerRenderProps, UseFormReturn } from 'react-hook-form'; @@ -41,6 +42,9 @@ const DateTimePicker = forwardRef< Date: Thu, 15 Aug 2024 21:01:18 +0530 Subject: [PATCH 2/3] chore: seperate date & time --- .../session/[type]/Steps/Timeline/index.tsx | 10 ++- src/components/ui/date-time.tsx | 80 ++++++++++--------- src/components/ui/time-picker.tsx | 73 +++++++---------- src/types/formbuilder.types.ts | 8 +- 4 files changed, 88 insertions(+), 83 deletions(-) diff --git a/src/app/session/[type]/Steps/Timeline/index.tsx b/src/app/session/[type]/Steps/Timeline/index.tsx index e532512..9deb077 100644 --- a/src/app/session/[type]/Steps/Timeline/index.tsx +++ b/src/app/session/[type]/Steps/Timeline/index.tsx @@ -15,14 +15,20 @@ const TimelineForm: FC = () => { return { startDate: { type: 'datetime', - label: 'Start Date And Time', + label: { + date: 'Start Date', + time: 'Start Time', + }, placeholder: 'Select start date and time', disableRange: (date: Date) => date < startOfToday() || date > addYears(startOfToday(), ALLOWED_YEARS), }, endDate: { type: 'datetime', - label: 'End Date And Time', + label: { + date: 'End Date', + time: 'End Time', + }, placeholder: 'Select end date and time', disableRange: (date: Date) => date < startOfToday() || date > addYears(startOfToday(), ALLOWED_YEARS), diff --git a/src/components/ui/date-time.tsx b/src/components/ui/date-time.tsx index c8db3da..407c228 100644 --- a/src/components/ui/date-time.tsx +++ b/src/components/ui/date-time.tsx @@ -19,43 +19,49 @@ const DateTimePicker = forwardRef< const { label, disabled, disableRange, ...restSchemaProps } = schema; return ( -
- {label} - - - - - - - - -
- -
-
-
+
+
+ {label?.date} + + + + + + + + + + +
+
+ {label?.time} + +
); }); diff --git a/src/components/ui/time-picker.tsx b/src/components/ui/time-picker.tsx index ef3edba..bdd7d2e 100644 --- a/src/components/ui/time-picker.tsx +++ b/src/components/ui/time-picker.tsx @@ -1,5 +1,4 @@ import { Input } from '@/components/ui/input'; -import { Label } from '@/components/ui/label'; import { Select, SelectContent, @@ -16,6 +15,7 @@ import { setDateByType, } from '@/lib/time-picker-utils'; import { cn } from '@/lib/utils'; +import { ClockIcon } from 'lucide-react'; import React from 'react'; export interface TimePickerInputProps extends React.InputHTMLAttributes { @@ -198,48 +198,35 @@ function TimePicker({ date, setDate }: TimePickerProps) { const periodRef = React.useRef(null); return ( -
-
- - minuteRef.current?.focus()} - /> -
-
- - hourRef.current?.focus()} - onRightFocus={() => periodRef.current?.focus()} - /> -
-
- - periodRef.current?.focus()} - /> -
+
+ + minuteRef.current?.focus()} + /> + : + hourRef.current?.focus()} + onRightFocus={() => periodRef.current?.focus()} + /> + periodRef.current?.focus()} + />
); } diff --git a/src/types/formbuilder.types.ts b/src/types/formbuilder.types.ts index 6cb6194..d7a2c6a 100644 --- a/src/types/formbuilder.types.ts +++ b/src/types/formbuilder.types.ts @@ -77,9 +77,15 @@ export interface MySwitchProps onCheckedChange?(checked: boolean, form: UseFormReturn): void; } -export interface MyDateTimeProps extends CommonFieldProps, Omit { +export interface MyDateTimeProps + extends Omit, + Omit { type: 'datetime'; disableRange: (date: Date) => boolean; + label?: { + date?: string; + time?: string; + }; } export interface CommonFieldProps { From b3d6eb67c134f6631d26cfdd7644e36352b87def Mon Sep 17 00:00:00 2001 From: Drish-xD Date: Sat, 17 Aug 2024 02:08:29 +0900 Subject: [PATCH 3/3] chore: fix timing issue --- src/Constants/index.ts | 1 + .../session/[type]/Steps/Timeline/index.tsx | 4 +-- src/components/ui/date-time.tsx | 17 +++++++-- src/components/ui/time-picker.tsx | 12 +++---- src/lib/time-picker-utils.ts | 35 ++++--------------- 5 files changed, 29 insertions(+), 40 deletions(-) diff --git a/src/Constants/index.ts b/src/Constants/index.ts index 4163eaf..ac6b612 100644 --- a/src/Constants/index.ts +++ b/src/Constants/index.ts @@ -12,6 +12,7 @@ export const POLLING_INTERVAL = 30000; // 30 seconds // Date Picker Allowed Range export const ALLOWED_YEARS = 5; +export const UTC_IST_OFFSET = 5.5 * 60; // +5:30 (in minutes) /** * Keys to be deleted before duplicating a session diff --git a/src/app/session/[type]/Steps/Timeline/index.tsx b/src/app/session/[type]/Steps/Timeline/index.tsx index 9deb077..eec6248 100644 --- a/src/app/session/[type]/Steps/Timeline/index.tsx +++ b/src/app/session/[type]/Steps/Timeline/index.tsx @@ -73,8 +73,8 @@ const TimelineForm: FC = () => { params: data.activeDays.sort((a, b) => a - b), }, is_active: data.isEnabled, - start_time: new Date(data.startDate).toISOString(), - end_time: new Date(data.endDate).toISOString(), + start_time: new Date(data.startDate).toUTCString(), + end_time: new Date(data.endDate).toUTCString(), }; updateFormData(addedData); diff --git a/src/components/ui/date-time.tsx b/src/components/ui/date-time.tsx index 407c228..1cf9e15 100644 --- a/src/components/ui/date-time.tsx +++ b/src/components/ui/date-time.tsx @@ -4,7 +4,7 @@ import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover import { ALLOWED_YEARS } from '@/Constants'; import { cn } from '@/lib/utils'; import { MyDateTimeProps } from '@/types'; -import { addYears, format, startOfToday } from 'date-fns'; +import { addDays, addYears, format, startOfToday } from 'date-fns'; import { CalendarIcon } from 'lucide-react'; import { ElementRef, forwardRef } from 'react'; import { ControllerRenderProps, UseFormReturn } from 'react-hook-form'; @@ -18,6 +18,18 @@ const DateTimePicker = forwardRef< const { value, onChange, ref: refField, ...restFieldProps } = field; const { label, disabled, disableRange, ...restSchemaProps } = schema; + const handleSelect = (newDay: Date | undefined) => { + if (!newDay) return; + if (!value) { + onChange?.(newDay); + return; + } + const diff = newDay.getTime() - value.getTime(); + const diffInDays = diff / (1000 * 60 * 60 * 24); + const newDateFull = addDays(value, Math.ceil(diffInDays)); + onChange?.(newDateFull); + }; + return (
@@ -51,7 +63,8 @@ const DateTimePicker = forwardRef< toYear={addYears(startOfToday(), ALLOWED_YEARS).getFullYear()} mode="single" selected={value} - onSelect={onChange} + onSelect={(d) => handleSelect(d)} + onMonthChange={handleSelect} initialFocus disabled={disableRange} /> diff --git a/src/components/ui/time-picker.tsx b/src/components/ui/time-picker.tsx index bdd7d2e..cdfcf41 100644 --- a/src/components/ui/time-picker.tsx +++ b/src/components/ui/time-picker.tsx @@ -73,7 +73,7 @@ const TimePickerInput = React.forwardRef * If picker is '12hours' and the first digit is 0, then the second digit is automatically set to 1. * The second entered digit will break the condition and the value will be set to 10-12. */ - if (picker === '12hours') { + if (picker === 'hours') { if (flag && calculatedValue.slice(1, 2) === '1' && prevIntKey === '0') return '0' + key; } @@ -93,7 +93,7 @@ const TimePickerInput = React.forwardRef setDate(setDateByType(tempDate, newValue, picker, period)); } if (e.key >= '0' && e.key <= '9') { - if (picker === '12hours') setPrevIntKey(e.key); + if (picker === 'hours') setPrevIntKey(e.key); const newValue = calculateNewValue(e.key); if (flag) onRightFocus?.(); @@ -157,9 +157,7 @@ const TimePeriodSelect = React.forwardRef('PM'); + const [period, setPeriod] = React.useState(date && date.getHours() >= 12 ? 'PM' : 'AM'); const minuteRef = React.useRef(null); const hourRef = React.useRef(null); @@ -202,7 +200,7 @@ function TimePicker({ date, setDate }: TimePickerProps) {