Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
ff8198b
FE-Refactor: 예약정보 호출 시 아이템정보 populate 명령어 수정
AdamSeungheonShin Oct 30, 2024
fc7a4bf
FE-Merge: branch 'develop' into 119-fe-feat-회의실-관리-페이지-데이터-로딩
AdamSeungheonShin Nov 1, 2024
dae58fc
FE-Feat: dropdown, modal 컴포넌트로 분리
AdamSeungheonShin Nov 1, 2024
beb3c72
FE-Feat: 회의실 하위 아이템 컴포넌트 UI 구현
AdamSeungheonShin Nov 3, 2024
d4e0cda
FE-Feat: 아이템 생성 버튼 , 아코디언 펼치기용 caret 버튼 추가
AdamSeungheonShin Nov 3, 2024
6ed3604
FE-Feat: 회의실 추가 아이템 등록 드로워 추가
AdamSeungheonShin Nov 6, 2024
366660e
FE-Merge: branch 'develop' into 119-fe-feat-회의실-관리-페이지-데이터-로딩
AdamSeungheonShin Nov 10, 2024
b304cad
FE-Feat: 회의실 추가 폼 제작
AdamSeungheonShin Nov 10, 2024
26e31eb
FE-Feat: 사이드패널 listItem 뎁스로 이동, 추가/수정 시 동일한 폼 재사용할 수 있도록 버튼 click시 동작 수정
AdamSeungheonShin Nov 10, 2024
abccec1
FE-Feat: 사이드패널, 모달 안내 텍스트 조건부 렌더링 추가
AdamSeungheonShin Nov 10, 2024
49dfaef
FE-Feat: 아이템 등록/수정 폼 react-hook-form 적용
AdamSeungheonShin Nov 11, 2024
0c56ff7
FE-Feat: 아이템 정보 입력 폼 submit 함수 및 핸들러 추가
AdamSeungheonShin Nov 11, 2024
ab50a1f
FE-Refactor: 리스트 아이템 펼치기 애니메이션 개선
AdamSeungheonShin Nov 14, 2024
2fc1a3e
FE-Feat: 카테고리 별 아이템 데이터 페칭 부분 작성
AdamSeungheonShin Nov 18, 2024
c94af4f
FE-Feat: room 아이템 생성 api함수 작성
AdamSeungheonShin Nov 21, 2024
6f91768
FE-Feat: 카테고리 추가 side panel 기본 ui
AdamSeungheonShin Nov 21, 2024
86b8fba
FE-Feat: submit 핸들러 버튼 추가
AdamSeungheonShin Nov 22, 2024
830864b
FE-Feat: form input 기본값 설정 useEffect 추가
AdamSeungheonShin Nov 22, 2024
23ccf77
FE-Merge: merge branch 'develop' into 119-fe-feat-회의실-관리-페이지-데이터-로딩
AdamSeungheonShin Nov 28, 2024
0f4cfb8
FE-Feat: 사이드패널 버튼 하단으로 위치 수정
AdamSeungheonShin Nov 29, 2024
1e6c183
FE-Refactor: zustand 적용, 불필요한 상태, 사이드이펙트 제거, sidePanel 한개로 관리
AdamSeungheonShin Dec 1, 2024
e0df074
FE-Refactor: item 등록/수정 form 초기화 로직 추가
AdamSeungheonShin Dec 1, 2024
be51efc
Merge: merge branch 'develop' into 119-fe-feat-회의실-관리-페이지-데이터-로딩
AdamSeungheonShin Dec 1, 2024
6cddbb9
FE-Refactor: Room 수정/추가 form 오류 해결 밑 zustand활용 초기값 설정
AdamSeungheonShin Dec 2, 2024
86ebd6e
FE-Refactor: 함수명 중복, 함수명 수정
AdamSeungheonShin Dec 2, 2024
5e56199
FE-Refactor: categories, rooms 데이터 로딩부분에 react-query 적용
AdamSeungheonShin Dec 2, 2024
1cfc374
FE-Refactor: editItemForm의 post요청 mutate로 데이터 업데이트 구현
AdamSeungheonShin Dec 2, 2024
16a34d1
FE-Feat: 회의실 삭제 구현
AdamSeungheonShin Dec 2, 2024
b20b5fd
FE-Feat: 카테고리 수정/삭제 구현
AdamSeungheonShin Dec 2, 2024
289c27b
FE-Refactor: form 초기화 안되던 부분 reset, useEffect로 수정
AdamSeungheonShin Dec 2, 2024
7230f8b
FE-Refactor: 불필요한 컴포넌트 삭제
AdamSeungheonShin Dec 2, 2024
cd7e374
FE-Merge: merge branch 'develop' into 119-fe-feat-회의실-관리-페이지-데이터-로딩
AdamSeungheonShin Dec 26, 2024
31e142c
FE-Refactor: 변경된 toast 트리거 적용
AdamSeungheonShin Dec 26, 2024
a2df06c
FE-Refactor: 회의실 관리자 페이지 헤더 컴포넌트명 변경
AdamSeungheonShin Dec 26, 2024
d4559d5
FE-Refactor: 사이드패널 조건부 렌더링 최적화
AdamSeungheonShin Dec 26, 2024
1d23d73
FE-Refactor: 카테고리 수정 팝오버 prop naming 수정
AdamSeungheonShin Dec 26, 2024
098c8c4
FE-Refactor: 카테고리 추가 폼 에러처리 개선
AdamSeungheonShin Dec 26, 2024
4349fa2
FE-Refactor: 회의실 카테고리 목록 쿼리 에러 얼리리턴, 로딩상태 및 예외처리 개선
AdamSeungheonShin Dec 27, 2024
7080e60
FE-Refactor: 카테고리별 회의실 필터링 중복로직 삭제
AdamSeungheonShin Dec 27, 2024
a08eb38
FE-Refactor: 회의실 수정 사이드바 동작 개선, 불필요 동작 삭제 및 내부 데이터만 변경되도록 변경
AdamSeungheonShin Dec 27, 2024
6101cb7
FE-Fix: patchCategory 함수 반환 타입 수정, API와 불일치 해소
AdamSeungheonShin Dec 27, 2024
5b10aa5
FE-Refactor: EditItemForm과 Store의 api 요청 핸들러 함수 에러 처리 방식 개선, response…
AdamSeungheonShin Jan 10, 2025
96c025b
FE-Refactor: MeetingsStore의 handleDeleteItem핸들러에도 로딩상테 추가
AdamSeungheonShin Jan 10, 2025
dbcde82
FE-Refactor: 카테고리 수정 input 값 상태 초기화 수정, 카테고리명 수정 시 깜빡임 개선
AdamSeungheonShin Jan 10, 2025
c8f93b9
FE-Refactor: 카테고리명 수정 input 빈값 또는 공백 입력하는 경우 예외처처리
AdamSeungheonShin Jan 10, 2025
028c023
FE-Refactor: AddCategoryForm 에러처리 서버에서 받는 에러메세지로 수정
AdamSeungheonShin Jan 10, 2025
14b86ab
FE-Merge: merge branch 'develop' into 119-fe-feat-회의실-관리-페이지-데이터-로딩
AdamSeungheonShin Jan 10, 2025
af5a6b5
FE-Refactor: AddCategoryForm 불필요한 비동기처리 삭제, 쿼리 키 상수화
AdamSeungheonShin Jan 10, 2025
c83703e
FE-Refactor: 카테고리 추가 mutation 구조분해 할당으로 함수명 addCategory로 수정
AdamSeungheonShin Jan 10, 2025
3556e60
FE-Refactor: 에러 메세지 String으로 변환
AdamSeungheonShin Jan 10, 2025
36d99c1
FE-Refactor: 카테고리 수정, 삭제 mutation 함수명 구조분해할당으로 작성
AdamSeungheonShin Jan 10, 2025
9138564
FE-Refactor: CategoryListItem의 카테고리 수정/삭제 뮤테이션 함수 불필요한 비동기처리 삭제
AdamSeungheonShin Jan 10, 2025
3e14bc1
FE-Refactor: EditItemForm 불필요한 에러 Throw 삭제
AdamSeungheonShin Jan 10, 2025
39fecd6
FE-Refactor: 카테고리 선택 핸들러로 분리
AdamSeungheonShin Jan 10, 2025
73915f1
FE-Refactor: Form 컴포넌트에 불필요한 name 속성 삭제
AdamSeungheonShin Jan 10, 2025
d19f344
FE-Refactor: 엔드포인트 상수값에 누락된 파라미터 추가
AdamSeungheonShin Jan 10, 2025
b478133
FE-Refactor: 불필요한 비동기 삭제, mutation 함수명 구조분해 할당으로 작성
AdamSeungheonShin Jan 10, 2025
0a20509
FE-Fix: items, meetings 폴더 admin/rooms로 경로 수정
AdamSeungheonShin Jan 13, 2025
402c5a3
FE-Merge: merge branch 'develop' into 119-fe-feat-회의실-관리-페이지-데이터-로딩
AdamSeungheonShin Jan 13, 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
5 changes: 1 addition & 4 deletions apps/api/src/controllers/reservationControllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,7 @@ export const getUserReservations = async (
})
.populate("user", "name email")
.populate("attendees", "name email")
.populate({
path: "item",
select: "name itemType",
})
.populate("item", "name itemType")
.sort({ itemType: 1, startAt: 1 });

if (userReservations.length === 0) {
Expand Down
2 changes: 1 addition & 1 deletion apps/web/api/items.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export const createItem = async (params: CreateItemParams): Promise<TBaseItem> =
const { data } = await axiosRequester<TBaseItem>({
options: {
method: "POST",
url: API_ENDPOINTS.ITEMS.CREATE_ITEM,
url: API_ENDPOINTS.ITEMS.CREATE_ITEM(itemType),
data: {
itemType,
...itemData,
Expand Down
105 changes: 105 additions & 0 deletions apps/web/api/meetings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { API_ENDPOINTS } from "@repo/constants";
import { type IRoom, type ICategory, type TItemType, type IEquipment } from "@repo/types";
import { axiosRequester } from "@/lib/axios";

export const getAllCategories = async (): Promise<ICategory[]> => {
const { data } = await axiosRequester<ICategory[]>({
options: {
method: "GET",
url: API_ENDPOINTS.CATEGORIES.GET_ALL,
},
});
return data;
};

export const getAllRooms = async (): Promise<IRoom[]> => {
const { data } = await axiosRequester<IRoom[]>({
options: {
method: "GET",
url: API_ENDPOINTS.ITEMS.GET_ALL("room"),
},
});
return data;
};

export const postNewRoom = async (itemType: TItemType, body: Record<string, string>): Promise<IRoom | IEquipment> => {
const { data } = await axiosRequester<IRoom | IEquipment>({
options: {
method: "POST",
url: API_ENDPOINTS.ITEMS.CREATE_ITEM(itemType),
headers: {
"Content-Type": "application/json",
},
data: body,
},
});

return data;
};

export const patchRoom = async (itemId: string, body: Record<string, string>): Promise<IRoom | IEquipment> => {
const { data } = await axiosRequester<IRoom | IEquipment>({
options: {
method: "PATCH",
url: API_ENDPOINTS.ITEMS.UPDATE_ITEM(itemId),
headers: {
"Content-Type": "application/json",
},
data: body,
},
});

return data;
};

export const deleteRoom = async (itemId: string): Promise<string> => {
const { data } = await axiosRequester<string>({
options: {
method: "DELETE",
url: API_ENDPOINTS.ITEMS.DELETE_ITEM(itemId),
},
});

return data;
};

export const postNewCategory = async (body: Record<string, string>): Promise<ICategory> => {
const { data } = await axiosRequester<ICategory>({
options: {
method: "POST",
url: API_ENDPOINTS.CATEGORIES.CREATE_CATEGORY,
headers: {
"Content-Type": "application/json",
},
data: body,
},
});

return data;
};
Comment on lines +66 to +79
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

에러 처리 및 입력값 검증 추가 필요

카테고리 생성 시 입력값 검증과 구체적인 에러 처리가 필요합니다.

export const postNewCategory = async (body: Record<string, string>): Promise<ICategory> => {
+ if (!body.name) {
+   throw new Error('카테고리 이름은 필수입니다');
+ }

  const { data } = await axiosRequester<ICategory>({
    options: {
      method: "POST",
      url: API_ENDPOINTS.CATEGORIES.CREATE_CATEGORY,
-     headers: {
-       "Content-Type": "application/json",
-     },
      data: body,
    },
  });

+ if (!data._id || !data.name) {
+   throw new Error('서버 응답이 올바른 카테고리 형식이 아닙니다');
+ }

  return data;
};
📝 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
export const postNewCategory = async (body: Record<string, string>): Promise<ICategory> => {
const { data } = await axiosRequester<ICategory>({
options: {
method: "POST",
url: API_ENDPOINTS.CATEGORIES.CREATE_CATEGORY,
headers: {
"Content-Type": "application/json",
},
data: body,
},
});
return data;
};
export const postNewCategory = async (body: Record<string, string>): Promise<ICategory> => {
if (!body.name) {
throw new Error('카테고리 이름은 필수입니다');
}
const { data } = await axiosRequester<ICategory>({
options: {
method: "POST",
url: API_ENDPOINTS.CATEGORIES.CREATE_CATEGORY,
data: body,
},
});
if (!data._id || !data.name) {
throw new Error('서버 응답이 올바른 카테고리 형식이 아닙니다');
}
return data;
};


export const patchCategory = async (categoryId: string, body: Record<string, string>): Promise<ICategory> => {
const { data } = await axiosRequester<ICategory>({
options: {
method: "PATCH",
url: API_ENDPOINTS.CATEGORIES.UPDATE_CATEGORY(categoryId),
headers: {
"Content-Type": "application/json",
},
data: body,
},
});

return data;
};

export const deleteCategory = async (categoryId: string): Promise<string> => {
const { data } = await axiosRequester<string>({
options: {
method: "DELETE",
url: API_ENDPOINTS.CATEGORIES.DELETE_CATEGORY(categoryId),
},
});

return data;
};
67 changes: 67 additions & 0 deletions apps/web/app/(admin)/rooms/_components/AddCategoryForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
"use client";

import { useMutation, useQueryClient } from "@tanstack/react-query";
import { Button, Input } from "@ui/index";
import { useForm } from "react-hook-form";
import { AxiosError } from "axios";
import { postNewCategory } from "@/api/meetings";
import { notify } from "@/app/store/useToastStore";
import { QUERY_KEYS } from "@/lib/queryKey";

export default function AddCategoryForm(): JSX.Element {
const {
register,
handleSubmit,
reset,
formState: { errors, isSubmitting },
} = useForm({
defaultValues: {
name: "",
},
});
const queryClient = useQueryClient();

const { mutate: addCategory } = useMutation({
mutationFn: async (payload: Record<string, string>) => {
return await postNewCategory(payload);
},
onSuccess: () => {
notify("success", "카테고리가 추가되었습니다!");
void queryClient.invalidateQueries({ queryKey: QUERY_KEYS.CATEGORIES });
reset();
},
onError: (error) => {
if (error instanceof AxiosError && error.response) {
notify("error", String(error.response.data.message));
} else {
notify("error", "알 수 없는 오류가 발생했습니다. 다시 시도해주세요.");
}
},
});
Comment on lines +24 to +40
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

뮤테이션 에러 처리 개선 필요

에러 처리 로직이 너무 일반적입니다. 구체적인 에러 케이스(예: 중복된 카테고리명)에 대한 처리가 필요합니다.

다음과 같이 개선하는 것을 제안합니다:

 onError: (error) => {
   if (error instanceof AxiosError && error.response) {
-    notify("error", String(error.response.data.message));
+    switch (error.response.status) {
+      case 409:
+        notify("error", "이미 존재하는 카테고리입니다.");
+        break;
+      case 400:
+        notify("error", "잘못된 카테고리명입니다.");
+        break;
+      default:
+        notify("error", String(error.response.data.message));
+    }
   } else {
     notify("error", "알 수 없는 오류가 발생했습니다. 다시 시도해주세요.");
   }
 },
📝 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
const { mutate: addCategory } = useMutation({
mutationFn: async (payload: Record<string, string>) => {
return await postNewCategory(payload);
},
onSuccess: () => {
notify("success", "카테고리가 추가되었습니다!");
void queryClient.invalidateQueries({ queryKey: QUERY_KEYS.CATEGORIES });
reset();
},
onError: (error) => {
if (error instanceof AxiosError && error.response) {
notify("error", String(error.response.data.message));
} else {
notify("error", "알 수 없는 오류가 발생했습니다. 다시 시도해주세요.");
}
},
});
const { mutate: addCategory } = useMutation({
mutationFn: async (payload: Record<string, string>) => {
return await postNewCategory(payload);
},
onSuccess: () => {
notify("success", "카테고리가 추가되었습니다!");
void queryClient.invalidateQueries({ queryKey: QUERY_KEYS.CATEGORIES });
reset();
},
onError: (error) => {
if (error instanceof AxiosError && error.response) {
switch (error.response.status) {
case 409:
notify("error", "이미 존재하는 카테고리입니다.");
break;
case 400:
notify("error", "잘못된 카테고리명입니다.");
break;
default:
notify("error", String(error.response.data.message));
}
} else {
notify("error", "알 수 없는 오류가 발생했습니다. 다시 시도해주세요.");
}
},
});


const handleSubmitForm = handleSubmit((data) => {
const payload = {
...data,
itemType: "room",
};

addCategory(payload);
});

return (
<form onSubmit={handleSubmitForm} className="flex h-full flex-col justify-between">
<div>
<h1 className="my-24">카테고리 추가</h1>
<Input
placeholder="카테고리명"
{...register("name", { required: true })}
error={errors.name}
disabled={isSubmitting}
/>
Comment on lines +55 to +60
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

입력 필드 유효성 검사 강화 필요

현재 required 검사만 수행하고 있습니다. 카테고리명에 대한 추가적인 유효성 검사가 필요합니다.

다음과 같이 개선하는 것을 제안합니다:

 <Input
   placeholder="카테고리명"
-  {...register("name", { required: true })}
+  {...register("name", {
+    required: "카테고리명은 필수입니다",
+    minLength: { value: 2, message: "최소 2자 이상 입력해주세요" },
+    maxLength: { value: 20, message: "최대 20자까지 입력 가능합니다" },
+    pattern: {
+      value: /^[가-힣a-zA-Z0-9\s]+$/,
+      message: "특수문자는 사용할 수 없습니다"
+    }
+  })}
   error={errors.name}
   disabled={isSubmitting}
 />
📝 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
<Input
placeholder="카테고리명"
{...register("name", { required: true })}
error={errors.name}
disabled={isSubmitting}
/>
<Input
placeholder="카테고리명"
{...register("name", {
required: "카테고리명은 필수입니다",
minLength: { value: 2, message: "최소 2자 이상 입력해주세요" },
maxLength: { value: 20, message: "최대 20자까지 입력 가능합니다" },
pattern: {
value: /^[-a-zA-Z0-9\s]+$/,
message: "특수문자는 사용할 수 없습니다"
}
})}
error={errors.name}
disabled={isSubmitting}
/>

</div>
<Button variant="Action" type="submit" disabled={isSubmitting} isPending={isSubmitting}>
카테고리 추가
</Button>
</form>
);
}
19 changes: 19 additions & 0 deletions apps/web/app/(admin)/rooms/_components/AddItemButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"use client";

import { PlusIcon } from "@ui/public";

interface AddItemButtonProps {
onClick: () => void;
}

export default function AddItemButton({ onClick }: AddItemButtonProps): JSX.Element {
return (
<button
className="hover:bg-custom-black/5 flex size-32 cursor-pointer justify-center rounded-full transition-colors duration-300 ease-in-out"
type="button"
onClick={onClick}
>
<PlusIcon width={20} fill="true" />
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

PlusIcon 컴포넌트의 fill 속성 전달 방법 확인 필요

fill 속성에 문자열 "true"를 전달하고 있습니다. fill 속성이 불리언 값을 기대한다면, fill={true} 또는 fill만 전달하는 것이 좋습니다.

다음과 같이 수정하십시오:

-          <PlusIcon width={20} fill="true" />
+          <PlusIcon width={20} fill />
📝 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
<PlusIcon width={20} fill="true" />
<PlusIcon width={20} fill />

</button>
);
}
33 changes: 33 additions & 0 deletions apps/web/app/(admin)/rooms/_components/CategoryEditDropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Modal } from "@ui/index";
import Dropdown from "@ui/src/components/common/Dropdown";

interface CategoryEditDropdownProps {
isEditing?: boolean;
onClickEdit: () => void;
}

export default function CategoryEditDropdown({ isEditing, onClickEdit }: CategoryEditDropdownProps): JSX.Element {
return (
<Dropdown
selectedValue={isEditing}
onSelect={(value: string | boolean) => {
if (value === "수정") {
onClickEdit();
}
}}
Comment on lines +13 to +17
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

타입 안전성 개선 필요

onSelect 콜백의 매개변수 타입이 너무 광범위합니다. 가능한 값을 명시적으로 정의하면 좋을 것 같습니다.

+ type DropdownAction = "수정" | "삭제";

  onSelect={(value: string | boolean) => {
-   if (value === "수정") {
+   if (value === "수정" as DropdownAction) {
      onClickEdit();
    }
  }}

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

size="sm"
>
<Dropdown.Toggle iconType="kebab" />
<Dropdown.Wrapper className="-left-30 top-56">
<Dropdown.Item hoverStyle="purple" value="수정">
수정
</Dropdown.Item>
<Modal.Trigger>
<Dropdown.Item hoverStyle="purple" value="삭제">
삭제
</Dropdown.Item>
</Modal.Trigger>
Comment on lines +20 to +29
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

접근성 및 키보드 네비게이션 개선 필요

드롭다운 메뉴의 접근성이 부족합니다. 키보드 네비게이션과 ARIA 속성을 추가하면 좋을 것 같습니다.

- <Dropdown.Toggle iconType="kebab" />
+ <Dropdown.Toggle 
+   iconType="kebab"
+   aria-label="카테고리 수정 메뉴"
+   aria-haspopup="true"
+ />
  <Dropdown.Wrapper className="-left-30 top-56">
-   <Dropdown.Item hoverStyle="purple" value="수정">
+   <Dropdown.Item 
+     hoverStyle="purple" 
+     value="수정"
+     role="menuitem"
+     tabIndex={0}
+   >
      수정
    </Dropdown.Item>
    <Modal.Trigger>
-     <Dropdown.Item hoverStyle="purple" value="삭제">
+     <Dropdown.Item 
+       hoverStyle="purple" 
+       value="삭제"
+       role="menuitem"
+       tabIndex={0}
+     >
        삭제
      </Dropdown.Item>
    </Modal.Trigger>
  </Dropdown.Wrapper>
📝 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
<Dropdown.Toggle iconType="kebab" />
<Dropdown.Wrapper className="-left-30 top-56">
<Dropdown.Item hoverStyle="purple" value="수정">
수정
</Dropdown.Item>
<Modal.Trigger>
<Dropdown.Item hoverStyle="purple" value="삭제">
삭제
</Dropdown.Item>
</Modal.Trigger>
<Dropdown.Toggle
iconType="kebab"
aria-label="카테고리 수정 메뉴"
aria-haspopup="true"
/>
<Dropdown.Wrapper className="-left-30 top-56">
<Dropdown.Item
hoverStyle="purple"
value="수정"
role="menuitem"
tabIndex={0}
>
수정
</Dropdown.Item>
<Modal.Trigger>
<Dropdown.Item
hoverStyle="purple"
value="삭제"
role="menuitem"
tabIndex={0}
>
삭제
</Dropdown.Item>
</Modal.Trigger>
</Dropdown.Wrapper>

</Dropdown.Wrapper>
</Dropdown>
);
}
62 changes: 59 additions & 3 deletions apps/web/app/(admin)/rooms/_components/CategoryList.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,65 @@
"use client";

import { useEffect } from "react";
import { useQuery } from "@tanstack/react-query";
import EmptyState from "@ui/src/components/common/EmptyState";
import { getAllCategories, getAllRooms } from "@/api/meetings";
import LoadingBar from "@/components/common/Skeleton/LoadingBar";
import { notify } from "@/app/store/useToastStore";
import useMeetingsStore from "../_store/useMeetingsStore";
import SidePanel from "./SidePanel";
import CategoryListItem from "./CategoryListItem";

export default function CategoryList(): JSX.Element {
const { categories, setCategories, rooms, setRooms } = useMeetingsStore();

const {
data: fetchedCategories,
isLoading: isCategoriesLoading,
error: categoriesError,
} = useQuery({
queryKey: ["categories"],
queryFn: getAllCategories,
});
Comment on lines +21 to +23
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

쿼리 키 상수화 필요

쿼리 키가 문자열 리터럴로 하드코딩되어 있습니다.

QUERY_KEYS 상수를 사용하도록 변경해주세요:

- queryKey: ["categories"],
+ queryKey: QUERY_KEYS.CATEGORIES,

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

const {
data: fetchedRooms,
isLoading: isRoomsLoading,
error: roomsError,
} = useQuery({ queryKey: ["rooms"], queryFn: getAllRooms });

useEffect(() => {
if (fetchedCategories) {
const roomCategories = fetchedCategories.filter((category) => category.itemType === "room");
setCategories(roomCategories);
}
}, [fetchedCategories, setCategories]);

useEffect(() => {
if (fetchedRooms) {
setRooms(fetchedRooms);
}
}, [fetchedRooms, setRooms]);

if (categoriesError ?? roomsError) {
notify("error", "데이터를 불러오는데 실패했습니다.");
return <div>데이터를 불러오는데 실패했습니다.</div>;
}

const categoriesWithRooms = categories.map((category) => ({
...category,
rooms: rooms.filter((room) => room.category._id === category._id),
}));

return (
<div>
<CategoryListItem />
</div>
<>
{isCategoriesLoading || (isRoomsLoading && <LoadingBar classNames="w-full h-72" />)}
{!isCategoriesLoading && categories.length === 0 && (
<EmptyState message={{ title: "", description: "등록된 카테고리가 없습니다." }} />
)}
{categoriesWithRooms.map((category) => {
return <CategoryListItem key={category._id} category={category} rooms={category.rooms} />;
})}
<SidePanel />
</>
);
}
Loading
Loading