Enhance AvailabilityCard and AvailabilitiesPage components by adding a copy functionality for time ranges across days. Introduce a modal for selecting target days and confirm copying of availability settings, improving user experience and flexibility.

This commit is contained in:
Arthur Belleville 2025-09-10 22:44:31 +02:00
parent fab34e70cb
commit d03022c21b
No known key found for this signature in database
2 changed files with 180 additions and 1 deletions

View file

@ -2,7 +2,7 @@ import { useState } from "react";
import { Switch } from "@ui/ui-library/switch";
import { Text } from "@ui/ui-library/text";
import { Button } from "@ui/ui-library/button";
import { MinusIcon, PlusIcon } from "@ui/ui-library/icons";
import { MinusIcon, PlusIcon, CopyIcon } from "@ui/ui-library/icons";
import {
Select,
SelectButton,
@ -22,6 +22,11 @@ interface AvailabilityCardProps {
onEnabledChange: (enabled: boolean) => void;
timeRanges: TimeRange[];
onTimeRangesChange: (ranges: TimeRange[]) => void;
onCopyToOtherDays?: (
sourceDay: number,
enabled: boolean,
timeRanges: TimeRange[]
) => void;
}
const MINUTES_IN_DAY = 24 * 60;
@ -146,6 +151,7 @@ export function AvailabilityCard({
onEnabledChange,
timeRanges,
onTimeRangesChange,
onCopyToOtherDays,
}: AvailabilityCardProps) {
const dayDisplay = DAYS_OF_WEEK_DISPLAY[day];
@ -239,6 +245,17 @@ export function AvailabilityCard({
<div className="flex flex-col gap-2 w-full">
<div className="flex items-center justify-between">
<Text className="text-lg font-semibold">{dayDisplay}</Text>
{onCopyToOtherDays && enabled && timeRanges.length > 0 && (
<Button
size="sm"
variant="outline"
onPress={() => onCopyToOtherDays(day, enabled, timeRanges)}
className="h-6 px-2 text-xs border-gray-300 hover:border-primary hover:bg-primary/5 text-gray-600 hover:text-primary"
>
<CopyIcon className="size-3 mr-1" />
Copier
</Button>
)}
</div>
<div className="flex items-center gap-2">
<Switch

View file

@ -8,8 +8,77 @@ import {
WeeklyAvailability,
} from "@ui/hooks/availabilities";
import { toast } from "@ui/ui-library/toast/toast-queue";
import { useState } from "react";
import { Checkbox } from "@ui/ui-library/checkbox";
// Custom Modal Component
interface CustomModalProps {
isOpen: boolean;
onClose: () => void;
title: string;
children: React.ReactNode;
}
function CustomModal({ isOpen, onClose, title, children }: CustomModalProps) {
if (!isOpen) return null;
return (
<div className="fixed inset-0 z-50 flex items-center justify-center">
{/* Backdrop */}
<div
className="absolute inset-0 bg-black/50 backdrop-blur-sm"
onClick={onClose}
/>
{/* Modal */}
<div className="relative bg-white dark:bg-gray-800 rounded-xl shadow-2xl border border-gray-200 dark:border-gray-700 w-full max-w-md mx-4 max-h-[90vh] overflow-hidden">
{/* Header */}
<div className="flex items-center justify-between p-6 border-b border-gray-200 dark:border-gray-700">
<h2 className="text-xl font-semibold text-gray-900 dark:text-gray-100">
{title}
</h2>
<button
onClick={onClose}
className="p-1 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"
>
<svg
className="w-5 h-5 text-gray-500"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
</div>
{/* Content */}
<div className="p-6">{children}</div>
</div>
</div>
);
}
const DAYS_OF_WEEK = [0, 1, 2, 3, 4, 5, 6];
const DAYS_OF_WEEK_DISPLAY = [
"Lundi",
"Mardi",
"Mercredi",
"Jeudi",
"Vendredi",
"Samedi",
"Dimanche",
];
interface TimeRange {
start: string;
end: string;
}
export function AvailabilitiesPage() {
const {
@ -19,6 +88,47 @@ export function AvailabilitiesPage() {
setDraftAvailabilities,
} = useAvailabilities();
const [copyModalOpen, setCopyModalOpen] = useState(false);
const [sourceDayData, setSourceDayData] = useState<{
day: number;
enabled: boolean;
timeRanges: TimeRange[];
} | null>(null);
const [selectedDays, setSelectedDays] = useState<number[]>([]);
const handleCopyToOtherDays = (
sourceDay: number,
enabled: boolean,
timeRanges: TimeRange[]
) => {
setSourceDayData({ day: sourceDay, enabled, timeRanges });
setSelectedDays([]);
setCopyModalOpen(true);
};
const applyCopyToSelectedDays = () => {
if (!sourceDayData) return;
const updatedAvailabilities = { ...draftAvailabilities };
selectedDays.forEach((day) => {
updatedAvailabilities[day] = {
enabled: sourceDayData.enabled,
timeRanges: [...sourceDayData.timeRanges],
};
});
setDraftAvailabilities(updatedAvailabilities);
setCopyModalOpen(false);
setSourceDayData(null);
setSelectedDays([]);
toast.add({
title: "Succès",
description: `Horaires copiés vers ${selectedDays.length} jour(s)`,
type: "success",
});
};
return (
<div className="h-full flex flex-col p-4">
<div className="flex justify-between items-start mb-3">
@ -91,6 +201,7 @@ export function AvailabilitiesPage() {
},
});
}}
onCopyToOtherDays={handleCopyToOtherDays}
/>
</div>
</div>
@ -164,6 +275,57 @@ export function AvailabilitiesPage() {
{isUpdating ? "Enregistrement..." : "Enregistrer les disponibilités"}
</Button>
</div>
{/* Copy Modal */}
<CustomModal
isOpen={copyModalOpen}
onClose={() => setCopyModalOpen(false)}
title={`Copier les horaires de ${
sourceDayData ? DAYS_OF_WEEK_DISPLAY[sourceDayData.day] : ""
}`}
>
<div className="space-y-4">
<Text className="text-gray-600 dark:text-gray-400">
Sélectionnez les jours vers lesquels vous souhaitez copier ces
horaires :
</Text>
<div className="space-y-3 max-h-60 overflow-y-auto">
{DAYS_OF_WEEK.filter((day) => day !== sourceDayData?.day).map(
(day) => (
<Checkbox
key={day}
isSelected={selectedDays.includes(day)}
onChange={(isSelected) => {
if (isSelected) {
setSelectedDays([...selectedDays, day]);
} else {
setSelectedDays(selectedDays.filter((d) => d !== day));
}
}}
>
<Text className="font-medium">
{DAYS_OF_WEEK_DISPLAY[day]}
</Text>
</Checkbox>
)
)}
</div>
<div className="flex justify-end gap-3 pt-4 border-t border-gray-200 dark:border-gray-700">
<Button variant="outline" onPress={() => setCopyModalOpen(false)}>
Annuler
</Button>
<Button
variant="solid"
isDisabled={selectedDays.length === 0}
onPress={applyCopyToSelectedDays}
>
Copier vers {selectedDays.length} jour(s)
</Button>
</div>
</div>
</CustomModal>
</div>
);
}