All checks were successful
Test CI / build (push) Successful in 16s
- 일정 색상 로직 수정: 커스텀 컬러 삭제 - 일정 시작/종료일 설정 구현 - 일정 타입 구현 - 일정 시작/종료 시간 구현 중
303 lines
8.6 KiB
TypeScript
303 lines
8.6 KiB
TypeScript
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>
|
|
)
|
|
} |