[#44] 매장 기능 도입 #45

Merged
pricelees merged 116 commits from feat/#44 into main 2025-09-20 03:15:06 +00:00
6 changed files with 34 additions and 26 deletions
Showing only changes of commit 163b7991d3 - Show all commits

View File

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

View File

@ -8,28 +8,28 @@ import type {
ScheduleUpdateRequest
} from './scheduleTypes';
export const fetchAvailableThemesByDate = async (date: string): Promise<AvailableThemeIdListResponse> => {
return await apiClient.get<AvailableThemeIdListResponse>(`/schedules/themes?date=${date}`);
export const fetchStoreAvailableThemesByDate = async (storeId: string, date: string): Promise<AvailableThemeIdListResponse> => {
return await apiClient.get<AvailableThemeIdListResponse>(`/stores/${storeId}/themes?date=${date}`);
};
export const fetchSchedulesByDateAndTheme = async (storeId: number, date: string, themeId: string): Promise<ScheduleRetrieveListResponse> => {
return await apiClient.get<ScheduleRetrieveListResponse>(`/schedules?storeId=${storeId}&date=${date}&themeId=${themeId}`);
export const fetchStoreSchedulesByDateAndTheme = async (storeId: string, date: string, themeId: string): Promise<ScheduleRetrieveListResponse> => {
return await apiClient.get<ScheduleRetrieveListResponse>(`/stores/${storeId}/schedules?date=${date}&themeId=${themeId}`);
};
export const fetchScheduleById = async (id: string): Promise<ScheduleDetailRetrieveResponse> => {
return await apiClient.get<ScheduleDetailRetrieveResponse>(`/schedules/${id}`);
}
export const fetchScheduleDetailById = async (id: string): Promise<ScheduleDetailRetrieveResponse> => {
return await apiClient.get<ScheduleDetailRetrieveResponse>(`/admin/schedules/${id}`);
};
export const createSchedule = async (request: ScheduleCreateRequest): Promise<ScheduleCreateResponse> => {
return await apiClient.post<ScheduleCreateResponse>('/schedules', request);
export const createSchedule = async (storeId: string, request: ScheduleCreateRequest): Promise<ScheduleCreateResponse> => {
return await apiClient.post<ScheduleCreateResponse>(`/admin/stores/${storeId}/schedules`, request);
};
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> => {
await apiClient.del(`/schedules/${id}`);
await apiClient.del(`/admin/schedules/${id}`);
};
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';
export interface SimpleStoreResponse {
id: number;
id: string;
name: string;
}
export interface StoreDetailResponse {
id: number;
id: string;
name: string;
address: string;
contact: string;

View File

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

View File

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

View File

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