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

- 일정 추가 로직 1차 구현
- 일정 목록 화면 구현 중
This commit is contained in:
geonhee-min
2025-12-10 17:12:29 +09:00
parent 0c13854257
commit e86fb3bac2
23 changed files with 631 additions and 282 deletions

View File

@@ -1,20 +1,20 @@
import { Validator } from '@/util/Validator'; import { Validator } from '@/util/Validator';
import * as z from 'zod'; import * as z from 'zod';
export const LoginSchema = z.object({ export const LoginSchema = z.object({
id: z id: z
.string() .string()
.refine((val) => { .refine((val) => {
if (val.includes('@')) { if (val.includes('@')) {
return Validator.isEmail(val); return Validator.isEmail(val);
} }
return true; return true;
}, { }, {
message: "이메일 형식이 올바르지 않습니다." message: "이메일 형식이 올바르지 않습니다."
}) })
, password: z , password: z
.string() .string()
.min(8, "비밀번호는 8-12 자리여야 합니다.") .min(8, "비밀번호는 8-12 자리여야 합니다.")
.max(12, "비밀번호는 8-12 자리여야 합니다.") .max(12, "비밀번호는 8-12 자리여야 합니다.")
.regex(/^(?=.*[0-9])(?=.*[!@#$%^])[a-zA-Z0-9!@#$%^]+$/, "비밀번호는 영소문자로 시작하여 숫자, 특수문자(!@#$)를 한 개 이상 포함하여야 합니다.") .regex(/^(?=.*[0-9])(?=.*[!@#$%^])[a-zA-Z0-9!@#$%^]+$/, "비밀번호는 영소문자로 시작하여 숫자, 특수문자(!@#$)를 한 개 이상 포함하여야 합니다.")
}); });

View File

@@ -1,21 +1,21 @@
import * as z from 'zod'; import * as z from 'zod';
export const ResetPasswordSchema = z.object({ export const ResetPasswordSchema = z.object({
email: z email: z
.email() .email()
, code: z , code: z
.string() .string()
.length(8) .length(8)
.regex(/^(?=.*[0-9])(?=.*[!@#$%^])[a-zA-Z0-9!@#$%^]+$/, "영소문자로 시작하고 숫자와 특수문자(!@#$%^)를 포함해야 합니다.") .regex(/^(?=.*[0-9])(?=.*[!@#$%^])[a-zA-Z0-9!@#$%^]+$/, "영소문자로 시작하고 숫자와 특수문자(!@#$%^)를 포함해야 합니다.")
, password: z , password: z
.string() .string()
.min(8, "비밀번호는 8-12 자리여야 합니다.") .min(8, "비밀번호는 8-12 자리여야 합니다.")
.max(12, "비밀번호는 8-12 자리여야 합니다.") .max(12, "비밀번호는 8-12 자리여야 합니다.")
.regex(/^(?=.*[0-9])(?=.*[!@#$%^])[a-zA-Z0-9!@#$%^]+$/, "영소문자로 시작하고 숫자와 특수문자(!@#$%^)를 포함해야 합니다.") .regex(/^(?=.*[0-9])(?=.*[!@#$%^])[a-zA-Z0-9!@#$%^]+$/, "영소문자로 시작하고 숫자와 특수문자(!@#$%^)를 포함해야 합니다.")
, passwordConfirm: z , passwordConfirm: z
.string() .string()
}) })
.refine((data) => data.password === data.passwordConfirm, { .refine((data) => data.password === data.passwordConfirm, {
path: ["passwordConfirm"], path: ["passwordConfirm"],
error: "비밀번호가 일치하지 않습니다." error: "비밀번호가 일치하지 않습니다."
}); });

View File

@@ -1,34 +1,34 @@
import * as z from 'zod'; import * as z from 'zod';
export const SignUpSchema = z.object({ export const SignUpSchema = z.object({
accountId: z accountId: z
.string() .string()
.min(5, "아이디는 6-14자여야 합니다.") .min(5, "아이디는 6-14자여야 합니다.")
.max(14, "아이디는 6-14자여야합니다.") .max(14, "아이디는 6-14자여야합니다.")
.refine((val) => { .refine((val) => {
return /^[a-zA-z-_.]*$/.test(val); return /^[a-zA-z-_.]*$/.test(val);
}, { }, {
message: "영문, 숫자, '- _ .' 를 제외한 문자를 사용할 수 없습니다." message: "영문, 숫자, '- _ .' 를 제외한 문자를 사용할 수 없습니다."
}) })
, email: z , email: z
.string() .string()
.min(5, "이메일을 입력해주십시오.") .min(5, "이메일을 입력해주십시오.")
, password: z , password: z
.string() .string()
.min(8, "비밀번호는 8-12자여야 합니다.") .min(8, "비밀번호는 8-12자여야 합니다.")
.max(12, "비밀번호는 8-12자여야 합니다.") .max(12, "비밀번호는 8-12자여야 합니다.")
.regex(/^(?=.*[0-9])(?=.*[!@#$%^])[a-zA-Z0-9!@#$%^]+$/, "영소문자로 시작하고 숫자와 특수문자(!@#$%^)를 포함해야 합니다.") .regex(/^(?=.*[0-9])(?=.*[!@#$%^])[a-zA-Z0-9!@#$%^]+$/, "영소문자로 시작하고 숫자와 특수문자(!@#$%^)를 포함해야 합니다.")
, name: z , name: z
.string() .string()
.min(1, "이름을 입력해주시십시오.") .min(1, "이름을 입력해주시십시오.")
.max(14, "너무 긴 이름은 사용하실 수 없습니다.") .max(14, "너무 긴 이름은 사용하실 수 없습니다.")
, nickname: z , nickname: z
.string() .string()
.min(1, "닉네임을 입력해주십시오.") .min(1, "닉네임을 입력해주십시오.")
, passwordConfirm: z , passwordConfirm: z
.string() .string()
}) })
.refine((data) => data.password === data.passwordConfirm, { .refine((data) => data.password === data.passwordConfirm, {
path: ["passwordConfirm"], path: ["passwordConfirm"],
error: "비밀번호가 일치하지 않습니다." error: "비밀번호가 일치하지 않습니다."
}); });

View File

@@ -1,4 +1,4 @@
export { SignUpSchema } from './signup.schema'; export { SignUpSchema } from './account/signup.schema';
export { LoginSchema } from './login.schema'; export { LoginSchema } from './account/login.schema';
export { ResetPasswordSchema } from './resetPassword.schema'; export { ResetPasswordSchema } from './account/resetPassword.schema';
export { EmailVerificationSchema } from './emailVerification.schema'; export { EmailVerificationSchema } from './account/emailVerification.schema';

View File

@@ -0,0 +1,22 @@
import * as z from "zod";
export const ListScheduleSchema = z.object({
name: z
.string()
.optional()
, startDate: z
.date()
.optional()
, endDate: z
.date()
.optional()
, status: z
.string()
.optional()
, styleList: z
.array(z.string())
.optional()
, typeList: z
.array(z.string())
.optional()
});

View File

@@ -11,7 +11,7 @@ export class CreateScheduleRequest {
startTime: string; startTime: string;
endTime: string; endTime: string;
style: string; style: string;
participantList: string[]; // participantList: string[];
constructor ( constructor (
name: string, name: string,
@@ -23,7 +23,7 @@ export class CreateScheduleRequest {
startTime: string, startTime: string,
endTime: string, endTime: string,
style: string, style: string,
participantList: string[] // participantList: string[]
) { ) {
this.name = name; this.name = name;
this.content = content; this.content = content;
@@ -34,6 +34,6 @@ export class CreateScheduleRequest {
this.startTime = startTime; this.startTime = startTime;
this.endTime = endTime; this.endTime = endTime;
this.style = style; this.style = style;
this.participantList = participantList; // this.participantList = participantList;
} }
} }

View File

@@ -2,12 +2,10 @@ import type { ScheduleStatus } from "@/const/schedule/ScheduleStatus";
import type { ScheduleType } from "@/const/schedule/ScheduleType"; import type { ScheduleType } from "@/const/schedule/ScheduleType";
export class ScheduleListRequest { export class ScheduleListRequest {
filterAccountIdList?: string[]; startDate?: Date;
filterStartDate?: Date; endDate?: Date;
filterEndDate?: Date; typeList?: ScheduleType[];
filterDate?: Date; styleList?: string[];
filterTypeList?: ScheduleType[]; status?: ScheduleStatus;
filterStyleList?: string[]; name?: string;
filterStatusList?: ScheduleStatus[];
filterName?: string;
} }

View File

@@ -1,21 +1,57 @@
export function useTime() { export function useTime() {
const getNowString = () => { const getCurrentTimeString = (type: 'standard' | 'continental') => {
const now = new Date(); const current = new Date();
const hour = now.getHours(); current.setSeconds(0, 0);
const minute = now.getMinutes(); const formatter = new Intl.DateTimeFormat('ko-KR', {
const ampm = hour < 12 ? '오전' : '오후'; hour: '2-digit',
minute: '2-digit',
second: type === 'standard' ? undefined : '2-digit',
hour12: type === 'standard',
hourCycle: type ==='standard' ? 'h12' : 'h23'
});
if (type === 'standard') {
return formatter.format(current).replace(':', '시 ').replace(/(\d+)\s*$/, '$1분');
}
return formatter.format(current);
};
return `${ampm} ${hour > 12 ? hour - 12 : hour}${minute.toString().padStart(2, '0')}`; const standardTimeToContinentalTime = (standardTime: string) => {
const match = standardTime.match(/(오전|오후)\s*(\d+)\s*시\s*(\d+)\s*분/);
if (!match) return '';
const [_, ampm, hourString, minuteString] = match;
let hour = parseInt(hourString, 10);
const minute = parseInt(minuteString, 10);
if (ampm === '오후' && hour !== 12) {
hour += 12;
} else if (ampm === '오전' && hour === 12) {
hour = 0;
}
return `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}:00`
} }
const timeStringToISOString = (time: string) => { const continentalTimeToStandardTime = (continentalTime: string) => {
const timeArray = time.split(' '); const [hour, minute, _] = continentalTime.split(':');
const hour = timeArray[0] === '오전' ? Number(timeArray[1]) : Number(timeArray[1]) + 12; const date = new Date();
return `${hour.toString().padStart(2, '0')}:${timeArray[2]}:00` date.setHours(parseInt(hour, 10), parseInt(minute, 10), 0, 0);
const formatter = new Intl.DateTimeFormat('ko-KR', {
hour: '2-digit',
minute: '2-digit',
hour12: true,
hourCycle: 'h12'
});
return formatter.format(date).replace(':', '시 ').replace(/(\d+)\s*$/, '$1분');
} }
return { return {
getNowString, getCurrentTimeString,
timeStringToISOString standardTimeToContinentalTime,
continentalTimeToStandardTime
} }
} }

View File

@@ -1,8 +1,11 @@
import type { CreateScheduleRequest } from "@/data/request/schedule/CreateScheduleRequest";
import { BaseNetwork } from "./BaseNetwork" import { BaseNetwork } from "./BaseNetwork"
import type { UpdateScheduleRequest } from "@/data/request/schedule/UpdateScheduleRequest"; import {
import type { DeleteScheduleRequest } from "@/data/request/schedule/DeleteScheduleRequest"; ScheduleListRequest,
import type { ScheduleListRequest } from "@/data/request/schedule/ScheduleListRequest"; CreateScheduleRequest,
UpdateScheduleRequest,
DeleteScheduleRequest
} from '@/data/request';
import { CreateScheduleResponse } from "@/data/response";
export class ScheduleNetwork extends BaseNetwork { export class ScheduleNetwork extends BaseNetwork {
private baseUrl = "/schedule"; private baseUrl = "/schedule";
@@ -21,7 +24,7 @@ export class ScheduleNetwork extends BaseNetwork {
} }
async create(data: CreateScheduleRequest) { async create(data: CreateScheduleRequest) {
return await this.post( return await this.post<CreateScheduleResponse>(
`${this.baseUrl}/create`, `${this.baseUrl}/create`,
data data
); );

View File

@@ -1,7 +1,6 @@
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { SidebarTrigger } from '@/components/ui/sidebar'; import { SidebarTrigger } from '@/components/ui/sidebar';
import { Separator } from '@/components/ui/separator'; import { Separator } from '@/components/ui/separator';
import { useState } from 'react';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { useAuthStore } from '@/store/authStore'; import { useAuthStore } from '@/store/authStore';
import { LogOutIcon } from 'lucide-react'; import { LogOutIcon } from 'lucide-react';
@@ -24,31 +23,31 @@ export default function Header() {
<Separator orientation="vertical" className="mr-2 data-[orientation=vertical]:h-4" /> <Separator orientation="vertical" className="mr-2 data-[orientation=vertical]:h-4" />
<Label>{import.meta.env.BASE_URL}</Label> <Label>{import.meta.env.BASE_URL}</Label>
</div> </div>
<div> <div>
<Button <Button
className={` className={`
group flex items-center justify-start group flex items-center justify-start
pr-2 pl-2 border border-red-500 bg-white pr-2 pl-2 border border-red-500 bg-white
transition-all duration-150 transition-all duration-150
w-10 hover:w-25 hover:bg-red-500 w-10 hover:w-25 hover:bg-red-500
overflow-hidden rounded-md overflow-hidden rounded-md
`} `}
type="button" type="button"
onClick={handleClickLogoutButton} onClick={handleClickLogoutButton}
> >
<LogOutIcon <LogOutIcon
className="text-red-500 transition-colors duration-150 group-hover:text-white" className="text-red-500 transition-colors duration-150 group-hover:text-white"
/> />
<span className=" <span className="
text-red-500 group-hover:text-white text-red-500 group-hover:text-white
opacity-0 scale-1 opacity-0 scale-1
transition-all duration-150 transition-all duration-150
group-hover:opacity-100 group-hover:scale-100 group-hover:opacity-100 group-hover:scale-100
"> ">
</span> </span>
</Button> </Button>
</div> </div>
</header> </header>
); );
} }

View File

@@ -1,6 +1,8 @@
import { PopoverContent } from "@/components/ui/popover" import { PopoverContent } from "@/components/ui/popover"
import type { ColorPaletteType } from "@/const/ColorPalette" import type { ColorPaletteType } from "@/const/ColorPalette"
import { usePalette } from "@/hooks/use-palette"; import { usePalette } from "@/hooks/use-palette";
import { cn } from "@/lib/utils";
import { Triangle } from "lucide-react";
import { useState } from "react"; import { useState } from "react";
interface ColorPickPopoverProps { interface ColorPickPopoverProps {
@@ -28,7 +30,10 @@ export const ColorPickPopover = ({ setColor }: ColorPickPopoverProps) => {
return ( return (
<PopoverContent <PopoverContent
className="flex flex-col gap-1.5 w-fit" className={cn(
"flex flex-col gap-1.5 w-fit relative",
seeMore ? "h-40" : "h-26"
)}
> >
{getSlicedList(mainPaletteList, 5).map((list) => ( {getSlicedList(mainPaletteList, 5).map((list) => (
<div className="flex flex-row gap-2.5"> <div className="flex flex-row gap-2.5">
@@ -41,10 +46,8 @@ export const ColorPickPopover = ({ setColor }: ColorPickPopoverProps) => {
))} ))}
</div> </div>
))} ))}
{ { seeMore && (
!seeMore <>
? <div className="w-full" onClick={() => setSeeMore(true)}> </div>
: <>
{getSlicedList(extraPaletteList, 5).map((list) => ( {getSlicedList(extraPaletteList, 5).map((list) => (
<div className="flex flex-row gap-2.5"> <div className="flex flex-row gap-2.5">
{list.map((palette) => ( {list.map((palette) => (
@@ -59,12 +62,29 @@ export const ColorPickPopover = ({ setColor }: ColorPickPopoverProps) => {
</div> </div>
))} ))}
</> </>
} )}
{ <div
seeMore className={cn(
? <div className="w-full" onClick={() => setSeeMore(false)}></div> "absolute h-8 bottom-0 left-0 w-full flex flex-row justify-center items-center gap-4 group bg-white hover:bg-indigo-300 transition-all duration-150",
: null "rounded-b-md"
} )}
onClick={() => setSeeMore(prev => !prev)}
>
<span
className={cn(
"text-indigo-300 group-hover:text-white transition-all duration-150"
)}
>
{ seeMore ? " 접기 " : "더 보기"}
</span>
<div
className={cn(
"w-0 h-0 border-l-8 border-l-transparent border-r-8 border-r-transparent border-b-14 border-b-indigo-300",
"group-hover:border-b-white trnasition-all duration-150",
seeMore && "rotate-180"
)}
/>
</div>
</PopoverContent> </PopoverContent>
) )
} }

View File

@@ -1,6 +1,7 @@
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar"; import { Calendar } from "@/components/ui/calendar";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { cn } from "@/lib/utils";
import { format } from "date-fns"; import { format } from "date-fns";
import { useState } from "react"; import { useState } from "react";
@@ -39,6 +40,7 @@ export const DatePickPopover = ({ ...props } : DaetPickPopoverProps) => {
) )
} }
const { startDate, setStartDate, endDate, setEndDate } = props; const { startDate, setStartDate, endDate, setEndDate } = props;
const [startOpen, setStartOpen] = useState(false); const [startOpen, setStartOpen] = useState(false);
const [endOpen, setEndOpen] = useState(false); const [endOpen, setEndOpen] = useState(false);
@@ -64,7 +66,12 @@ export const DatePickPopover = ({ ...props } : DaetPickPopoverProps) => {
> >
<PopoverTrigger asChild> <PopoverTrigger asChild>
<Button <Button
className="flex-9 h-full border border-indigo-100 bg-white hover:bg-indigo-100 text-black" className={cn(
"flex-9 h-full",
!startOpen
? "bg-white text-indigo-300 hover:bg-indigo-300 hover:text-white"
: "bg-indigo-300 text-white hover:bg-indigo-300"
)}
disabled={disabled} disabled={disabled}
> >
{ {
@@ -98,7 +105,12 @@ export const DatePickPopover = ({ ...props } : DaetPickPopoverProps) => {
> >
<PopoverTrigger asChild> <PopoverTrigger asChild>
<Button <Button
className="flex-9 h-full border border-indigo-100 bg-white hover:bg-indigo-100 text-black" className={cn(
"flex-9 h-full",
!endOpen
? "bg-white text-indigo-300 hover:bg-indigo-300 hover:text-white"
: "bg-indigo-300 text-white hover:bg-indigo-300"
)}
disabled={disabled} disabled={disabled}
> >
{ {

View File

@@ -0,0 +1,11 @@
import { Popover, PopoverTrigger } from "@/components/ui/popover"
export const FilterPopover = () => {
return (
<Popover>
<PopoverTrigger asChild>
</PopoverTrigger>
</Popover>
)
}

View File

@@ -5,6 +5,9 @@ import { useState } from 'react';
import { PenSquare } from 'lucide-react'; import { PenSquare } from 'lucide-react';
import { ScheduleCreateContent } from './content/ScheduleCreateContent'; import { ScheduleCreateContent } from './content/ScheduleCreateContent';
import { Button } from '@/components/ui/button';
import { ScheduleNetwork } from '@/network/ScheduleNetwork';
import { ScheduleListContent } from './content/ScheduleListContent';
interface ScheduleSheetProps { interface ScheduleSheetProps {
date: Date | undefined; date: Date | undefined;
@@ -14,17 +17,8 @@ interface ScheduleSheetProps {
} }
export const SchedulePopover = ({ date, open, popoverSide, popoverAlign }: ScheduleSheetProps) => { export const SchedulePopover = ({ date, open, popoverSide, popoverAlign }: ScheduleSheetProps) => {
const [mode, setMode] = useState<'list' | 'create' | 'detail' | 'update'>('list'); const [mode, setMode] = useState<'list' | 'create' | 'detail' | 'update'>('list');
const ListContent = () => {
return (
<div>
<PenSquare onClick={() => setMode('create')}/>
</div>
)
}
const DetailContent = () => { const DetailContent = () => {
return ( return (
<div> <div>
@@ -44,7 +38,13 @@ export const SchedulePopover = ({ date, open, popoverSide, popoverAlign }: Sched
const SchedulePopoverContent = () => { const SchedulePopoverContent = () => {
switch(mode) { switch(mode) {
case 'list': case 'list':
return <ListContent /> return <ScheduleListContent
setMode={setMode}
date={date}
popoverAlign={popoverAlign}
popoverSide={popoverSide}
open={open}
/>
case 'create': case 'create':
return <ScheduleCreateContent return <ScheduleCreateContent
setMode={setMode} setMode={setMode}
@@ -62,18 +62,10 @@ export const SchedulePopover = ({ date, open, popoverSide, popoverAlign }: Sched
return ( return (
<PopoverContent <PopoverContent
className="rounded-xl xl:w-[calc(100vw/4)] xl:max-w-[480px] min-w-[384px]" className="rounded-xl xl:w-[calc(100vw/4)] xl:max-w-[480px] min-w-[384px] min-h-[125px] h-[calc(100vh/2.3)]"
align={popoverAlign} side={popoverSide} align={popoverAlign} side={popoverSide}
> >
<ScrollArea {<SchedulePopoverContent />}
className={
cn(
"min-h-[125px] h-[calc(100vh/2.3)] w-full flex flex-col",
)
}
>
{<SchedulePopoverContent />}
</ScrollArea>
</PopoverContent> </PopoverContent>
) )
} }

View File

@@ -2,6 +2,7 @@ import { Button } from "@/components/ui/button";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { ScrollArea } from "@/components/ui/scroll-area"; import { ScrollArea } from "@/components/ui/scroll-area";
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
import { cn } from "@/lib/utils";
import { useState } from "react"; import { useState } from "react";
interface BaseProps { interface BaseProps {
@@ -83,6 +84,7 @@ export const TimePickPopover = ({ ...props }: TimePickPopoverProps) => {
}; };
const startTime = `${startAmPm} ${startHour}${startMinute}`; const startTime = `${startAmPm} ${startHour}${startMinute}`;
setStartTime(startTime); setStartTime(startTime);
setStartOpen(false);
} }
const cancelStartTime = () => { const cancelStartTime = () => {
setStartOpen(false); setStartOpen(false);
@@ -128,7 +130,6 @@ export const TimePickPopover = ({ ...props }: TimePickPopoverProps) => {
return; return;
} }
const { const {
endAmPm, endAmPm,
endHour, endHour,
@@ -140,6 +141,7 @@ export const TimePickPopover = ({ ...props }: TimePickPopoverProps) => {
}; };
const endTime = `${endAmPm} ${endHour}${endMinute}`; const endTime = `${endAmPm} ${endHour}${endMinute}`;
setEndTime(endTime); setEndTime(endTime);
setEndOpen(false);
} }
const cancelEndTime = () => { const cancelEndTime = () => {
setEndOpen(false); setEndOpen(false);
@@ -161,7 +163,12 @@ export const TimePickPopover = ({ ...props }: TimePickPopoverProps) => {
> >
<PopoverTrigger asChild> <PopoverTrigger asChild>
<Button <Button
className="flex-9 h-full border border-indigo-100 bg-white hover:bg-indigo-100 text-black" className={cn(
"flex-9 h-full",
!startOpen
? "bg-white text-indigo-300 hover:bg-indigo-300 hover:text-white"
: "bg-indigo-300 text-white hover:bg-indigo-300"
)}
disabled={disabled} disabled={disabled}
> >
{ {
@@ -183,13 +190,21 @@ export const TimePickPopover = ({ ...props }: TimePickPopoverProps) => {
> >
<ToggleGroupItem <ToggleGroupItem
value={"오전"} value={"오전"}
className="w-full h-7 rounded-none! rounded-tl-md!" className={cn(
"w-full h-7 rounded-none! rounded-tl-md!",
"text-indigo-300! bg-white! hover:bg-indigo-300! hover:text-white!",
"data-[state=on]:bg-indigo-300! data-[state=on]:text-white!"
)}
> >
</ToggleGroupItem> </ToggleGroupItem>
<ToggleGroupItem <ToggleGroupItem
value={"오후"} value={"오후"}
className="w-full h-7 rounded-none!" className={cn(
"w-full h-7 rounded-none!",
"text-indigo-300! bg-white! hover:bg-indigo-300! hover:text-white!",
"data-[state=on]:bg-indigo-300! data-[state=on]:text-white!"
)}
> >
</ToggleGroupItem> </ToggleGroupItem>
@@ -200,18 +215,22 @@ export const TimePickPopover = ({ ...props }: TimePickPopoverProps) => {
<ToggleGroup <ToggleGroup
type="single" type="single"
id="startHour" id="startHour"
className="w-15 border-r rounded-none border-l flex flex-col justify-start items-center" className="w-15 border-r border-r-indigo-300 rounded-none border-l border-l-indigo-300 flex flex-col justify-start items-center"
> >
{ {
[1,2,3,4,5,6,7,8,9,10,11,12].map((time) => ( [1,2,3,4,5,6,7,8,9,10,11,12].map((time) => {
<ToggleGroupItem return <ToggleGroupItem
className="w-full h-7 rounded-none!" className={cn(
"w-full h-7 rounded-none!",
"bg-white! text-indigo-300! hover:bg-indigo-300! hover:text-white!",
"data-[state=on]:bg-indigo-300! data-[state=on]:text-white!"
)}
value={time.toString().padStart(2, '0')} value={time.toString().padStart(2, '0')}
key={`startHour${time.toString().padStart(2, '0')}`} key={`startHour${time.toString().padStart(2, '0')}`}
> >
{time.toString().padStart(2, '0')} {time.toString().padStart(2, '0')}
</ToggleGroupItem> </ToggleGroupItem>
)) })
} }
</ToggleGroup> </ToggleGroup>
</ScrollArea> </ScrollArea>
@@ -221,13 +240,17 @@ export const TimePickPopover = ({ ...props }: TimePickPopoverProps) => {
<ToggleGroup <ToggleGroup
type="single" type="single"
id="startMinute" id="startMinute"
className="w-full h-full rounded-none border-r flex flex-col justify-start items-center" className="w-full h-full rounded-none border-r border-r-indigo-300 flex flex-col justify-start items-center"
> >
{ {
Array.from({ length: 60 }).map((_, idx) => ( Array.from({ length: 60 }).map((_, idx) => (
<ToggleGroupItem <ToggleGroupItem
value={idx.toString().padStart(2, '0')} value={idx.toString().padStart(2, '0')}
className="w-full h-7 rounded-none!" className={cn(
"w-full h-7 rounded-none!",
"bg-white! text-indigo-300! hover:bg-indigo-300! hover:text-white!",
"data-[state=on]:bg-indigo-300! data-[state=on]:text-white!"
)}
key={`startMinute${idx.toString().padStart(2, '0')}`} key={`startMinute${idx.toString().padStart(2, '0')}`}
> >
{idx.toString().padStart(2, '0')} {idx.toString().padStart(2, '0')}
@@ -238,13 +261,21 @@ export const TimePickPopover = ({ ...props }: TimePickPopoverProps) => {
</ScrollArea> </ScrollArea>
<div className="w-15 h-full flex flex-col"> <div className="w-15 h-full flex flex-col">
<div <div
className="cursor-default text-sm flex justify-center items-center w-full h-7 rounded-none rounded-tr-md! hover:bg-gray-100" className={cn(
"cursor-default text-sm flex justify-center items-center w-full h-7 rounded-none rounded-tr-md!",
"text-indigo-300 bg-white tarnsition-all duration-150",
"hover:text-white hover:bg-indigo-300"
)}
onClick={applyStartTime} onClick={applyStartTime}
> >
</div> </div>
<div <div
className="cursor-default text-sm flex justify-center items-center w-full h-7 rounded-none rounded-tr-md! hover:bg-gray-100" className={cn(
"cursor-default text-sm flex justify-center items-center w-full h-7 rounded-none",
"text-red-400 bg- transition-all duration-150",
"hover:text-white hover:bg-red-400"
)}
onClick={cancelStartTime} onClick={cancelStartTime}
> >
@@ -259,7 +290,12 @@ export const TimePickPopover = ({ ...props }: TimePickPopoverProps) => {
> >
<PopoverTrigger asChild> <PopoverTrigger asChild>
<Button <Button
className="flex-9 h-full border border-indigo-100 bg-white hover:bg-indigo-100 text-black" className={cn(
"flex-9 h-full",
!endOpen
? "bg-white text-indigo-300 hover:bg-indigo-300 hover:text-white"
: "bg-indigo-300 text-white hover:bg-indigo-300"
)}
disabled={disabled} disabled={disabled}
> >
{ {
@@ -281,13 +317,21 @@ export const TimePickPopover = ({ ...props }: TimePickPopoverProps) => {
> >
<ToggleGroupItem <ToggleGroupItem
value={"오전"} value={"오전"}
className="w-full h-7 rounded-none! rounded-tl-md!" className={cn(
"w-full h-7 rounded-none! rounded-tl-md!",
"text-indigo-300! bg-white! hover:bg-indigo-300! hover:text-white!",
"data-[state=on]:bg-indigo-300! data-[state=on]:text-white!"
)}
> >
</ToggleGroupItem> </ToggleGroupItem>
<ToggleGroupItem <ToggleGroupItem
value={"오후"} value={"오후"}
className="w-full h-7 rounded-none!" className={cn(
"w-full h-7 rounded-none!",
"text-indigo-300! bg-white! hover:bg-indigo-300! hover:text-white!",
"data-[state=on]:bg-indigo-300! data-[state=on]:text-white!"
)}
> >
</ToggleGroupItem> </ToggleGroupItem>
@@ -298,12 +342,16 @@ export const TimePickPopover = ({ ...props }: TimePickPopoverProps) => {
<ToggleGroup <ToggleGroup
id="endHour" id="endHour"
type="single" type="single"
className="w-15 border-r rounded-none border-l flex flex-col justify-start items-center" className="w-15 border-r border-r-indigo-300 rounded-none border-l border-l-indigo-300 flex flex-col justify-start items-center"
> >
{ {
[1,2,3,4,5,6,7,8,9,10,11,12].map((time) => ( [1,2,3,4,5,6,7,8,9,10,11,12].map((time) => (
<ToggleGroupItem <ToggleGroupItem
className="w-full h-7 rounded-none!" className={cn(
"w-full h-7 rounded-none!",
"bg-white! text-indigo-300! hover:bg-indigo-300! hover:text-white!",
"data-[state=on]:bg-indigo-300! data-[state=on]:text-white!"
)}
key={`endHour${time.toString().padStart(2, '0')}`} key={`endHour${time.toString().padStart(2, '0')}`}
value={time.toString().padStart(2, '0')} value={time.toString().padStart(2, '0')}
> >
@@ -319,14 +367,18 @@ export const TimePickPopover = ({ ...props }: TimePickPopoverProps) => {
<ToggleGroup <ToggleGroup
id="endMinute" id="endMinute"
type="single" type="single"
className="w-full h-full rounded-none border-r flex flex-col justify-start items-center" className="w-full h-full rounded-none border-r border-r-indigo-300 flex flex-col justify-start items-center"
> >
{ {
Array.from({ length: 60 }).map((_, idx) => ( Array.from({ length: 60 }).map((_, idx) => (
<ToggleGroupItem <ToggleGroupItem
value={idx.toString().padStart(2, '0')} value={idx.toString().padStart(2, '0')}
key={`endMinute${idx.toString().padStart(2, '0')}`} key={`endMinute${idx.toString().padStart(2, '0')}`}
className="w-full h-7 rounded-none!" className={cn(
"w-full h-7 rounded-none!",
"bg-white! text-indigo-300! hover:bg-indigo-300! hover:text-white!",
"data-[state=on]:bg-indigo-300! data-[state=on]:text-white!"
)}
> >
{idx.toString().padStart(2, '0')} {idx.toString().padStart(2, '0')}
</ToggleGroupItem> </ToggleGroupItem>
@@ -336,13 +388,21 @@ export const TimePickPopover = ({ ...props }: TimePickPopoverProps) => {
</ScrollArea> </ScrollArea>
<div className="w-15 h-full flex flex-col"> <div className="w-15 h-full flex flex-col">
<div <div
className="cursor-default text-sm flex justify-center items-center w-full h-7 rounded-none rounded-tr-md! hover:bg-gray-100" className={cn(
"cursor-default text-sm flex justify-center items-center w-full h-7 rounded-none rounded-tr-md!",
"text-indigo-300 bg-white transition-all duration-150",
"hover:text-white hover:bg-indigo-300"
)}
onClick={applyEndTime} onClick={applyEndTime}
> >
</div> </div>
<div <div
className="cursor-default text-sm flex justify-center items-center w-full h-7 rounded-none rounded-tr-md! hover:bg-gray-100" className={cn(
"cursor-default text-sm flex justify-center items-center w-full h-7 rounded-none",
"text-red-400 bg-white transition-all duration-150",
"hover:text-white hover:bg-red-400"
)}
onClick={cancelEndTime} onClick={cancelEndTime}
> >

View File

@@ -1,31 +1,68 @@
import { PopoverContent } from "@/components/ui/popover" import { Button } from "@/components/ui/button";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
import { ScheduleTypeLabel, type ScheduleType } from "@/const/schedule/ScheduleType"; import { ScheduleTypeLabel, type ScheduleType } from "@/const/schedule/ScheduleType";
import { useRecord } from "@/hooks/use-record"; import { useRecord } from "@/hooks/use-record";
import { cn } from "@/lib/utils";
import { useState } from "react";
interface TypePickPopoverProps { interface TypePickPopoverProps {
popoverSide : 'left' | 'right'; popoverSide : 'left' | 'right';
type: ScheduleType;
setType: (type: ScheduleType) => void; setType: (type: ScheduleType) => void;
} }
export const TypePickPopover = ({ popoverSide, setType }: TypePickPopoverProps) => { export const TypePickPopover = ({ popoverSide, type, setType }: TypePickPopoverProps) => {
const [open, setOpen] = useState(false);
const typeLabelList = useRecord(ScheduleTypeLabel).keys.map((key) => { const typeLabelList = useRecord(ScheduleTypeLabel).keys.map((key) => {
return { return {
type: key, type: key,
label: ScheduleTypeLabel[key as keyof typeof ScheduleTypeLabel] label: ScheduleTypeLabel[key as keyof typeof ScheduleTypeLabel]
} as { type: ScheduleType, label: string}; } as { type: ScheduleType, label: string};
}); });
const selectType = (type: ScheduleType) => {
setType(type);
setOpen(false);
}
return ( return (
<PopoverContent side={popoverSide} align={'start'} className="p-0 w-fit h-fit"> <div className="w-full h-10">
<div className="w-20 h-62.5 flex flex-col"> <Popover
{typeLabelList.map((type) => ( open={open}
<div onOpenChange={setOpen}
className="cursor-default flex-1 h-full flex justify-center items-center hover:bg-gray-100" >
onClick={() => setType(type.type)} <PopoverTrigger asChild>
<Button
className={cn(
"w-full h-10 rounded-md",
!open
? "bg-white text-indigo-300 hover:bg-indigo-300 hover:text-white"
: "bg-indigo-300 text-white hover:bg-indigo-300!"
)}
type="button"
> >
{type.label} {ScheduleTypeLabel[type as keyof typeof ScheduleTypeLabel]}
</Button>
</PopoverTrigger>
<PopoverContent side={popoverSide} align={'start'} className="p-0 w-fit h-fit">
<div className="w-20 h-62.5 flex flex-col rounded-md">
{typeLabelList.map((label, idx) => (
<div
className={cn(
"cursor-default flex-1 h-full flex justify-center items-center transition-all duration-150",
type === label.type
? "bg-indigo-300 text-white hover:bg-indigo-300!"
: "bg-white text-indigo-300 hover:bg-indigo-300 hover:text-white",
(idx === 0 && "rounded-t-md"),
(idx === typeLabelList.length - 1 && "rounded-b-md")
)}
onClick={() => selectType(label.type)}
>
{label.label}
</div>
))}
</div> </div>
))} </PopoverContent>
</div> </Popover>
</PopoverContent> </div>
) )
} }

View File

@@ -1,13 +1,15 @@
import type { ColorPaletteType } from "@/const/ColorPalette";
interface BaseProps { interface BaseProps {
date: Date | undefined; date: Date | undefined;
open: boolean; open: boolean;
popoverSide: 'left' | 'right'; popoverSide: 'left' | 'right';
popoverAlign: 'start' | 'end'; popoverAlign: 'start' | 'end';
setMode: (mode: 'list' | 'create' | 'detail' | 'update') => void;
} }
export interface ScheduleCreateContentProps extends BaseProps { export interface ScheduleCreateContentProps extends BaseProps {
date: Date | undefined;
setMode: (mode: 'list' | 'create' | 'detail' | 'update') => void; }
export interface ScheduleListContentProps extends BaseProps {
} }

View File

@@ -1,41 +1,44 @@
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 { 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 { ArrowLeft, PenSquare, X, XIcon } 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';
import { useTime } from '@/hooks/use-time';
import type { ScheduleCreateContentProps } from './ContentProps';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Field, FieldError } from '@/components/ui/field';
import { Input } from '@/components/ui/input';
import { Popover, PopoverTrigger } from '@/components/ui/popover';
import { ScrollArea } from '@/components/ui/scroll-area';
import { Textarea } from '@/components/ui/textarea'; import { Textarea } from '@/components/ui/textarea';
import { ParticipantPopover } from '../ParticipantPopover'; import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group';
import { type ColorPaletteType } from '@/const/ColorPalette';
import { ScheduleDay } from '@/const/schedule/ScheduleDay';
import type { ScheduleStatus } from '@/const/schedule/ScheduleStatus';
import { type ScheduleType } from '@/const/schedule/ScheduleType';
import { CreateScheduleSchema } from '@/data/form/schedule/createSchedule.schema';
import { usePalette } from '@/hooks/use-palette';
import { useRecord } from '@/hooks/use-record';
import { useTime } from '@/hooks/use-time';
import { cn } from '@/lib/utils';
import { ScheduleNetwork } from '@/network/ScheduleNetwork';
import { zodResolver } from '@hookform/resolvers/zod';
import { ArrowLeft } from 'lucide-react';
import { useState } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { toast } from 'sonner';
import * as z from 'zod';
import { ColorPickPopover } from '../ColorPickPopover';
import { DatePickPopover } from '../DatePickPopover';
import { TimePickPopover } from '../TimePickPopover';
import { TypePickPopover } from '../TypePickPopover';
import type { ScheduleCreateContentProps } from './ContentProps';
export const ScheduleCreateContent = ({ date, open, setMode, popoverSide, popoverAlign }: ScheduleCreateContentProps) => { export const ScheduleCreateContent = ({ date, setMode, popoverSide, popoverAlign }: ScheduleCreateContentProps) => {
const [colorPopoverOpen, setColorPopoverOpen] = useState(false); const [colorPopoverOpen, setColorPopoverOpen] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const { getPaletteByKey } = usePalette(); const { getPaletteByKey } = usePalette();
const { getNowString } = useTime(); const { getCurrentTimeString, standardTimeToContinentalTime } = useTime();
const dayLabelList = useRecord(ScheduleDay).keys.map((key) => { const dayLabelList = useRecord(ScheduleDay).keys.map((key) => {
return { return {
day: Number(key), day: Number(key),
label: ScheduleDay[Number(key)] label: ScheduleDay[Number(key)]
} as { day: number, label: string }; } as { day: number, label: string };
}) });
const scheduleNetwork = new ScheduleNetwork();
const createScheduleForm = useForm<z.infer<typeof CreateScheduleSchema>>({ const createScheduleForm = useForm<z.infer<typeof CreateScheduleSchema>>({
resolver: zodResolver(CreateScheduleSchema), resolver: zodResolver(CreateScheduleSchema),
@@ -44,8 +47,8 @@ export const ScheduleCreateContent = ({ date, open, setMode, popoverSide, popove
startDate: date || new Date(), startDate: date || new Date(),
endDate: date || new Date(), endDate: date || new Date(),
content: "", content: "",
startTime: getNowString(), startTime: getCurrentTimeString('standard'),
endTime: getNowString(), endTime: getCurrentTimeString('standard'),
type: "once", type: "once",
status: "yet", status: "yet",
style: getPaletteByKey('Black').style, style: getPaletteByKey('Black').style,
@@ -65,10 +68,48 @@ export const ScheduleCreateContent = ({ date, open, setMode, popoverSide, popove
status, status,
style, style,
dayList, dayList,
participantList // participantList
} = createScheduleForm.watch(); } = createScheduleForm.watch();
const selectColor = (color: ColorPaletteType) => { const reqCreate = async () => {
if (isLoading) return;
const data = {
name,
startDate,
endDate,
content,
startTime: standardTimeToContinentalTime(startTime),
endTime: standardTimeToContinentalTime(endTime),
type: type as ScheduleType,
status: status as ScheduleStatus,
dayList,
style
};
const createPromise = scheduleNetwork.create(data);
setIsLoading(true);
toast.promise(
createPromise,
{
loading: '일정 생성 중입니다',
success: (res) => {
setIsLoading(false);
if (res.data.success) {
setMode('list');
return '일정이 생성되었습니다'
} else {
throw new Error(res.data.error);
}
},
error: (err: Error) => {
setIsLoading(false);
return err.message || "에러 발생";
}
}
)
}
const selectColor = (color: ColorPaletteType) => {
createScheduleForm.setValue('style', color.style); createScheduleForm.setValue('style', color.style);
setColorPopoverOpen(false); setColorPopoverOpen(false);
} }
@@ -92,9 +133,9 @@ export const ScheduleCreateContent = ({ date, open, setMode, popoverSide, popove
createScheduleForm.setValue(type, time); createScheduleForm.setValue(type, time);
} }
const selectParticipant = (participantList: string[]) => { // const selectParticipant = (participantList: string[]) => {
createScheduleForm.setValue('participantList', participantList); // createScheduleForm.setValue('participantList', participantList);
} // }
const OnceContent = () => { const OnceContent = () => {
return ( return (
@@ -158,7 +199,7 @@ export const ScheduleCreateContent = ({ date, open, setMode, popoverSide, popove
<div <div
onClick={() => setMode('list')} onClick={() => setMode('list')}
> >
<ArrowLeft /> <ArrowLeft className="stroke-indigo-100 hover:stroke-indigo-300 transition-all duration-150" />
</div> </div>
<Controller <Controller
name="name" name="name"
@@ -169,7 +210,7 @@ export const ScheduleCreateContent = ({ date, open, setMode, popoverSide, popove
{...field} {...field}
id="form-create-schedule-name" id="form-create-schedule-name"
placeholder="제목" placeholder="제목"
className="font-bold border-t-0 border-r-0 border-l-0 p-0 border-b-2 rounded-none shadow-none border-indigo-100 focus-visible:ring-0 focus-visible:border-b-indigo-300" className="placeholder-indigo-200! font-bold border-t-0 border-r-0 border-l-0 p-0 border-b-2 rounded-none shadow-none border-indigo-100 focus-visible:ring-0 focus-visible:border-b-indigo-300"
style={{ style={{
fontSize: '20px' fontSize: '20px'
}} }}
@@ -198,52 +239,82 @@ export const ScheduleCreateContent = ({ date, open, setMode, popoverSide, popove
/> />
</Popover> </Popover>
</div> </div>
<Popover> <ScrollArea
<PopoverTrigger asChild> className={
<div className="hover:bg-indigo-100 cursor-default w-full h-10 border border-indigo-100 flex justify-center items-center rounded-sm"> cn(
{ScheduleTypeLabel[type as keyof typeof ScheduleTypeLabel]} "min-h-[125px] h-[calc(100vh/2.3-40px)]! w-full",
</div> )
</PopoverTrigger> }
<TypePickPopover >
<div
className="w-full h-full flex! flex-col! gap-4!"
>
<TypePickPopover
type={type as ScheduleType}
setType={selectType} setType={selectType}
popoverSide={popoverSide} popoverSide={popoverSide}
/> />
</Popover> <div
<div className="w-full h-10"
className="w-full h-10" >
> {renderContent()}
{renderContent()} </div>
</div> <div className="w-full h-10">
<div className="w-full h-10"> <TimePickPopover
<TimePickPopover mode='range'
mode='range' popoverAlign={popoverAlign}
popoverAlign={popoverAlign} disabled={false}
disabled={false} startTime={startTime}
startTime={startTime} setStartTime={(time: string | undefined) => selectTime('startTime', time ?? '')}
setStartTime={(time: string | undefined) => selectTime('startTime', time ?? '')} endTime={endTime}
endTime={endTime} setEndTime={(time: string | undefined) => selectTime('endTime', time ?? '')}
setEndTime={(time: string | undefined) => selectTime('endTime', time ?? '')} />
/> </div>
</div> <Controller
<Controller name="content"
name="content" control={createScheduleForm.control}
control={createScheduleForm.control} render={({ field }) => (
render={({ field }) => ( <Textarea
<Textarea {...field}
{...field} rows={2}
rows={2} placeholder="일정 상세 사항"
placeholder="일정 상세 사항" className="placeholder-indigo-200! focus-visible:placeholder-indigo-300! border-indigo-100 focus-visible:border-indigo-300 resize-none focus-visible:ring-0"
className="placeholder-gray-300! focus-visible:placeholder-gray-400! border-indigo-100 focus-visible:border-indigo-300 resize-none focus-visible:ring-0" style={{
style={{ 'scrollbarWidth': 'none'
'scrollbarWidth': 'none' }}
}} />
)}
/> />
)} {/* <ParticipantPopover
/> participantList={participantList}
<ParticipantPopover setParticipantList={selectParticipant}
participantList={participantList} /> */}
setParticipantList={selectParticipant} </div>
/> </ScrollArea>
<div
className="absolute w-full border-b border-l border-r rounded-b-md left-0 bottom-0 h-10 flex flex-row justify-start items-end p-0"
>
<Button
className={cn(
"h-full flex-5 rounded-none rounded-bl-md flex justify-center items-center",
"text-indigo-300 bg-white",
"hover:text-white hover:bg-indigo-300"
)}
type="button"
onClick={reqCreate}
>
</Button>
<Button
className={cn(
"h-full flex-5 rounded-none rounded-br-md flex justify-center items-center",
"bg-white text-red-400",
"hover:text-white hover:bg-red-400"
)}
>
</Button>
</div>
</div> </div>
) )

View File

@@ -0,0 +1,86 @@
import { ScrollArea } from "@/components/ui/scroll-area";
import { ListScheduleSchema } from "@/data/form/schedule/listSchedule.schema";
import { format } from "date-fns";
import { List, PenSquare } from "lucide-react";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import * as z from "zod";
import type { ScheduleListContentProps } from "./ContentProps";
import { zodResolver } from "@hookform/resolvers/zod";
import { ScheduleNetwork } from "@/network/ScheduleNetwork";
import type { ScheduleStatus } from "@/const/schedule/ScheduleStatus";
import type { ScheduleType } from "@/const/schedule/ScheduleType";
export const ScheduleListContent = ({ date, setMode, popoverAlign, popoverSide, open }: ScheduleListContentProps) => {
const [isLoading, setIsLoading] = useState(false);
const [scheduleList, setScheduleList] = useState([]);
const [filteredList, setFilteredList] = useState([]);
const scheduleNetwork = new ScheduleNetwork();
const listScheduleForm = useForm<z.infer<typeof ListScheduleSchema>>({
resolver: zodResolver(ListScheduleSchema),
defaultValues: {
name: undefined,
startDate: undefined,
endDate: undefined,
status: undefined,
typeList: undefined,
styleList: undefined
}
});
const {
name,
startDate,
endDate,
status,
typeList,
styleList
} = listScheduleForm.watch();
useEffect(() => {
if (isLoading) {
return;
}
return (() => {
setIsLoading(false);
});
}, []);
const reqList = async () => {
const data = {
name,
startDate,
endDate,
status: status as ScheduleStatus | undefined,
styleList,
typeList: typeList as ScheduleType[] | undefined
};
const result = await scheduleNetwork.getList(data);
}
return (
<div className="w-full h-full flex flex-col gap-4">
<div className="relative w-full h-10 border-b border-b-indigo-300 flex flex-row items-center justify-center">
<span className="text-indigo-400">{date && format(date, "yyyy년 MM월 dd일")}</span>
<div className="absolute top-3 right-0.5 group">
<PenSquare
className="transition-all duration-150 group-hover:stroke-indigo-600 stroke-indigo-400"
size={18}
onClick={() => setTimeout(() => {setMode('create')}, 150)}
/>
</div>
</div>
<div className="w-full h-[calc(100%-40px)]">
<ScrollArea
className="w-full h-full flex flex-col justify-start items-center"
>
</ScrollArea>
</div>
</div>
)
}

View File

@@ -9,7 +9,7 @@ const certPath = path.resolve(__dirname, 'certs');
// https://vite.dev/config/ // https://vite.dev/config/
export default defineConfig({ export default defineConfig({
server: { server: {
host: '0.0.0.0', // host: '0.0.0.0',
port: 5185, port: 5185,
https: { https: {
key: fs.readFileSync(path.join(certPath, 'localhost+2-key.pem')), key: fs.readFileSync(path.join(certPath, 'localhost+2-key.pem')),