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

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

View File

@@ -7,30 +7,16 @@ import { PageRouting } from './const/PageRouting';
import LoginPage from './ui/page/account/login/LoginPage';
import ResetPasswordPage from './ui/page/account/resetPassword/ResetPasswordPage';
import { HomePage } from './ui/page/home/HomePage';
import type { AuthData } from './data/AuthData';
import { useEffect } from 'react';
import { ScheduleMainPage } from './ui/page/schedule/ScheduleMainPage';
import { BaseNetwork } from './network/BaseNetwork';
import { TempPage } from './ui/page/home/TempPage';
function App() {
const { authData, login } = useAuthStore();
const baseNetwork = new BaseNetwork();
useEffect(() => {
const autoLogin = localStorage.getItem('autoLogin') === 'true';
if (autoLogin) {
try {
(async () => {
await baseNetwork.refreshToken();
})();
} catch (err) {
localStorage.setItem('autoLogin', 'false');
}
}
}, []);
const { authData } = useAuthStore();
return (
<Router>
<Routes>
<Route element={<TempPage />} path={"/"} /> {/* 자동로그인용 대기 화면 */}
<Route element={<Layout />}>
{
!authData
@@ -39,7 +25,6 @@ function App() {
<Route element={<SignUpPage />} path={PageRouting["SIGN_UP"].path} />
<Route element={<ResetPasswordPage />} path={PageRouting["RESET_PASSWORD"].path} />
<Route element={<Navigate to={PageRouting["LOGIN"].path} />} path="*" />
<Route element={<div />} path={"/"} /> {/* 자동로그인용 대기 화면 */}
</>
: <>
<Route element={<Navigate to={PageRouting["HOME"].path} />} path="*" />

View File

@@ -58,7 +58,7 @@ function Calendar({
return '';
},
formatMonthDropdown: (date) =>
date.toLocaleString("", { month: "short" }),
date.toLocaleString("default", { month: "short" }),
...formatters,
}}
classNames={{

View File

@@ -13,7 +13,7 @@ function ScrollArea({
return (
<ScrollAreaPrimitive.Root
data-slot="scroll-area"
className={cn("relative", className)}
className={cn("relative", "[&>div>div:last-child]:hidden", className)}
{...props}
>
<ScrollAreaPrimitive.Viewport

View File

@@ -1,99 +1,88 @@
export type ColorPaletteType = {
index: number;
style: string;
main: boolean;
}
export const ColorPalette: Record<any, ColorPaletteType> = {
Black: {
index: 0,
style: '#000000',
main: true
style: '#000000'
},
White: {
index: 1,
style: '#FFFFFF',
main: true
style: '#FFFFFF'
},
PeachCream: {
SerenityBlue: {
index: 2,
style: '#FFDAB9',
main: false
style: '#92A8D1'
},
CoralPink: {
index: 3,
style: '#F08080',
main: true
style: '#F08080'
},
MintIcing: {
index: 4,
style: '#C1E1C1',
main: false
style: '#C1E1C1'
},
Vanilla: {
index: 5,
style: '#FFFACD',
main: true
style: '#FFFACD'
},
Wheat: {
index: 6,
style: '#F5DEB3',
main: false
style: '#F5DEB3'
},
AliceBlue: {
index: 7,
style: '#F0F8FF',
main: true
style: '#F0F8FF'
},
Lavender: {
index: 8,
style: '#E6E6FA',
main: false
style: '#E6E6FA'
},
LightAqua: {
SageGreen: {
index: 9,
style: '#A8E6CF',
main: true
style: '#b2ac88'
},
CloudWhite: {
index: 10,
style: '#F0F8FF',
main: false
style: '#F2F2ED'
},
LightGray: {
index: 11,
style: '#D3D3D3',
main: true
style: '#D3D3D3'
},
LightKhakki: {
index: 12,
style: '#F0F8E6',
main: false
style: '#F0F8E6'
},
DustyRose: {
index: 13,
style: '#D8BFD8',
main: true
style: '#D8BFD8'
},
CreamBeige: {
index: 14,
style: '#FAF0E6',
main: true,
style: '#FAF0E6'
},
Oatmeal: {
index: 15,
style: '#FDF5E6',
main: false
style: '#FDF5E6'
},
CharcoalLight: {
index: 16,
style: '#A9A9A9',
main: true
style: '#A9A9A9'
},
Custom: {
PeachCream: {
index: 17,
style: 'transparent',
main: false
},
style: '#FFDAB9'
},
LavenderBlue: {
index: 18,
style :'#CCCCFF'
},
SeaFoamGreen: {
index: 19,
style: '#93E9BE'
}
}

View File

@@ -0,0 +1,9 @@
export const ScheduleDay: Record<number, string> = {
1: '일',
2: '월',
3: '화',
4: '수',
5: '목',
6: '금',
7: '토'
}

View File

@@ -1 +1,6 @@
export type ScheduleStatus = 'yet' | 'completed';
export type ScheduleStatus = 'yet' | 'completed';
export const ScheduleStatusLabel: Record<ScheduleStatus, string> = {
'yet': '미완료',
'completed': '완료'
};

View File

@@ -1 +1,9 @@
export type ScheduleType = 'once' | 'daily' | 'weekly' | 'aweekly' | 'monthly' | 'annual';
export type ScheduleType = 'once' | 'daily' | 'weekly' | 'monthly' | 'annual';
export const ScheduleTypeLabel: Record<ScheduleType, string> = {
'once': '한 번만',
'daily': '매일',
'weekly': '매주',
'monthly': '매월',
'annual': '매년'
};

View File

@@ -19,4 +19,6 @@ export const CreateScheduleSchema = z.object({
.string()
, endTime: z
.string()
, dayList: z
.string()
});

View File

@@ -10,12 +10,10 @@ export function usePalette() {
const getMainPaletteList = () => {
const paletteKeys = Object.keys(ColorPalette);
let paletteList: ColorPaletteType[] = [];
paletteKeys.forEach((paletteKey) => {
paletteKeys.slice(0, 10).forEach((paletteKey) => {
const key = paletteKey as keyof typeof ColorPalette;
const palette: ColorPaletteType = ColorPalette[key];
if (palette.main) {
paletteList.push(palette);
}
paletteList.push(palette);
});
paletteList = paletteList.sort((a, b) => a.index - b.index);
@@ -26,12 +24,11 @@ export function usePalette() {
const getExtraPaletteList = () => {
const paletteKeys = Object.keys(ColorPalette);
let paletteList: ColorPaletteType[] = [];
paletteKeys.forEach((paletteKey) => {
paletteKeys.slice(10).forEach((paletteKey) => {
const key = paletteKey as keyof typeof ColorPalette;
const palette: ColorPaletteType = ColorPalette[key];
if (!palette.main) {
paletteList.push(palette);
}
paletteList.push(palette);
});
paletteList = paletteList.sort((a, b) => a.index - b.index);
return paletteList;
@@ -43,12 +40,6 @@ export function usePalette() {
return ColorPalette[key];
}
const getCustomColor = (style: string) => {
return {
style: `#${style}`,
main: false
} as ColorPaletteType;
}
const getStyle = (palette: ColorPaletteType) => {
return palette.style;
@@ -61,7 +52,6 @@ export function usePalette() {
getExtraPaletteList,
getAllPaletteList,
getPaletteByKey,
getCustomColor,
getStyle
}
}

13
src/hooks/use-record.ts Normal file
View File

@@ -0,0 +1,13 @@
import { useMemo } from "react";
export function useRecord(record: Record<any, any>) {
const keys = useMemo(() => {
return Object.keys(record);
}, [record]);
const values = useMemo(() => {
return Object.values(record);
}, [record]);
return { keys, values };
}

View File

@@ -134,19 +134,19 @@ input[type="number"]::-webkit-outer-spin-button {
margin: 0;
}
.rdp-day {
aspect-ratio: unset;
}
/* Firefox */
input[type="number"] {
-moz-appearance: textfield;
}
.rdp-week:not(:first-child) {
@apply border-t;
.custom-rdp-day {
aspect-ratio: unset!;
}
.rdp-day:not(:first-child) {
@apply border-l;
.custom-rdp-week:not(:first-child) {
@apply border-t!;
}
.custom-rdp-day:not(:first-child) {
@apply border-l!;
}

View File

@@ -5,7 +5,7 @@ import { getDefaultClassNames } from "react-day-picker";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { ScrollArea } from "@/components/ui/scroll-area";
import { isSameDay, getWeeksInMonth, getWeekOfMonth } from "date-fns";
import { SchedulePopover } from "../popover/SchedulePopover";
import { SchedulePopover } from "../popover/schedule/SchedulePopover";
interface CustomCalendarProps {
data?: any;
@@ -127,11 +127,13 @@ export const CustomCalendar = ({ data }: CustomCalendarProps) => {
),
week: cn(
defaultClassNames.week,
`w-full`
`w-full`,
'custom-rdp-week'
),
day: cn(
defaultClassNames.day,
`w-[calc(100%/7)] rounded-none`
`w-[calc(100%/7)] rounded-none`,
'custom-rdp-day'
),
day_button: cn(
defaultClassNames.day_button,
@@ -181,6 +183,7 @@ export const CustomCalendar = ({ data }: CustomCalendarProps) => {
/>
<SchedulePopover
date={selectedDate}
open={popoverOpen}
popoverSide={popoverSide}
popoverAlign={popoverAlign}
/>

View File

@@ -1,129 +0,0 @@
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { ScrollArea } from '@/components/ui/scroll-area';
import { Sheet, SheetContent, SheetHeader } from '@/components/ui/sheet';
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 } from 'lucide-react';
interface ScheduleSheetProps {
date: Date | undefined;
popoverSide: 'left' | 'right';
popoverAlign: 'start' | 'end';
}
export const SchedulePopover = ({ date, popoverSide, popoverAlign }: ScheduleSheetProps) => {
const {
getPaletteByKey,
} = usePalette();
const defaultColor = getPaletteByKey('Black');
const [scheduleColor, setScheduleColor] = useState(defaultColor);
const [colorPopoverOpen, setColorPopoverOpen] = useState(false);
const [mode, setMode] = useState<'list' | 'detail' | 'create' | 'edit'>('list');
const selectColor = (color: ColorPaletteType) => {
setScheduleColor(color);
setColorPopoverOpen(false);
}
const createScheduleForm = useForm<z.infer<typeof CreateScheduleSchema>>({
resolver: zodResolver(CreateScheduleSchema),
defaultValues: {
name: "",
startDate: date,
endDate: date,
content: "",
startTime: "",
endTime: "",
type: "once",
status: "yet",
style: defaultColor.style,
}
});
const Content = () => {
switch(mode) {
case 'list':
return (
<div>
<PenSquare onClick={() => setMode('create')}/>
</div>
);
case 'create':
return (
<div className="w-full h-full flex flex-col justify-start items-start gap-2.5">
<div className="w-full flex flex-row justify-center items-center gap-5">
<Popover open={colorPopoverOpen} onOpenChange={setColorPopoverOpen}>
<PopoverTrigger asChild>
<div
className={cn(
'rounded-full w-5 h-5 border-2 border-gray-300',
)}
style={{
backgroundColor: `${scheduleColor.style !== 'transparent' && scheduleColor.style}`,
background: `${scheduleColor.style === 'transparent' && 'linear-gradient(135deg, black 50%, white 50%)' }`
}}
/>
</PopoverTrigger>
<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>
<div
className="flex flex-row self-end justify-self-end items-center justify-center cursor-default"
onClick={() => setMode('list')}
>
<X />
<span></span>
</div>
</div>
)
default: return (<></>)
}
}
return (
<PopoverContent
className="rounded-xl xl:w-[calc(100vw/4)] xl:max-w-[480px] min-w-[320px]"
align={popoverAlign} side={popoverSide}
>
<ScrollArea
className={
cn(
"[&>div>div:last-child]:hidden min-h-[125px] h-[calc(100vh/2)] p-2.5 w-full flex flex-col",
)
}
>
{<Content />}
</ScrollArea>
</PopoverContent>
)
}

View File

@@ -51,8 +51,7 @@ export const ColorPickPopover = ({ setColor }: ColorPickPopoverProps) => {
<div
className="rounded-full w-5 h-5 border border-gray-300"
style={{
backgroundColor: `${palette.style !== 'transparent' && palette.style}`,
background: `${palette.style === 'transparent' && 'linear-gradient(135deg, black 50%, white 50%)' }`
backgroundColor: `${palette.style}`
}}
onClick={() => setColor(palette)}
/>

View File

@@ -0,0 +1,129 @@
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { format } from "date-fns";
import { useState } from "react";
interface BaseProps {
disabled: boolean;
popoverAlign: 'start' | 'end';
}
interface SingleModeProps extends BaseProps {
mode: 'single';
date: Date | undefined;
setDate: (date: Date | undefined) => void;
}
interface RangeModeProps extends BaseProps {
mode: 'range';
startDate: Date | undefined;
endDate: Date | undefined;
setStartDate: (date: Date | undefined) => void;
setEndDate: (date: Date | undefined) => void;
}
type DaetPickPopoverProps = SingleModeProps | RangeModeProps;
export const DatePickPopover = ({ ...props } : DaetPickPopoverProps) => {
const { mode, popoverAlign, disabled } = props;
if (mode === 'single') {
const { date, setDate } = props;
const [open, setOpen] = useState(false);
const onDaySelected = (open: boolean) => {
setOpen(open);
}
return(
<div>{date?.toString()}</div>
)
}
const { startDate, setStartDate, endDate, setEndDate } = props;
const [startOpen, setStartOpen] = useState(false);
const [endOpen, setEndOpen] = useState(false);
const onStartDaySelected = (date: Date | undefined) => {
setStartDate(date);
setStartOpen(false);
}
const onEndDaySelected = (date: Date | undefined) => {
setEndDate(date);
setEndOpen(false);
}
return (
<div
className="w-full h-full flex flex-row justify-around items-center"
>
<Popover
open={startOpen}
onOpenChange={setStartOpen}
>
<PopoverTrigger>
<Button
className="border border-indigo-100 bg-white hover:bg-indigo-100 text-black"
disabled={disabled}
>
{
!startDate
? "시작일 선택"
: format(startDate, "yyyy년 MM월 dd일")
}
</Button>
</PopoverTrigger>
<PopoverContent
className="w-fit h-fit"
align={'start'}
side={popoverAlign === 'start' ? 'bottom' : 'top'}
>
<Calendar
mode={'single'}
onSelect={onStartDaySelected}
disabled={
endDate
? { after: endDate }
: undefined
}
/>
</PopoverContent>
</Popover>
<span> - </span>
<Popover
open={endOpen}
onOpenChange={setEndOpen}
>
<PopoverTrigger>
<Button
className="border border-indigo-100 bg-white hover:bg-indigo-100 text-black"
disabled={disabled}
>
{
!endDate
? "종료일 선택"
: format(endDate, "yyyy년 MM월 dd일")
}
</Button>
</PopoverTrigger>
<PopoverContent
className="w-fit h-fit"
align={'end'}
side={popoverAlign === 'start' ? 'bottom' : 'top'}
>
<Calendar
mode='single'
onSelect={onEndDaySelected}
disabled={
startDate
? { before: startDate }
: undefined
}
/>
</PopoverContent>
</Popover>
</div>
)
}

View File

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

View File

@@ -0,0 +1,135 @@
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";
interface BaseProps {
disabled: boolean;
popoverAlign: 'start' | 'end';
}
interface SingleModeProps extends BaseProps {
mode: 'single';
time: string | undefined;
setTime: (time: string | undefined) => void;
}
interface RangeModeProps extends BaseProps {
mode: 'range';
startTime: string | undefined;
setStartTime: (time: string | undefined) => void;
endTime: string | undefined;
setEndTime: (time: string | undefined) => void;
}
type TimePickPopoverProps = SingleModeProps | RangeModeProps;
export const TimePickPopover = ({ ...props }: TimePickPopoverProps) => {
const { mode, disabled, popoverAlign } = props;
if (mode === 'single') {
return (
<div>single</div>
)
}
// const { startTime, setStartTime, endTime, setEndTime } = props;
return (
<div
className="w-full h-full flex flex-row justify-around items-center"
>
<Popover
>
<PopoverTrigger>
<Button
className="border border-indigo-100 bg-white hover:bg-indigo-100 text-black"
disabled={disabled}
>
</Button>
</PopoverTrigger>
<PopoverContent
className="w-fit h-42 flex flex-row p-0"
align={'start'}
side={popoverAlign === 'start' ? 'bottom' : 'top'}
>
<ToggleGroup
type="single"
className="w-15 flex flex-col justify-start items-center"
>
<ToggleGroupItem
value={"오전"}
className="w-full h-7"
>
</ToggleGroupItem>
<ToggleGroupItem
value={"오후"}
className="w-full h-7"
>
</ToggleGroupItem>
</ToggleGroup>
<ScrollArea
className="w-15 h-full"
>
<ToggleGroup
type="single"
className="w-15 border-r rounded-none border-l flex flex-col justify-start items-center"
>
{
[1,2,3,4,5,6,7,8,9,10,11,12].map((time) => (
<ToggleGroupItem
className="w-full h-7 rounded-none"
value={time.toString().padStart(2, '0')}
>
{time.toString().padStart(2, '0')}
</ToggleGroupItem>
))
}
</ToggleGroup>
</ScrollArea>
<ScrollArea
className="w-15 h-full"
>
<ToggleGroup
type="single"
className="w-full h-full flex flex-col justify-start items-center"
>
{
Array.from({ length: 60 }).map((_, idx) => (
<ToggleGroupItem
value={idx.toString().padStart(2, '0')}
className="w-full h-7"
>
{idx.toString().padStart(0, '2')}
</ToggleGroupItem>
))
}
</ToggleGroup>
</ScrollArea>
</PopoverContent>
</Popover>
<span> - </span>
<Popover
>
<PopoverTrigger>
<Button
className="border border-indigo-100 bg-white hover:bg-indigo-100 text-black"
disabled={disabled}
>
</Button>
</PopoverTrigger>
<PopoverContent
className="w-fit h-fit"
align={'end'}
side={popoverAlign === 'start' ? 'bottom' : 'top'}
>
</PopoverContent>
</Popover>
</div>
)
}

View File

@@ -0,0 +1,31 @@
import { PopoverContent } from "@/components/ui/popover"
import { ScheduleTypeLabel, type ScheduleType } from "@/const/schedule/ScheduleType";
import { useRecord } from "@/hooks/use-record";
interface TypePickPopoverProps {
popoverSide : 'left' | 'right';
setType: (type: ScheduleType) => void;
}
export const TypePickPopover = ({ popoverSide, setType }: TypePickPopoverProps) => {
const typeLabelList = useRecord(ScheduleTypeLabel).keys.map((key) => {
return {
type: key,
label: ScheduleTypeLabel[key as keyof typeof ScheduleTypeLabel]
} as { type: ScheduleType, label: string};
});
return (
<PopoverContent side={popoverSide} align={'start'} className="p-0 w-fit h-fit">
<div className="w-20 h-62.5 flex flex-col">
{typeLabelList.map((type) => (
<div
className="cursor-default flex-1 h-full flex justify-center items-center hover:bg-gray-100"
onClick={() => setType(type.type)}
>
{type.label}
</div>
))}
</div>
</PopoverContent>
)
}

View File

@@ -0,0 +1,25 @@
import { PageRouting } from "@/const/PageRouting";
import { BaseNetwork } from "@/network/BaseNetwork";
import { useEffect } from "react";
import { useNavigate } from "react-router-dom";
export const TempPage = () => {
const baseNetwork = new BaseNetwork();
const navigate = useNavigate();
useEffect(() => {
const autoLogin = localStorage.getItem('autoLogin') === 'true';
if (autoLogin) {
try {
(async () => {
await baseNetwork.refreshToken();
navigate(PageRouting["HOME"].path);
})();
} catch (err) {
localStorage.setItem('autoLogin', 'false');
navigate(PageRouting["LOGIN"].path);
}
}
}, []);
return (<div />);
}