Improve planning a lot

This commit is contained in:
Arthur Belleville 2025-07-09 08:01:53 +02:00
parent 34d700f2f4
commit 6b4710a902
No known key found for this signature in database
4 changed files with 465 additions and 140 deletions

View file

@ -3,6 +3,17 @@ import { useState } from "react";
import { useTablosList } from "@ui/hooks/tablos";
import { useCreateEvent } from "@ui/hooks/events";
import { useUser } from "@ui/providers/UserStoreProvider";
import {
Select,
SelectButton,
SelectPopover,
SelectListBox,
SelectListItem,
} from "@ui/ui-library/select";
import { useTimePicker } from "@ui/ui-library/time-picker";
import { DatePicker, DatePickerButton } from "@ui/ui-library/date-picker";
import { Group } from "react-aria-components";
import { getLocalTimeZone, parseDate, today } from "@internationalized/date";
interface EventModalProps {
date: Date;
@ -13,9 +24,43 @@ export const CreateEventModal = ({ date, onClose }: EventModalProps) => {
const user = useUser();
const { data: tablos, isLoading: tablosLoading } = useTablosList();
const createEvent = useCreateEvent();
const timeOptions = useTimePicker({ intervalInMinute: 15 });
// Get the local date string without timezone conversion
const getLocalDateString = (date: Date) => {
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, "0");
const day = date.getDate().toString().padStart(2, "0");
return `${year}-${month}-${day}`;
};
// Find the nearest time option to the selected date
const getNearestTimeOption = (date: Date, type: "start" | "end") => {
const dateMinutes = date.getHours() * 60 + date.getMinutes();
let nearestOption = timeOptions[0];
let smallestDiff = Infinity;
for (const option of timeOptions) {
const optionMinutes = option.hour * 60 + option.minute;
const diff =
type === "start"
? Math.abs(dateMinutes - optionMinutes)
: dateMinutes + 30 - optionMinutes;
if (0 <= diff && diff < smallestDiff) {
smallestDiff = diff;
nearestOption = option;
}
}
return nearestOption?.id || "";
};
const [createdEvent, setCreatedEvent] = useState<EventInsert>({
start_date: date?.toISOString().split("T")[0] || "",
start_time: date?.toISOString().split("T")[1] || "",
start_date: date ? getLocalDateString(date) : "",
start_time: date ? getNearestTimeOption(date, "start") : "",
end_time: date ? getNearestTimeOption(date, "end") : "",
tablo_id: "",
title: "",
created_by: user.id,
@ -31,6 +76,7 @@ export const CreateEventModal = ({ date, onClose }: EventModalProps) => {
<button
onClick={onClose}
className="text-white hover:text-gray-200 transition-colors"
aria-label="Fermer le modal"
>
<svg
className="w-6 h-6"
@ -72,6 +118,7 @@ export const CreateEventModal = ({ date, onClose }: EventModalProps) => {
}
className="w-full text-lg font-medium border-none outline-none bg-transparent text-gray-900 dark:text-white placeholder-gray-400 focus:ring-0 px-0"
placeholder="Ajouter un titre"
aria-label="Titre de l'événement"
autoFocus
/>
<div className="border-b border-gray-200 dark:border-gray-700"></div>
@ -82,61 +129,121 @@ export const CreateEventModal = ({ date, onClose }: EventModalProps) => {
<label className="block text-sm font-medium text-gray-600 dark:text-gray-400">
Tablo *
</label>
<select
value={createdEvent?.tablo_id}
onChange={(e) =>
<Select
placeholder="Sélectionner un tablo"
selectedKey={createdEvent?.tablo_id}
onSelectionChange={(key) =>
setCreatedEvent({
...createdEvent,
tablo_id: e.target.value,
tablo_id: key as string,
} as Event)
}
className="w-full px-3 py-2.5 border border-gray-200 dark:border-gray-700 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent dark:bg-gray-800 dark:text-white transition-all"
disabled={tablosLoading}
className="w-full"
aria-label="Sélectionner un tablo"
isDisabled={tablosLoading}
>
<option value="">Sélectionner un tablo</option>
{tablos?.map((tablo) => (
<option key={tablo.id} value={tablo.id}>
{tablo.name}
</option>
))}
</select>
<SelectButton className="w-full px-3 py-2.5 border border-gray-200 dark:border-gray-700 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent dark:bg-gray-800 dark:text-white transition-all text-left" />
<SelectPopover>
<SelectListBox>
{tablos?.map((tablo) => (
<SelectListItem key={tablo.id} id={tablo.id}>
{tablo.name}
</SelectListItem>
))}
</SelectListBox>
</SelectPopover>
</Select>
</div>
{/* Time Selection */}
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<label className="block text-sm font-medium text-gray-600 dark:text-gray-400">
<Group className="flex flex-row gap-4">
<div className="flex flex-col gap-2">
<label className="text-sm font-medium text-gray-600 dark:text-gray-400">
Date
</label>
<DatePicker
aria-label="Date de l'événement"
value={
createdEvent?.start_date
? parseDate(createdEvent?.start_date)
: null
}
minValue={today(getLocalTimeZone())}
onChange={(value) => {
if (value === null) {
return;
}
setCreatedEvent({
...createdEvent,
start_date: value.toString(),
});
}}
>
<DatePickerButton className="h-[36px]" />
</DatePicker>
</div>
<div className="flex flex-col gap-2">
<label className="text-sm font-medium text-gray-600 dark:text-gray-400">
Début
</label>
<input
type="time"
value={createdEvent?.start_time}
onChange={(e) =>
setCreatedEvent({
...createdEvent,
start_time: e.target.value,
} as Event)
}
className="w-full px-3 py-2.5 border border-gray-200 dark:border-gray-700 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent dark:bg-gray-800 dark:text-white transition-all"
/>
<Select
aria-label="Heure de début"
className="min-w-[110px]"
selectedKey={createdEvent?.start_time}
onSelectionChange={(value) => {
const option = timeOptions.find(
(option) => option.id === value
);
if (option) {
setCreatedEvent({
...createdEvent,
start_time: value.toString(),
});
}
}}
>
<SelectButton />
<SelectPopover className="w-36">
<SelectListBox items={timeOptions}>
{(item) => {
return <SelectListItem>{item.value}</SelectListItem>;
}}
</SelectListBox>
</SelectPopover>
</Select>
</div>
<div className="space-y-2">
<label className="block text-sm font-medium text-gray-600 dark:text-gray-400">
<div className="flex flex-col gap-2">
<label className="text-sm font-medium text-gray-600 dark:text-gray-400">
Fin
</label>
<input
type="time"
value={createdEvent?.end_time ?? ""}
onChange={(e) =>
setCreatedEvent({
...createdEvent,
end_time: e.target.value,
} as Event)
}
className="w-full px-3 py-2.5 border border-gray-200 dark:border-gray-700 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent dark:bg-gray-800 dark:text-white transition-all"
/>
<Select
aria-label="Heure de fin"
className="min-w-[110px]"
selectedKey={createdEvent?.end_time}
onSelectionChange={(value) => {
const option = timeOptions.find(
(option) => option.id === value
);
if (option) {
setCreatedEvent({
...createdEvent,
end_time: value.toString(),
});
}
}}
>
<SelectButton />
<SelectPopover className="w-36">
<SelectListBox items={timeOptions}>
{(item) => {
return <SelectListItem>{item.value}</SelectListItem>;
}}
</SelectListBox>
</SelectPopover>
</Select>
</div>
</div>
</Group>
{/* Description */}
<div className="space-y-2">
@ -154,6 +261,7 @@ export const CreateEventModal = ({ date, onClose }: EventModalProps) => {
rows={3}
className="w-full px-3 py-2.5 border border-gray-200 dark:border-gray-700 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent dark:bg-gray-800 dark:text-white resize-none transition-all"
placeholder="Ajouter une description (optionnel)"
aria-label="Description de l'événement"
/>
</div>
</div>
@ -164,16 +272,22 @@ export const CreateEventModal = ({ date, onClose }: EventModalProps) => {
type="button"
className="px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors"
onClick={onClose}
aria-label="Annuler la création d'événement"
>
Annuler
</button>
<button
type="button"
className="px-6 py-2 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed transition-all shadow-sm hover:shadow-md"
onClick={() =>
createEvent(createdEvent, { onSuccess: () => onClose() })
}
disabled={!createdEvent?.title.trim() || !createdEvent?.tablo_id}
onClick={() => {
const eventName = createdEvent?.title.trim() || "(Sans titre)";
createEvent(
{ ...createdEvent, title: eventName },
{ onSuccess: () => onClose() }
);
}}
disabled={!createdEvent?.tablo_id}
aria-label="Enregistrer l'événement"
>
Enregistrer
</button>

View file

@ -10,10 +10,20 @@ import {
} from "@ui/types/events.types";
// Fetch events for a specific tablo
export const useEventsByTablo = (tabloId: string) => {
export const useEventsByTablo = (tabloId: string | null) => {
return useQuery({
queryKey: ["events", tabloId],
queryFn: async () => {
if (!tabloId) {
const { data, error } = await supabase
.from("events_and_tablos")
.select("*")
.order("start_date", { ascending: true })
.order("start_time", { ascending: true });
if (error) throw error;
return data as EventAndTablo[];
}
const { data, error } = await supabase
.from("events_and_tablos")
.select("*")
@ -24,7 +34,6 @@ export const useEventsByTablo = (tabloId: string) => {
if (error) throw error;
return data as EventAndTablo[];
},
enabled: !!tabloId,
});
};
@ -92,8 +101,8 @@ export const useCreateEvent = () => {
if (error) throw error;
return data as Event;
},
onSuccess: (data) => {
queryClient.invalidateQueries({ queryKey: ["events", data.tablo_id] });
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["events"] });
toast.add(
{
title: "Événement créé",

View file

@ -1,29 +1,33 @@
import { useState, useEffect } from "react";
import { useState } from "react";
import { useTablosList } from "@ui/hooks/tablos";
import { useEventsByTablo, useDeleteEvent } from "@ui/hooks/events";
import { CreateEventModal } from "@ui/components/CreateEventModal";
import {
Select,
SelectButton,
SelectPopover,
SelectListBox,
SelectListItem,
} from "@ui/ui-library/select";
import { useParams } from "react-router-dom";
type ViewType = "month" | "week" | "day";
export const PlanningPage = () => {
const { tablo_id } = useParams();
const [currentDate, setCurrentDate] = useState(new Date());
const [selectedDate, setSelectedDate] = useState(new Date());
const [currentView, setCurrentView] = useState<ViewType>("month");
const [selectedTabloId, setSelectedTabloId] = useState<string>("");
const [selectedTabloId, setSelectedTabloId] = useState<string>(
tablo_id || "all"
);
// Fetch tablos
const { data: tablos, isLoading: tablosLoading } = useTablosList();
// Set default tablo if none selected
useEffect(() => {
if (tablos && tablos.length > 0 && !selectedTabloId) {
setSelectedTabloId(tablos[0].id);
}
}, [tablos, selectedTabloId]);
// Fetch events for selected tablo
const { data: events = [], isLoading: eventsLoading } =
useEventsByTablo(selectedTabloId);
// Fetch events for selected tablo or all tablos
const { data: tabloEvents = [], isLoading: tabloEventsLoading } =
useEventsByTablo(selectedTabloId !== "all" ? selectedTabloId : null);
const deleteEvent = useDeleteEvent();
@ -64,9 +68,68 @@ export const PlanningPage = () => {
return `${year}-${month}-${day}`;
};
const formatTime = (time: string) => {
// Remove seconds from time string (HH:MM:SS -> HH:MM)
return time.substring(0, 5);
};
const calculateEventHeight = (startTime: string, endTime: string) => {
if (!endTime) return 60; // Default height if no end time
const [startHour, startMinute] = startTime.split(":").map(Number);
const [endHour, endMinute] = endTime.split(":").map(Number);
const startTotalMinutes = startHour * 60 + startMinute;
const endTotalMinutes = endHour * 60 + endMinute;
const durationMinutes = endTotalMinutes - startTotalMinutes;
// Each hour is 60px, so calculate proportional height
return Math.max(durationMinutes, 30); // Minimum 30px height
};
const calculateEventOffset = (startTime: string, slotTime: string) => {
const [startHour, startMinute] = startTime.split(":").map(Number);
const [slotHour] = slotTime.split(":").map(Number);
if (startHour === slotHour) {
// Calculate offset within the hour slot
return startMinute; // Each minute is 1px
}
return 0;
};
const getEventsForDate = (date: Date) => {
const dateString = formatDate(date);
return events.filter((event) => event.start_date === dateString);
return tabloEvents.filter((event) => event.start_date === dateString);
};
const getCurrentTimePosition = () => {
const now = new Date();
const minutes = now.getMinutes();
// Position within the current hour slot (0-60px based on minutes)
return minutes;
};
const isWithinCurrentHour = (fullDate: Date, time: string) => {
const now = new Date();
const nowHour = now.getHours();
// const nowMinute = now.getMinutes();
const nowDay = now.getDate();
fullDate.setHours(
Number(time.split(":")[0]),
Number(time.split(":")[1]),
0,
0
);
const hour = fullDate.getHours();
// const minute = fullDate.getMinutes();
const day = fullDate.getDate();
return nowDay === day && nowHour === hour;
};
const navigateDate = (direction: number) => {
@ -220,20 +283,31 @@ export const PlanningPage = () => {
.map((event) => (
<div
key={event.event_id}
className={`text-xs px-2 py-1 rounded text-white ${event.tablo_color} truncate cursor-pointer hover:opacity-80 group relative`}
title={`${event.start_time} ${event.title}`}
className={`text-[10px] px-1.5 py-0.5 rounded text-white ${event.tablo_color} truncate cursor-pointer hover:opacity-80 group relative leading-tight`}
title={`${formatTime(event.start_time)} ${event.title}${
selectedTabloId === "all" && event.tablo_name
? ` - ${event.tablo_name}`
: ""
}`}
onClick={(e) => {
e.stopPropagation();
setIsEventModalOpen(true);
}}
>
{event.title}
<div className="truncate">
{formatTime(event.start_time)} {event.title}
{selectedTabloId === "all" && event.tablo_name && (
<span className="opacity-75 ml-1">
{event.tablo_name}
</span>
)}
</div>
<button
onClick={(e) => {
e.stopPropagation();
deleteEvent.mutate(event.event_id);
}}
className="absolute -top-1 -right-1 w-4 h-4 bg-red-500 text-white rounded-full opacity-0 group-hover:opacity-100 transition-opacity text-xs flex items-center justify-center hover:bg-red-600"
className="absolute -top-1 -right-1 w-5 h-5 bg-red-500 text-white rounded-full opacity-0 group-hover:opacity-100 transition-all text-sm flex items-center justify-center hover:bg-red-600 hover:scale-110 shadow-sm"
title="Supprimer l'événement"
>
×
@ -299,25 +373,78 @@ export const PlanningPage = () => {
index === 6 ? "border-r-0" : ""
}`}
onClick={() => {
setSelectedDate(day);
const [hour] = time.split(":").map(Number);
const dateWithTime = new Date(day);
dateWithTime.setHours(hour, 0, 0, 0);
setSelectedDate(dateWithTime);
setIsEventModalOpen(true);
}}
>
{/* Current time indicator for today */}
{isWithinCurrentHour(day, time) && (
<div
className="absolute left-0 right-0 h-0.5 bg-red-500 z-20 pointer-events-none"
style={{
top: `${getCurrentTimePosition()}px`,
}}
>
<div className="absolute -left-1 -top-1 w-2 h-2 bg-red-500 rounded-full"></div>
</div>
)}
{getEventsForDate(day)
.filter((event) =>
event.start_time.startsWith(time.split(":")[0])
)
.map((event) => (
<div
key={event.event_id}
className={`absolute left-1 right-1 top-1 p-1 rounded text-white ${event.tablo_color} text-xs`}
>
<div className="font-medium truncate">{event.title}</div>
<div className="opacity-75">
{event.start_time} - {event.end_time}
.map((event) => {
const eventHeight = calculateEventHeight(
event.start_time,
event.end_time
);
const eventOffset = calculateEventOffset(
event.start_time,
time
);
return (
<div
key={event.event_id}
className={`absolute left-1 right-1 p-0.5 rounded text-white ${event.tablo_color} text-xs overflow-hidden z-10 group cursor-pointer hover:opacity-90`}
style={{
top: `${eventOffset}px`,
height: `${eventHeight}px`,
minHeight: "30px",
}}
onClick={(e) => {
e.stopPropagation();
}}
>
<div className="text-[10px] font-medium truncate leading-tight">
{event.title}
{selectedTabloId === "all" && event.tablo_name && (
<span className="opacity-75 ml-1">
{event.tablo_name}
</span>
)}
</div>
{eventHeight >= 30 && (
<div className="text-[9px] opacity-75 leading-tight">
{formatTime(event.start_time)} -{" "}
{formatTime(event.end_time)}
</div>
)}
<button
onClick={(e) => {
e.stopPropagation();
deleteEvent.mutate(event.event_id);
}}
className="absolute -top-1 -right-1 w-4 h-4 bg-red-500 text-white rounded-full opacity-0 group-hover:opacity-100 transition-all text-xs flex items-center justify-center hover:bg-red-600 hover:scale-110 shadow-sm z-30"
title="Supprimer l'événement"
>
×
</button>
</div>
</div>
))}
);
})}
</div>
))}
</div>
@ -345,7 +472,10 @@ export const PlanningPage = () => {
key={time}
className="flex border-b border-gray-100 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700 cursor-pointer relative min-h-[60px]"
onClick={() => {
setSelectedDate(currentDate);
const [hour] = time.split(":").map(Number);
const dateWithTime = new Date(currentDate);
dateWithTime.setHours(hour, 0, 0, 0);
setSelectedDate(dateWithTime);
setIsEventModalOpen(true);
}}
>
@ -353,26 +483,76 @@ export const PlanningPage = () => {
{time}
</div>
<div className="flex-1 p-2 relative">
{/* Current time indicator for today */}
{isWithinCurrentHour(new Date(currentDate), time) && (
<div
className="absolute left-0 right-0 h-0.5 bg-red-500 z-20 pointer-events-none"
style={{
top: `${getCurrentTimePosition()}px`,
}}
>
<div className="absolute -left-1 -top-1 w-2 h-2 bg-red-500 rounded-full"></div>
</div>
)}
{getEventsForDate(currentDate)
.filter((event) =>
event.start_time.startsWith(time.split(":")[0])
)
.map((event) => (
<div
key={event.event_id}
className={`p-2 rounded text-white ${event.tablo_color} mb-1`}
>
<div className="font-medium">{event.title}</div>
<div className="text-sm opacity-75">
{event.start_time} - {event.end_time}
</div>
{event.description && (
<div className="text-sm mt-1 opacity-75">
{event.description}
.map((event) => {
const eventHeight = calculateEventHeight(
event.start_time,
event.end_time
);
const eventOffset = calculateEventOffset(
event.start_time,
time
);
return (
<div
key={event.event_id}
className={`absolute left-2 right-2 p-1 rounded text-white ${event.tablo_color} overflow-hidden z-10 group cursor-pointer hover:opacity-90`}
style={{
top: `${eventOffset}px`,
height: `${eventHeight}px`,
minHeight: "30px",
}}
onClick={(e) => {
e.stopPropagation();
}}
>
<div className="text-[10px] font-medium truncate leading-tight">
{event.title}
{selectedTabloId === "all" && event.tablo_name && (
<span className="opacity-75 ml-1">
{event.tablo_name}
</span>
)}
</div>
)}
</div>
))}
{eventHeight >= 30 && (
<div className="text-[9px] opacity-75 leading-tight">
{formatTime(event.start_time)} -{" "}
{formatTime(event.end_time)}
</div>
)}
{eventHeight >= 75 && event.description && (
<div className="text-[8px] mt-0.5 opacity-75 leading-tight">
{event.description}
</div>
)}
<button
onClick={(e) => {
e.stopPropagation();
deleteEvent.mutate(event.event_id);
}}
className="absolute -top-1 -right-1 w-4 h-4 bg-red-500 text-white rounded-full opacity-0 group-hover:opacity-100 transition-all text-xs flex items-center justify-center hover:bg-red-600 hover:scale-110 shadow-sm z-30"
title="Supprimer l'événement"
>
×
</button>
</div>
);
})}
</div>
</div>
))}
@ -391,27 +571,32 @@ export const PlanningPage = () => {
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Tablo
</label>
<select
value={selectedTabloId}
onChange={(e) => setSelectedTabloId(e.target.value)}
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white"
disabled={tablosLoading}
<Select
placeholder={
tablosLoading ? "Chargement..." : "Sélectionner un tablo"
}
selectedKey={selectedTabloId}
onSelectionChange={(key) => setSelectedTabloId(key as string)}
className="w-full"
isDisabled={tablosLoading}
>
<option value="">
{tablosLoading ? "Chargement..." : "Sélectionner un tablo"}
</option>
{tablos?.map((tablo) => (
<option key={tablo.id} value={tablo.id}>
{tablo.name}
</option>
))}
</select>
<SelectButton className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white text-left" />
<SelectPopover>
<SelectListBox>
<SelectListItem id="all">Tous les tablos</SelectListItem>
{tablos?.map((tablo) => (
<SelectListItem key={tablo.id} id={tablo.id}>
{tablo.name}
</SelectListItem>
))}
</SelectListBox>
</SelectPopover>
</Select>
</div>
<button
onClick={() => setIsEventModalOpen(true)}
className="w-full px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors font-medium shadow-sm disabled:opacity-50 disabled:cursor-not-allowed"
disabled={!selectedTabloId}
>
+ Créer un événement
</button>
@ -541,21 +726,13 @@ export const PlanningPage = () => {
{/* Calendar Views */}
<div className="flex-1 p-4">
{eventsLoading && selectedTabloId ? (
{tabloEventsLoading ? (
<div className="flex items-center justify-center h-64">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
<span className="ml-2 text-gray-600 dark:text-gray-300">
Chargement des événements...
</span>
</div>
) : !selectedTabloId ? (
<div className="flex items-center justify-center h-64">
<div className="text-center">
<p className="text-gray-500 dark:text-gray-400">
Sélectionnez un tablo pour voir les événements
</p>
</div>
</div>
) : (
<>
{currentView === "month" && renderMonthView()}

View file

@ -1,4 +1,4 @@
import React from 'react';
import React from "react";
export type TimeOption = {
hour: number;
@ -9,35 +9,60 @@ export type TimeOption = {
export function useTimePicker({
intervalInMinute,
format = "24h",
}: {
intervalInMinute: 15 | 30;
format?: "12h" | "24h";
}): Array<TimeOption> {
return React.useMemo(() => {
const options = [];
for (let hour = 0; hour < 24; hour++) {
const period = hour >= 12 ? 'PM' : 'AM';
let hourIn12Format = hour % 12;
if (format === "12h") {
for (let hour = 0; hour < 24; hour++) {
const period = hour >= 12 ? "PM" : "AM";
let hourIn12Format = hour % 12;
if (hourIn12Format === 0) {
hourIn12Format = 12;
if (hourIn12Format === 0) {
hourIn12Format = 12;
}
for (
let interval = 0;
interval < Math.floor(60 / intervalInMinute);
interval++
) {
const minutes = interval * intervalInMinute;
options.push({
hour,
minute: minutes,
value: `${hourIn12Format}:${
minutes === 0 ? "00" : minutes
} ${period}`,
id: `${hourIn12Format}:${minutes === 0 ? "00" : minutes} ${period}`,
});
}
}
for (
let interval = 0;
interval < Math.floor(60 / intervalInMinute);
interval++
) {
const minutes = interval * intervalInMinute;
options.push({
hour,
minute: minutes,
value: `${hourIn12Format}:${minutes === 0 ? '00' : minutes} ${period}`,
id: `${hourIn12Format}:${minutes === 0 ? '00' : minutes} ${period}`,
});
} else {
for (let hour = 0; hour < 24; hour++) {
for (
let interval = 0;
interval < Math.floor(60 / intervalInMinute);
interval++
) {
const minutes = interval * intervalInMinute;
options.push({
hour,
minute: minutes,
value: `${hour.toString().padStart(2, "0")}:${minutes
.toString()
.padStart(2, "0")}`,
id: `${hour.toString().padStart(2, "0")}:${minutes
.toString()
.padStart(2, "0")}`,
});
}
}
}
return options;
}, [intervalInMinute]);
}