- 일정 색상 로직 수정: 커스텀 컬러 삭제 - 일정 시작/종료일 설정 구현 - 일정 타입 구현 - 일정 시작/종료 시간 구현 중
This commit is contained in:
21
src/App.tsx
21
src/App.tsx
@@ -7,30 +7,16 @@ import { PageRouting } from './const/PageRouting';
|
|||||||
import LoginPage from './ui/page/account/login/LoginPage';
|
import LoginPage from './ui/page/account/login/LoginPage';
|
||||||
import ResetPasswordPage from './ui/page/account/resetPassword/ResetPasswordPage';
|
import ResetPasswordPage from './ui/page/account/resetPassword/ResetPasswordPage';
|
||||||
import { HomePage } from './ui/page/home/HomePage';
|
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 { ScheduleMainPage } from './ui/page/schedule/ScheduleMainPage';
|
||||||
import { BaseNetwork } from './network/BaseNetwork';
|
import { TempPage } from './ui/page/home/TempPage';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const { authData, login } = useAuthStore();
|
const { authData } = 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');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Router>
|
<Router>
|
||||||
<Routes>
|
<Routes>
|
||||||
|
<Route element={<TempPage />} path={"/"} /> {/* 자동로그인용 대기 화면 */}
|
||||||
<Route element={<Layout />}>
|
<Route element={<Layout />}>
|
||||||
{
|
{
|
||||||
!authData
|
!authData
|
||||||
@@ -39,7 +25,6 @@ function App() {
|
|||||||
<Route element={<SignUpPage />} path={PageRouting["SIGN_UP"].path} />
|
<Route element={<SignUpPage />} path={PageRouting["SIGN_UP"].path} />
|
||||||
<Route element={<ResetPasswordPage />} path={PageRouting["RESET_PASSWORD"].path} />
|
<Route element={<ResetPasswordPage />} path={PageRouting["RESET_PASSWORD"].path} />
|
||||||
<Route element={<Navigate to={PageRouting["LOGIN"].path} />} path="*" />
|
<Route element={<Navigate to={PageRouting["LOGIN"].path} />} path="*" />
|
||||||
<Route element={<div />} path={"/"} /> {/* 자동로그인용 대기 화면 */}
|
|
||||||
</>
|
</>
|
||||||
: <>
|
: <>
|
||||||
<Route element={<Navigate to={PageRouting["HOME"].path} />} path="*" />
|
<Route element={<Navigate to={PageRouting["HOME"].path} />} path="*" />
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ function Calendar({
|
|||||||
return '';
|
return '';
|
||||||
},
|
},
|
||||||
formatMonthDropdown: (date) =>
|
formatMonthDropdown: (date) =>
|
||||||
date.toLocaleString("", { month: "short" }),
|
date.toLocaleString("default", { month: "short" }),
|
||||||
...formatters,
|
...formatters,
|
||||||
}}
|
}}
|
||||||
classNames={{
|
classNames={{
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ function ScrollArea({
|
|||||||
return (
|
return (
|
||||||
<ScrollAreaPrimitive.Root
|
<ScrollAreaPrimitive.Root
|
||||||
data-slot="scroll-area"
|
data-slot="scroll-area"
|
||||||
className={cn("relative", className)}
|
className={cn("relative", "[&>div>div:last-child]:hidden", className)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<ScrollAreaPrimitive.Viewport
|
<ScrollAreaPrimitive.Viewport
|
||||||
|
|||||||
@@ -1,99 +1,88 @@
|
|||||||
export type ColorPaletteType = {
|
export type ColorPaletteType = {
|
||||||
index: number;
|
index: number;
|
||||||
style: string;
|
style: string;
|
||||||
main: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const ColorPalette: Record<any, ColorPaletteType> = {
|
export const ColorPalette: Record<any, ColorPaletteType> = {
|
||||||
Black: {
|
Black: {
|
||||||
index: 0,
|
index: 0,
|
||||||
style: '#000000',
|
style: '#000000'
|
||||||
main: true
|
|
||||||
},
|
},
|
||||||
White: {
|
White: {
|
||||||
index: 1,
|
index: 1,
|
||||||
style: '#FFFFFF',
|
style: '#FFFFFF'
|
||||||
main: true
|
|
||||||
},
|
},
|
||||||
PeachCream: {
|
SerenityBlue: {
|
||||||
index: 2,
|
index: 2,
|
||||||
style: '#FFDAB9',
|
style: '#92A8D1'
|
||||||
main: false
|
|
||||||
},
|
},
|
||||||
CoralPink: {
|
CoralPink: {
|
||||||
index: 3,
|
index: 3,
|
||||||
style: '#F08080',
|
style: '#F08080'
|
||||||
main: true
|
|
||||||
},
|
},
|
||||||
MintIcing: {
|
MintIcing: {
|
||||||
index: 4,
|
index: 4,
|
||||||
style: '#C1E1C1',
|
style: '#C1E1C1'
|
||||||
main: false
|
|
||||||
},
|
},
|
||||||
Vanilla: {
|
Vanilla: {
|
||||||
index: 5,
|
index: 5,
|
||||||
style: '#FFFACD',
|
style: '#FFFACD'
|
||||||
main: true
|
|
||||||
},
|
},
|
||||||
Wheat: {
|
Wheat: {
|
||||||
index: 6,
|
index: 6,
|
||||||
style: '#F5DEB3',
|
style: '#F5DEB3'
|
||||||
main: false
|
|
||||||
},
|
},
|
||||||
AliceBlue: {
|
AliceBlue: {
|
||||||
index: 7,
|
index: 7,
|
||||||
style: '#F0F8FF',
|
style: '#F0F8FF'
|
||||||
main: true
|
|
||||||
},
|
},
|
||||||
Lavender: {
|
Lavender: {
|
||||||
index: 8,
|
index: 8,
|
||||||
style: '#E6E6FA',
|
style: '#E6E6FA'
|
||||||
main: false
|
|
||||||
},
|
},
|
||||||
LightAqua: {
|
SageGreen: {
|
||||||
index: 9,
|
index: 9,
|
||||||
style: '#A8E6CF',
|
style: '#b2ac88'
|
||||||
main: true
|
|
||||||
},
|
},
|
||||||
CloudWhite: {
|
CloudWhite: {
|
||||||
index: 10,
|
index: 10,
|
||||||
style: '#F0F8FF',
|
style: '#F2F2ED'
|
||||||
main: false
|
|
||||||
},
|
},
|
||||||
LightGray: {
|
LightGray: {
|
||||||
index: 11,
|
index: 11,
|
||||||
style: '#D3D3D3',
|
style: '#D3D3D3'
|
||||||
main: true
|
|
||||||
},
|
},
|
||||||
LightKhakki: {
|
LightKhakki: {
|
||||||
index: 12,
|
index: 12,
|
||||||
style: '#F0F8E6',
|
style: '#F0F8E6'
|
||||||
main: false
|
|
||||||
},
|
},
|
||||||
DustyRose: {
|
DustyRose: {
|
||||||
index: 13,
|
index: 13,
|
||||||
style: '#D8BFD8',
|
style: '#D8BFD8'
|
||||||
main: true
|
|
||||||
},
|
},
|
||||||
CreamBeige: {
|
CreamBeige: {
|
||||||
index: 14,
|
index: 14,
|
||||||
style: '#FAF0E6',
|
style: '#FAF0E6'
|
||||||
main: true,
|
|
||||||
},
|
},
|
||||||
Oatmeal: {
|
Oatmeal: {
|
||||||
index: 15,
|
index: 15,
|
||||||
style: '#FDF5E6',
|
style: '#FDF5E6'
|
||||||
main: false
|
|
||||||
},
|
},
|
||||||
CharcoalLight: {
|
CharcoalLight: {
|
||||||
index: 16,
|
index: 16,
|
||||||
style: '#A9A9A9',
|
style: '#A9A9A9'
|
||||||
main: true
|
|
||||||
},
|
},
|
||||||
Custom: {
|
PeachCream: {
|
||||||
index: 17,
|
index: 17,
|
||||||
style: 'transparent',
|
style: '#FFDAB9'
|
||||||
main: false
|
},
|
||||||
},
|
LavenderBlue: {
|
||||||
|
index: 18,
|
||||||
|
style :'#CCCCFF'
|
||||||
|
},
|
||||||
|
SeaFoamGreen: {
|
||||||
|
index: 19,
|
||||||
|
style: '#93E9BE'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
9
src/const/schedule/ScheduleDay.ts
Normal file
9
src/const/schedule/ScheduleDay.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
export const ScheduleDay: Record<number, string> = {
|
||||||
|
1: '일',
|
||||||
|
2: '월',
|
||||||
|
3: '화',
|
||||||
|
4: '수',
|
||||||
|
5: '목',
|
||||||
|
6: '금',
|
||||||
|
7: '토'
|
||||||
|
}
|
||||||
@@ -1 +1,6 @@
|
|||||||
export type ScheduleStatus = 'yet' | 'completed';
|
export type ScheduleStatus = 'yet' | 'completed';
|
||||||
|
|
||||||
|
export const ScheduleStatusLabel: Record<ScheduleStatus, string> = {
|
||||||
|
'yet': '미완료',
|
||||||
|
'completed': '완료'
|
||||||
|
};
|
||||||
@@ -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': '매년'
|
||||||
|
};
|
||||||
@@ -19,4 +19,6 @@ export const CreateScheduleSchema = z.object({
|
|||||||
.string()
|
.string()
|
||||||
, endTime: z
|
, endTime: z
|
||||||
.string()
|
.string()
|
||||||
|
, dayList: z
|
||||||
|
.string()
|
||||||
});
|
});
|
||||||
@@ -10,12 +10,10 @@ export function usePalette() {
|
|||||||
const getMainPaletteList = () => {
|
const getMainPaletteList = () => {
|
||||||
const paletteKeys = Object.keys(ColorPalette);
|
const paletteKeys = Object.keys(ColorPalette);
|
||||||
let paletteList: ColorPaletteType[] = [];
|
let paletteList: ColorPaletteType[] = [];
|
||||||
paletteKeys.forEach((paletteKey) => {
|
paletteKeys.slice(0, 10).forEach((paletteKey) => {
|
||||||
const key = paletteKey as keyof typeof ColorPalette;
|
const key = paletteKey as keyof typeof ColorPalette;
|
||||||
const palette: ColorPaletteType = ColorPalette[key];
|
const palette: ColorPaletteType = ColorPalette[key];
|
||||||
if (palette.main) {
|
paletteList.push(palette);
|
||||||
paletteList.push(palette);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
paletteList = paletteList.sort((a, b) => a.index - b.index);
|
paletteList = paletteList.sort((a, b) => a.index - b.index);
|
||||||
@@ -26,12 +24,11 @@ export function usePalette() {
|
|||||||
const getExtraPaletteList = () => {
|
const getExtraPaletteList = () => {
|
||||||
const paletteKeys = Object.keys(ColorPalette);
|
const paletteKeys = Object.keys(ColorPalette);
|
||||||
let paletteList: ColorPaletteType[] = [];
|
let paletteList: ColorPaletteType[] = [];
|
||||||
paletteKeys.forEach((paletteKey) => {
|
paletteKeys.slice(10).forEach((paletteKey) => {
|
||||||
const key = paletteKey as keyof typeof ColorPalette;
|
const key = paletteKey as keyof typeof ColorPalette;
|
||||||
const palette: ColorPaletteType = ColorPalette[key];
|
const palette: ColorPaletteType = ColorPalette[key];
|
||||||
if (!palette.main) {
|
paletteList.push(palette);
|
||||||
paletteList.push(palette);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
paletteList = paletteList.sort((a, b) => a.index - b.index);
|
paletteList = paletteList.sort((a, b) => a.index - b.index);
|
||||||
return paletteList;
|
return paletteList;
|
||||||
@@ -43,12 +40,6 @@ export function usePalette() {
|
|||||||
return ColorPalette[key];
|
return ColorPalette[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
const getCustomColor = (style: string) => {
|
|
||||||
return {
|
|
||||||
style: `#${style}`,
|
|
||||||
main: false
|
|
||||||
} as ColorPaletteType;
|
|
||||||
}
|
|
||||||
|
|
||||||
const getStyle = (palette: ColorPaletteType) => {
|
const getStyle = (palette: ColorPaletteType) => {
|
||||||
return palette.style;
|
return palette.style;
|
||||||
@@ -61,7 +52,6 @@ export function usePalette() {
|
|||||||
getExtraPaletteList,
|
getExtraPaletteList,
|
||||||
getAllPaletteList,
|
getAllPaletteList,
|
||||||
getPaletteByKey,
|
getPaletteByKey,
|
||||||
getCustomColor,
|
|
||||||
getStyle
|
getStyle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
13
src/hooks/use-record.ts
Normal file
13
src/hooks/use-record.ts
Normal 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 };
|
||||||
|
}
|
||||||
@@ -134,19 +134,19 @@ input[type="number"]::-webkit-outer-spin-button {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rdp-day {
|
|
||||||
aspect-ratio: unset;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Firefox */
|
/* Firefox */
|
||||||
input[type="number"] {
|
input[type="number"] {
|
||||||
-moz-appearance: textfield;
|
-moz-appearance: textfield;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rdp-week:not(:first-child) {
|
.custom-rdp-day {
|
||||||
@apply border-t;
|
aspect-ratio: unset!;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rdp-day:not(:first-child) {
|
.custom-rdp-week:not(:first-child) {
|
||||||
@apply border-l;
|
@apply border-t!;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-rdp-day:not(:first-child) {
|
||||||
|
@apply border-l!;
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,7 @@ import { getDefaultClassNames } from "react-day-picker";
|
|||||||
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 { isSameDay, getWeeksInMonth, getWeekOfMonth } from "date-fns";
|
import { isSameDay, getWeeksInMonth, getWeekOfMonth } from "date-fns";
|
||||||
import { SchedulePopover } from "../popover/SchedulePopover";
|
import { SchedulePopover } from "../popover/schedule/SchedulePopover";
|
||||||
|
|
||||||
interface CustomCalendarProps {
|
interface CustomCalendarProps {
|
||||||
data?: any;
|
data?: any;
|
||||||
@@ -127,11 +127,13 @@ export const CustomCalendar = ({ data }: CustomCalendarProps) => {
|
|||||||
),
|
),
|
||||||
week: cn(
|
week: cn(
|
||||||
defaultClassNames.week,
|
defaultClassNames.week,
|
||||||
`w-full`
|
`w-full`,
|
||||||
|
'custom-rdp-week'
|
||||||
),
|
),
|
||||||
day: cn(
|
day: cn(
|
||||||
defaultClassNames.day,
|
defaultClassNames.day,
|
||||||
`w-[calc(100%/7)] rounded-none`
|
`w-[calc(100%/7)] rounded-none`,
|
||||||
|
'custom-rdp-day'
|
||||||
),
|
),
|
||||||
day_button: cn(
|
day_button: cn(
|
||||||
defaultClassNames.day_button,
|
defaultClassNames.day_button,
|
||||||
@@ -181,6 +183,7 @@ export const CustomCalendar = ({ data }: CustomCalendarProps) => {
|
|||||||
/>
|
/>
|
||||||
<SchedulePopover
|
<SchedulePopover
|
||||||
date={selectedDate}
|
date={selectedDate}
|
||||||
|
open={popoverOpen}
|
||||||
popoverSide={popoverSide}
|
popoverSide={popoverSide}
|
||||||
popoverAlign={popoverAlign}
|
popoverAlign={popoverAlign}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -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>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -51,8 +51,7 @@ export const ColorPickPopover = ({ setColor }: ColorPickPopoverProps) => {
|
|||||||
<div
|
<div
|
||||||
className="rounded-full w-5 h-5 border border-gray-300"
|
className="rounded-full w-5 h-5 border border-gray-300"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: `${palette.style !== 'transparent' && palette.style}`,
|
backgroundColor: `${palette.style}`
|
||||||
background: `${palette.style === 'transparent' && 'linear-gradient(135deg, black 50%, white 50%)' }`
|
|
||||||
}}
|
}}
|
||||||
onClick={() => setColor(palette)}
|
onClick={() => setColor(palette)}
|
||||||
/>
|
/>
|
||||||
129
src/ui/component/popover/schedule/DatePickPopover.tsx
Normal file
129
src/ui/component/popover/schedule/DatePickPopover.tsx
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
||||||
303
src/ui/component/popover/schedule/SchedulePopover.tsx
Normal file
303
src/ui/component/popover/schedule/SchedulePopover.tsx
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
||||||
135
src/ui/component/popover/schedule/TimePickPopover.tsx
Normal file
135
src/ui/component/popover/schedule/TimePickPopover.tsx
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
||||||
31
src/ui/component/popover/schedule/TypePickPopover.tsx
Normal file
31
src/ui/component/popover/schedule/TypePickPopover.tsx
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
||||||
25
src/ui/page/home/TempPage.tsx
Normal file
25
src/ui/page/home/TempPage.tsx
Normal 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 />);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user