🏷️(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:
@@ -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
|
||||||
|
];
|
||||||
@@ -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,
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user