Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
2227752
Feat: reservation api 경로 변경 및 API ENDPOINTS 적용
dudwns0213 Nov 14, 2024
6035d2d
Chore: reservation api rename(복수)
dudwns0213 Nov 14, 2024
b59b319
Feat: items api 구현
dudwns0213 Nov 14, 2024
ba36929
Feat: meeting main에서 회의실, 해당 날짜 예약 현황 useQuery 작성
dudwns0213 Nov 14, 2024
a965e5e
Feat: ScheduleTable api 연동
dudwns0213 Nov 14, 2024
edec6c4
Feat: ScheduleTableDesktop api 연동
dudwns0213 Nov 14, 2024
b699d6a
Feat: ScheduleTableMobile api 연동
dudwns0213 Nov 14, 2024
5d172a9
Feat: ScheduleRow api 연동 및 time api에 맞게 수정
dudwns0213 Nov 14, 2024
89256a2
Feat: Tooltip props children으로 변경
dudwns0213 Nov 14, 2024
5455e74
Feat: ScheduleItem type 수정(api에 맞게)
dudwns0213 Nov 14, 2024
ff0a603
Feat: DesktopReservationSheet interface 수정(api에 맞게)
dudwns0213 Nov 14, 2024
bcd7153
Feat: MobileReservationSheet interface 수정(api에 맞게)
dudwns0213 Nov 14, 2024
a31a9a5
Feat: ReservationForm interface 수정 (api에 맞게)
dudwns0213 Nov 14, 2024
5b13d7d
Chore: 날짜 문자열 처리를 위한 date-fns install
dudwns0213 Nov 14, 2024
68b7509
Feat: parseISO 적용
dudwns0213 Nov 14, 2024
f4fe529
Feat: 현재 userId와 schedule의 userId 비교하여 표현(css 및 사이드바)
dudwns0213 Nov 14, 2024
91538f8
Feat: 예약되어 있는 회의 클릭 시 내용 표시
dudwns0213 Nov 14, 2024
36d19c4
Chore: query data, loading 이름 변경
dudwns0213 Nov 14, 2024
7ca617e
Fix: lint error(import 순서)
dudwns0213 Nov 14, 2024
148782a
Feat: scheduletype 수정 및 room _id props로 전달
dudwns0213 Nov 18, 2024
7415987
Feat: ReservationForm rooms query로 데이터 연결
dudwns0213 Nov 18, 2024
bd4251d
Feat: getAllUser API 함수 구현
dudwns0213 Nov 18, 2024
a06463d
Feat: getAllUser query로 데이터 연동
dudwns0213 Nov 18, 2024
9adf451
Feat: 전체 사용자 Dropdown 선택 가능 기능 구현
dudwns0213 Nov 18, 2024
8741df0
Fix: Dropdown storybook state type error
dudwns0213 Nov 18, 2024
1a61069
Feat: MultiSelectDropdown wrapper className props 추가
dudwns0213 Nov 18, 2024
2212335
Style: Profile className 추가(전역으로 h-auto 설정되는 문제 해결을 위해)
dudwns0213 Nov 18, 2024
5e9b927
Feat: response에 맞게 데이터 조정 및 불필요한 코드 삭제
dudwns0213 Nov 18, 2024
1bff89b
Feat: createReservationMutation 구현 및 Form을 통해 예약되는 기능 구현
dudwns0213 Nov 18, 2024
922819e
Chore: createReservation interface 및 data 수정
dudwns0213 Nov 18, 2024
ccacd92
Remove: Modal notify remove(mutation에 존재하므로 삭제)
dudwns0213 Nov 18, 2024
63c159a
Fix: 09:00 이후의 시간대만 표시되는 문제 해결(parseISO에서 Date 사용)
dudwns0213 Nov 18, 2024
04bf146
Feat: 시작 시간에서 30분 뒤의 종료시간 자동 설정, error 처리
dudwns0213 Nov 18, 2024
50ecef9
Feat: ReservationForm 제출 데이터 state type 설정
dudwns0213 Nov 19, 2024
eae0d8d
Feat: 예약 시 이미 예약된 스케줄을 침범하는 경우 error 표시
dudwns0213 Nov 19, 2024
8c0ea9a
Feat: ScheduleItem 대신 Slot에서 예약된 스케줄도 표시하도록 변경
dudwns0213 Nov 19, 2024
d43dfec
Feat: 예약 내용을 수정할updateReservation 함수 생성
dudwns0213 Nov 19, 2024
c5e6928
Feat: 예약된 스케줄을 클릭할 경우 버튼을 수정하기로 표시
dudwns0213 Nov 19, 2024
5e530ca
Fix: reservationId 전달되지 않던 error 수정
dudwns0213 Nov 19, 2024
63ef483
Feat: deleteReservation 구현
dudwns0213 Nov 21, 2024
d40cc8d
Feat: ReservationModal title, content 받을 수 있게 수정
dudwns0213 Nov 21, 2024
e017100
Feat: ReservationSheetContent 컴포넌트 생성으로 인한 로직 분리
dudwns0213 Nov 21, 2024
51302b2
Feat: invalidateQueries를 통한 데이터 최신화(생성, 수정, 삭제 시)
dudwns0213 Nov 21, 2024
baa7c99
Feat: ReservationForm 삭제하기 버튼 및 기능 구현
dudwns0213 Nov 21, 2024
7acf60f
Test: 예약 시간들 text로 확인해보기(임시)
dudwns0213 Nov 21, 2024
fe70af3
Fix: type 및 자동 수정
dudwns0213 Nov 21, 2024
f34e28e
Feat: useRoomsData hooks 구현
dudwns0213 Nov 21, 2024
3a13add
Feat: useMeetingsData hook 구현
dudwns0213 Nov 21, 2024
f40963b
Feat: meetingroomstype 상수화 및 formatDate 유틸함수 생성
dudwns0213 Nov 21, 2024
8e1d62f
Refactor: MeetingRoomSchedule 리팩토링
dudwns0213 Nov 21, 2024
470e286
Remove: query hook delete
dudwns0213 Nov 25, 2024
68f7525
Feat: getRoomSchedules 생성 및 컴포넌트 적용
dudwns0213 Nov 25, 2024
50de238
Feat: RoomName children 수정
dudwns0213 Nov 25, 2024
4c49e9a
Feat: CurrentTimeLabel 컴포넌트 리팩토링
dudwns0213 Nov 25, 2024
231f0d6
Feat: useSlotReservations hook 구현
dudwns0213 Nov 25, 2024
c3fe489
Feat: timeToMinutes util 함수 생성
dudwns0213 Nov 25, 2024
0a93601
Refactor: ScheduleRow 컴포넌트 리팩토링
dudwns0213 Nov 25, 2024
fdb399a
Refactor: ReservationSheetContent 리팩토링
dudwns0213 Nov 25, 2024
66e93ae
Feat: reservationForm 내부 컴포넌트 리팩토링
dudwns0213 Nov 25, 2024
46827bc
Feat: Form Type, constants, 유틸 함수 생성
dudwns0213 Nov 25, 2024
40141c4
Refactor: Form, sheet 리팩토링
dudwns0213 Nov 25, 2024
6cd3733
Fix: 시간 파싱 에러로 인한 getRoomSchedule 삭제
dudwns0213 Nov 25, 2024
607ef16
Fix: eslint error 해결
dudwns0213 Nov 25, 2024
2b8e892
Chore: pnpm install
dudwns0213 Nov 25, 2024
e07c405
Merge: branch 'develop' into 146-feat-회의실-예약-api-연동
dudwns0213 Nov 25, 2024
e2e7d37
Fix: build error 해결(import)
dudwns0213 Nov 25, 2024
039ce1c
Feat: TimeText skeleton 제작
dudwns0213 Nov 28, 2024
47c3efb
Feat: RoomName skeleton 제작
dudwns0213 Nov 28, 2024
5c113e8
Feat: Slot Skeleton 제작 및 디자인에 맞게 구현
dudwns0213 Nov 28, 2024
2ffd91e
Feat: skeleton 적용
dudwns0213 Nov 28, 2024
740b31f
Fix: eslint error
dudwns0213 Nov 28, 2024
35b4ee8
Fix: Mobile Date 수정
dudwns0213 Nov 28, 2024
3fbdd59
Style: TimeIndicator 수정
dudwns0213 Nov 28, 2024
8f8e3c4
Fix: 영준pr 작업_ merge conflict해결
bokeeeey Jan 1, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 79 additions & 1 deletion apps/web/api/items.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,31 @@
import { API_ENDPOINTS } from "@repo/constants";
import { type ISeat } from "@repo/types";
import {
type TBaseItem,
type TItemType,
type IRoom,
type ISeat,
type IEquipment,
type MessageResponse,
} from "@repo/types";
import { axiosRequester } from "@/lib/axios";

// 특정 타입의 아이템 조회
interface GetAllItemsParams {
itemType: TItemType;
}

export const getAllItems = async (params: GetAllItemsParams): Promise<TBaseItem[]> => {
const { itemType } = params;
const { data } = await axiosRequester<TBaseItem[]>({
options: {
method: "GET",
url: API_ENDPOINTS.ITEMS.GET_ALL(itemType),
},
});

return data;
};

/**
* 모든 좌석 데이터를 가져옵니다.
* @returns 모든 좌석 데이터를 포함하는 Promise.
Expand All @@ -17,6 +41,59 @@ export const getAllSeats = async (): Promise<ISeat[]> => {
return data;
};

// 아이템 생성
interface CreateItemParams {
itemType: TItemType;
itemData: Partial<IRoom | ISeat | IEquipment>;
}

export const createItem = async (params: CreateItemParams): Promise<TBaseItem> => {
const { itemType, itemData } = params;
const { data } = await axiosRequester<TBaseItem>({
options: {
method: "POST",
url: API_ENDPOINTS.ITEMS.CREATE_ITEM,
data: {
itemType,
...itemData,
},
},
});

return data;
};

// 아이템 업데이트
interface UpdateItemParams {
itemId: string;
itemData: Partial<IRoom | ISeat | IEquipment>;
}

export const updateItem = async (params: UpdateItemParams): Promise<TBaseItem> => {
const { itemId, itemData } = params;
const { data } = await axiosRequester<TBaseItem>({
options: {
method: "PATCH",
url: API_ENDPOINTS.ITEMS.UPDATE_ITEM(itemId),
data: itemData,
},
});

return data;
};

// 아이템 삭제
export const deleteItem = async (itemId: string): Promise<MessageResponse> => {
const { data } = await axiosRequester<MessageResponse>({
options: {
method: "DELETE",
url: API_ENDPOINTS.ITEMS.DELETE_ITEM(itemId),
},
});

return data;
};

/**
* 특정 아이템 데이터를 수정합니다.
* @returns 수정 결과 메시지를 포함하는 Promise.
Expand All @@ -29,5 +106,6 @@ export const patchItem = async (itemId: string, formData: FormData): Promise<str
data: formData,
},
});

return data;
};
117 changes: 117 additions & 0 deletions apps/web/api/reservations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { API_ENDPOINTS } from "@repo/constants";
import { type IReservation, type TReservationStatus } from "@repo/types/src/reservationType";
import { axiosRequester } from "@/lib/axios";

// 특정 유저의 오늘 날짜 예약 전체 조회
interface GetUserReservationsParams {
userId: string;
}

export const getUserReservations = async (params: GetUserReservationsParams): Promise<IReservation[]> => {
const { userId } = params;
const { data } = await axiosRequester<IReservation[]>({
options: {
method: "GET",
url: API_ENDPOINTS.RESERVATION.GET_USER_RESERVATIONS(userId),
},
});

return data;
};

// 아이템 타입 및 날짜에 대한 예약 조회
interface GetReservationsByTypeAndDateParams {
itemType: "room" | "seat" | "equipment";
date: string;
status?: TReservationStatus;
}

export const getReservationsByTypeAndDate = async (
params: GetReservationsByTypeAndDateParams,
): Promise<IReservation[]> => {
const { itemType, date, status } = params;
const { data } = await axiosRequester<IReservation[]>({
options: {
method: "GET",
url: API_ENDPOINTS.RESERVATION.GET_RESERVATIONS_BY_TYPE_AND_DATE(itemType, date),
params: {
status,
},
},
});

return data;
};

// 예약 생성
export interface CreateReservationParams {
itemId: string;
savedReservation: IReservation;
}

export interface CreateReservationRequest {
userId: string;
itemType: "room";
startAt: string;
endAt: string;
status: "reserved";
notes: string;
attendees: string[];
}
Comment on lines +52 to +60
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

itemTypestatus의 하드코딩된 값 검토 필요

CreateReservationRequest 인터페이스에서 itemType"room"으로, status"reserved"로 고정되어 있습니다. 예약 생성 시 다른 아이템 타입이나 상태를 지원해야 한다면 해당 필드를 유연하게 변경하는 것이 좋습니다.

다음과 같이 수정할 것을 제안합니다:

 export interface CreateReservationRequest {
   userId: string;
-  itemType: "room";
+  itemType: "room" | "seat" | "equipment";
   startAt: string;
   endAt: string;
-  status: "reserved";
+  status: TReservationStatus;
   notes: string;
   attendees: string[];
 }

Committable suggestion skipped: line range outside the PR's diff.


export interface CreateReservationResponse {
message: string;
savedReservation: IReservation;
}

export const createReservation = async (
itemId: string,
reservationData: CreateReservationRequest,
): Promise<IReservation> => {
const { data } = await axiosRequester<CreateReservationResponse>({
options: {
method: "POST",
url: API_ENDPOINTS.RESERVATION.CREATE_RESERVATION(itemId),
data: reservationData,
},
});

return data.savedReservation;
};
Comment on lines +71 to +80
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

에러 핸들링 추가 필요

createReservation 함수에서 API 호출 실패 시 에러에 대한 처리가 이루어지지 않고 있습니다. 안정적인 서비스 제공을 위해 예외 상황에 대한 처리를 추가하는 것이 좋습니다.

다음과 같이 코드에 에러 핸들링을 추가할 수 있습니다:

 export const createReservation = async (
   itemId: string,
   reservationData: CreateReservationRequest,
 ): Promise<IReservation> => {
-  const { data } = await axiosRequester<CreateReservationResponse>({
-    options: {
-      method: "POST",
-      url: API_ENDPOINTS.RESERVATION.CREATE_RESERVATION(itemId),
-      data: reservationData,
-    },
-  });
-
-  return data.savedReservation;
+  try {
+    const { data } = await axiosRequester<CreateReservationResponse>({
+      options: {
+        method: "POST",
+        url: API_ENDPOINTS.RESERVATION.CREATE_RESERVATION(itemId),
+        data: reservationData,
+      },
+    });
+
+    return data.savedReservation;
+  } catch (error) {
+    // 에러 처리 로직 추가
+    throw error;
+  }
 };

Committable suggestion skipped: line range outside the PR's diff.


export interface UpdateReservationRequest {
startAt?: string;
endAt?: string;
status?: TReservationStatus;
notes?: string;
attendees?: string[];
}

export interface UpdateReservationResponse {
message: string;
updatedReservation: IReservation;
}

export const updateReservation = async (
reservationId: string,
reservationData: UpdateReservationRequest,
): Promise<IReservation> => {
const { data } = await axiosRequester<UpdateReservationResponse>({
options: {
method: "PATCH",
url: API_ENDPOINTS.RESERVATION.UPDATE_RESERVATION(reservationId),
data: reservationData,
},
});

return data.updatedReservation;
};
Comment on lines +95 to +108
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

updateReservation 함수의 에러 핸들링 필요

updateReservation 함수에서도 API 요청 실패 시 에러 처리가 없습니다. 예외 상황에 대비하여 에러 핸들링을 추가하는 것이 바람직합니다.

다음과 같이 수정할 수 있습니다:

 export const updateReservation = async (
   reservationId: string,
   reservationData: UpdateReservationRequest,
 ): Promise<IReservation> => {
-  const { data } = await axiosRequester<UpdateReservationResponse>({
-    options: {
-      method: "PATCH",
-      url: API_ENDPOINTS.RESERVATION.UPDATE_RESERVATION(reservationId),
-      data: reservationData,
-    },
-  });
-
-  return data.updatedReservation;
+  try {
+    const { data } = await axiosRequester<UpdateReservationResponse>({
+      options: {
+        method: "PATCH",
+        url: API_ENDPOINTS.RESERVATION.UPDATE_RESERVATION(reservationId),
+        data: reservationData,
+      },
+    });
+
+    return data.updatedReservation;
+  } catch (error) {
+    // 에러 처리 로직 추가
+    throw error;
+  }
 };

Committable suggestion skipped: line range outside the PR's diff.


export const deleteReservation = async (reservationId: string): Promise<void> => {
await axiosRequester({
options: {
method: "DELETE",
url: API_ENDPOINTS.RESERVATION.DELETE_RESERVATION(reservationId),
},
});
};
Comment on lines +110 to +117
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

deleteReservation 함수의 에러 핸들링 필요

deleteReservation 함수에서 API 호출 실패 시 에러 처리가 없습니다. 요청 실패 시 적절한 대응을 위해 에러 핸들링을 추가하는 것이 좋습니다.

에러 처리를 추가하려면 다음과 같이 수정할 수 있습니다:

 export const deleteReservation = async (reservationId: string): Promise<void> => {
-  await axiosRequester({
-    options: {
-      method: "DELETE",
-      url: API_ENDPOINTS.RESERVATION.DELETE_RESERVATION(reservationId),
-    },
-  });
+  try {
+    await axiosRequester({
+      options: {
+        method: "DELETE",
+        url: API_ENDPOINTS.RESERVATION.DELETE_RESERVATION(reservationId),
+      },
+    });
+  } catch (error) {
+    // 에러 처리 로직 추가
+    throw error;
+  }
 };

Committable suggestion skipped: line range outside the PR's diff.

54 changes: 54 additions & 0 deletions apps/web/app/_hooks/useCurrentTimePosition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { useEffect, useState } from "react";

interface UseCurrentTimePositionProps {
slotWidth: number;
startHour: number;
endHour: number;
}
Comment on lines +3 to +7
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

props보다는 prams로 이름짓는건 어떨까?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

헉 그렇네 hook이였네 이걸 어케봤담


interface UseCurrentTimePositionReturn {
currentPosition: number | null;
currentTime: string;
}
Comment on lines +9 to +12
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return type 같은데 return 보다는 result로 이름짓는건 어떨까?


export function useCurrentTimePosition({
slotWidth,
startHour,
endHour,
}: UseCurrentTimePositionProps): UseCurrentTimePositionReturn {
const [currentPosition, setCurrentPosition] = useState<number | null>(null);
const [currentTime, setCurrentTime] = useState<string>("");

useEffect(() => {
const updatePosition = (): void => {
const now = new Date();
const currentMinutes = now.getHours() * 60 + now.getMinutes();
const totalMinutes = (endHour - startHour) * 60;

if (currentMinutes < startHour * 60 || currentMinutes >= endHour * 60) {
setCurrentPosition(null);
setCurrentTime("");
return;
}

const relativeMinutes = currentMinutes - startHour * 60;
const scheduleWidth = slotWidth * (endHour - startHour) * 2; // Assuming 30-minute slots
const position = (relativeMinutes / totalMinutes) * scheduleWidth;

setCurrentPosition(position);

const hours = String(now.getHours()).padStart(2, "0");
const minutes = String(now.getMinutes()).padStart(2, "0");
setCurrentTime(`${hours}:${minutes}`);
};

updatePosition();
const interval = setInterval(updatePosition, 60000);

return () => {
clearInterval(interval);
};
}, [slotWidth, startHour, endHour]);
Comment on lines +22 to +51
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

useEffect 구현에 대한 몇 가지 개선사항이 있습니다.

  1. 매직 넘버를 상수로 추출하는 것이 좋습니다.
  2. 시간 계산 로직이 복잡하여 별도 함수로 분리가 필요합니다.
  3. 타임존 처리가 명시적이지 않습니다.

다음과 같은 개선을 제안합니다:

+const MINUTE_IN_MS = 60000;
+const MINUTES_PER_HOUR = 60;
+
+const calculateTimePosition = (
+  now: Date,
+  startHour: number,
+  endHour: number,
+  slotWidth: number,
+): number | null => {
+  const currentMinutes = now.getHours() * MINUTES_PER_HOUR + now.getMinutes();
+  const totalMinutes = (endHour - startHour) * MINUTES_PER_HOUR;
+
+  if (currentMinutes < startHour * MINUTES_PER_HOUR || currentMinutes >= endHour * MINUTES_PER_HOUR) {
+    return null;
+  }
+
+  const relativeMinutes = currentMinutes - startHour * MINUTES_PER_HOUR;
+  const scheduleWidth = slotWidth * (endHour - startHour) * 2;
+  return (relativeMinutes / totalMinutes) * scheduleWidth;
+};
+
+const formatTime = (date: Date): string => {
+  const hours = String(date.getHours()).padStart(2, "0");
+  const minutes = String(date.getMinutes()).padStart(2, "0");
+  return `${hours}:${minutes}`;
+};

 useEffect(() => {
   const updatePosition = (): void => {
     const now = new Date();
-    const currentMinutes = now.getHours() * 60 + now.getMinutes();
-    const totalMinutes = (endHour - startHour) * 60;
-
-    if (currentMinutes < startHour * 60 || currentMinutes >= endHour * 60) {
-      setCurrentPosition(null);
-      setCurrentTime("");
-      return;
-    }
-
-    const relativeMinutes = currentMinutes - startHour * 60;
-    const scheduleWidth = slotWidth * (endHour - startHour) * 2;
-    const position = (relativeMinutes / totalMinutes) * scheduleWidth;
-
+    const position = calculateTimePosition(now, startHour, endHour, slotWidth);
     setCurrentPosition(position);
-
-    const hours = String(now.getHours()).padStart(2, "0");
-    const minutes = String(now.getMinutes()).padStart(2, "0");
-    setCurrentTime(`${hours}:${minutes}`);
+    setCurrentTime(position === null ? "" : formatTime(now));
   };

   updatePosition();
-  const interval = setInterval(updatePosition, 60000);
+  const interval = setInterval(updatePosition, MINUTE_IN_MS);

   return () => {
     clearInterval(interval);
   };
 }, [slotWidth, startHour, endHour]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useEffect(() => {
const updatePosition = (): void => {
const now = new Date();
const currentMinutes = now.getHours() * 60 + now.getMinutes();
const totalMinutes = (endHour - startHour) * 60;
if (currentMinutes < startHour * 60 || currentMinutes >= endHour * 60) {
setCurrentPosition(null);
setCurrentTime("");
return;
}
const relativeMinutes = currentMinutes - startHour * 60;
const scheduleWidth = slotWidth * (endHour - startHour) * 2; // Assuming 30-minute slots
const position = (relativeMinutes / totalMinutes) * scheduleWidth;
setCurrentPosition(position);
const hours = String(now.getHours()).padStart(2, "0");
const minutes = String(now.getMinutes()).padStart(2, "0");
setCurrentTime(`${hours}:${minutes}`);
};
updatePosition();
const interval = setInterval(updatePosition, 60000);
return () => {
clearInterval(interval);
};
}, [slotWidth, startHour, endHour]);
const MINUTE_IN_MS = 60000;
const MINUTES_PER_HOUR = 60;
const calculateTimePosition = (
now: Date,
startHour: number,
endHour: number,
slotWidth: number,
): number | null => {
const currentMinutes = now.getHours() * MINUTES_PER_HOUR + now.getMinutes();
const totalMinutes = (endHour - startHour) * MINUTES_PER_HOUR;
if (currentMinutes < startHour * MINUTES_PER_HOUR || currentMinutes >= endHour * MINUTES_PER_HOUR) {
return null;
}
const relativeMinutes = currentMinutes - startHour * MINUTES_PER_HOUR;
const scheduleWidth = slotWidth * (endHour - startHour) * 2;
return (relativeMinutes / totalMinutes) * scheduleWidth;
};
const formatTime = (date: Date): string => {
const hours = String(date.getHours()).padStart(2, "0");
const minutes = String(date.getMinutes()).padStart(2, "0");
return `${hours}:${minutes}`;
};
useEffect(() => {
const updatePosition = (): void => {
const now = new Date();
const position = calculateTimePosition(now, startHour, endHour, slotWidth);
setCurrentPosition(position);
setCurrentTime(position === null ? "" : formatTime(now));
};
updatePosition();
const interval = setInterval(updatePosition, MINUTE_IN_MS);
return () => {
clearInterval(interval);
};
}, [slotWidth, startHour, endHour]);


return { currentPosition, currentTime };
}
40 changes: 40 additions & 0 deletions apps/web/app/_hooks/useSlotReservations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { useMemo } from "react";
import { type IReservation } from "@repo/types";
import { timeToMinutes } from "../utils/timeToMinutes";

/**
* 예약 데이터를 슬롯 인덱스에 매핑하는 커스텀 훅
* @param schedules 예약 데이터 배열
* @param startHour 시작 시간
* @param endHour 종료 시간
* @param minutesPerSlot 슬롯당 분 단위
* @returns 슬롯별 예약 상태 배열
*/
export const useSlotReservations = (
schedules: IReservation[],
startHour: number,
endHour: number,
minutesPerSlot: number,
): (IReservation | null)[] => {
Comment on lines +14 to +19
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

입력값 유효성 검사 추가 필요

startHour, endHour, minutesPerSlot에 대한 유효성 검사가 없습니다. 잘못된 입력값으로 인한 예기치 않은 동작을 방지하기 위해 검증 로직이 필요합니다.

다음과 같은 검증 로직 추가를 제안합니다:

 export const useSlotReservations = (
   schedules: IReservation[],
   startHour: number,
   endHour: number,
   minutesPerSlot: number,
 ): (IReservation | null)[] => {
+  if (startHour >= endHour) {
+    throw new Error('시작 시간은 종료 시간보다 작아야 합니다.');
+  }
+  if (minutesPerSlot <= 0 || minutesPerSlot > 60) {
+    throw new Error('슬롯당 분은 1-60 사이여야 합니다.');
+  }
   return useMemo(() => {

return useMemo(() => {
const totalSlots = (endHour - startHour) * 2; // 30분 단위
const slotReservations: (IReservation | null)[] = Array(totalSlots).fill(null);

schedules.forEach((schedule) => {
const startMinutes = timeToMinutes(schedule.startAt) - startHour * 60;
const endMinutes = timeToMinutes(schedule.endAt) - startHour * 60;

const startIndex = Math.floor(startMinutes / minutesPerSlot);
const endIndex = Math.ceil(endMinutes / minutesPerSlot);

for (let i = startIndex; i < endIndex; i++) {
if (i >= 0 && i < totalSlots) {
slotReservations[i] = schedule;
}
}
});

return slotReservations;
}, [schedules, startHour, endHour, minutesPerSlot]);
};
65 changes: 0 additions & 65 deletions apps/web/app/api/reservation.ts

This file was deleted.

1 change: 1 addition & 0 deletions apps/web/app/constants/meetingRoomsType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const MEETING_ROOMS_TYPE = "room";
Loading
Loading