- 시작/종료 시간 설정 화면 및 로직 구현 - 일정 상세 사항 화면 및 로직 구현 - 참여자 추가 화면 구현 중
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
"hooks": "@/hooks"
|
||||
},
|
||||
"registries": {
|
||||
"@reui": "https://reui.io/r/{name}.json"
|
||||
"@reui": "https://reui.io/r/{name}.json",
|
||||
"@diceui": "https://diceui.com/r/{name}.json"
|
||||
}
|
||||
}
|
||||
|
||||
67
package-lock.json
generated
67
package-lock.json
generated
@@ -8,6 +8,7 @@
|
||||
"name": "scheduler",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@diceui/mention": "^0.8.0",
|
||||
"@hookform/resolvers": "^5.2.2",
|
||||
"@radix-ui/react-accordion": "^1.2.12",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.15",
|
||||
@@ -376,6 +377,50 @@
|
||||
"integrity": "sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@diceui/mention": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@diceui/mention/-/mention-0.8.0.tgz",
|
||||
"integrity": "sha512-BHTYLG4qJA+9TJ/n5otlIHoJuLz5v13xAayYsRL7lnCnp4ik37uYrLpA+25y+uQmJ5i1W3kv2MfD2ZasvDzDDw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@diceui/shared": "0.12.0",
|
||||
"@floating-ui/react": "^0.27.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@diceui/shared": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@diceui/shared/-/shared-0.12.0.tgz",
|
||||
"integrity": "sha512-aJ+gxHTleFD8b9DgXOb7G0sHymW8nMK4C3KXDMca4+PPl1NsfRG/Lnb2dN8vq44XOb/W7Wjn3gmgakXAOzgrJA==",
|
||||
"peerDependencies": {
|
||||
"@floating-ui/react": "^0.27.4",
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.25.11",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.11.tgz",
|
||||
@@ -968,6 +1013,22 @@
|
||||
"@floating-ui/utils": "^0.2.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/react": {
|
||||
"version": "0.27.16",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.27.16.tgz",
|
||||
"integrity": "sha512-9O8N4SeG2z++TSM8QA/KTeKFBVCNEz/AGS7gWPJf6KFRzmRWixFRnCnkPHRDwSVZW6QPDO6uT0P2SpWNKCc9/g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@floating-ui/react-dom": "^2.1.6",
|
||||
"@floating-ui/utils": "^0.2.10",
|
||||
"tabbable": "^6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=17.0.0",
|
||||
"react-dom": ">=17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/react-dom": {
|
||||
"version": "2.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.6.tgz",
|
||||
@@ -6375,6 +6436,12 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/tabbable": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.3.0.tgz",
|
||||
"integrity": "sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tailwind-merge": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz",
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@diceui/mention": "^0.8.0",
|
||||
"@hookform/resolvers": "^5.2.2",
|
||||
"@radix-ui/react-accordion": "^1.2.12",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.15",
|
||||
|
||||
91
src/components/ui/mention.tsx
Normal file
91
src/components/ui/mention.tsx
Normal file
@@ -0,0 +1,91 @@
|
||||
import * as MentionPrimitive from "@diceui/mention";
|
||||
import type * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
function Mention({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof MentionPrimitive.Root>) {
|
||||
return (
|
||||
<MentionPrimitive.Root
|
||||
data-slot="mention"
|
||||
className={cn(
|
||||
"**:data-tag:rounded **:data-tag:bg-blue-200 **:data-tag:py-px **:data-tag:text-blue-950 dark:**:data-tag:bg-blue-800 dark:**:data-tag:text-blue-50",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function MentionLabel({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof MentionPrimitive.Label>) {
|
||||
return (
|
||||
<MentionPrimitive.Label
|
||||
data-slot="mention-label"
|
||||
className={cn("px-0.5 py-1.5 font-semibold text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function MentionInput({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof MentionPrimitive.Input>) {
|
||||
return (
|
||||
<MentionPrimitive.Input
|
||||
data-slot="mention-input"
|
||||
className={cn(
|
||||
"flex w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-xs placeholder:text-muted-foreground focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function MentionContent({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof MentionPrimitive.Content>) {
|
||||
return (
|
||||
<MentionPrimitive.Portal>
|
||||
<MentionPrimitive.Content
|
||||
data-slot="mention-content"
|
||||
className={cn(
|
||||
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=closed]:animate-out data-[state=open]:animate-in",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</MentionPrimitive.Content>
|
||||
</MentionPrimitive.Portal>
|
||||
);
|
||||
}
|
||||
|
||||
function MentionItem({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof MentionPrimitive.Item>) {
|
||||
return (
|
||||
<MentionPrimitive.Item
|
||||
data-slot="mention-item"
|
||||
className={cn(
|
||||
"relative flex w-full cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden data-disabled:pointer-events-none data-highlighted:bg-accent data-highlighted:text-accent-foreground data-disabled:opacity-50",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</MentionPrimitive.Item>
|
||||
);
|
||||
}
|
||||
|
||||
export { Mention, MentionContent, MentionInput, MentionItem, MentionLabel };
|
||||
@@ -7,7 +7,7 @@ function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
|
||||
<textarea
|
||||
data-slot="textarea"
|
||||
className={cn(
|
||||
"border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||
"border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -3,6 +3,7 @@ import * as z from 'zod';
|
||||
export const CreateScheduleSchema = z.object({
|
||||
name: z
|
||||
.string()
|
||||
.nonempty()
|
||||
, startDate: z
|
||||
.date()
|
||||
, endDate: z
|
||||
@@ -21,4 +22,6 @@ export const CreateScheduleSchema = z.object({
|
||||
.string()
|
||||
, dayList: z
|
||||
.string()
|
||||
, participantList: z
|
||||
.array(z.string())
|
||||
});
|
||||
@@ -6,7 +6,7 @@ export const LoginSchema = z.object({
|
||||
.string()
|
||||
.refine((val) => {
|
||||
if (val.includes('@')) {
|
||||
return Validator.isEmail(val);;
|
||||
return Validator.isEmail(val);
|
||||
}
|
||||
return true;
|
||||
}, {
|
||||
|
||||
@@ -3,7 +3,8 @@ import * as z from 'zod';
|
||||
export const SignUpSchema = z.object({
|
||||
accountId: z
|
||||
.string()
|
||||
.min(5, "아이디는 5 자리 이상이어야 합니다.")
|
||||
.min(5, "아이디는 6-14자여야 합니다.")
|
||||
.max(14, "아이디는 6-14자여야합니다.")
|
||||
.refine((val) => {
|
||||
return /^[a-zA-z-_.]*$/.test(val);
|
||||
}, {
|
||||
@@ -14,12 +15,13 @@ export const SignUpSchema = z.object({
|
||||
.min(5, "이메일을 입력해주십시오.")
|
||||
, password: z
|
||||
.string()
|
||||
.min(8, "비밀번호는 8-12 자리여야 합니다.")
|
||||
.max(12, "비밀번호는 8-12 자리여야 합니다.")
|
||||
.min(8, "비밀번호는 8-12자여야 합니다.")
|
||||
.max(12, "비밀번호는 8-12자여야 합니다.")
|
||||
.regex(/^(?=.*[0-9])(?=.*[!@#$%^])[a-zA-Z0-9!@#$%^]+$/, "영소문자로 시작하고 숫자와 특수문자(!@#$%^)를 포함해야 합니다.")
|
||||
, name: z
|
||||
.string()
|
||||
.min(1, "이름을 입력해주시십시오.")
|
||||
.max(14, "너무 긴 이름은 사용하실 수 없습니다.")
|
||||
, nickname: z
|
||||
.string()
|
||||
.min(1, "닉네임을 입력해주십시오.")
|
||||
|
||||
21
src/hooks/use-time.ts
Normal file
21
src/hooks/use-time.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
export function useTime() {
|
||||
const getNowString = () => {
|
||||
const now = new Date();
|
||||
const hour = now.getHours();
|
||||
const minute = now.getMinutes();
|
||||
const ampm = hour < 12 ? '오전' : '오후';
|
||||
|
||||
return `${ampm} ${hour > 12 ? hour - 12 : hour}시 ${minute.toString().padStart(2, '0')}분`;
|
||||
}
|
||||
|
||||
const timeStringToISOString = (time: string) => {
|
||||
const timeArray = time.split(' ');
|
||||
const hour = timeArray[0] === '오전' ? Number(timeArray[1]) : Number(timeArray[1]) + 12;
|
||||
return `${hour.toString().padStart(2, '0')}:${timeArray[2]}:00`
|
||||
}
|
||||
|
||||
return {
|
||||
getNowString,
|
||||
timeStringToISOString
|
||||
}
|
||||
}
|
||||
@@ -56,15 +56,15 @@ export const DatePickPopover = ({ ...props } : DaetPickPopoverProps) => {
|
||||
|
||||
return (
|
||||
<div
|
||||
className="w-full h-full flex flex-row justify-around items-center"
|
||||
className="w-full h-full flex flex-row justify-between gap-3 items-center"
|
||||
>
|
||||
<Popover
|
||||
open={startOpen}
|
||||
onOpenChange={setStartOpen}
|
||||
>
|
||||
<PopoverTrigger>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
className="border border-indigo-100 bg-white hover:bg-indigo-100 text-black"
|
||||
className="flex-9 h-full border border-indigo-100 bg-white hover:bg-indigo-100 text-black"
|
||||
disabled={disabled}
|
||||
>
|
||||
{
|
||||
@@ -91,14 +91,14 @@ export const DatePickPopover = ({ ...props } : DaetPickPopoverProps) => {
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<span> - </span>
|
||||
<div className="flex-2 h-px bg-indigo-300" />
|
||||
<Popover
|
||||
open={endOpen}
|
||||
onOpenChange={setEndOpen}
|
||||
>
|
||||
<PopoverTrigger>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
className="border border-indigo-100 bg-white hover:bg-indigo-100 text-black"
|
||||
className="flex-9 h-full border border-indigo-100 bg-white hover:bg-indigo-100 text-black"
|
||||
disabled={disabled}
|
||||
>
|
||||
{
|
||||
|
||||
101
src/ui/component/popover/schedule/ParticipantPopover.tsx
Normal file
101
src/ui/component/popover/schedule/ParticipantPopover.tsx
Normal file
@@ -0,0 +1,101 @@
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { cn } from "@/lib/utils";
|
||||
import * as Mention from '@diceui/mention';
|
||||
import { AtSign, PlusIcon, SearchIcon } from "lucide-react";
|
||||
import { useRef, useState } from "react";
|
||||
|
||||
const dummyUser = [
|
||||
{
|
||||
accountId: "dummy1",
|
||||
name: "더미1"
|
||||
},
|
||||
{
|
||||
accountId: "test2",
|
||||
name: "테스트2"
|
||||
},
|
||||
{
|
||||
accountId: "dummy3",
|
||||
name: "테스트3"
|
||||
},
|
||||
{
|
||||
accountId: "test4",
|
||||
name: "더미4"
|
||||
}
|
||||
]
|
||||
|
||||
export const ParticipantPopover = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [filterString, setFilterString] = useState('');
|
||||
|
||||
return (
|
||||
<div className="w-full h-10 flex flex-row justify-start items-center">
|
||||
<Popover
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
className={cn(
|
||||
!open
|
||||
? "bg-white text-indigo-300 hover:bg-indigo-300 hover:text-white"
|
||||
: "bg-indigo-300 text-white hover:bg-indigo-300!"
|
||||
|
||||
)}
|
||||
type="button"
|
||||
>
|
||||
<PlusIcon
|
||||
/>
|
||||
<span>
|
||||
참여자 추가
|
||||
</span>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
className="py-0 px-0 w-60 h-80 flex flex-col justify-start items-start"
|
||||
>
|
||||
<div
|
||||
className="flex flex-row items-center px-3 h-10 w-full border-b border-b-indigo-100"
|
||||
>
|
||||
<SearchIcon size={16} className="stroke-indigo-300" />
|
||||
<Input
|
||||
placeholder="검색"
|
||||
value={filterString}
|
||||
className="shadow-none placeholder-indigo-300! focus-visible:placeholder-indigo-400! flex-1 focus-visible:ring-0 focus-visible:border-0 border-0"
|
||||
/>
|
||||
</div>
|
||||
<ScrollArea
|
||||
className="w-full h-60 flex flex-col"
|
||||
>
|
||||
|
||||
</ScrollArea>
|
||||
<div
|
||||
className="border-t border-t-indigo-300 w-full h-10 flex flex-row cursor-default"
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"flex-5 h-full flex flex-row justify-center items-center transition-all duration-150",
|
||||
"bg-white text-indigo-300",
|
||||
"hover:bg-indigo-300 hover:text-white"
|
||||
)}
|
||||
>
|
||||
확인
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
"flex-5 h-full flex flex-row justify-center items-center transition-all duration-150",
|
||||
"bg-white text-red-400",
|
||||
"hover:bg-red-400 hover:text-white"
|
||||
)}
|
||||
>
|
||||
취소
|
||||
</div>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -3,7 +3,7 @@ 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 { type ColorPaletteType } from '@/const/ColorPalette';
|
||||
import { ColorPickPopover } from './ColorPickPopover';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
@@ -20,6 +20,8 @@ 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 { ScheduleCreateContent } from './content/ScheduleCreateContent';
|
||||
|
||||
interface ScheduleSheetProps {
|
||||
date: Date | undefined;
|
||||
@@ -29,85 +31,8 @@ interface ScheduleSheetProps {
|
||||
}
|
||||
|
||||
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 (
|
||||
@@ -117,143 +42,6 @@ export const SchedulePopover = ({ date, open, popoverSide, popoverAlign }: Sched
|
||||
)
|
||||
}
|
||||
|
||||
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>
|
||||
@@ -275,7 +63,13 @@ export const SchedulePopover = ({ date, open, popoverSide, popoverAlign }: Sched
|
||||
case 'list':
|
||||
return <ListContent />
|
||||
case 'create':
|
||||
return <CreateContent />
|
||||
return <ScheduleCreateContent
|
||||
setMode={setMode}
|
||||
date={date}
|
||||
popoverAlign={popoverAlign}
|
||||
popoverSide={popoverSide}
|
||||
open={open}
|
||||
/>
|
||||
case 'detail':
|
||||
return <DetailContent />
|
||||
case 'update':
|
||||
@@ -288,11 +82,10 @@ export const SchedulePopover = ({ date, open, popoverSide, popoverAlign }: Sched
|
||||
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",
|
||||
"min-h-[125px] h-[calc(100vh/2.3)] w-full flex flex-col",
|
||||
)
|
||||
}
|
||||
>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Button } from "@/components/ui/button";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
|
||||
import { useState } from "react";
|
||||
|
||||
interface BaseProps {
|
||||
disabled: boolean;
|
||||
@@ -33,20 +34,141 @@ export const TimePickPopover = ({ ...props }: TimePickPopoverProps) => {
|
||||
)
|
||||
}
|
||||
|
||||
// const { startTime, setStartTime, endTime, setEndTime } = props;
|
||||
const [startOpen, setStartOpen] = useState(false);
|
||||
const [endOpen, setEndOpen] = useState(false);
|
||||
const { startTime, setStartTime, endTime, setEndTime } = props;
|
||||
|
||||
const onStartOpenChange = (open: boolean) => {
|
||||
if (open) {
|
||||
setStartOpen(true);
|
||||
if (startTime) {
|
||||
const startTimeArray = startTime.split(' ');
|
||||
setTimeout(() => {
|
||||
const startAmPmList = document.querySelectorAll('#startAmPm button');
|
||||
const targetAmPm = Array.from(startAmPmList).find(button => {
|
||||
return button.textContent.trim() === startTimeArray[0];
|
||||
});
|
||||
const startHourList = document.querySelectorAll('#startHour button');
|
||||
const targetHour = Array.from(startHourList).find(button => {
|
||||
return button.textContent.trim() === startTimeArray[1].slice(0, 2);
|
||||
});
|
||||
const startMinuteList = document.querySelectorAll('#startMinute button');
|
||||
const targetMinute = Array.from(startMinuteList).find(button => {
|
||||
return button.textContent.trim() === startTimeArray[2].slice(0, 2);
|
||||
});
|
||||
[targetAmPm, targetHour, targetMinute].forEach(element => {
|
||||
(element as HTMLButtonElement).click();
|
||||
});
|
||||
}, 150);
|
||||
|
||||
}
|
||||
} else {
|
||||
setStartOpen(false);
|
||||
}
|
||||
}
|
||||
const applyStartTime = () => {
|
||||
const startOnStateElements = document.querySelectorAll('[id^="start"] [data-state="on"]');
|
||||
if (startOnStateElements.length < 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
startAmPm,
|
||||
startHour,
|
||||
startMinute
|
||||
} = {
|
||||
startAmPm: document.querySelector('#startAmPm [data-state="on"]')?.textContent.trim(),
|
||||
startHour: document.querySelector('#startHour [data-state="on"]')?.textContent.trim(),
|
||||
startMinute: document.querySelector('#startMinute [data-state="on"]')?.textContent.trim()
|
||||
};
|
||||
const startTime = `${startAmPm} ${startHour}시 ${startMinute}분`;
|
||||
setStartTime(startTime);
|
||||
}
|
||||
const cancelStartTime = () => {
|
||||
setStartOpen(false);
|
||||
setTimeout(() => {
|
||||
const startOnElements = document.querySelectorAll('[id^="start"] [data-state="on"]');
|
||||
startOnElements.forEach(element => {
|
||||
(element as HTMLButtonElement).click();
|
||||
});
|
||||
}, 150);
|
||||
}
|
||||
|
||||
const onEndOpenChange = (open: boolean) => {
|
||||
if (open) {
|
||||
setEndOpen(true);
|
||||
if (endTime) {
|
||||
const endTimeArray = endTime.split(' ');
|
||||
setTimeout(() => {
|
||||
const endAmPmList = document.querySelectorAll('#endAmPm button');
|
||||
const targetAmPm = Array.from(endAmPmList).find(button => {
|
||||
return button.textContent.trim() === endTimeArray[0];
|
||||
});
|
||||
const endHourList = document.querySelectorAll('#endHour button');
|
||||
const targetHour = Array.from(endHourList).find(button => {
|
||||
return button.textContent.trim() === endTimeArray[1].slice(0, 2);
|
||||
});
|
||||
const endMinuteList = document.querySelectorAll('#endMinute button');
|
||||
const targetMinute = Array.from(endMinuteList).find(button => {
|
||||
return button.textContent.trim() === endTimeArray[2].slice(0, 2);
|
||||
});
|
||||
[targetAmPm, targetHour, targetMinute].forEach(element => {
|
||||
(element as HTMLButtonElement).click();
|
||||
});
|
||||
}, 150);
|
||||
|
||||
}
|
||||
} else {
|
||||
setEndOpen(false);
|
||||
}
|
||||
}
|
||||
const applyEndTime = () => {
|
||||
const endOnStateElements = document.querySelectorAll('[id^="end"] [data-state="on"]');
|
||||
if (endOnStateElements.length < 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const {
|
||||
endAmPm,
|
||||
endHour,
|
||||
endMinute
|
||||
} = {
|
||||
endAmPm: document.querySelector('#endAmPm [data-state="on"]')?.textContent.trim(),
|
||||
endHour: document.querySelector('#endHour [data-state="on"]')?.textContent.trim(),
|
||||
endMinute: document.querySelector('#endMinute [data-state="on"]')?.textContent.trim()
|
||||
};
|
||||
const endTime = `${endAmPm} ${endHour}시 ${endMinute}분`;
|
||||
setEndTime(endTime);
|
||||
}
|
||||
const cancelEndTime = () => {
|
||||
setEndOpen(false);
|
||||
setTimeout(() => {
|
||||
const endOnElements = document.querySelectorAll('[id^="end"] [data-state="on"]');
|
||||
endOnElements.forEach(element => {
|
||||
(element as HTMLButtonElement).click();
|
||||
});
|
||||
}, 150);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="w-full h-full flex flex-row justify-around items-center"
|
||||
className="w-full h-full flex flex-row justify-between gap-3 items-center"
|
||||
>
|
||||
<Popover
|
||||
open={startOpen}
|
||||
onOpenChange={onStartOpenChange}
|
||||
>
|
||||
<PopoverTrigger>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
className="border border-indigo-100 bg-white hover:bg-indigo-100 text-black"
|
||||
className="flex-9 h-full border border-indigo-100 bg-white hover:bg-indigo-100 text-black"
|
||||
disabled={disabled}
|
||||
>
|
||||
시작 시간 설정
|
||||
{
|
||||
!startTime
|
||||
? '시작 시간 설정'
|
||||
: startTime
|
||||
}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
@@ -55,6 +177,7 @@ export const TimePickPopover = ({ ...props }: TimePickPopoverProps) => {
|
||||
side={popoverAlign === 'start' ? 'bottom' : 'top'}
|
||||
>
|
||||
<ToggleGroup
|
||||
id="startAmPm"
|
||||
type="single"
|
||||
className="w-15 flex flex-col justify-start items-center"
|
||||
>
|
||||
@@ -76,6 +199,7 @@ export const TimePickPopover = ({ ...props }: TimePickPopoverProps) => {
|
||||
>
|
||||
<ToggleGroup
|
||||
type="single"
|
||||
id="startHour"
|
||||
className="w-15 border-r rounded-none border-l flex flex-col justify-start items-center"
|
||||
>
|
||||
{
|
||||
@@ -83,6 +207,7 @@ export const TimePickPopover = ({ ...props }: TimePickPopoverProps) => {
|
||||
<ToggleGroupItem
|
||||
className="w-full h-7 rounded-none!"
|
||||
value={time.toString().padStart(2, '0')}
|
||||
key={`startHour${time.toString().padStart(2, '0')}`}
|
||||
>
|
||||
{time.toString().padStart(2, '0')}
|
||||
</ToggleGroupItem>
|
||||
@@ -95,6 +220,7 @@ export const TimePickPopover = ({ ...props }: TimePickPopoverProps) => {
|
||||
>
|
||||
<ToggleGroup
|
||||
type="single"
|
||||
id="startMinute"
|
||||
className="w-full h-full rounded-none border-r flex flex-col justify-start items-center"
|
||||
>
|
||||
{
|
||||
@@ -102,6 +228,7 @@ export const TimePickPopover = ({ ...props }: TimePickPopoverProps) => {
|
||||
<ToggleGroupItem
|
||||
value={idx.toString().padStart(2, '0')}
|
||||
className="w-full h-7 rounded-none!"
|
||||
key={`startMinute${idx.toString().padStart(2, '0')}`}
|
||||
>
|
||||
{idx.toString().padStart(2, '0')}
|
||||
</ToggleGroupItem>
|
||||
@@ -112,26 +239,34 @@ export const TimePickPopover = ({ ...props }: TimePickPopoverProps) => {
|
||||
<div className="w-15 h-full flex flex-col">
|
||||
<div
|
||||
className="cursor-default text-sm flex justify-center items-center w-full h-7 rounded-none rounded-tr-md! hover:bg-gray-100"
|
||||
onClick={applyStartTime}
|
||||
>
|
||||
적용
|
||||
</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"
|
||||
onClick={cancelStartTime}
|
||||
>
|
||||
취소
|
||||
</div>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<span> - </span>
|
||||
<div className="flex-2 h-px bg-indigo-300" />
|
||||
<Popover
|
||||
open={endOpen}
|
||||
onOpenChange={onEndOpenChange}
|
||||
>
|
||||
<PopoverTrigger>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
className="border border-indigo-100 bg-white hover:bg-indigo-100 text-black"
|
||||
className="flex-9 h-full border border-indigo-100 bg-white hover:bg-indigo-100 text-black"
|
||||
disabled={disabled}
|
||||
>
|
||||
종료 시간 설정
|
||||
{
|
||||
!endTime
|
||||
? '종료 시간 설정'
|
||||
: `${endTime}`
|
||||
}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
@@ -140,6 +275,7 @@ export const TimePickPopover = ({ ...props }: TimePickPopoverProps) => {
|
||||
side={popoverAlign === 'start' ? 'bottom' : 'top'}
|
||||
>
|
||||
<ToggleGroup
|
||||
id="endAmPm"
|
||||
type="single"
|
||||
className="w-15 flex flex-col justify-start items-center"
|
||||
>
|
||||
@@ -160,6 +296,7 @@ export const TimePickPopover = ({ ...props }: TimePickPopoverProps) => {
|
||||
className="w-15 h-full"
|
||||
>
|
||||
<ToggleGroup
|
||||
id="endHour"
|
||||
type="single"
|
||||
className="w-15 border-r rounded-none border-l flex flex-col justify-start items-center"
|
||||
>
|
||||
@@ -167,6 +304,7 @@ export const TimePickPopover = ({ ...props }: TimePickPopoverProps) => {
|
||||
[1,2,3,4,5,6,7,8,9,10,11,12].map((time) => (
|
||||
<ToggleGroupItem
|
||||
className="w-full h-7 rounded-none!"
|
||||
key={`endHour${time.toString().padStart(2, '0')}`}
|
||||
value={time.toString().padStart(2, '0')}
|
||||
>
|
||||
{time.toString().padStart(2, '0')}
|
||||
@@ -179,6 +317,7 @@ export const TimePickPopover = ({ ...props }: TimePickPopoverProps) => {
|
||||
className="w-15 h-full"
|
||||
>
|
||||
<ToggleGroup
|
||||
id="endMinute"
|
||||
type="single"
|
||||
className="w-full h-full rounded-none border-r flex flex-col justify-start items-center"
|
||||
>
|
||||
@@ -186,6 +325,7 @@ export const TimePickPopover = ({ ...props }: TimePickPopoverProps) => {
|
||||
Array.from({ length: 60 }).map((_, idx) => (
|
||||
<ToggleGroupItem
|
||||
value={idx.toString().padStart(2, '0')}
|
||||
key={`endMinute${idx.toString().padStart(2, '0')}`}
|
||||
className="w-full h-7 rounded-none!"
|
||||
>
|
||||
{idx.toString().padStart(2, '0')}
|
||||
@@ -197,11 +337,13 @@ export const TimePickPopover = ({ ...props }: TimePickPopoverProps) => {
|
||||
<div className="w-15 h-full flex flex-col">
|
||||
<div
|
||||
className="cursor-default text-sm flex justify-center items-center w-full h-7 rounded-none rounded-tr-md! hover:bg-gray-100"
|
||||
onClick={applyEndTime}
|
||||
>
|
||||
적용
|
||||
확인
|
||||
</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"
|
||||
onClick={cancelEndTime}
|
||||
>
|
||||
취소
|
||||
</div>
|
||||
|
||||
13
src/ui/component/popover/schedule/content/ContentProps.ts
Normal file
13
src/ui/component/popover/schedule/content/ContentProps.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { ColorPaletteType } from "@/const/ColorPalette";
|
||||
|
||||
interface BaseProps {
|
||||
date: Date | undefined;
|
||||
open: boolean;
|
||||
popoverSide: 'left' | 'right';
|
||||
popoverAlign: 'start' | 'end';
|
||||
}
|
||||
|
||||
export interface ScheduleCreateContentProps extends BaseProps {
|
||||
date: Date | undefined;
|
||||
setMode: (mode: 'list' | 'create' | 'detail' | 'update') => void;
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
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 { Textarea } from '@/components/ui/textarea';
|
||||
import { ParticipantPopover } from '../ParticipantPopover';
|
||||
|
||||
export const ScheduleCreateContent = ({ date, open, setMode, popoverSide, popoverAlign }: ScheduleCreateContentProps) => {
|
||||
const [colorPopoverOpen, setColorPopoverOpen] = useState(false);
|
||||
const { getPaletteByKey } = usePalette();
|
||||
const { getNowString } = useTime();
|
||||
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: getNowString(),
|
||||
endTime: getNowString(),
|
||||
type: "once",
|
||||
status: "yet",
|
||||
style: getPaletteByKey('Black').style,
|
||||
dayList: "",
|
||||
participantList: []
|
||||
}
|
||||
});
|
||||
|
||||
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 selectParticipant = (participantList: string[]) => {
|
||||
createScheduleForm.setValue('participantList', participantList);
|
||||
}
|
||||
|
||||
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 w-[calc(100%/7)] h-10 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-between items-center gap-4">
|
||||
<div
|
||||
onClick={() => setMode('list')}
|
||||
>
|
||||
<ArrowLeft />
|
||||
</div>
|
||||
<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-100 focus-visible:ring-0 focus-visible:border-b-indigo-300"
|
||||
style={{
|
||||
fontSize: '20px'
|
||||
}}
|
||||
tabIndex={1}
|
||||
arai-invalid={fieldState.invalid}
|
||||
/>
|
||||
<FieldError errors={[fieldState.error]} />
|
||||
</Field>
|
||||
)}
|
||||
/>
|
||||
<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>
|
||||
</div>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<div className="hover:bg-indigo-100 cursor-default w-full h-10 border border-indigo-100 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={startTime}
|
||||
setStartTime={(time: string | undefined) => selectTime('startTime', time ?? '')}
|
||||
endTime={endTime}
|
||||
setEndTime={(time: string | undefined) => selectTime('endTime', time ?? '')}
|
||||
/>
|
||||
</div>
|
||||
<Controller
|
||||
name="content"
|
||||
control={createScheduleForm.control}
|
||||
render={({ field }) => (
|
||||
<Textarea
|
||||
{...field}
|
||||
rows={2}
|
||||
placeholder="일정 상세 사항"
|
||||
className="placeholder-gray-300! focus-visible:placeholder-gray-400! border-indigo-100 focus-visible:border-indigo-300 resize-none focus-visible:ring-0"
|
||||
style={{
|
||||
'scrollbarWidth': 'none'
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<ParticipantPopover />
|
||||
</div>
|
||||
)
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user