refactor: 일정 관련 프론트엔드 페이지 및 기능 구현

This commit is contained in:
이상진 2025-09-15 16:06:14 +09:00
parent cdf7a98867
commit 163b7991d3
6 changed files with 34 additions and 26 deletions

View File

@ -28,7 +28,7 @@ export interface UserLoginSuccessResponse extends LoginSuccessResponse {
export interface AdminLoginSuccessResponse extends LoginSuccessResponse { export interface AdminLoginSuccessResponse extends LoginSuccessResponse {
type: AdminType; type: AdminType;
storeId: number | null; storeId: string | null;
} }
export interface CurrentUserContext { export interface CurrentUserContext {

View File

@ -8,28 +8,28 @@ import type {
ScheduleUpdateRequest ScheduleUpdateRequest
} from './scheduleTypes'; } from './scheduleTypes';
export const fetchAvailableThemesByDate = async (date: string): Promise<AvailableThemeIdListResponse> => { export const fetchStoreAvailableThemesByDate = async (storeId: string, date: string): Promise<AvailableThemeIdListResponse> => {
return await apiClient.get<AvailableThemeIdListResponse>(`/schedules/themes?date=${date}`); return await apiClient.get<AvailableThemeIdListResponse>(`/stores/${storeId}/themes?date=${date}`);
}; };
export const fetchSchedulesByDateAndTheme = async (storeId: number, date: string, themeId: string): Promise<ScheduleRetrieveListResponse> => { export const fetchStoreSchedulesByDateAndTheme = async (storeId: string, date: string, themeId: string): Promise<ScheduleRetrieveListResponse> => {
return await apiClient.get<ScheduleRetrieveListResponse>(`/schedules?storeId=${storeId}&date=${date}&themeId=${themeId}`); return await apiClient.get<ScheduleRetrieveListResponse>(`/stores/${storeId}/schedules?date=${date}&themeId=${themeId}`);
}; };
export const fetchScheduleById = async (id: string): Promise<ScheduleDetailRetrieveResponse> => { export const fetchScheduleDetailById = async (id: string): Promise<ScheduleDetailRetrieveResponse> => {
return await apiClient.get<ScheduleDetailRetrieveResponse>(`/schedules/${id}`); return await apiClient.get<ScheduleDetailRetrieveResponse>(`/admin/schedules/${id}`);
} };
export const createSchedule = async (request: ScheduleCreateRequest): Promise<ScheduleCreateResponse> => { export const createSchedule = async (storeId: string, request: ScheduleCreateRequest): Promise<ScheduleCreateResponse> => {
return await apiClient.post<ScheduleCreateResponse>('/schedules', request); return await apiClient.post<ScheduleCreateResponse>(`/admin/stores/${storeId}/schedules`, request);
}; };
export const updateSchedule = async (id: string, request: ScheduleUpdateRequest): Promise<void> => { export const updateSchedule = async (id: string, request: ScheduleUpdateRequest): Promise<void> => {
await apiClient.patch(`/schedules/${id}`, request); await apiClient.patch(`/admin/schedules/${id}`, request);
}; };
export const deleteSchedule = async (id: string): Promise<void> => { export const deleteSchedule = async (id: string): Promise<void> => {
await apiClient.del(`/schedules/${id}`); await apiClient.del(`/admin/schedules/${id}`);
}; };
export const holdSchedule = async (id: string): Promise<void> => { export const holdSchedule = async (id: string): Promise<void> => {

View File

@ -2,12 +2,12 @@ import { type AuditInfo } from '@_api/common/commonTypes';
import type { RegionInfoResponse } from '@_api/region/regionTypes'; import type { RegionInfoResponse } from '@_api/region/regionTypes';
export interface SimpleStoreResponse { export interface SimpleStoreResponse {
id: number; id: string;
name: string; name: string;
} }
export interface StoreDetailResponse { export interface StoreDetailResponse {
id: number; id: string;
name: string; name: string;
address: string; address: string;
contact: string; contact: string;

View File

@ -10,7 +10,7 @@ interface AdminAuthContextType {
isAdmin: boolean; isAdmin: boolean;
name: string | null; name: string | null;
type: AdminType | null; type: AdminType | null;
storeId: number | null; storeId: string | null;
loading: boolean; loading: boolean;
login: (data: Omit<LoginRequest, 'principalType'>) => Promise<AdminLoginSuccessResponse>; login: (data: Omit<LoginRequest, 'principalType'>) => Promise<AdminLoginSuccessResponse>;
logout: () => Promise<void>; logout: () => Promise<void>;
@ -22,7 +22,7 @@ export const AdminAuthProvider: React.FC<{ children: ReactNode }> = ({ children
const [isAdmin, setIsAdmin] = useState(false); const [isAdmin, setIsAdmin] = useState(false);
const [name, setName] = useState<string | null>(null); const [name, setName] = useState<string | null>(null);
const [type, setType] = useState<AdminType | null>(null); const [type, setType] = useState<AdminType | null>(null);
const [storeId, setStoreId] = useState<number | null>(null); const [storeId, setStoreId] = useState<string | null>(null);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
useEffect(() => { useEffect(() => {
@ -36,7 +36,7 @@ export const AdminAuthProvider: React.FC<{ children: ReactNode }> = ({ children
setIsAdmin(true); setIsAdmin(true);
setName(storedName); setName(storedName);
setType(storedType); setType(storedType);
setStoreId(storedStoreId ? parseInt(storedStoreId, 10) : null); setStoreId(storedStoreId ? storedStoreId : null);
} }
} catch (error) { } catch (error) {
console.error("Failed to load admin auth state from storage", error); console.error("Failed to load admin auth state from storage", error);

View File

@ -1,5 +1,5 @@
import {isLoginRequiredError} from '@_api/apiClient'; import {isLoginRequiredError} from '@_api/apiClient';
import {fetchAvailableThemesByDate, fetchSchedulesByDateAndTheme, holdSchedule} from '@_api/schedule/scheduleAPI'; import {fetchStoreAvailableThemesByDate, fetchStoreSchedulesByDateAndTheme, holdSchedule} from '@_api/schedule/scheduleAPI';
import {type ScheduleRetrieveResponse, ScheduleStatus} from '@_api/schedule/scheduleTypes'; import {type ScheduleRetrieveResponse, ScheduleStatus} from '@_api/schedule/scheduleTypes';
import {fetchThemesByIds} from '@_api/theme/themeAPI'; import {fetchThemesByIds} from '@_api/theme/themeAPI';
import {mapThemeResponse, type ThemeInfoResponse} from '@_api/theme/themeTypes'; import {mapThemeResponse, type ThemeInfoResponse} from '@_api/theme/themeTypes';
@ -35,7 +35,7 @@ const ReservationStep1Page: React.FC = () => {
useEffect(() => { useEffect(() => {
if (selectedDate) { if (selectedDate) {
const dateStr = selectedDate.toLocaleDateString('en-CA'); // yyyy-mm-dd const dateStr = selectedDate.toLocaleDateString('en-CA'); // yyyy-mm-dd
fetchAvailableThemesByDate(dateStr) fetchStoreAvailableThemesByDate(dateStr)
.then(res => { .then(res => {
console.log('Available themes response:', res); console.log('Available themes response:', res);
const themeIds: string[] = res.themeIds; const themeIds: string[] = res.themeIds;
@ -69,7 +69,7 @@ const ReservationStep1Page: React.FC = () => {
useEffect(() => { useEffect(() => {
if (selectedDate && selectedTheme) { if (selectedDate && selectedTheme) {
const dateStr = selectedDate.toLocaleDateString('en-CA'); const dateStr = selectedDate.toLocaleDateString('en-CA');
fetchSchedulesByDateAndTheme(dateStr, selectedTheme.id) fetchStoreSchedulesByDateAndTheme(dateStr, selectedTheme.id)
.then(res => { .then(res => {
setSchedules(res.schedules); setSchedules(res.schedules);
setSelectedSchedule(null); setSelectedSchedule(null);

View File

@ -2,8 +2,8 @@ import { isLoginRequiredError } from '@_api/apiClient';
import { import {
createSchedule, createSchedule,
deleteSchedule, deleteSchedule,
fetchScheduleById, fetchScheduleDetailById,
fetchSchedulesByDateAndTheme, fetchStoreSchedulesByDateAndTheme,
updateSchedule updateSchedule
} from '@_api/schedule/scheduleAPI'; } from '@_api/schedule/scheduleAPI';
import { import {
@ -108,7 +108,7 @@ const AdminSchedulePage: React.FC = () => {
const fetchSchedules = () => { const fetchSchedules = () => {
const storeId = adminType === 'HQ' ? selectedStoreId : adminStoreId; const storeId = adminType === 'HQ' ? selectedStoreId : adminStoreId;
if (storeId && selectedDate && selectedThemeId) { if (storeId && selectedDate && selectedThemeId) {
fetchSchedulesByDateAndTheme(Number(storeId), selectedDate, selectedThemeId) fetchStoreSchedulesByDateAndTheme(storeId, selectedDate, selectedThemeId)
.then(res => setSchedules(res.schedules)) .then(res => setSchedules(res.schedules))
.catch(err => { .catch(err => {
setSchedules([]); setSchedules([]);
@ -147,8 +147,16 @@ const AdminSchedulePage: React.FC = () => {
alert('시간 형식이 올바르지 않습니다. HH:MM 형식으로 입력해주세요.'); alert('시간 형식이 올바르지 않습니다. HH:MM 형식으로 입력해주세요.');
return; return;
} }
if (adminType !== 'STORE' || !adminStoreId) {
alert('매장 관리자만 일정을 추가할 수 있습니다.');
return;
}
if (!selectedDate || !selectedThemeId) {
alert('날짜와 테마를 선택해주세요.');
return;
}
try { try {
await createSchedule({ await createSchedule(adminStoreId, {
date: selectedDate, date: selectedDate,
themeId: selectedThemeId, themeId: selectedThemeId,
time: newScheduleTime, time: newScheduleTime,
@ -183,7 +191,7 @@ const AdminSchedulePage: React.FC = () => {
if (!detailedSchedules[scheduleId]) { if (!detailedSchedules[scheduleId]) {
setIsLoadingDetails(true); setIsLoadingDetails(true);
try { try {
const details = await fetchScheduleById(scheduleId); const details = await fetchScheduleDetailById(scheduleId);
setDetailedSchedules(prev => ({ ...prev, [scheduleId]: details })); setDetailedSchedules(prev => ({ ...prev, [scheduleId]: details }));
} catch (error) { } catch (error) {
handleError(error); handleError(error);
@ -222,7 +230,7 @@ const AdminSchedulePage: React.FC = () => {
status: editingSchedule.status, status: editingSchedule.status,
}); });
// Refresh data // Refresh data
const details = await fetchScheduleById(editingSchedule.id); const details = await fetchScheduleDetailById(editingSchedule.id);
setDetailedSchedules(prev => ({ ...prev, [editingSchedule.id]: details })); setDetailedSchedules(prev => ({ ...prev, [editingSchedule.id]: details }));
setSchedules(schedules.map(s => s.id === editingSchedule.id ? { ...s, time: details.time, status: details.status } : s)); setSchedules(schedules.map(s => s.id === editingSchedule.id ? { ...s, time: details.time, status: details.status } : s));