diff --git a/src/components/common/Tab/FolderTab.tsx b/src/components/common/Tab/FolderTab.tsx
index 9c2754b..779ba5a 100644
--- a/src/components/common/Tab/FolderTab.tsx
+++ b/src/components/common/Tab/FolderTab.tsx
@@ -1,23 +1,37 @@
import React from 'react';
+import BaseBadge from '../Tag/BaseBadge';
type FolderTabProps = {
- children: React.ReactNode;
+ TabTitle: React.ReactNode;
+ TabAlarm?: number;
isActive?: boolean;
className?: string;
onClick?: () => void;
};
-const FolderTab = ({ children, isActive = false, className="", onClick }: FolderTabProps) => {
+const FolderTab = ({
+ TabTitle,
+ TabAlarm,
+ isActive = false,
+ className = '',
+ onClick,
+}: FolderTabProps) => {
+ const badge =
+ TabAlarm && TabAlarm > 0 ? (
+ {TabAlarm > 99 ? '99+' : TabAlarm}
+ ) : null;
+
return (
- {children}
+
{TabTitle}
+ {badge}
);
};
diff --git a/src/components/common/Tab/FolderTabGroup.tsx b/src/components/common/Tab/FolderTabGroup.tsx
index d11ca42..da60e9f 100644
--- a/src/components/common/Tab/FolderTabGroup.tsx
+++ b/src/components/common/Tab/FolderTabGroup.tsx
@@ -4,6 +4,7 @@ import FolderTab from "./FolderTab";
interface TabItem {
id: string | number;
label: React.ReactNode;
+ alarm?: number;
className?: string;
}
@@ -32,9 +33,9 @@ export const FolderTabGroup: React.FC = ({
isActive={idx === activeIndex}
onClick={() => handleClick(idx)}
className={`flex-1 basis-0 cursor-pointer ${tab.className ?? ""}`}
- >
- {tab.label}
-
+ TabTitle={tab.label}
+ TabAlarm={tab.alarm}
+ />
))}
);
diff --git a/src/constants/ProfilePostColumns.tsx b/src/constants/ProfilePostColumns.tsx
index 041b909..828806d 100644
--- a/src/constants/ProfilePostColumns.tsx
+++ b/src/constants/ProfilePostColumns.tsx
@@ -14,7 +14,6 @@ import EditIc from '../assets/icons/profile/ic_edit.svg?react';
import TrashIc from '../assets/icons/profile/ic_trashcan.svg?react';
import BaseTag from '../components/common/Tag/BaseTag';
import ArrowIc from '../assets/icons/ic_arrow_down_large.svg?react';
-import BaseBadge from '../components/common/Tag/BaseBadge.tsx';
export type PostRow = {
id: string;
diff --git a/src/hooks/useApplicants.ts b/src/hooks/useApplicants.ts
index 5a3c8a3..c9cff5e 100644
--- a/src/hooks/useApplicants.ts
+++ b/src/hooks/useApplicants.ts
@@ -4,12 +4,15 @@ import {
approveApplicant,
rejectApplicant,
UserProfile,
+ getMyApply,
+ postApply,
} from '../api/applicants';
+import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
export function useApplicants(projectId?: number) {
const [list, setList] = useState([]);
const [loading, setLoading] = useState(false);
- const [error, setError] = useState(null);
+ const [error, setError] = useState(null);
const fetch = useCallback(async () => {
if (projectId == null) return;
@@ -25,29 +28,63 @@ export function useApplicants(projectId?: number) {
}
}, [projectId]);
- useEffect(() => { fetch(); }, [fetch]);
+ useEffect(() => {
+ fetch();
+ }, [fetch]);
- const approve = useCallback(async (userId: string) => {
- if (projectId == null) return;
- setList(prev => prev.filter(u => u.id !== userId));
- try {
- await approveApplicant(projectId, userId);
- } finally {
- fetch();
- }
- }, [projectId, fetch]);
+ const approve = useCallback(
+ async (userId: string) => {
+ if (projectId == null) return;
+ setList((prev) => prev.filter((u) => u.id !== userId));
+ try {
+ await approveApplicant(projectId, userId);
+ } finally {
+ fetch();
+ }
+ },
+ [projectId, fetch],
+ );
- const reject = useCallback(async (userId: string) => {
- if (projectId == null) return;
- setList(prev => prev.filter(u => u.id !== userId));
- try {
- await rejectApplicant(projectId, userId);
- } finally {
- fetch();
- }
- }, [projectId, fetch]);
+ const reject = useCallback(
+ async (userId: string) => {
+ if (projectId == null) return;
+ setList((prev) => prev.filter((u) => u.id !== userId));
+ try {
+ await rejectApplicant(projectId, userId);
+ } finally {
+ fetch();
+ }
+ },
+ [projectId, fetch],
+ );
return { applicants: list, loading, error, refetch: fetch, approve, reject };
}
export default useApplicants;
+
+// 프로젝트 지원
+
+export const usePostApply = (projectId: number) => {
+ const queryClient = useQueryClient();
+ return useMutation({
+ mutationFn: (position: string) => postApply(projectId, position),
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ['postApply', projectId] });
+ },
+ onError: (error) => {
+ console.error('Error Apply:', error);
+ },
+ });
+};
+
+// 내가 지원한 프로젝트 조회
+
+export const useGetMyApply = (status?: string) => {
+ return useQuery({
+ queryKey: ['getMyApply', status],
+ queryFn: () => getMyApply(status),
+ staleTime: 1000 * 60 * 5, // 5분 동안 캐시 유지
+ refetchOnWindowFocus: false, // 윈도우 포커스 시 재요청하지 않음
+ });
+};
diff --git a/src/pages/profile/ProfilePosts.tsx b/src/pages/profile/ProfilePosts.tsx
index 9839829..b476b25 100644
--- a/src/pages/profile/ProfilePosts.tsx
+++ b/src/pages/profile/ProfilePosts.tsx
@@ -127,7 +127,8 @@ export default function ProfilePosts() {
[data, sortKey, searchText],
);
- const [baselines, setBaselines] = useState>(loadBaselines());
+ const [baselines, setBaselines] =
+ useState>(loadBaselines());
const [totals, setTotals] = useState>({
postManagement: 0,
applicantManagement: 0,
@@ -143,7 +144,9 @@ export default function ProfilePosts() {
useEffect(() => {
let alive = true;
(async () => {
- const ids = rows.map((r) => Number(r.id)).filter((n) => Number.isFinite(n));
+ const ids = rows
+ .map((r) => Number(r.id))
+ .filter((n) => Number.isFinite(n));
if (ids.length === 0) {
if (!alive) return;
setTotals((prev) => ({ ...prev, memberManagement: 0 }));
@@ -177,9 +180,18 @@ export default function ProfilePosts() {
}, [postsTotal, applicantsTotal]);
const badgeCounts = {
- postManagement: Math.max(0, totals.postManagement - baselines.postManagement),
- applicantManagement: Math.max(0, totals.applicantManagement - baselines.applicantManagement),
- memberManagement: Math.max(0, totals.memberManagement - baselines.memberManagement),
+ postManagement: Math.max(
+ 0,
+ totals.postManagement - baselines.postManagement,
+ ),
+ applicantManagement: Math.max(
+ 0,
+ totals.applicantManagement - baselines.applicantManagement,
+ ),
+ memberManagement: Math.max(
+ 0,
+ totals.memberManagement - baselines.memberManagement,
+ ),
};
useEffect(() => {
@@ -193,18 +205,10 @@ export default function ProfilePosts() {
const tabsWithBadges = useMemo(() => {
return profilePostTabs.map((t) => {
const key = t.key as TabKey;
- const n = badgeCounts[key];
- const badge =
- n > 0 ? {n > 99 ? '99+' : n} : null;
-
return {
...t,
- label: (
-
- {t.label}
- {badge}
-
- ),
+ label: t.label,
+ alarm: badgeCounts[key],
};
});
}, [badgeCounts]);
@@ -237,7 +241,8 @@ export default function ProfilePosts() {
({
id: t.key,
- label: t.label,
+ label: <>{t.label}>,
+ alarm: badgeCounts[t.key],
}))}
activeIndex={tabsWithBadges.findIndex((t) => t.key === activeKey)}
onTabChange={(idx) => setActiveKey(tabsWithBadges[idx].key as TabKey)}