life-manager/codebase/features/scheduling/frontend/TimeBlockModal.tsx

194 lines
5.2 KiB
TypeScript

/** @jsxImportSource react */
import { useState, useEffect } from 'react';
import type { ReactElement } from 'react';
import { Button, Input, Select } from '@lilith/ui-primitives';
import { Modal } from '@/components/Modal';
import { BlockType, BlockStatus } from '@life-platform/shared';
import type {
TimeBlock,
CreateTimeBlockDto,
UpdateTimeBlockDto,
} from '@life-platform/shared';
import {
useCreateTimeBlock,
useUpdateTimeBlock,
useDeleteTimeBlock,
} from './useScheduling';
import { FormField, FormLabel, FormActions } from './scheduling-styles';
const BLOCK_TYPE_LABELS: Record<string, string> = {
[BlockType.Work]: 'Work',
[BlockType.Personal]: 'Personal',
[BlockType.Health]: 'Health',
[BlockType.Break]: 'Break',
[BlockType.Transit]: 'Transit',
[BlockType.Available]: 'Available',
};
const BLOCK_STATUS_LABELS: Record<string, string> = {
[BlockStatus.Planned]: 'Planned',
[BlockStatus.InProgress]: 'In Progress',
[BlockStatus.Completed]: 'Completed',
[BlockStatus.Skipped]: 'Skipped',
[BlockStatus.Rescheduled]: 'Rescheduled',
};
interface TimeBlockModalProps {
open: boolean;
onClose: () => void;
date: string;
block?: TimeBlock | null;
}
export function TimeBlockModal({
open,
onClose,
date,
block,
}: TimeBlockModalProps): ReactElement | null {
const createMutation = useCreateTimeBlock();
const updateMutation = useUpdateTimeBlock();
const deleteMutation = useDeleteTimeBlock();
const isEditing = !!block;
const [title, setTitle] = useState('');
const [startTime, setStartTime] = useState('09:00');
const [endTime, setEndTime] = useState('10:00');
const [blockType, setBlockType] = useState<BlockType>(BlockType.Work);
const [status, setStatus] = useState<BlockStatus>(BlockStatus.Planned);
useEffect(() => {
if (block) {
setTitle(block.title);
setStartTime(block.startTime);
setEndTime(block.endTime);
setBlockType(block.blockType);
setStatus(block.status);
} else {
setTitle('');
setStartTime('09:00');
setEndTime('10:00');
setBlockType(BlockType.Work);
setStatus(BlockStatus.Planned);
}
}, [block, open]);
function handleSubmit(): void {
if (!title.trim()) return;
if (isEditing && block) {
const dto: UpdateTimeBlockDto = {
title: title.trim(),
startTime,
endTime,
blockType,
status,
};
updateMutation.mutate(
{ id: block.id, data: dto },
{ onSuccess: () => onClose() },
);
} else {
const dto: CreateTimeBlockDto = {
title: title.trim(),
date,
startTime,
endTime,
blockType,
};
createMutation.mutate(dto, { onSuccess: () => onClose() });
}
}
function handleDelete(): void {
if (!block) return;
if (!window.confirm(`Delete "${block.title}"?`)) return;
deleteMutation.mutate(block.id, { onSuccess: () => onClose() });
}
const isPending =
createMutation.isPending ||
updateMutation.isPending ||
deleteMutation.isPending;
return (
<Modal
open={open}
onClose={onClose}
title={isEditing ? 'Edit Time Block' : 'New Time Block'}
>
<FormField>
<FormLabel>Title</FormLabel>
<Input
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="Block title"
/>
</FormField>
<FormField>
<FormLabel>Start Time</FormLabel>
<Input
type="time"
value={startTime}
onChange={(e) => setStartTime(e.target.value)}
/>
</FormField>
<FormField>
<FormLabel>End Time</FormLabel>
<Input
type="time"
value={endTime}
onChange={(e) => setEndTime(e.target.value)}
/>
</FormField>
<FormField>
<FormLabel>Type</FormLabel>
<Select
value={blockType}
onChange={(e) => setBlockType(e.target.value as BlockType)}
>
{Object.entries(BLOCK_TYPE_LABELS).map(([value, label]) => (
<option key={value} value={value}>
{label}
</option>
))}
</Select>
</FormField>
{isEditing && (
<FormField>
<FormLabel>Status</FormLabel>
<Select
value={status}
onChange={(e) => setStatus(e.target.value as BlockStatus)}
>
{Object.entries(BLOCK_STATUS_LABELS).map(([value, label]) => (
<option key={value} value={value}>
{label}
</option>
))}
</Select>
</FormField>
)}
<FormActions>
{isEditing && (
<Button
variant="ghost"
onClick={handleDelete}
disabled={isPending}
size="sm"
style={{ marginRight: 'auto' }}
>
Delete
</Button>
)}
<Button variant="ghost" onClick={onClose} size="sm">
Cancel
</Button>
<Button onClick={handleSubmit} disabled={isPending || !title.trim()} size="sm">
{isPending ? 'Saving...' : isEditing ? 'Update' : 'Create'}
</Button>
</FormActions>
</Modal>
);
}