🏷️(front) add CalendarList types and constants

Add TypeScript types for CalendarList component state and
props. Add color palette constants and custom hook for
managing calendar list state.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Nathan Panchout
2026-01-25 20:34:35 +01:00
parent 034e8f5c79
commit cfae08451a
3 changed files with 305 additions and 0 deletions

View File

@@ -0,0 +1,17 @@
/**
* Constants for the CalendarList components.
*/
/**
* Default color palette for calendar creation.
*/
export const DEFAULT_COLORS = [
"#3788d8", // Blue
"#28a745", // Green
"#dc3545", // Red
"#ffc107", // Yellow
"#6f42c1", // Purple
"#fd7e14", // Orange
"#20c997", // Teal
"#e83e8c", // Pink
];

View File

@@ -0,0 +1,200 @@
/**
* useCalendarListState hook.
* Manages state and handlers for the CalendarList component.
*/
import { useState, useCallback } from "react";
import type {
CalDavCalendar,
CalDavCalendarCreate,
CalDavCalendarUpdate,
} from "../../../services/dav/types/caldav-service";
import type { CalendarModalState, DeleteState } from "../types";
interface UseCalendarListStateProps {
createCalendar: (
params: CalDavCalendarCreate
) => Promise<{ success: boolean; error?: string }>;
updateCalendar: (
url: string,
options: CalDavCalendarUpdate
) => Promise<{ success: boolean; error?: string }>;
deleteCalendar: (url: string) => Promise<{ success: boolean; error?: string }>;
shareCalendar: (
url: string,
email: string
) => Promise<{ success: boolean; error?: string }>;
}
export const useCalendarListState = ({
createCalendar,
updateCalendar,
deleteCalendar,
shareCalendar,
}: UseCalendarListStateProps) => {
// Modal states
const [modalState, setModalState] = useState<CalendarModalState>({
isOpen: false,
mode: "create",
calendar: null,
});
const [deleteState, setDeleteState] = useState<DeleteState>({
isOpen: false,
calendar: null,
isLoading: false,
});
const [isMyCalendarsExpanded, setIsMyCalendarsExpanded] = useState(true);
const [isSharedCalendarsExpanded, setIsSharedCalendarsExpanded] =
useState(true);
const [openMenuUrl, setOpenMenuUrl] = useState<string | null>(null);
// Modal handlers
const handleOpenCreateModal = useCallback(() => {
setModalState({
isOpen: true,
mode: "create",
calendar: null,
});
}, []);
const handleOpenEditModal = useCallback((calendar: CalDavCalendar) => {
setModalState({
isOpen: true,
mode: "edit",
calendar,
});
}, []);
const handleCloseModal = useCallback(() => {
setModalState({
isOpen: false,
mode: "create",
calendar: null,
});
}, []);
const handleSaveCalendar = useCallback(
async (name: string, color: string, description?: string) => {
if (modalState.mode === "create") {
const result = await createCalendar({
displayName: name,
color,
description,
components: ['VEVENT'],
});
if (!result.success) {
throw new Error(result.error);
}
} else if (modalState.calendar) {
const result = await updateCalendar(modalState.calendar.url, {
displayName: name,
color,
description,
});
if (!result.success) {
throw new Error(result.error);
}
}
},
[modalState, createCalendar, updateCalendar]
);
const handleShareCalendar = useCallback(
async (email: string): Promise<{ success: boolean; error?: string }> => {
if (!modalState.calendar) {
return { success: false, error: 'No calendar selected' };
}
return shareCalendar(modalState.calendar.url, email);
},
[modalState.calendar, shareCalendar]
);
// Delete handlers
const handleOpenDeleteModal = useCallback((calendar: CalDavCalendar) => {
setDeleteState({
isOpen: true,
calendar,
isLoading: false,
});
}, []);
const handleCloseDeleteModal = useCallback(() => {
setDeleteState({
isOpen: false,
calendar: null,
isLoading: false,
});
}, []);
const handleConfirmDelete = useCallback(async () => {
if (!deleteState.calendar) return;
setDeleteState((prev) => ({ ...prev, isLoading: true }));
try {
const result = await deleteCalendar(deleteState.calendar.url);
if (!result.success) {
console.error("Failed to delete calendar:", result.error);
}
handleCloseDeleteModal();
} catch (error) {
console.error("Error deleting calendar:", error);
setDeleteState((prev) => ({ ...prev, isLoading: false }));
}
}, [deleteState.calendar, deleteCalendar, handleCloseDeleteModal]);
// Menu handlers
const handleMenuToggle = useCallback(
(calendarUrl: string, e: React.MouseEvent) => {
e.stopPropagation();
setOpenMenuUrl(openMenuUrl === calendarUrl ? null : calendarUrl);
},
[openMenuUrl]
);
const handleCloseMenu = useCallback(() => {
setOpenMenuUrl(null);
}, []);
// Expansion handlers
const handleToggleMyCalendars = useCallback(() => {
setIsMyCalendarsExpanded((prev) => !prev);
}, []);
const handleToggleSharedCalendars = useCallback(() => {
setIsSharedCalendarsExpanded((prev) => !prev);
}, []);
return {
// Modal state
modalState,
deleteState,
// Expansion state
isMyCalendarsExpanded,
isSharedCalendarsExpanded,
openMenuUrl,
// Modal handlers
handleOpenCreateModal,
handleOpenEditModal,
handleCloseModal,
handleSaveCalendar,
handleShareCalendar,
// Delete handlers
handleOpenDeleteModal,
handleCloseDeleteModal,
handleConfirmDelete,
// Menu handlers
handleMenuToggle,
handleCloseMenu,
// Expansion handlers
handleToggleMyCalendars,
handleToggleSharedCalendars,
};
};

View File

@@ -0,0 +1,88 @@
/**
* Type definitions for CalendarList components.
*/
import type { CalDavCalendar } from "../../services/dav/types/caldav-service";
import type { Calendar } from "../../types";
/**
* Props for the CalendarModal component.
*/
export interface CalendarModalProps {
isOpen: boolean;
mode: "create" | "edit";
calendar?: CalDavCalendar | null;
onClose: () => void;
onSave: (name: string, color: string, description?: string) => Promise<void>;
onShare?: (email: string) => Promise<{ success: boolean; error?: string }>;
}
/**
* Props for the CalendarItemMenu component.
*/
export interface CalendarItemMenuProps {
onEdit: () => void;
onDelete: () => void;
onSubscription?: () => void;
onClose: () => void;
}
/**
* Props for the DeleteConfirmModal component.
*/
export interface DeleteConfirmModalProps {
isOpen: boolean;
calendarName: string;
onConfirm: () => void;
onCancel: () => void;
isLoading: boolean;
}
/**
* Props for the CalendarListItem component.
*/
export interface CalendarListItemProps {
calendar: CalDavCalendar;
isVisible: boolean;
isMenuOpen: boolean;
onToggleVisibility: (url: string) => void;
onMenuToggle: (url: string, e: React.MouseEvent) => void;
onEdit: (calendar: CalDavCalendar) => void;
onDelete: (calendar: CalDavCalendar) => void;
onSubscription?: (calendar: CalDavCalendar) => void;
onCloseMenu: () => void;
}
/**
* Props for the SharedCalendarListItem component.
*/
export interface SharedCalendarListItemProps {
calendar: Calendar;
isVisible: boolean;
onToggleVisibility: (id: string) => void;
}
/**
* Props for the main CalendarList component.
*/
export interface CalendarListProps {
calendars: Calendar[];
}
/**
* State for the calendar modal.
*/
export interface CalendarModalState {
isOpen: boolean;
mode: "create" | "edit";
calendar: CalDavCalendar | null;
}
/**
* State for the delete confirmation.
*/
export interface DeleteState {
isOpen: boolean;
calendar: CalDavCalendar | null;
isLoading: boolean;
}