Files
hjjeong 8bdc9a958f feat(favorites): 사용자별 메뉴 즐겨찾기
사이드바 leaf 메뉴 옆 별표로 토글하고, 사이드바 최상단 '즐겨찾기'
섹션에 등록된 메뉴를 모아 보여준다. 테넌트 DB 별로 격리.

- V020 + StartupSchemaMigrator: USER_MENU_FAVORITES (USER_ID,
  MENU_OBJID UNIQUE) 메타·테넌트 DB 동기 멱등 생성
- 백엔드: GET/POST /api/favorites/menus, DELETE
  /api/favorites/menus/{menuObjid} — FavoritesController/Service
  + mapper/favorites.xml (MENU_INFO JOIN)
- 프론트: favoritesAPI 클라이언트 + FavoritesContext (낙관적
  toggle/롤백) + (main)/layout 에 Provider 마운트
- AppLayout: leaf/sub-item 옆 Star 토글 (등록 시 primary 컬러,
  미등록 시 0.35 dimmed) + 사이드바 최상단 favorites 섹션. uiMenus
  의 leaf 평탄화 결과를 isFavorite 으로 필터링해 권한 필터/메뉴
  클릭 핸들러를 그대로 재사용

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 11:29:18 +09:00

31 lines
1.0 KiB
TypeScript

import { apiClient } from "./client";
import { ApiResponse } from "@/types/commonCode";
/**
* 즐겨찾기 메뉴 API
*
* 백엔드 엔드포인트 ↔ DB-per-tenant 의 USER_MENU_FAVORITES 테이블.
* 응답 행은 MENU_INFO 와 JOIN 된 형태 (menu_name_kor / menu_url / menu_icon 등 포함).
*/
export const favoritesAPI = {
getList: async (): Promise<ApiResponse<Record<string, any>[]>> => {
const res = await apiClient.get<ApiResponse<Record<string, any>[]>>("/favorites/menus");
return res.data;
},
add: async (menuObjid: string | number): Promise<ApiResponse<Record<string, any>>> => {
const res = await apiClient.post<ApiResponse<Record<string, any>>>(
"/favorites/menus",
{ menu_objid: String(menuObjid) },
);
return res.data;
},
remove: async (menuObjid: string | number): Promise<ApiResponse<Record<string, any>>> => {
const res = await apiClient.delete<ApiResponse<Record<string, any>>>(
`/favorites/menus/${encodeURIComponent(String(menuObjid))}`,
);
return res.data;
},
};