From 5e74e30806481783068eb75a930a3f7e479e909d Mon Sep 17 00:00:00 2001 From: SeongHwan Date: Thu, 7 May 2026 17:29:24 +0900 Subject: [PATCH 01/11] =?UTF-8?q?feat:=20navbar=20=ED=95=98=EB=8B=A8=20?= =?UTF-8?q?=EC=8A=A4=EC=BC=80=EC=A4=84=20=ED=83=80=EC=9E=85=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=ED=83=AD=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/manager/index.ts | 2 + .../schedule/constants/workerSchedule.ts | 3 ++ .../manager/schedule/types/workerSchedule.ts | 1 + src/pages/manager/worker-schedule/index.tsx | 46 ++++++++++--------- 4 files changed, 31 insertions(+), 21 deletions(-) create mode 100644 src/features/manager/schedule/constants/workerSchedule.ts create mode 100644 src/features/manager/schedule/types/workerSchedule.ts diff --git a/src/features/manager/index.ts b/src/features/manager/index.ts index 891fdea..3312e30 100644 --- a/src/features/manager/index.ts +++ b/src/features/manager/index.ts @@ -3,3 +3,5 @@ export { WorkspaceChangeList } from '@/features/manager/home/ui/WorkspaceChangeL export { WorkspaceChangeCard } from '@/features/manager/home/ui/WorkspaceChangeCard' export { TodayWorkerList } from '@/features/manager/home/ui/TodayWorkerList' export { useManagerHomeViewModel } from '@/features/manager/home/hooks/useManagerHomeViewModel' +export type { ScheduleTab } from '@/features/manager/schedule/types/workerSchedule' +export { SCHEDULE_TABS } from '@/features/manager/schedule/constants/workerSchedule' diff --git a/src/features/manager/schedule/constants/workerSchedule.ts b/src/features/manager/schedule/constants/workerSchedule.ts new file mode 100644 index 0000000..939f6ed --- /dev/null +++ b/src/features/manager/schedule/constants/workerSchedule.ts @@ -0,0 +1,3 @@ +import type { ScheduleTab } from '@/features/manager/schedule/types/workerSchedule' + +export const SCHEDULE_TABS: ScheduleTab[] = ['고정', '일반'] diff --git a/src/features/manager/schedule/types/workerSchedule.ts b/src/features/manager/schedule/types/workerSchedule.ts new file mode 100644 index 0000000..992889a --- /dev/null +++ b/src/features/manager/schedule/types/workerSchedule.ts @@ -0,0 +1 @@ +export type ScheduleTab = '고정' | '일반' diff --git a/src/pages/manager/worker-schedule/index.tsx b/src/pages/manager/worker-schedule/index.tsx index 860d783..cfaed00 100644 --- a/src/pages/manager/worker-schedule/index.tsx +++ b/src/pages/manager/worker-schedule/index.tsx @@ -1,8 +1,10 @@ -import { useNavigate } from 'react-router-dom' +import { useState } from 'react' import { WorkerRoleBadge } from '@/shared/ui/home/WorkerRoleBadge' import { useWorkerScheduleManageViewModel } from '@/features/manager/home/hooks/useWorkerScheduleManageViewModel' -import chevronLeftIcon from '@/assets/icons/chevron-left.svg' import chevronDownIcon from '@/assets/icons/home/chevron-down.svg' +import { Navbar } from '@/shared/ui/common/Navbar' +import type { ScheduleTab } from '@/features/manager' +import { SCHEDULE_TABS } from '@/features/manager' interface TimeSelectBoxProps { value: string @@ -21,7 +23,7 @@ function TimeSelectBox({ value, unit }: TimeSelectBoxProps) { } export function ManagerWorkerSchedulePage() { - const navigate = useNavigate() + const [activeTab, setActiveTab] = useState('고정') const { worker, workdayOptions, @@ -36,24 +38,26 @@ export function ManagerWorkerSchedulePage() { return (
-
- -

- 근무자 스케줄 관리 -

-
+ +
+ {SCHEDULE_TABS.map(tab => { + const isActive = activeTab === tab + return ( + + ) + })} +
From 684f2696f17c3bdece883b4cfcfba7f0b96ac882 Mon Sep 17 00:00:00 2001 From: SeongHwan Date: Thu, 7 May 2026 17:42:17 +0900 Subject: [PATCH 02/11] =?UTF-8?q?fix:=20=EA=B7=BC=EB=AC=B4=EC=8B=9C?= =?UTF-8?q?=EA=B0=84=20=EC=9E=85=EB=A0=A5=ED=95=A0=20=EC=88=98=20=EC=9E=88?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hooks/useWorkerScheduleManageViewModel.ts | 34 ++++---- src/pages/manager/worker-schedule/index.tsx | 85 ++++++++++++------- 2 files changed, 75 insertions(+), 44 deletions(-) diff --git a/src/features/manager/home/hooks/useWorkerScheduleManageViewModel.ts b/src/features/manager/home/hooks/useWorkerScheduleManageViewModel.ts index 35787bc..01013e4 100644 --- a/src/features/manager/home/hooks/useWorkerScheduleManageViewModel.ts +++ b/src/features/manager/home/hooks/useWorkerScheduleManageViewModel.ts @@ -4,23 +4,23 @@ const WORKDAY_OPTIONS = ['월', '화', '수', '목', '금', '토', '일'] as con const DEFAULT_SELECTED_DAYS = ['수', '금'] -const DEFAULT_TIME = { - startHour: '00', - startMinute: '00', - endHour: '00', - endMinute: '00', -} export function useWorkerScheduleManageViewModel() { const [selectedDays, setSelectedDays] = useState( DEFAULT_SELECTED_DAYS ) + const [startHour, setStartHour] = useState('') + const [startMinute, setStartMinute] = useState('') + const [endHour, setEndHour] = useState('') + const [endMinute, setEndMinute] = useState('') - const workTimeRangeLabel = useMemo( - () => - `${DEFAULT_TIME.startHour}:${DEFAULT_TIME.startMinute} ~ ${DEFAULT_TIME.endHour}:${DEFAULT_TIME.endMinute}`, - [] - ) + const workTimeRangeLabel = useMemo(() => { + const sh = startHour || '00' + const sm = startMinute || '00' + const eh = endHour || '00' + const em = endMinute || '00' + return `${sh}:${sm} ~ ${eh}:${em}` + }, [startHour, startMinute, endHour, endMinute]) function toggleDay(day: string) { setSelectedDays(prev => @@ -36,10 +36,14 @@ export function useWorkerScheduleManageViewModel() { workdayOptions: WORKDAY_OPTIONS, selectedDays, workTimeRangeLabel, - startHour: DEFAULT_TIME.startHour, - startMinute: DEFAULT_TIME.startMinute, - endHour: DEFAULT_TIME.endHour, - endMinute: DEFAULT_TIME.endMinute, + startHour, + startMinute, + endHour, + endMinute, + setStartHour, + setStartMinute, + setEndHour, + setEndMinute, toggleDay, } } diff --git a/src/pages/manager/worker-schedule/index.tsx b/src/pages/manager/worker-schedule/index.tsx index cfaed00..a1d6b4d 100644 --- a/src/pages/manager/worker-schedule/index.tsx +++ b/src/pages/manager/worker-schedule/index.tsx @@ -2,6 +2,7 @@ import { useState } from 'react' import { WorkerRoleBadge } from '@/shared/ui/home/WorkerRoleBadge' import { useWorkerScheduleManageViewModel } from '@/features/manager/home/hooks/useWorkerScheduleManageViewModel' import chevronDownIcon from '@/assets/icons/home/chevron-down.svg' +import calendarIcon from '@/assets/icons/nav/calendar.svg' import { Navbar } from '@/shared/ui/common/Navbar' import type { ScheduleTab } from '@/features/manager' import { SCHEDULE_TABS } from '@/features/manager' @@ -9,15 +10,23 @@ import { SCHEDULE_TABS } from '@/features/manager' interface TimeSelectBoxProps { value: string unit: string + onChange: (value: string) => void } -function TimeSelectBox({ value, unit }: TimeSelectBoxProps) { +function TimeSelectBox({ value, unit, onChange }: TimeSelectBoxProps) { return (
- {value} - - {unit} - + onChange(e.target.value.replace(/\D/g, ''))} + className="w-8 bg-transparent text-center typography-body01-semibold text-text-100 placeholder:text-text-50 outline-none" + aria-label={unit} + /> + {unit}
) } @@ -33,6 +42,10 @@ export function ManagerWorkerSchedulePage() { startMinute, endHour, endMinute, + setStartHour, + setStartMinute, + setEndHour, + setEndMinute, toggleDay, } = useWorkerScheduleManageViewModel() @@ -90,26 +103,40 @@ export function ManagerWorkerSchedulePage() {
-
-

근무일 선택

-
- {workdayOptions.map(day => { - const selected = selectedDays.includes(day) - return ( - - ) - })} -
-
+ {activeTab === '고정' ? ( +
+

근무일 선택

+
+ {workdayOptions.map(day => { + const selected = selectedDays.includes(day) + return ( + + ) + })} +
+
+ ) : ( +
+ +
+ )}

@@ -124,7 +151,7 @@ export function ManagerWorkerSchedulePage() { 출근 시간
- +
- +
@@ -144,7 +171,7 @@ export function ManagerWorkerSchedulePage() { 퇴근 시간
- +
- +

From 0a4ad874863745697105b0d336579d54dfdd201b Mon Sep 17 00:00:00 2001 From: SeongHwan Date: Thu, 7 May 2026 18:43:41 +0900 Subject: [PATCH 03/11] =?UTF-8?q?feat:=20=EC=8A=A4=EC=BC=80=EC=A4=84=20?= =?UTF-8?q?=EB=82=A0=EC=A7=9C=20=EC=84=A0=ED=83=9D=20=EC=BA=98=EB=A6=B0?= =?UTF-8?q?=EB=8D=94=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/schedule/constants/calendar.ts | 16 --- .../hooks/useMonthlyCalendarViewModel.ts | 43 ++---- .../hooks/useWorkerScheduleManageViewModel.ts | 1 - .../user/home/schedule/api/schedule.ts | 2 +- .../user/home/schedule/constants/calendar.ts | 3 - src/features/user/home/schedule/lib/date.ts | 5 +- .../home/workspace/api/workspaceSchedule.ts | 6 +- src/pages/manager/worker-schedule/index.tsx | 57 +++++++- src/shared/constants/calendar.ts | 14 ++ src/shared/lib/calendarUtils.ts | 30 ++++ src/shared/ui/common/ScheduleCalendar.tsx | 132 ++++++++++++++++++ 11 files changed, 245 insertions(+), 64 deletions(-) create mode 100644 src/shared/constants/calendar.ts create mode 100644 src/shared/lib/calendarUtils.ts create mode 100644 src/shared/ui/common/ScheduleCalendar.tsx diff --git a/src/features/home/common/schedule/constants/calendar.ts b/src/features/home/common/schedule/constants/calendar.ts index 6c4a0ae..ea47ca0 100644 --- a/src/features/home/common/schedule/constants/calendar.ts +++ b/src/features/home/common/schedule/constants/calendar.ts @@ -1,19 +1,3 @@ -export const WEEKDAY_LABELS = [ - '일', - '월', - '화', - '수', - '목', - '금', - '토', -] as const - -/** 월요일 시작 주( date-fns weekStartsOn: 1 )와 그리드 열 순서를 맞춤 */ -export const WEEKDAY_LABELS_MONDAY_FIRST = [ - ...WEEKDAY_LABELS.slice(1), - WEEKDAY_LABELS[0], -] as const - export const DATE_KEY_FORMAT = 'yyyy-MM-dd' export const MONTH_LABEL_FORMAT = 'yyyy년 M월' diff --git a/src/features/home/common/schedule/hooks/useMonthlyCalendarViewModel.ts b/src/features/home/common/schedule/hooks/useMonthlyCalendarViewModel.ts index ab4565d..6c125fa 100644 --- a/src/features/home/common/schedule/hooks/useMonthlyCalendarViewModel.ts +++ b/src/features/home/common/schedule/hooks/useMonthlyCalendarViewModel.ts @@ -1,45 +1,19 @@ -import { - addDays, - eachDayOfInterval, - endOfMonth, - endOfWeek, - format, - isSameMonth, - startOfDay, - startOfMonth, - startOfWeek, -} from 'date-fns' +import { addDays, format, startOfDay } from 'date-fns' +import { getCalendarCells } from '@/shared/lib/calendarUtils' import { useMemo } from 'react' import { DATE_KEY_FORMAT, MONTH_LABEL_FORMAT, - WEEKDAY_LABELS, } from '@/features/home/common/schedule/constants/calendar' +import { WEEKDAY_LABELS } from '@/shared/constants/calendar' import { useMonthlyDateCellsState } from '@/features/home/common/schedule/hooks/useMonthlyDateCellsState' import type { MonthlyCalendarViewModel, - MonthlyCellInput, MonthlyDayMetrics, MonthlyCalendarPropsBase, } from '@/features/home/common/schedule/types/monthlyCalendar' import type { CalendarViewData } from '@/features/home/common/schedule/types/calendarView' -function getMonthlyCells(baseDate: Date): MonthlyCellInput[] { - const monthStart = startOfMonth(baseDate) - const monthEnd = endOfMonth(baseDate) - const intervalStart = startOfWeek(monthStart, { weekStartsOn: 0 }) - const intervalEnd = endOfWeek(monthEnd, { weekStartsOn: 0 }) - - return eachDayOfInterval({ start: intervalStart, end: intervalEnd }).map( - date => ({ - dateKey: format(date, DATE_KEY_FORMAT), - dayText: format(date, 'd'), - isCurrentMonth: isSameMonth(date, baseDate), - weekDay: date.getDay(), - }) - ) -} - type MinuteRange = [number, number] function mergeMinuteRanges(ranges: MinuteRange[]) { @@ -116,7 +90,16 @@ export function useMonthlyCalendarViewModel({ workspaceName, selectedDateKey, }: MonthlyCalendarPropsBase): MonthlyCalendarViewModel { - const cells = useMemo(() => getMonthlyCells(baseDate), [baseDate]) + const cells = useMemo( + () => + getCalendarCells(baseDate, 0).map(({ date, isCurrentMonth }) => ({ + dateKey: format(date, DATE_KEY_FORMAT), + dayText: format(date, 'd'), + isCurrentMonth, + weekDay: date.getDay(), + })), + [baseDate] + ) const selectedKey = selectedDateKey ?? format(baseDate, DATE_KEY_FORMAT) const dayMetricsByDate = useMemo(() => getDayMetricsByDate(data), [data]) diff --git a/src/features/manager/home/hooks/useWorkerScheduleManageViewModel.ts b/src/features/manager/home/hooks/useWorkerScheduleManageViewModel.ts index 01013e4..ecb1694 100644 --- a/src/features/manager/home/hooks/useWorkerScheduleManageViewModel.ts +++ b/src/features/manager/home/hooks/useWorkerScheduleManageViewModel.ts @@ -4,7 +4,6 @@ const WORKDAY_OPTIONS = ['월', '화', '수', '목', '금', '토', '일'] as con const DEFAULT_SELECTED_DAYS = ['수', '금'] - export function useWorkerScheduleManageViewModel() { const [selectedDays, setSelectedDays] = useState( DEFAULT_SELECTED_DAYS diff --git a/src/features/user/home/schedule/api/schedule.ts b/src/features/user/home/schedule/api/schedule.ts index fb4b3ea..e12cd8b 100644 --- a/src/features/user/home/schedule/api/schedule.ts +++ b/src/features/user/home/schedule/api/schedule.ts @@ -11,7 +11,7 @@ import { getDurationHours, toDateKey, toTimeLabel, -} from '@/features/user/home/schedule/lib/date' +} from '@/features/home/common/schedule/lib/date' import type { SelfScheduleQueryParams } from '@/shared/types/schedule' function mapToCalendarEvent( diff --git a/src/features/user/home/schedule/constants/calendar.ts b/src/features/user/home/schedule/constants/calendar.ts index 5678e0b..e8451b4 100644 --- a/src/features/user/home/schedule/constants/calendar.ts +++ b/src/features/user/home/schedule/constants/calendar.ts @@ -1,7 +1,4 @@ -// constants는 common으로 이동 — 하위 호환 re-export export { - WEEKDAY_LABELS, - WEEKDAY_LABELS_MONDAY_FIRST, DATE_KEY_FORMAT, MONTH_LABEL_FORMAT, DAILY_TIMELINE_HEIGHT, diff --git a/src/features/user/home/schedule/lib/date.ts b/src/features/user/home/schedule/lib/date.ts index a76d78b..871d000 100644 --- a/src/features/user/home/schedule/lib/date.ts +++ b/src/features/user/home/schedule/lib/date.ts @@ -14,16 +14,13 @@ import type { ScheduleDataDto } from '@/features/user/home/schedule/types/schedu import type { HomeCalendarMode } from '@/features/user/home/schedule/types/schedule' import type { SelfScheduleQueryParams } from '@/shared/types/schedule' import type { ScheduleListItem } from '@/features/user/home/schedule/types/scheduleList' -import { WEEKDAY_LABELS } from '@/features/user/home/schedule/constants/calendar' +import { WEEKDAY_LABELS } from '@/shared/constants/calendar' import { toDateKey, toTimeLabel, getDurationHours, } from '@/features/home/common/schedule/lib/date' -// 순수 날짜 유틸은 common으로 이동 — 하위 호환 re-export -export { toDateKey, toTimeLabel, getDurationHours } - export function getMonthlyDateCells(baseDate: Date) { const monthStart = startOfMonth(baseDate) const monthEnd = endOfMonth(baseDate) diff --git a/src/features/user/home/workspace/api/workspaceSchedule.ts b/src/features/user/home/workspace/api/workspaceSchedule.ts index 5aaf1ec..9c973f7 100644 --- a/src/features/user/home/workspace/api/workspaceSchedule.ts +++ b/src/features/user/home/workspace/api/workspaceSchedule.ts @@ -4,11 +4,11 @@ import type { CalendarViewData, } from '@/features/user/home/schedule/types/schedule' import { + getDurationHours, toDateKey, toTimeLabel, - getDurationHours, - formatScheduleTimeRange, -} from '@/features/user/home/schedule/lib/date' +} from '@/features/home/common/schedule/lib/date' +import { formatScheduleTimeRange } from '@/features/user/home/schedule/lib/date' import type { WorkspaceScheduleApiResponse, WorkspaceScheduleQueryParams, diff --git a/src/pages/manager/worker-schedule/index.tsx b/src/pages/manager/worker-schedule/index.tsx index a1d6b4d..6b2e9fb 100644 --- a/src/pages/manager/worker-schedule/index.tsx +++ b/src/pages/manager/worker-schedule/index.tsx @@ -4,6 +4,7 @@ import { useWorkerScheduleManageViewModel } from '@/features/manager/home/hooks/ import chevronDownIcon from '@/assets/icons/home/chevron-down.svg' import calendarIcon from '@/assets/icons/nav/calendar.svg' import { Navbar } from '@/shared/ui/common/Navbar' +import { ScheduleCalendar } from '@/shared/ui/common/ScheduleCalendar' import type { ScheduleTab } from '@/features/manager' import { SCHEDULE_TABS } from '@/features/manager' @@ -33,6 +34,8 @@ function TimeSelectBox({ value, unit, onChange }: TimeSelectBoxProps) { export function ManagerWorkerSchedulePage() { const [activeTab, setActiveTab] = useState('고정') + const [showCalendar, setShowCalendar] = useState(false) + const [selectedDate, setSelectedDate] = useState(null) const { worker, workdayOptions, @@ -128,12 +131,22 @@ export function ManagerWorkerSchedulePage() {
)} @@ -151,7 +164,11 @@ export function ManagerWorkerSchedulePage() { 출근 시간
- +
- +
@@ -182,12 +203,36 @@ export function ManagerWorkerSchedulePage() { aria-hidden="true" /> - + + {showCalendar && ( +
+
+ )} +
+ + {viewYear}년 {viewMonth + 1}월 + + +
+ + {/* Weekday headers */} +
+ {WEEKDAY_LABELS.map(day => ( +
+ + {day} + +
+ ))} +
+ + {/* Date grid */} +
+ {weeks.map((week, wi) => ( +
+ {week.map(({ date, isCurrentMonth }, di) => { + const isSelected = selectedDate + ? isSameDay(date, selectedDate) + : false + + let textColor = 'text-text-90' + if (!isCurrentMonth) textColor = 'text-text-50' + else if (di === 0) textColor = 'text-error' + else if (di === 6) textColor = 'text-subBlue' + + return ( + + ) + })} +
+ ))} +
+ + ) +} From 412ee18be3b1a2eecf486f069bbcc8d349932a79 Mon Sep 17 00:00:00 2001 From: SeongHwan Date: Sun, 10 May 2026 17:36:42 +0900 Subject: [PATCH 04/11] =?UTF-8?q?feat:=20=EA=B7=BC=EB=AC=B4=EC=9E=90=20?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=ED=86=A0=EA=B8=80=20UI=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../icons/schedule/schedule_calendar.svg | 3 + .../hooks/useWorkerScheduleManageViewModel.ts | 17 ++++-- src/pages/manager/worker-schedule/index.tsx | 60 ++++++++++++++----- 3 files changed, 62 insertions(+), 18 deletions(-) create mode 100644 src/assets/icons/schedule/schedule_calendar.svg diff --git a/src/assets/icons/schedule/schedule_calendar.svg b/src/assets/icons/schedule/schedule_calendar.svg new file mode 100644 index 0000000..0786b6a --- /dev/null +++ b/src/assets/icons/schedule/schedule_calendar.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/features/manager/home/hooks/useWorkerScheduleManageViewModel.ts b/src/features/manager/home/hooks/useWorkerScheduleManageViewModel.ts index ecb1694..5aa2de7 100644 --- a/src/features/manager/home/hooks/useWorkerScheduleManageViewModel.ts +++ b/src/features/manager/home/hooks/useWorkerScheduleManageViewModel.ts @@ -4,6 +4,14 @@ const WORKDAY_OPTIONS = ['월', '화', '수', '목', '금', '토', '일'] as con const DEFAULT_SELECTED_DAYS = ['수', '금'] +const MOCK_WORKERS = [ + { name: '이름임', role: 'manager' as const }, + { name: '김민준', role: 'staff' as const }, + { name: '박지은', role: 'staff' as const }, + { name: '이수호', role: 'staff' as const }, + { name: '정하나', role: 'manager' as const }, +] + export function useWorkerScheduleManageViewModel() { const [selectedDays, setSelectedDays] = useState( DEFAULT_SELECTED_DAYS @@ -12,6 +20,7 @@ export function useWorkerScheduleManageViewModel() { const [startMinute, setStartMinute] = useState('') const [endHour, setEndHour] = useState('') const [endMinute, setEndMinute] = useState('') + const [selectedWorkerIndex, setSelectedWorkerIndex] = useState(0) const workTimeRangeLabel = useMemo(() => { const sh = startHour || '00' @@ -28,10 +37,10 @@ export function useWorkerScheduleManageViewModel() { } return { - worker: { - name: '이름임', - role: 'manager' as const, - }, + worker: MOCK_WORKERS[selectedWorkerIndex], + workers: MOCK_WORKERS, + selectedWorkerIndex, + setSelectedWorkerIndex, workdayOptions: WORKDAY_OPTIONS, selectedDays, workTimeRangeLabel, diff --git a/src/pages/manager/worker-schedule/index.tsx b/src/pages/manager/worker-schedule/index.tsx index 6b2e9fb..31c3a88 100644 --- a/src/pages/manager/worker-schedule/index.tsx +++ b/src/pages/manager/worker-schedule/index.tsx @@ -2,7 +2,7 @@ import { useState } from 'react' import { WorkerRoleBadge } from '@/shared/ui/home/WorkerRoleBadge' import { useWorkerScheduleManageViewModel } from '@/features/manager/home/hooks/useWorkerScheduleManageViewModel' import chevronDownIcon from '@/assets/icons/home/chevron-down.svg' -import calendarIcon from '@/assets/icons/nav/calendar.svg' +import calendarIcon from '@/assets/icons/schedule/schedule_calendar.svg' import { Navbar } from '@/shared/ui/common/Navbar' import { ScheduleCalendar } from '@/shared/ui/common/ScheduleCalendar' import type { ScheduleTab } from '@/features/manager' @@ -36,8 +36,12 @@ export function ManagerWorkerSchedulePage() { const [activeTab, setActiveTab] = useState('고정') const [showCalendar, setShowCalendar] = useState(false) const [selectedDate, setSelectedDate] = useState(null) + const [isWorkerDropdownOpen, setIsWorkerDropdownOpen] = useState(false) const { worker, + workers, + selectedWorkerIndex, + setSelectedWorkerIndex, workdayOptions, selectedDays, workTimeRangeLabel, @@ -76,9 +80,14 @@ export function ManagerWorkerSchedulePage() {
-
+

근무자 선택

-
+ -
+ + + + {isWorkerDropdownOpen && ( +
+ {workers.map((w, index) => ( + + ))} +
+ )}
{activeTab === '고정' ? ( @@ -134,9 +168,7 @@ export function ManagerWorkerSchedulePage() { onClick={() => setShowCalendar(true)} className="mt-4 flex h-12 w-full items-center gap-1 rounded-2xl bg-white px-4" > - + {selectedDate ? `${selectedDate.getFullYear()}년 ${selectedDate.getMonth() + 1}월 ${selectedDate.getDate()}일` : '날짜 선택'} From c9bb18ae2553440653bbb46fb68259e7333b558c Mon Sep 17 00:00:00 2001 From: SeongHwan Date: Sun, 10 May 2026 17:49:44 +0900 Subject: [PATCH 05/11] =?UTF-8?q?feat:=20=EC=8A=A4=EC=BC=80=EC=A4=84=20?= =?UTF-8?q?=EB=82=A0=EC=A7=9C=20=EC=84=A0=ED=83=9D=20=EB=B2=84=ED=8A=BC=20?= =?UTF-8?q?=ED=86=A0=EA=B8=80=20UI=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= =?UTF-8?q?=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/manager/worker-schedule/index.tsx | 68 +++++++++++---------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/src/pages/manager/worker-schedule/index.tsx b/src/pages/manager/worker-schedule/index.tsx index 31c3a88..0f67049 100644 --- a/src/pages/manager/worker-schedule/index.tsx +++ b/src/pages/manager/worker-schedule/index.tsx @@ -162,24 +162,47 @@ export function ManagerWorkerSchedulePage() {
) : ( -
+
+

날짜 선택

+ + {showCalendar && ( +
+ { + setSelectedDate(date) + setShowCalendar(false) + }} + /> +
+ )}
)} @@ -245,25 +268,6 @@ export function ManagerWorkerSchedulePage() {
- {showCalendar && ( -
-
- )}
+ <> +
+

날짜 선택

+ - {showCalendar && ( -
- { - setSelectedDate(date) - setShowCalendar(false) - }} - /> -
- )} -
+ {showCalendar && ( +
+ { + setSelectedDate(date) + setShowCalendar(false) + }} + /> +
+ )} + +
+ setIsColorPickerOpen(!isColorPickerOpen)} + /> +
+ )} - -
-

- 근무 시간 선택 -

+ {!isColorPickerOpen && ( +
+

+ 근무 시간 선택 +

{workTimeRangeLabel}

@@ -266,9 +282,9 @@ export function ManagerWorkerSchedulePage() {
+ )} -
+ + {isOpen && ( +
+
+ {colors.map(color => ( + + ))} +
+
+ )} +
+ ) +} From 7ac5de7d9c5dcabd60d5e2616400da0ba251e84e Mon Sep 17 00:00:00 2001 From: SeongHwan Date: Sun, 10 May 2026 18:24:05 +0900 Subject: [PATCH 07/11] =?UTF-8?q?fix:=20prettier=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/manager/worker-schedule/index.tsx | 100 +++++++++++--------- 1 file changed, 53 insertions(+), 47 deletions(-) diff --git a/src/pages/manager/worker-schedule/index.tsx b/src/pages/manager/worker-schedule/index.tsx index bed91c2..87eef86 100644 --- a/src/pages/manager/worker-schedule/index.tsx +++ b/src/pages/manager/worker-schedule/index.tsx @@ -40,7 +40,9 @@ export function ManagerWorkerSchedulePage() { const [selectedDate, setSelectedDate] = useState(null) const [isWorkerDropdownOpen, setIsWorkerDropdownOpen] = useState(false) const [isColorPickerOpen, setIsColorPickerOpen] = useState(false) - const [selectedColor, setSelectedColor] = useState(ScheduleColor.Pink) + const [selectedColor, setSelectedColor] = useState( + ScheduleColor.Pink + ) const { worker, workers, @@ -226,62 +228,66 @@ export function ManagerWorkerSchedulePage() {

근무 시간 선택

-

- {workTimeRangeLabel} -

+

+ {workTimeRangeLabel} +

-
- - 출근 시간 - -
- -
-
-
-
-
- - 퇴근 시간 - -
- -
-
-
-
- + )} From c4b9c48e83e96de7b16a25426a5a59553100fa0d Mon Sep 17 00:00:00 2001 From: SeongHwan Date: Sun, 10 May 2026 18:35:05 +0900 Subject: [PATCH 08/11] =?UTF-8?q?fix:=20=ED=8C=8C=EC=9D=BC=20=EC=9C=84?= =?UTF-8?q?=EC=B9=98=20=EB=B3=80=EA=B2=BD=EC=97=90=20=EB=94=B0=EB=A5=B8=20?= =?UTF-8?q?import=20=EC=97=90=EB=9F=AC=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/home/common/schedule/types/monthlyCalendar.ts | 2 +- src/features/home/common/schedule/ui/MonthlyCalendar.tsx | 2 +- src/features/user/home/schedule/lib/date.test.ts | 7 +++++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/features/home/common/schedule/types/monthlyCalendar.ts b/src/features/home/common/schedule/types/monthlyCalendar.ts index 586086d..515ad46 100644 --- a/src/features/home/common/schedule/types/monthlyCalendar.ts +++ b/src/features/home/common/schedule/types/monthlyCalendar.ts @@ -1,4 +1,4 @@ -import type { WEEKDAY_LABELS } from '@/features/home/common/schedule/constants/calendar' +import type { WEEKDAY_LABELS } from '@/shared/constants/calendar' import type { BaseCalendarProps } from '@/features/home/common/schedule/types/calendarBase' export interface MonthlyCellInput { diff --git a/src/features/home/common/schedule/ui/MonthlyCalendar.tsx b/src/features/home/common/schedule/ui/MonthlyCalendar.tsx index 2e32540..fffc7aa 100644 --- a/src/features/home/common/schedule/ui/MonthlyCalendar.tsx +++ b/src/features/home/common/schedule/ui/MonthlyCalendar.tsx @@ -94,7 +94,7 @@ export function MonthlyCalendar({ className={layout === 'manager' ? 'mt-5 px-[11px] pb-4' : 'mt-4'} >
- {weekdayLabels.map((label, index) => ( + {weekdayLabels.map((label: string, index: number) => ( { it('ISO 문자열에서 날짜 부분만 반환한다', () => { From 91a0739d2ce45a9af2bef0b5d8e85ff1009df301 Mon Sep 17 00:00:00 2001 From: SeongHwan Date: Sun, 10 May 2026 18:43:20 +0900 Subject: [PATCH 09/11] =?UTF-8?q?fix:=20=EC=8B=9C=EA=B0=84=20=EB=B2=94?= =?UTF-8?q?=EC=9C=84=20=EC=A7=80=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/manager/worker-schedule/index.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/pages/manager/worker-schedule/index.tsx b/src/pages/manager/worker-schedule/index.tsx index 87eef86..168c9b9 100644 --- a/src/pages/manager/worker-schedule/index.tsx +++ b/src/pages/manager/worker-schedule/index.tsx @@ -17,6 +17,17 @@ interface TimeSelectBoxProps { } function TimeSelectBox({ value, unit, onChange }: TimeSelectBoxProps) { + const handleChange = (e: React.ChangeEvent) => { + const inputValue = e.target.value.replace(/\D/g, '') + if (inputValue === '') { + onChange('') + return + } + const maxValue = unit === '시' ? 23 : 59 + const numValue = Math.min(parseInt(inputValue, 10), maxValue) + onChange(numValue.toString().padStart(2, '0')) + } + return (
onChange(e.target.value.replace(/\D/g, ''))} + onChange={handleChange} className="w-8 bg-transparent text-center typography-body01-semibold text-text-100 placeholder:text-text-50 outline-none" aria-label={unit} /> From a158f1baf943e01f8bab29f072589ff3010dea72 Mon Sep 17 00:00:00 2001 From: SeongHwan Date: Sun, 10 May 2026 19:02:13 +0900 Subject: [PATCH 10/11] =?UTF-8?q?fix:=20viewYear,=20viewMonth=EB=A5=BC=20?= =?UTF-8?q?=EB=8F=99=EA=B8=B0=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/manager/worker-schedule/index.tsx | 15 ++++++++++----- src/shared/ui/common/ScheduleCalendar.tsx | 13 ++++++++++++- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/pages/manager/worker-schedule/index.tsx b/src/pages/manager/worker-schedule/index.tsx index 168c9b9..82ca5e7 100644 --- a/src/pages/manager/worker-schedule/index.tsx +++ b/src/pages/manager/worker-schedule/index.tsx @@ -18,14 +18,20 @@ interface TimeSelectBoxProps { function TimeSelectBox({ value, unit, onChange }: TimeSelectBoxProps) { const handleChange = (e: React.ChangeEvent) => { - const inputValue = e.target.value.replace(/\D/g, '') - if (inputValue === '') { + let inputValue = e.target.value.replace(/\D/g, '') + + if (!inputValue) { onChange('') return } + + if (inputValue.length > 2) { + inputValue = inputValue.slice(-2) + } + const maxValue = unit === '시' ? 23 : 59 - const numValue = Math.min(parseInt(inputValue, 10), maxValue) - onChange(numValue.toString().padStart(2, '0')) + const num = Math.min(parseInt(inputValue, 10), maxValue) + onChange(num.toString().padStart(2, '0')) } return ( @@ -33,7 +39,6 @@ function TimeSelectBox({ value, unit, onChange }: TimeSelectBoxProps) { { + if (selectedDate) { + const year = selectedDate.getFullYear() + const month = selectedDate.getMonth() + if (viewYear !== year || viewMonth !== month) { + setViewYear(year) + setViewMonth(month) + } + } + }, [selectedDate, viewYear, viewMonth]) + const baseDate = new Date(viewYear, viewMonth, 1) const cells = getCalendarCells(baseDate, 0) const weeks = [] From 47a57769cfb68c8c498a1c497f44b29db4ffb208 Mon Sep 17 00:00:00 2001 From: SeongHwan Date: Sun, 10 May 2026 19:10:16 +0900 Subject: [PATCH 11/11] =?UTF-8?q?fix:=20=EB=A6=B0=ED=8A=B8=20=EA=B2=BD?= =?UTF-8?q?=EA=B3=A0=20=EB=AC=B4=EC=8B=9C=ED=95=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/shared/ui/common/ScheduleCalendar.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/shared/ui/common/ScheduleCalendar.tsx b/src/shared/ui/common/ScheduleCalendar.tsx index c2d270d..b30693d 100644 --- a/src/shared/ui/common/ScheduleCalendar.tsx +++ b/src/shared/ui/common/ScheduleCalendar.tsx @@ -30,7 +30,8 @@ export function ScheduleCalendar({ setViewMonth(month) } } - }, [selectedDate, viewYear, viewMonth]) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [selectedDate]) const baseDate = new Date(viewYear, viewMonth, 1) const cells = getCalendarCells(baseDate, 0)