issue #60
All checks were successful
Test CI / build (push) Successful in 16s

- 일정 색상 로직 수정: 커스텀 컬러 삭제
- 일정 시작/종료일 설정 구현
- 일정 타입 구현
- 일정 시작/종료 시간 구현 중
This commit is contained in:
geonhee-min
2025-12-08 16:57:09 +09:00
parent c0941d0680
commit 6bbffbcb50
19 changed files with 717 additions and 220 deletions

View File

@@ -0,0 +1,303 @@
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { ScrollArea } from '@/components/ui/scroll-area';
import { cn } from '@/lib/utils';
import { useEffect, useState } from 'react';
import { usePalette } from '@/hooks/use-palette';
import { ColorPalette, type ColorPaletteType } from '@/const/ColorPalette';
import { ColorPickPopover } from './ColorPickPopover';
import { Input } from '@/components/ui/input';
import { Controller, useForm } from 'react-hook-form';
import * as z from 'zod';
import { CreateScheduleSchema } from '@/data/form/createSchedule.schema';
import { zodResolver } from '@hookform/resolvers/zod';
import { Field, FieldError } from '@/components/ui/field';
import { PenSquare, X } from 'lucide-react';
import { ScheduleTypeLabel, type ScheduleType } from '@/const/schedule/ScheduleType';
import { useRecord } from '@/hooks/use-record';
import { TypePickPopover } from './TypePickPopover';
import { ScheduleDay } from '@/const/schedule/ScheduleDay';
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group';
import { DatePickPopover } from './DatePickPopover';
import { format } from 'date-fns';
import { TimePickPopover } from './TimePickPopover';
interface ScheduleSheetProps {
date: Date | undefined;
open: boolean;
popoverSide: 'left' | 'right';
popoverAlign: 'start' | 'end';
}
export const SchedulePopover = ({ date, open, popoverSide, popoverAlign }: ScheduleSheetProps) => {
const {
getPaletteByKey
} = usePalette();
const [mode, setMode] = useState<'list' | 'create' | 'detail' | 'update'>('list');
const [colorPopoverOpen, setColorPopoverOpen] = useState(false);
const dayLabelList = useRecord(ScheduleDay).keys.map((key) => {
return {
day: Number(key),
label: ScheduleDay[Number(key)]
} as { day: number, label: string };
})
const createScheduleForm = useForm<z.infer<typeof CreateScheduleSchema>>({
resolver: zodResolver(CreateScheduleSchema),
defaultValues: {
name: "",
startDate: date || new Date(),
endDate: date || new Date(),
content: "",
startTime: "",
endTime: "",
type: "once",
status: "yet",
style: getPaletteByKey('Black').style,
dayList: ""
}
});
useEffect(() => {
console.log(date);
if (open && date) {
createScheduleForm.setValue('startDate', date);
createScheduleForm.setValue('endDate', date);
return;
}
setTimeout(() => {
setMode('list');
createScheduleForm.clearErrors();
createScheduleForm.reset();
}, 150);
}, [open]);
const {
name,
startDate,
endDate,
content,
startTime,
endTime,
type,
status,
style,
dayList
} = createScheduleForm.watch();
const selectColor = (color: ColorPaletteType) => {
createScheduleForm.setValue('style', color.style);
setColorPopoverOpen(false);
}
const selectType = (type: ScheduleType) => {
createScheduleForm.setValue('type', type);
}
const selectDayList = (newValues: string[]) => {
const sortedValues = newValues.sort();
const newDayList = sortedValues.join('');
createScheduleForm.setValue('dayList', newDayList);
}
const selectDate = (type: 'startDate' | 'endDate', date: Date | undefined) => {
if (!date) return;
createScheduleForm.setValue(type, date);
}
const selectTime = (type: 'startTime' | 'endTime', time: string) => {
createScheduleForm.setValue(type, time);
}
const ListContent = () => {
return (
<div>
<PenSquare onClick={() => setMode('create')}/>
</div>
)
}
const CreateContent = () => {
const OnceContent = () => {
return (
<div
className="w-full h-10"
>
<DatePickPopover
disabled={false}
mode={'range'}
popoverAlign={popoverAlign}
startDate={startDate}
endDate={endDate}
setStartDate={(date: Date | undefined) => selectDate('startDate', date)}
setEndDate={(date: Date | undefined) => selectDate('endDate', date)}
/>
</div>
)
}
const DailyContent = () => {
return null;
}
const DefaultContent = () => {
return (
(
<ToggleGroup
type="multiple"
className="w-full h-10 flex flex-row justify-center items-center"
onValueChange={selectDayList}
>
{
dayLabelList.map((day) => (
<ToggleGroupItem
value={`${day.day}`}
className="border rounded-none not-last:border-r-0"
>
{day.label}
</ToggleGroupItem>
))
}
</ToggleGroup>
)
)
}
const renderContent = () => {
switch (type) {
case 'Once':
return <OnceContent />;
case 'Daily':
return <DailyContent />;
default:
return <DefaultContent />;
}
}
return (
<div className="w-full h-full flex flex-col justify-start items-start gap-4">
<div className="w-full flex flex-row justify-center items-center gap-5">
<Popover open={colorPopoverOpen} onOpenChange={setColorPopoverOpen}>
<div className="w-6 h-6 flex justify-center items-center">
<PopoverTrigger asChild>
<div
className={cn(
'rounded-full w-5 h-5 border-2 border-gray-300',
)}
style={{
backgroundColor: `${style}`,
}}
/>
</PopoverTrigger>
</div>
<ColorPickPopover
setColor={selectColor}
/>
</Popover>
<Controller
name="name"
control={createScheduleForm.control}
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<Input
{...field}
id="form-create-schedule-name"
placeholder="제목"
className="font-bold border-t-0 border-r-0 border-l-0 p-0 border-b-2 rounded-none shadow-none border-indigo-300 focus-visible:ring-0 focus-visible:border-b-indigo-500"
style={{
fontSize: '20px'
}}
tabIndex={1}
arai-invalid={fieldState.invalid}
/>
<FieldError errors={[fieldState.error]} />
</Field>
)}
/>
</div>
<Popover>
<PopoverTrigger asChild>
<div className="hover:bg-gray-100 cursor-default w-full h-10 border flex justify-center items-center rounded-sm">
{ScheduleTypeLabel[type as keyof typeof ScheduleTypeLabel]}
</div>
</PopoverTrigger>
<TypePickPopover
setType={selectType}
popoverSide={popoverSide}
/>
</Popover>
<div
className="w-full h-10"
>
{renderContent()}
</div>
<div className="w-full h-10">
<TimePickPopover
mode='range'
popoverAlign={popoverAlign}
disabled={false}
startTime=''
setStartTime={(time: string | undefined) => {}}
endTime=''
setEndTime={(time: string | undefined) => {}}
/>
</div>
<div
className="flex flex-row self-end justify-self-end items-center justify-center cursor-default"
onClick={() => setMode('list')}
>
<X />
<span></span>
</div>
</div>
)
}
const DetailContent = () => {
return (
<div>
Detail
</div>
)
}
const UpdateContent = () => {
return (
<div>
Update
</div>
)
}
const SchedulePopoverContent = () => {
switch(mode) {
case 'list':
return <ListContent />
case 'create':
return <CreateContent />
case 'detail':
return <DetailContent />
case 'update':
return <UpdateContent />
}
}
return (
<PopoverContent
className="rounded-xl xl:w-[calc(100vw/4)] xl:max-w-[480px] min-w-[384px]"
align={popoverAlign} side={popoverSide}
>
<div>{date && format(date, "yyyy년 MM월 dd일")}</div>
<ScrollArea
className={
cn(
"min-h-[125px] h-[calc(100vh/2.5)] p-2.5 w-full flex flex-col",
)
}
>
{<SchedulePopoverContent />}
</ScrollArea>
</PopoverContent>
)
}