From 22f45850c5d632b6a875bbacb25cb0226f196972 Mon Sep 17 00:00:00 2001 From: yummjin Date: Sat, 21 Feb 2026 00:57:58 +0800 Subject: [PATCH 01/11] =?UTF-8?q?=F0=9F=92=84=20[style]=20=EC=9D=BC?= =?UTF-8?q?=EC=A0=95=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=95=84=EC=9D=B4?= =?UTF-8?q?=ED=85=9C=20=ED=83=9C=EA=B7=B8=20=ED=83=80=EC=9E=85=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EB=B0=8F=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=A1=B0?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../schedule/styles/ScheduleList.css.ts | 4 +--- .../entities/schedule/ui/ScheduleListItem.tsx | 23 ++++++++----------- .../src/pages/schedule/ui/SchedulePage.tsx | 10 ++++++-- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/apps/web/src/entities/schedule/styles/ScheduleList.css.ts b/apps/web/src/entities/schedule/styles/ScheduleList.css.ts index 2fae6d7c..36990514 100644 --- a/apps/web/src/entities/schedule/styles/ScheduleList.css.ts +++ b/apps/web/src/entities/schedule/styles/ScheduleList.css.ts @@ -6,7 +6,6 @@ export const listContainer = style({ flexDirection: 'column', gap: '10px', width: '100%', - height: '100%', }); export const emptyContainer = style({ @@ -15,8 +14,7 @@ export const emptyContainer = style({ alignItems: 'center', justifyContent: 'center', width: '100%', - height: '100px', - flex: 1, + height: '200px', }); export const emptyText = style([ diff --git a/apps/web/src/entities/schedule/ui/ScheduleListItem.tsx b/apps/web/src/entities/schedule/ui/ScheduleListItem.tsx index 6eed6fa7..bfb0ec01 100644 --- a/apps/web/src/entities/schedule/ui/ScheduleListItem.tsx +++ b/apps/web/src/entities/schedule/ui/ScheduleListItem.tsx @@ -4,7 +4,7 @@ import { ClockIcon, MarkerPinIcon, UsersIcon } from '@azit/design-system/icon'; import type { ScheduleListItem as ScheduleListItemType } from '@/entities/schedule/model/schedule.types'; import * as styles from '@/entities/schedule/styles/ScheduleListItem.css.ts'; -function formatMeetingAt(meetingAt: string | undefined) { +const formatMeetingAt = (meetingAt: string | undefined) => { if (!meetingAt) return { month: '', day: '', time: '' }; try { const date = new Date(meetingAt); @@ -20,30 +20,30 @@ function formatMeetingAt(meetingAt: string | undefined) { } catch { return { month: '', day: '', time: '' }; } -} +}; -function buildTags( +const buildTags = ( item: ScheduleListItemType -): { label: string; type: 'primary' | 'secondary' }[] { - const tags: { label: string; type: 'primary' | 'secondary' }[] = []; +): { label: string; type: 'primary' | 'secondary' | 'gray' }[] => { + const tags: { label: string; type: 'primary' | 'secondary' | 'gray' }[] = []; if (item.runType) { tags.push({ label: item.runType === 'REGULAR' ? '정기런' : '번개런', - type: 'primary', + type: item.runType === 'REGULAR' ? 'primary' : 'secondary', }); } if (item.distance != null) - tags.push({ label: `${item.distance}km`, type: 'secondary' }); + tags.push({ label: `${item.distance}km`, type: 'gray' }); if (item.pace != null) { const min = Math.floor(item.pace); const sec = Math.round((item.pace - min) * 60); tags.push({ label: `${min}'${sec.toString().padStart(2, '0')}"/km`, - type: 'secondary', + type: 'gray', }); } return tags; -} +}; interface ScheduleListItemProps { item: ScheduleListItemType; @@ -77,10 +77,7 @@ export function ScheduleListItem({ item, handleClick }: ScheduleListItemProps) {
{tags.map((tag, index) => ( - + {tag.label} ))} diff --git a/apps/web/src/pages/schedule/ui/SchedulePage.tsx b/apps/web/src/pages/schedule/ui/SchedulePage.tsx index dd9e3329..90b426cb 100644 --- a/apps/web/src/pages/schedule/ui/SchedulePage.tsx +++ b/apps/web/src/pages/schedule/ui/SchedulePage.tsx @@ -21,13 +21,19 @@ import { ScheduleList } from '@/entities/schedule/ui'; export function SchedulePage() { const [activeFilter, setActiveFilter] = useState(undefined); const [selectedDate, setSelectedDate] = useState(new Date()); + const [date, setDate] = useState(undefined); + + const handleDateChange = (date: Date) => { + setSelectedDate(date); + setDate(dayjs(date).format('YYYY-MM-DD')); + }; const { data: myInfoData } = useQuery(memberQueries.myInfoQuery()); const crewId = myInfoData?.ok ? myInfoData.data.result.crewId : 0; const { data: scheduleList = [], isLoading } = useQuery({ ...scheduleQueries.getScheduleListQuery(crewId, { runType: activeFilter, - date: dayjs(selectedDate).format('YYYY-MM-DD'), + date, }), enabled: crewId > 0, }); @@ -45,7 +51,7 @@ export function SchedulePage() { topSection={ } scheduleContent={ From 7f6426384570df17ba2466053eda354c9d9f8d35 Mon Sep 17 00:00:00 2001 From: yummjin Date: Sat, 21 Feb 2026 00:59:02 +0800 Subject: [PATCH 02/11] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20[refactor]=20?= =?UTF-8?q?=ED=95=84=ED=84=B0=20=EB=B2=84=ED=8A=BC=20=ED=82=A4=20=EA=B0=92?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/widgets/schedule-filter-tab/ui/ScheduleFilterTab.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/widgets/schedule-filter-tab/ui/ScheduleFilterTab.tsx b/apps/web/src/widgets/schedule-filter-tab/ui/ScheduleFilterTab.tsx index 8b469859..59586261 100644 --- a/apps/web/src/widgets/schedule-filter-tab/ui/ScheduleFilterTab.tsx +++ b/apps/web/src/widgets/schedule-filter-tab/ui/ScheduleFilterTab.tsx @@ -23,7 +23,7 @@ export function ScheduleFilterTab({
{FILTERS.map((filter) => ( +
+ ); + } return (

등록된 일정이 없어요

diff --git a/apps/web/src/pages/home/ui/HomePage.tsx b/apps/web/src/pages/home/ui/HomePage.tsx index b23817aa..b1966495 100644 --- a/apps/web/src/pages/home/ui/HomePage.tsx +++ b/apps/web/src/pages/home/ui/HomePage.tsx @@ -1,13 +1,16 @@ import { Header } from '@azit/design-system/header'; import { BellIcon } from '@azit/design-system/icon'; import { AppScreen } from '@stackflow/plugin-basic-ui'; +import { useQuery } from '@tanstack/react-query'; import { useFlow } from '@/app/routes/stackflow'; import { ScheduleAttendanceSection } from '@/widgets/schedule-attendance/ui'; import { ScheduleSectionLayout } from '@/widgets/schedule-section-layout/ui'; -import { mockActivityActivation, mockScheduleList } from '@/shared/mock/home'; +import { mockActivityActivation } from '@/shared/mock/home'; +import { memberQueries } from '@/shared/queries'; +import { scheduleQueries } from '@/shared/queries/schedule'; import { scrollContainer } from '@/shared/styles/container.css'; import { logo } from '@/shared/styles/logo.css'; import { AppLayout } from '@/shared/ui/layout'; @@ -22,6 +25,14 @@ export function HomePage() { push('AlertPage', {}); }; + const { data: myInfoData } = useQuery(memberQueries.myInfoQuery()); + const crewId = myInfoData?.ok ? myInfoData.data.result.crewId : 0; + + const { data: scheduleList = [], isLoading } = useQuery({ + ...scheduleQueries.getMemberScheduleListQuery(), + enabled: crewId > 0, + }); + return ( @@ -40,7 +51,9 @@ export function HomePage() { } scheduleTitle="내 일정" - scheduleContent={} + scheduleContent={ + !isLoading && + } />
From 778c2bca2d585bec834a05e4bcffea74c1f319e3 Mon Sep 17 00:00:00 2001 From: yummjin Date: Sat, 21 Feb 2026 01:31:57 +0800 Subject: [PATCH 05/11] =?UTF-8?q?=E2=9C=A8=20[feat]=20=EC=9D=BC=EC=A0=95?= =?UTF-8?q?=20=EB=A1=9C=EB=94=A9=20=EC=8B=9C=20=EC=8A=A4=EC=BC=88=EB=A0=88?= =?UTF-8?q?=ED=86=A4=20UI=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/src/pages/home/ui/HomePage.tsx | 7 +- .../src/pages/schedule/ui/SchedulePage.tsx | 7 +- .../styles/ScheduleListSkeleton.css.ts | 83 +++++++++++++++++++ .../skeleton/ui/ScheduleListSkeleton.tsx | 33 ++++++++ apps/web/src/widgets/skeleton/ui/index.ts | 1 + 5 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 apps/web/src/widgets/skeleton/styles/ScheduleListSkeleton.css.ts create mode 100644 apps/web/src/widgets/skeleton/ui/ScheduleListSkeleton.tsx diff --git a/apps/web/src/pages/home/ui/HomePage.tsx b/apps/web/src/pages/home/ui/HomePage.tsx index b1966495..7df35117 100644 --- a/apps/web/src/pages/home/ui/HomePage.tsx +++ b/apps/web/src/pages/home/ui/HomePage.tsx @@ -5,6 +5,7 @@ import { useQuery } from '@tanstack/react-query'; import { useFlow } from '@/app/routes/stackflow'; +import { ScheduleListSkeleton } from '@/widgets/skeleton/ui'; import { ScheduleAttendanceSection } from '@/widgets/schedule-attendance/ui'; import { ScheduleSectionLayout } from '@/widgets/schedule-section-layout/ui'; @@ -52,7 +53,11 @@ export function HomePage() { } scheduleTitle="내 일정" scheduleContent={ - !isLoading && + isLoading ? ( + + ) : ( + + ) } />
diff --git a/apps/web/src/pages/schedule/ui/SchedulePage.tsx b/apps/web/src/pages/schedule/ui/SchedulePage.tsx index 90b426cb..2fa0013e 100644 --- a/apps/web/src/pages/schedule/ui/SchedulePage.tsx +++ b/apps/web/src/pages/schedule/ui/SchedulePage.tsx @@ -8,6 +8,7 @@ import { useState } from 'react'; import { ScheduleWeekCalendar } from '@/widgets/schedule-calendar/ui/ScheduleWeekCalendar'; import { ScheduleFilterTab } from '@/widgets/schedule-filter-tab/ui'; import { ScheduleSectionLayout } from '@/widgets/schedule-section-layout/ui'; +import { ScheduleListSkeleton } from '@/widgets/skeleton/ui'; import { memberQueries } from '@/shared/queries/member'; import { scheduleQueries } from '@/shared/queries/schedule'; @@ -60,7 +61,11 @@ export function SchedulePage() { activeFilter={activeFilter} onFilterChange={setActiveFilter} /> - {!isLoading && } + {isLoading ? ( + + ) : ( + + )} } /> diff --git a/apps/web/src/widgets/skeleton/styles/ScheduleListSkeleton.css.ts b/apps/web/src/widgets/skeleton/styles/ScheduleListSkeleton.css.ts new file mode 100644 index 00000000..e7de6822 --- /dev/null +++ b/apps/web/src/widgets/skeleton/styles/ScheduleListSkeleton.css.ts @@ -0,0 +1,83 @@ +import { vars } from '@azit/design-system'; +import { keyframes, style } from '@vanilla-extract/css'; + +const pulse = keyframes({ + '0%, 100%': { opacity: 1 }, + '50%': { opacity: 0.5 }, +}); + +const skeletonBase = style({ + backgroundColor: vars.colors.gray20, + borderRadius: '4px', + animation: `${pulse} 1.5s ease-in-out infinite`, +}); + +export const listContainer = style({ + display: 'flex', + flexDirection: 'column', + gap: '10px', + width: '100%', +}); + +export const itemContainer = style({ + display: 'flex', + gap: '16px', + width: '100%', + padding: '16px', + backgroundColor: vars.colors.white, + borderRadius: '16px', + border: `1px solid ${vars.colors.gray10}`, +}); + +export const dateBlock = style([ + skeletonBase, + { + width: '44px', + height: '44px', + flexShrink: 0, + }, +]); + +export const contentContainer = style({ + display: 'flex', + flexDirection: 'column', + gap: '8px', + flex: 1, + minWidth: 0, +}); + +export const tagsRow = style({ + display: 'flex', + gap: '4px', + flexWrap: 'wrap', +}); + +export const tagLine = style([ + skeletonBase, + { + width: '52px', + height: '24px', + }, +]); + +export const titleLine = style([ + skeletonBase, + { + width: '80%', + height: '16px', + }, +]); + +export const detailsRow = style({ + display: 'flex', + gap: '12px', + flexWrap: 'wrap', +}); + +export const detailLine = style([ + skeletonBase, + { + width: '64px', + height: '14px', + }, +]); diff --git a/apps/web/src/widgets/skeleton/ui/ScheduleListSkeleton.tsx b/apps/web/src/widgets/skeleton/ui/ScheduleListSkeleton.tsx new file mode 100644 index 00000000..53647ea3 --- /dev/null +++ b/apps/web/src/widgets/skeleton/ui/ScheduleListSkeleton.tsx @@ -0,0 +1,33 @@ +import * as styles from '@/widgets/skeleton/styles/ScheduleListSkeleton.css'; + +const SKELETON_ITEM_COUNT = 4; + +function ScheduleListSkeletonItem() { + return ( +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ); +} + +export function ScheduleListSkeleton() { + return ( +
+ {Array.from({ length: SKELETON_ITEM_COUNT }, (_, index) => ( + + ))} +
+ ); +} diff --git a/apps/web/src/widgets/skeleton/ui/index.ts b/apps/web/src/widgets/skeleton/ui/index.ts index 73573fac..78b723a7 100644 --- a/apps/web/src/widgets/skeleton/ui/index.ts +++ b/apps/web/src/widgets/skeleton/ui/index.ts @@ -1,3 +1,4 @@ export { CartSkeleton } from './CartSkeleton'; +export { ScheduleListSkeleton } from './ScheduleListSkeleton'; export { StoreSkeleton } from './StoreSkeleton'; export { StoreDetailSkeleton } from './StoreDetailSkeleton'; From 0835e1284096d89572c3e4b02060b0696efc8365 Mon Sep 17 00:00:00 2001 From: yummjin Date: Sat, 21 Feb 2026 01:53:18 +0800 Subject: [PATCH 06/11] =?UTF-8?q?=E2=9C=A8=20[feat]=20=EC=BA=98=EB=A6=B0?= =?UTF-8?q?=EB=8D=94=EC=97=90=20=EC=9D=BC=EC=A0=95=20=ED=91=9C=EC=8B=9C=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../schedule/api/getScheduleCalendar.ts | 15 +++-- .../entities/schedule/model/schedule.types.ts | 8 ++- apps/web/src/pages/home/ui/HomePage.tsx | 2 +- .../src/pages/schedule/ui/SchedulePage.tsx | 17 +++++- apps/web/src/shared/queries/schedule.ts | 16 +++++- .../style/ScheduleCalendar.css.ts | 32 ++++++++++- .../schedule-calendar/ui/ScheduleCalendar.tsx | 56 +++++++++++++------ 7 files changed, 117 insertions(+), 29 deletions(-) diff --git a/apps/web/src/entities/schedule/api/getScheduleCalendar.ts b/apps/web/src/entities/schedule/api/getScheduleCalendar.ts index c5d25d0e..cfeb690c 100644 --- a/apps/web/src/entities/schedule/api/getScheduleCalendar.ts +++ b/apps/web/src/entities/schedule/api/getScheduleCalendar.ts @@ -2,10 +2,17 @@ import { auth } from '@/shared/api/apiClient'; import type { ApiResponse } from '@/shared/api/baseTypes'; import { END_POINT } from '@/shared/constants/endpoint'; -import type { CrewScheduleCalendarResponse } from '../model/schedule.model'; +import type { + CrewScheduleCalendarRequest, + CrewScheduleCalendarResponse, +} from '../model/schedule.model'; -export const getScheduleCalendar = (crewId: number) => { - return auth.get>( - END_POINT.SCHEDULE.CALENDAR(crewId) +export const getScheduleCalendar = ( + crewId: number, + request?: CrewScheduleCalendarRequest +) => { + return auth.get>( + END_POINT.SCHEDULE.CALENDAR(crewId), + request ? { searchParams: request } : undefined ); }; diff --git a/apps/web/src/entities/schedule/model/schedule.types.ts b/apps/web/src/entities/schedule/model/schedule.types.ts index c52f5d16..06409655 100644 --- a/apps/web/src/entities/schedule/model/schedule.types.ts +++ b/apps/web/src/entities/schedule/model/schedule.types.ts @@ -1,5 +1,11 @@ -import type { CrewScheduleListResponse } from './schedule.model'; +import type { + CrewScheduleCalendarResponse, + CrewScheduleListResponse, +} from './schedule.model'; export type ScheduleListItem = CrewScheduleListResponse; export type ScheduleList = ScheduleListItem[]; export type RunType = ScheduleListItem['runType']; + +export type ScheduleCalendarItem = CrewScheduleCalendarResponse; +export type ScheduleCalendarList = ScheduleCalendarItem[]; diff --git a/apps/web/src/pages/home/ui/HomePage.tsx b/apps/web/src/pages/home/ui/HomePage.tsx index 7df35117..77a4ff85 100644 --- a/apps/web/src/pages/home/ui/HomePage.tsx +++ b/apps/web/src/pages/home/ui/HomePage.tsx @@ -5,9 +5,9 @@ import { useQuery } from '@tanstack/react-query'; import { useFlow } from '@/app/routes/stackflow'; -import { ScheduleListSkeleton } from '@/widgets/skeleton/ui'; import { ScheduleAttendanceSection } from '@/widgets/schedule-attendance/ui'; import { ScheduleSectionLayout } from '@/widgets/schedule-section-layout/ui'; +import { ScheduleListSkeleton } from '@/widgets/skeleton/ui'; import { mockActivityActivation } from '@/shared/mock/home'; import { memberQueries } from '@/shared/queries'; diff --git a/apps/web/src/pages/schedule/ui/SchedulePage.tsx b/apps/web/src/pages/schedule/ui/SchedulePage.tsx index 2fa0013e..b5c5442c 100644 --- a/apps/web/src/pages/schedule/ui/SchedulePage.tsx +++ b/apps/web/src/pages/schedule/ui/SchedulePage.tsx @@ -5,7 +5,8 @@ import { useQuery } from '@tanstack/react-query'; import dayjs from 'dayjs'; import { useState } from 'react'; -import { ScheduleWeekCalendar } from '@/widgets/schedule-calendar/ui/ScheduleWeekCalendar'; +import { ScheduleCalendar } from '@/widgets/schedule-calendar/ui/ScheduleCalendar'; +// import { ScheduleWeekCalendar } from '@/widgets/schedule-calendar/ui/ScheduleWeekCalendar'; import { ScheduleFilterTab } from '@/widgets/schedule-filter-tab/ui'; import { ScheduleSectionLayout } from '@/widgets/schedule-section-layout/ui'; import { ScheduleListSkeleton } from '@/widgets/skeleton/ui'; @@ -39,6 +40,13 @@ export function SchedulePage() { enabled: crewId > 0, }); + const { data: scheduleCalendarList = [] } = useQuery({ + ...scheduleQueries.getScheduleCalendarQuery(crewId, { + yearMonth: dayjs(selectedDate).format('YYYY-MM'), + }), + enabled: crewId > 0, + }); + return ( @@ -50,10 +58,15 @@ export function SchedulePage() {
+ // } scheduleContent={ <> diff --git a/apps/web/src/shared/queries/schedule.ts b/apps/web/src/shared/queries/schedule.ts index d561a7d7..ef2d2c3a 100644 --- a/apps/web/src/shared/queries/schedule.ts +++ b/apps/web/src/shared/queries/schedule.ts @@ -3,7 +3,10 @@ import { queryOptions } from '@tanstack/react-query'; import { getMemberScheduleList } from '@/entities/schedule/api/getMemberScheduleList'; import { getScheduleCalendar } from '@/entities/schedule/api/getScheduleCalendar'; import { getScheduleList } from '@/entities/schedule/api/getScheduleList'; -import type { CrewScheduleListRequest } from '@/entities/schedule/model/schedule.model'; +import type { + CrewScheduleCalendarRequest, + CrewScheduleListRequest, +} from '@/entities/schedule/model/schedule.model'; export const scheduleQueries = { all: ['schedule'] as const, @@ -29,9 +32,16 @@ export const scheduleQueries = { return res.data.result ?? []; }, }), - getScheduleCalendarQuery: (crewId: number) => + getScheduleCalendarQuery: ( + crewId: number, + request?: CrewScheduleCalendarRequest + ) => queryOptions({ queryKey: [...scheduleQueries.all, 'getScheduleCalendar', crewId], - queryFn: () => getScheduleCalendar(crewId), + queryFn: async () => { + const res = await getScheduleCalendar(crewId, request); + if (!res.ok) return []; + return res.data.result ?? []; + }, }), }; diff --git a/apps/web/src/widgets/schedule-calendar/style/ScheduleCalendar.css.ts b/apps/web/src/widgets/schedule-calendar/style/ScheduleCalendar.css.ts index 8b2243d6..4804a974 100644 --- a/apps/web/src/widgets/schedule-calendar/style/ScheduleCalendar.css.ts +++ b/apps/web/src/widgets/schedule-calendar/style/ScheduleCalendar.css.ts @@ -20,11 +20,18 @@ export const calendarHeaderSection = style({ display: 'flex', flexDirection: 'row', alignItems: 'center', - justifyContent: 'center', + justifyContent: 'space-between', gap: '32px', marginBottom: '16px', }); +export const calendarButtonWrapper = style({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + gap: '10px', +}); + export const calendarHeaderButton = style({ display: 'flex', alignItems: 'center', @@ -34,3 +41,26 @@ export const calendarHeaderButton = style({ background: 'transparent', cursor: 'pointer', }); + +export const lightningTile = style({ + width: '6px', + height: '6px', + backgroundColor: vars.colors.secondary, + borderRadius: '100%', +}); + +export const regularTile = style({ + width: '6px', + height: '6px', + backgroundColor: vars.colors.blue60, + borderRadius: '100%', +}); + +export const tileContainer = style({ + width: '100%', + height: '100%', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + gap: '2px', +}); diff --git a/apps/web/src/widgets/schedule-calendar/ui/ScheduleCalendar.tsx b/apps/web/src/widgets/schedule-calendar/ui/ScheduleCalendar.tsx index 94e2cf9a..1efe4dcb 100644 --- a/apps/web/src/widgets/schedule-calendar/ui/ScheduleCalendar.tsx +++ b/apps/web/src/widgets/schedule-calendar/ui/ScheduleCalendar.tsx @@ -5,31 +5,46 @@ import '@/widgets/schedule-calendar/style/ScheduleCalendarBase.css.ts'; import * as styles from '@/widgets/schedule-calendar/style/ScheduleCalendar.css.ts'; +import type { ScheduleCalendarList } from '@/entities/schedule/model/schedule.types'; + interface ScheduleCalendarProps { value: Date; onChange: (date: Date) => void; - activeStartDate: Date; - onActiveStartDateChange: (date: Date) => void; + scheduleData: ScheduleCalendarList; } export function ScheduleCalendar({ value, onChange, - activeStartDate, - onActiveStartDateChange, + scheduleData, }: ScheduleCalendarProps) { + const handlePreviousMonth = () => { + onChange(dayjs(value).subtract(1, 'month').toDate()); + }; + const handleNextMonth = () => { + onChange(dayjs(value).add(1, 'month').toDate()); + }; + return (
- - {dayjs(activeStartDate).format('YYYY년 M월')} + {dayjs(value).format('YYYY년 M월')} - +
+ + +
{ - if (activeStartDate) { - onActiveStartDateChange(activeStartDate); - } - }} formatShortWeekday={(_, date) => dayjs(date).format('ddd').toUpperCase() } formatDay={(_, date) => dayjs(date).format('D')} + tileContent={({ date }: { date: Date }) => { + const schedule = scheduleData.find((item) => + dayjs(item.date).isSame(date, 'day') + ); + const hasLightning = schedule?.hasLightning; + const hasRegular = schedule?.hasRegular; + return ( +
+ {hasRegular &&
} + {hasLightning &&
} +
+ ); + }} />
); From 9c322d3ca6c0ffabfde3ade64b56ac4f696dd267 Mon Sep 17 00:00:00 2001 From: yummjin Date: Sat, 21 Feb 2026 02:46:06 +0800 Subject: [PATCH 07/11] =?UTF-8?q?=F0=9F=90=9B=20[fix]=20=EC=BA=98=EB=A6=B0?= =?UTF-8?q?=EB=8D=94=EC=97=90=EC=84=9C=20=EC=9D=B4=EC=9B=83=20=EB=8B=AC=20?= =?UTF-8?q?=EB=82=A0=EC=A7=9C=20=EC=88=A8=EA=B9=80=20=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0=20=EB=B0=8F=20=EC=9D=BC=EC=A0=95=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=EC=84=A0=ED=83=9D=EC=A0=81=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../style/ScheduleCalendarBase.css.ts | 5 +++-- .../schedule-calendar/ui/ScheduleCalendar.tsx | 14 +++++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/apps/web/src/widgets/schedule-calendar/style/ScheduleCalendarBase.css.ts b/apps/web/src/widgets/schedule-calendar/style/ScheduleCalendarBase.css.ts index abb7a9b6..681ca0d4 100644 --- a/apps/web/src/widgets/schedule-calendar/style/ScheduleCalendarBase.css.ts +++ b/apps/web/src/widgets/schedule-calendar/style/ScheduleCalendarBase.css.ts @@ -30,7 +30,7 @@ globalStyle('.react-calendar__navigation', { }); globalStyle( - '.react-calendar__month-view__days__day--neighboringMonth, .react-calendar__decade-view__years__year--neighboringDecade, .react-calendar__century-view__decades__decade--neighboringCentury', + '.react-calendar__decade-view__years__year--neighboringDecade, .react-calendar__century-view__decades__decade--neighboringCentury', { display: 'none', } @@ -92,7 +92,8 @@ globalStyle('.react-calendar__tile:disabled', { }); globalStyle('.react-calendar__month-view__days__day--neighboringMonth', { - display: 'none', + visibility: 'hidden', + pointerEvents: 'none', }); globalStyle('.react-calendar__tile--hasActive', { diff --git a/apps/web/src/widgets/schedule-calendar/ui/ScheduleCalendar.tsx b/apps/web/src/widgets/schedule-calendar/ui/ScheduleCalendar.tsx index 1efe4dcb..d539fced 100644 --- a/apps/web/src/widgets/schedule-calendar/ui/ScheduleCalendar.tsx +++ b/apps/web/src/widgets/schedule-calendar/ui/ScheduleCalendar.tsx @@ -10,7 +10,7 @@ import type { ScheduleCalendarList } from '@/entities/schedule/model/schedule.ty interface ScheduleCalendarProps { value: Date; onChange: (date: Date) => void; - scheduleData: ScheduleCalendarList; + scheduleData?: ScheduleCalendarList; } export function ScheduleCalendar({ @@ -25,6 +25,8 @@ export function ScheduleCalendar({ onChange(dayjs(value).add(1, 'month').toDate()); }; + const activeStartDate = dayjs(value).startOf('month').toDate(); + return (
@@ -49,17 +51,19 @@ export function ScheduleCalendar({ { + if (nextStart) onChange(nextStart); + }} onChange={(value) => { - if (value instanceof Date) { - onChange(value); - } + if (value instanceof Date) onChange(value); }} formatShortWeekday={(_, date) => dayjs(date).format('ddd').toUpperCase() } formatDay={(_, date) => dayjs(date).format('D')} tileContent={({ date }: { date: Date }) => { - const schedule = scheduleData.find((item) => + const schedule = scheduleData?.find((item) => dayjs(item.date).isSame(date, 'day') ); const hasLightning = schedule?.hasLightning; From 5ce4d453633c34545eb4453ea47045ce472d0a3e Mon Sep 17 00:00:00 2001 From: yummjin Date: Sat, 21 Feb 2026 02:48:53 +0800 Subject: [PATCH 08/11] =?UTF-8?q?=E2=9C=A8=20[feat]=20=EC=9D=BC=EC=A0=95?= =?UTF-8?q?=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=ED=95=84=ED=84=B0=EB=A7=81=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80=20(=EC=84=A0=ED=83=9D?= =?UTF-8?q?=EB=90=9C=20=EB=82=A0=EC=A7=9C=20=EC=9D=B4=EC=A0=84=EC=9D=98=20?= =?UTF-8?q?=EC=8A=A4=EC=BC=80=EC=A4=84=EC=9D=80=20=EB=B3=B4=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8F=84=EB=A1=9D)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/src/pages/schedule/ui/SchedulePage.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/web/src/pages/schedule/ui/SchedulePage.tsx b/apps/web/src/pages/schedule/ui/SchedulePage.tsx index b5c5442c..bb8b89de 100644 --- a/apps/web/src/pages/schedule/ui/SchedulePage.tsx +++ b/apps/web/src/pages/schedule/ui/SchedulePage.tsx @@ -77,7 +77,11 @@ export function SchedulePage() { {isLoading ? ( ) : ( - + + dayjs(item.meetingAt).isAfter(dayjs(selectedDate)) + )} + /> )} } From f10a784bd49e0de58c549d34444309bb3892331a Mon Sep 17 00:00:00 2001 From: yummjin Date: Sat, 21 Feb 2026 02:55:54 +0800 Subject: [PATCH 09/11] =?UTF-8?q?=F0=9F=90=9B=20[fix]=20=EC=9D=BC=EC=A0=95?= =?UTF-8?q?=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=BA=98=EB=A6=B0=EB=8D=94=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20props=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=EC=97=90=20=EB=94=B0=EB=9D=BC=20=EC=82=AC=EC=9A=A9=20?= =?UTF-8?q?=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/src/pages/mypage/ui/MyAttendancePage.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/apps/web/src/pages/mypage/ui/MyAttendancePage.tsx b/apps/web/src/pages/mypage/ui/MyAttendancePage.tsx index 1ce2fd0d..9592adc5 100644 --- a/apps/web/src/pages/mypage/ui/MyAttendancePage.tsx +++ b/apps/web/src/pages/mypage/ui/MyAttendancePage.tsx @@ -32,12 +32,7 @@ export function MyAttendancePage() { /> {}} - activeStartDate={new Date()} - onActiveStartDateChange={() => {}} - /> + {}} /> } scheduleContent={ <> From a4acaab1d542c006642f5bf4121de2122d5d2c0e Mon Sep 17 00:00:00 2001 From: yummjin Date: Mon, 23 Feb 2026 01:29:44 +0900 Subject: [PATCH 10/11] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20[refactor]=20?= =?UTF-8?q?=EC=9D=BC=EC=A0=95=20API=20=EC=97=94=EB=93=9C=ED=8F=AC=EC=9D=B8?= =?UTF-8?q?=ED=8A=B8=20=EC=83=81=EC=88=98=20=EC=9D=B4=EB=A6=84=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/src/entities/schedule/api/getMemberScheduleList.ts | 2 +- apps/web/src/shared/constants/endpoint.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/web/src/entities/schedule/api/getMemberScheduleList.ts b/apps/web/src/entities/schedule/api/getMemberScheduleList.ts index a68b8ff3..467f263a 100644 --- a/apps/web/src/entities/schedule/api/getMemberScheduleList.ts +++ b/apps/web/src/entities/schedule/api/getMemberScheduleList.ts @@ -6,6 +6,6 @@ import type { CrewScheduleListResponse } from '../model/schedule.model'; export const getMemberScheduleList = () => { return auth.get>( - END_POINT.SCHEDULE.MEMBER_LIST + END_POINT.SCHEDULE.JOINED_LIST ); }; diff --git a/apps/web/src/shared/constants/endpoint.ts b/apps/web/src/shared/constants/endpoint.ts index 27d10279..0479ad4c 100644 --- a/apps/web/src/shared/constants/endpoint.ts +++ b/apps/web/src/shared/constants/endpoint.ts @@ -51,6 +51,6 @@ export const END_POINT = { SCHEDULE: { LIST: (crewId: number) => `crews/${crewId}/schedules`, CALENDAR: (crewId: number) => `crews/${crewId}/schedules/calendar`, - MEMBER_LIST: 'members/me/schedules', + JOINED_LIST: 'members/me/schedules', }, } as const; From 1b92bd0fb3642fb6d7435ce7950036b8e36aaafb Mon Sep 17 00:00:00 2001 From: yummjin Date: Mon, 23 Feb 2026 01:36:04 +0900 Subject: [PATCH 11/11] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20[refactor]=20?= =?UTF-8?q?=EB=82=A0=EC=A7=9C=20=ED=8F=AC=EB=A7=B7=ED=8C=85=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EC=9D=BC=EC=A0=95?= =?UTF-8?q?=20=ED=8E=98=EC=9D=B4=EC=A7=80=EC=97=90=EC=84=9C=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/src/pages/schedule/ui/SchedulePage.tsx | 5 +++-- apps/web/src/shared/lib/formatters.ts | 6 ++++++ .../widgets/schedule-calendar/ui/ScheduleCalendar.tsx | 10 +++++----- .../schedule-calendar/ui/ScheduleWeekCalendar.tsx | 10 +++++----- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/apps/web/src/pages/schedule/ui/SchedulePage.tsx b/apps/web/src/pages/schedule/ui/SchedulePage.tsx index bb8b89de..987ae83c 100644 --- a/apps/web/src/pages/schedule/ui/SchedulePage.tsx +++ b/apps/web/src/pages/schedule/ui/SchedulePage.tsx @@ -11,6 +11,7 @@ import { ScheduleFilterTab } from '@/widgets/schedule-filter-tab/ui'; import { ScheduleSectionLayout } from '@/widgets/schedule-section-layout/ui'; import { ScheduleListSkeleton } from '@/widgets/skeleton/ui'; +import { formatDate } from '@/shared/lib/formatters'; import { memberQueries } from '@/shared/queries/member'; import { scheduleQueries } from '@/shared/queries/schedule'; import { scrollContainer } from '@/shared/styles/container.css'; @@ -27,7 +28,7 @@ export function SchedulePage() { const handleDateChange = (date: Date) => { setSelectedDate(date); - setDate(dayjs(date).format('YYYY-MM-DD')); + setDate(formatDate(date, 'YYYY-MM-DD')); }; const { data: myInfoData } = useQuery(memberQueries.myInfoQuery()); @@ -42,7 +43,7 @@ export function SchedulePage() { const { data: scheduleCalendarList = [] } = useQuery({ ...scheduleQueries.getScheduleCalendarQuery(crewId, { - yearMonth: dayjs(selectedDate).format('YYYY-MM'), + yearMonth: formatDate(selectedDate, 'YYYY-MM'), }), enabled: crewId > 0, }); diff --git a/apps/web/src/shared/lib/formatters.ts b/apps/web/src/shared/lib/formatters.ts index 3e667126..e3f3d50d 100644 --- a/apps/web/src/shared/lib/formatters.ts +++ b/apps/web/src/shared/lib/formatters.ts @@ -1,3 +1,5 @@ +import dayjs from 'dayjs'; + const WEEKDAYS = ['일', '월', '화', '수', '목', '금', '토'] as const; export const formatOrderDate = (dateString: string | undefined) => { @@ -42,3 +44,7 @@ export const formatExpectedShippingDate = (dateStr: string) => { const weekday = date.toLocaleDateString('ko-KR', { weekday: 'short' }); return `${month}월 ${day}일 (${weekday}) 이내 판매자 발송 예정`; }; + +export const formatDate = (date: Date, format: string) => { + return dayjs(date).format(format); +}; diff --git a/apps/web/src/widgets/schedule-calendar/ui/ScheduleCalendar.tsx b/apps/web/src/widgets/schedule-calendar/ui/ScheduleCalendar.tsx index d539fced..49646653 100644 --- a/apps/web/src/widgets/schedule-calendar/ui/ScheduleCalendar.tsx +++ b/apps/web/src/widgets/schedule-calendar/ui/ScheduleCalendar.tsx @@ -5,6 +5,8 @@ import '@/widgets/schedule-calendar/style/ScheduleCalendarBase.css.ts'; import * as styles from '@/widgets/schedule-calendar/style/ScheduleCalendar.css.ts'; +import { formatDate } from '@/shared/lib/formatters'; + import type { ScheduleCalendarList } from '@/entities/schedule/model/schedule.types'; interface ScheduleCalendarProps { @@ -31,7 +33,7 @@ export function ScheduleCalendar({
- {dayjs(value).format('YYYY년 M월')} + {formatDate(value, 'YYYY년 M월')}