diff --git a/apps/client/src/shared/apis/axios.ts b/apps/client/src/shared/apis/axios.ts index efec01f4..b2f8b402 100644 --- a/apps/client/src/shared/apis/axios.ts +++ b/apps/client/src/shared/apis/axios.ts @@ -7,16 +7,22 @@ export const getDashboardCategories = async () => { return data.data; }; -export const postCategory = async (categoryName: string) => { - const response = await apiRequest.post('/api/v1/categories', { +export const postCategory = async (categoryName: string, isPublic: boolean) => { + const response = await apiRequest.post('/api/v3/categories', { categoryName, + isPublic, }); return response; }; -export const putCategory = async (id: number, categoryName: string) => { - const response = await apiRequest.put(`/api/v1/categories/${id}`, { +export const patchCategory = async ( + id: number, + categoryName: string, + isPublic: boolean +) => { + const response = await apiRequest.patch(`/api/v3/categories/${id}`, { categoryName, + isPublic, }); return response; }; @@ -97,3 +103,8 @@ export const patchUserJob = async (requestData: patchUserJobRequest) => { const { data } = await apiRequest.patch('/api/v3/users/job', requestData); return data; }; + +export const getCategoryDetail = async (categoryId: number) => { + const { data } = await apiRequest.get(`/api/v3/categories/${categoryId}`); + return data.data; +}; diff --git a/apps/client/src/shared/apis/queries.ts b/apps/client/src/shared/apis/queries.ts index 47481dfa..316d822f 100644 --- a/apps/client/src/shared/apis/queries.ts +++ b/apps/client/src/shared/apis/queries.ts @@ -3,23 +3,25 @@ import { deleteRemindArticle, getAcorns, getArticleDetail, + getCategoryDetail, getDashboardCategories, getGoogleProfile, getJobs, getMyProfile, + patchCategory, patchUserJob, patchUserJobRequest, postCategory, postSignUp, postSignUpRequest, putArticleReadStatus, - putCategory, putEditArticle, } from '@shared/apis/axios'; import { AcornsResponse, ArticleDetailResponse, ArticleReadStatusResponse, + CategoryDetailResponse, DashboardCategoriesResponse, EditArticleRequest, JobsResponse, @@ -47,13 +49,26 @@ export const useGetDashboardCategories = (): UseQueryResult< export const usePostCategory = () => { return useMutation({ - mutationFn: (categoryName: string) => postCategory(categoryName), + mutationFn: ({ + categoryName, + isPublic, + }: { + categoryName: string; + isPublic: boolean; + }) => postCategory(categoryName, isPublic), }); }; -export const usePutCategory = () => { +export const usePatchCategory = () => { return useMutation({ - mutationFn: ({ id, categoryName }: { id: number; categoryName: string }) => - putCategory(id, categoryName), + mutationFn: ({ + id, + categoryName, + isPublic, + }: { + id: number; + categoryName: string; + isPublic: boolean; + }) => patchCategory(id, categoryName, isPublic), }); }; @@ -203,3 +218,13 @@ export const usePatchUserJob = () => { mutationFn: (data: patchUserJobRequest) => patchUserJob(data), }); }; + +export const useGetCategoryDetail = (): UseMutationResult< + CategoryDetailResponse, + AxiosError, + number +> => { + return useMutation({ + mutationFn: (categoryId: number) => getCategoryDetail(categoryId), + }); +}; diff --git a/apps/client/src/shared/components/sidebar/OptionsMenuPortal.tsx b/apps/client/src/shared/components/sidebar/OptionsMenuPortal.tsx index a76cd63b..d2ec82aa 100644 --- a/apps/client/src/shared/components/sidebar/OptionsMenuPortal.tsx +++ b/apps/client/src/shared/components/sidebar/OptionsMenuPortal.tsx @@ -1,13 +1,17 @@ -import { createPortal } from 'react-dom'; import OptionsMenuButton from '@shared/components/optionsMenuButton/OptionsMenuButton'; +import { createPortal } from 'react-dom'; interface OptionsMenuPortalProps { open: boolean; style?: React.CSSProperties | null; containerRef: React.RefObject; categoryId: number | null; - getCategoryName: (id: number | null) => string; - onEdit: (id: number, name: string) => void; + getCategoryName?: (id: number | null) => string; + getCategory?: (id: number | null) => { + id: number; + name: string; + } | null; + onEdit: (id: number) => void; onDelete: (id: number, name: string) => void; onClose: () => void; } @@ -18,24 +22,46 @@ export default function OptionsMenuPortal({ containerRef, categoryId, getCategoryName, + getCategory, onEdit, onDelete, onClose, }: OptionsMenuPortalProps) { if (!open || !style) return null; - const id = categoryId; - const name = getCategoryName(categoryId); + let id: number | null = categoryId; + let name = ''; + + if (getCategory) { + const category = getCategory(categoryId); + + if (!category) return null; + + id = category.id; + name = category.name; + } else if (getCategoryName) { + name = getCategoryName(categoryId); + } return createPortal( -
+
{ - if (id != null) onEdit(id, name); + if (id != null) { + onEdit(id); + } onClose(); }} onDelete={() => { - if (id != null) onDelete(id, name); + if (id != null) { + onDelete(id, name); + } onClose(); }} /> diff --git a/apps/client/src/shared/components/sidebar/PopupPortal.tsx b/apps/client/src/shared/components/sidebar/PopupPortal.tsx index 0ea36715..5744adfb 100644 --- a/apps/client/src/shared/components/sidebar/PopupPortal.tsx +++ b/apps/client/src/shared/components/sidebar/PopupPortal.tsx @@ -8,8 +8,14 @@ interface Props { popup: PopupState | null; onClose: () => void; onChange?: (value: string) => void; - onCreateConfirm?: () => void; - onEditConfirm?: (id: number, draft?: string) => void; + + onCreateConfirm?: (shareToJobUsers: boolean) => void; + onEditConfirm?: ( + id: number, + draft?: string, + shareToJobUsers?: boolean + ) => void; + onDeleteConfirm?: (id: number) => void; categoryList?: { id: number; name: string }[]; isToastOpen?: boolean; @@ -35,9 +41,14 @@ export default function PopupPortal({ }: Props) { const [draft, setDraft] = useState(''); + const [shareToJobUsers, setShareToJobUsers] = useState(true); + useEffect(() => { if (!popup) return; + setDraft(popup.kind === 'edit' ? (popup.name ?? '') : ''); + + setShareToJobUsers(popup.kind === 'edit' ? popup.isPublic : false); }, [popup]); if (!popup) return null; @@ -78,12 +89,12 @@ export default function PopupPortal({ const handleCreate = () => { if (blocked) return; - onCreateConfirm?.(); + onCreateConfirm?.(shareToJobUsers); }; const handleEdit = () => { if (blocked || popup.kind !== 'edit') return; - onEditConfirm?.(popup.id, value); + onEditConfirm?.(popup.id, value, shareToJobUsers); }; const handleDelete = () => { @@ -95,6 +106,8 @@ export default function PopupPortal({ const actionLabel = action === 'create' ? '추가' : action === 'edit' ? '수정' : '삭제'; + const showCheckbox = popup.kind === 'create' || popup.kind === 'edit'; + return createPortal(
@@ -112,6 +125,15 @@ export default function PopupPortal({ placeholder="카테고리 제목을 입력해주세요" onLeftClick={onClose} onRightClick={handleCreate} + checkboxOption={ + showCheckbox + ? { + label: '같은 관심 직무 사용자들에게 공유하기', + isSelected: shareToJobUsers, + onSelectedChange: setShareToJobUsers, + } + : undefined + } /> )} @@ -127,6 +149,15 @@ export default function PopupPortal({ onInputChange={handleInputChange} onLeftClick={onClose} onRightClick={handleEdit} + checkboxOption={ + showCheckbox + ? { + label: '같은 관심 직무 사용자들에게 공유하기', + isSelected: shareToJobUsers, + onSelectedChange: setShareToJobUsers, + } + : undefined + } /> )} diff --git a/apps/client/src/shared/components/sidebar/Sidebar.tsx b/apps/client/src/shared/components/sidebar/Sidebar.tsx index 70de0def..d9477824 100644 --- a/apps/client/src/shared/components/sidebar/Sidebar.tsx +++ b/apps/client/src/shared/components/sidebar/Sidebar.tsx @@ -76,6 +76,7 @@ export function Sidebar() { handlePatchCategory, handleDeleteCategory, handlePopupClose, + handleEditCategory, } = useCategoryActions({ close, setActiveTab, @@ -117,8 +118,15 @@ export function Sidebar() { prevAcornRef.current = acornCount; }, [acornCount, isAcornPending]); - const getCategoryName = (id: number | null) => - categories?.categories.find((c) => c.id === id)?.name ?? ''; + const getCategory = (id: number | null) => { + const c = categories?.categories.find((c) => c.id === id) ?? null; + if (!c) return null; + + return { + id: c.id, + name: c.name, + }; + }; return (
)} - {/* type===default일 떄는 아무것도 없음 */}