diff --git a/src/api/activityLog.api.ts b/src/api/activityLog.api.ts index 3a2498b0..b16adef5 100644 --- a/src/api/activityLog.api.ts +++ b/src/api/activityLog.api.ts @@ -1,4 +1,3 @@ -import { ApiAllInquiries } from '../models/admin/mainPreview'; import type { ApiMyComments, ApiMyInquiries } from './../models/activityLog'; import { httpClient } from './http.api'; diff --git a/src/api/admin/customerService/FAQ.api.ts b/src/api/admin/customerService/FAQ.api.ts index 85bf8995..09e008da 100644 --- a/src/api/admin/customerService/FAQ.api.ts +++ b/src/api/admin/customerService/FAQ.api.ts @@ -1,4 +1,4 @@ -import { ApiCommonBasicType } from '../../../models/apiCommon'; +import type { ApiCommonBasicType } from '../../../models/apiCommon'; import type { ApiFAQDetail, WriteBody } from '../../../models/customerService'; import { httpClient } from '../../http.api'; diff --git a/src/api/admin/user.api.ts b/src/api/admin/user.api.ts new file mode 100644 index 00000000..20bbb081 --- /dev/null +++ b/src/api/admin/user.api.ts @@ -0,0 +1,48 @@ +import type { ApiUserApplicantsData } from '../../models/admin/userDetail/userDetail.applicants'; +import type { ApiUserProjectDataResponse } from '../../models/admin/userDetail/userProjectData'; +import { httpClient } from '../http.api'; + +export const postBanUser = async (id: string) => { + try { + await httpClient.post(`/ban/${id}`); + } catch (e) { + console.error(e); + throw e; + } +}; + +export const postWarningUser = async (id: string) => { + try { + await httpClient.post(`/warning/${id}`); + } catch (e) { + console.error(e); + throw e; + } +}; + +export const getUserApplicants = async ( + projectId: number, + applicantId: number +) => { + try { + const response = await httpClient.get( + `/admin/project/${projectId}/full?applicantId=${applicantId}` + ); + return response.data; + } catch (e) { + console.error(e); + throw e; + } +}; + +export const getUserProjectData = async (userId: number) => { + try { + const response = await httpClient.get( + `/users/${userId}/projects` + ); + return response.data.data.appliedProjects; + } catch (e) { + console.error(e); + throw e; + } +}; diff --git a/src/api/admin/userActivity.api.ts b/src/api/admin/userActivity.api.ts new file mode 100644 index 00000000..465207e0 --- /dev/null +++ b/src/api/admin/userActivity.api.ts @@ -0,0 +1,14 @@ +import { httpClient } from '../http.api'; +import type { ApiUserActivityResponse } from '../../models/admin/userDetail/userActivity'; + +export const getUserActivityData = async (userId: number) => { + try { + const response = await httpClient.get( + `/users/${userId}/activities` + ); + return response.data; + } catch (e) { + console.error(e); + throw e; + } +}; diff --git a/src/api/mypage.api.ts b/src/api/mypage.api.ts index ea03a835..c3778531 100644 --- a/src/api/mypage.api.ts +++ b/src/api/mypage.api.ts @@ -72,7 +72,7 @@ export const getMyAppliedStatusList = async () => { '/user/applications' ); - return response.data; + return response.data.data; } catch (error) { console.error('내가 지원한 프로젝트 리스트: ', error); throw error; diff --git a/src/components/admin/adminInquiry/AdminInquiryAnswerWrite.tsx b/src/components/admin/adminInquiry/AdminInquiryAnswerWrite.tsx index fc38e43a..0283739c 100644 --- a/src/components/admin/adminInquiry/AdminInquiryAnswerWrite.tsx +++ b/src/components/admin/adminInquiry/AdminInquiryAnswerWrite.tsx @@ -1,7 +1,7 @@ import { useOutletContext } from 'react-router-dom'; import * as S from './AdminInquiryAnswerWrite.styled'; import React, { useEffect, useRef, useState } from 'react'; -import { InquiryAnswerBody } from '../../../models/inquiry'; +import type { InquiryAnswerBody } from '../../../models/inquiry'; import { UseMutationResult } from '@tanstack/react-query'; import { AxiosError } from 'axios'; import Modal from '../../common/modal/Modal'; diff --git a/src/components/admin/adminUserDetail/AdminUserDetail.tsx b/src/components/admin/adminUserDetail/AdminUserDetail.tsx index 743aad64..c4400b33 100644 --- a/src/components/admin/adminUserDetail/AdminUserDetail.tsx +++ b/src/components/admin/adminUserDetail/AdminUserDetail.tsx @@ -12,14 +12,19 @@ import useGetUserInfo from '../../../hooks/admin/useGetUserInfo'; import Spinner from '../../user/mypage/Spinner'; import Sidebar from '../../common/sidebar/Sidebar'; import ScrollPreventor from '../../common/modal/ScrollPreventor'; - -type TabKey = 'basic' | 'log' | 'inquiry' | 'joined' | 'created' | 'applied'; +import useGetUserProjectData from '../../../hooks/admin/useGetUserProjectData'; +import type { TabKey } from '../../../models/admin/userDetail/routing'; const AdminUserDetail = () => { const { userId } = useParams(); const { userData, isLoading, isFetching } = useGetUserInfo(Number(userId)); + const { + userData: projectData, + isLoading: projectLoading, + isFetching: projectFetching, + } = useGetUserProjectData(Number(userId)); - if (isLoading || isFetching) { + if (isLoading || isFetching || projectLoading || projectFetching) { return ( @@ -34,9 +39,9 @@ const AdminUserDetail = () => { icon: React.ReactNode; }[] = [ { - key: 'basic', + key: 'profile', label: '기본 정보', - path: `/admin/users/${userId}/${ADMIN_ROUTE.basic}`, + path: `/admin/users/${userId}`, icon: , }, { @@ -46,23 +51,11 @@ const AdminUserDetail = () => { icon: , }, { - key: 'joined', - label: '참여 프로젝트', - path: `/admin/users/${userId}/${ADMIN_ROUTE.joinedProject}`, - icon: , - }, - { - key: 'created', - label: '기획 프로젝트', - path: `/admin/users/${userId}/${ADMIN_ROUTE.createdProject}`, + key: 'projects', + label: '지원/참여/기획한 프로젝트', + path: `/admin/users/${userId}/${ADMIN_ROUTE.projects}`, icon: , }, - { - key: 'applied', - label: '지원한 프로젝트', - path: `/admin/users/${userId}/${ADMIN_ROUTE.appliedProject}`, - icon: , - }, ]; return ( @@ -88,6 +81,7 @@ const AdminUserDetail = () => { diff --git a/src/components/common/sidebar/Sidebar.styled.ts b/src/components/common/sidebar/Sidebar.styled.ts index 544831ef..016f3201 100644 --- a/src/components/common/sidebar/Sidebar.styled.ts +++ b/src/components/common/sidebar/Sidebar.styled.ts @@ -1,6 +1,6 @@ import styled from 'styled-components'; -export const Container = styled.div<{ $isAdmin: boolean }>` +export const Container = styled.div<{ $isAdmin: boolean | undefined }>` display: flex; flex-direction: column; border: 2px solid #f0f0f0; diff --git a/src/components/common/sidebar/Sidebar.tsx b/src/components/common/sidebar/Sidebar.tsx index 754607a8..50575864 100644 --- a/src/components/common/sidebar/Sidebar.tsx +++ b/src/components/common/sidebar/Sidebar.tsx @@ -22,9 +22,9 @@ interface SidebarProps { const Sidebar = ({ menuItems, profileImage, nickname }: SidebarProps) => { const isLoggedIn = useAuthStore((state) => state.isLoggedIn); const location = useLocation(); + const isAdmin = location.pathname.includes('/admin'); const isUserPage = location.pathname.includes('/user'); const isManagePage = location.pathname.includes('/manage'); - const isAdmin = location.pathname.includes('/admin'); const isMyProfile = isLoggedIn && !isUserPage && !isManagePage; const getActiveIndex = useCallback(() => { diff --git a/src/components/user/mypage/ContentTab.tsx b/src/components/user/mypage/ContentTab.tsx index 2c2329f6..9f11a91d 100644 --- a/src/components/user/mypage/ContentTab.tsx +++ b/src/components/user/mypage/ContentTab.tsx @@ -1,9 +1,9 @@ import { useEffect, useState } from 'react'; import * as S from './ContentTab.styled'; import { Link, Outlet, useLocation } from 'react-router-dom'; -import { ROUTES } from '../../../constants/routes'; import ScrollWrapper from './ScrollWrapper'; import MovedInquiredLink from '../customerService/MoveInquiredLink'; +import { ADMIN_ROUTE, ROUTES } from '../../../constants/routes'; interface Filter { title: string; @@ -19,22 +19,24 @@ interface ContentProps { export default function ContentTab({ filter, $justifyContent }: ContentProps) { const { pathname } = useLocation(); - const [filterId, setFilterId] = useState(); const isAdmin = pathname.includes('/admin'); + const [filterId, setFilterId] = useState(); - function handleChangeId(id: number) { - setFilterId(id); - } useEffect(() => { if ( pathname.includes(ROUTES.notificationsAppliedProjects) || - pathname.includes(ROUTES.activityInquiries) + pathname.includes(ROUTES.activityInquiries) || + pathname.includes(ADMIN_ROUTE.appliedProject) ) { return setFilterId(1); - } else if (pathname.includes(ROUTES.notificationsCheckedApplicants)) { + } else if ( + pathname.includes(ROUTES.notificationsCheckedApplicants) || + pathname.includes(ADMIN_ROUTE.joinedProject) + ) { return setFilterId(2); } else if ( - pathname.includes(`${ROUTES.myPageNotifications}/${ROUTES.comments}`) + pathname.includes(`${ROUTES.myPageNotifications}/${ROUTES.comments}`) || + pathname.includes(ADMIN_ROUTE.createdProject) ) { return setFilterId(3); } else { @@ -42,6 +44,10 @@ export default function ContentTab({ filter, $justifyContent }: ContentProps) { } }, [setFilterId, pathname]); + function handleChangeId(id: number) { + setFilterId(id); + } + return ( @@ -51,6 +57,7 @@ export default function ContentTab({ filter, $justifyContent }: ContentProps) { to={filter.url} onClick={() => handleChangeId(filter.id as number)} > + {' '} {filter.title} @@ -64,7 +71,7 @@ export default function ContentTab({ filter, $justifyContent }: ContentProps) { - + diff --git a/src/components/user/mypage/activityLog/ActivityLog.tsx b/src/components/user/mypage/activityLog/ActivityLog.tsx index 62bd6087..0350be96 100644 --- a/src/components/user/mypage/activityLog/ActivityLog.tsx +++ b/src/components/user/mypage/activityLog/ActivityLog.tsx @@ -1,9 +1,9 @@ -import { useLocation } from 'react-router-dom'; import { ACTIVITY_FILTER, ACTIVITY_FILTER_ADMIN, } from '../../../../constants/user/myPageFilter'; import ContentTab from '../ContentTab'; +import { useLocation } from 'react-router-dom'; export default function ActivityLog() { const { pathname } = useLocation(); diff --git a/src/components/user/mypage/activityLog/commentsActivity/CommentsActivity.tsx b/src/components/user/mypage/activityLog/commentsActivity/CommentsActivity.tsx index 2b7ad066..135cf017 100644 --- a/src/components/user/mypage/activityLog/commentsActivity/CommentsActivity.tsx +++ b/src/components/user/mypage/activityLog/commentsActivity/CommentsActivity.tsx @@ -3,10 +3,18 @@ import Spinner from '../../Spinner'; import CommentActivity from './commentActivity/CommentActivity'; import * as S from './CommentsActivity.styled'; import NoContent from '../../../../common/noContent/NoContent'; -import { useGetMyComments } from '../../../../../hooks/user/useGetMyComments'; +import useGetUserActivity from '../../../../../hooks/admin/useGetAllUserActivity'; +import { useParams } from 'react-router-dom'; +import type { MyComments } from '../../../../../models/activityLog'; +import type { UserComment } from '../../../../../models/admin/userDetail/userActivity'; export default function CommentsActivity() { - const { myCommentsData, isLoading } = useGetMyComments(); + const { userId } = useParams(); + + const { userActivityData, isLoading } = useGetUserActivity( + Number(userId), + 'comments' + ); if (isLoading) { return ( @@ -16,7 +24,11 @@ export default function CommentsActivity() { ); } - if (!myCommentsData || myCommentsData.length === 0) { + if ( + !userActivityData || + !Array.isArray(userActivityData) || + userActivityData.length === 0 + ) { return ( @@ -24,13 +36,15 @@ export default function CommentsActivity() { ); } + const commentsData = userActivityData as MyComments[] | UserComment[]; + return ( - {myCommentsData.map((list, idx: number) => ( + {commentsData.map((list: MyComments | UserComment, idx: number) => ( - {idx !== myCommentsData.length - 1 && ( + {idx !== commentsData.length - 1 && ( )} diff --git a/src/components/user/mypage/activityLog/inquiries/Inquiries.tsx b/src/components/user/mypage/activityLog/inquiries/Inquiries.tsx index 338eaa9e..dd8ba511 100644 --- a/src/components/user/mypage/activityLog/inquiries/Inquiries.tsx +++ b/src/components/user/mypage/activityLog/inquiries/Inquiries.tsx @@ -1,12 +1,18 @@ -import { useGetMyInquiries } from '../../../../../hooks/user/useGetMyInquiries'; +import { useParams } from 'react-router-dom'; +import useGetUserActivity from '../../../../../hooks/admin/useGetAllUserActivity'; import ContentBorder from '../../../../common/contentBorder/ContentBorder'; import NoContent from '../../../../common/noContent/NoContent'; import Spinner from '../../Spinner'; import * as S from './Inquiries.styled'; import Inquiry from './inquiry/Inquiry'; +import type { MyInquiries } from '../../../../../models/activityLog'; export default function Inquiries() { - const { myInquiriesData, isLoading } = useGetMyInquiries(); + const { userId } = useParams(); + const { userActivityData, isLoading } = useGetUserActivity( + Number(userId), + 'inquiries' + ); if (isLoading) { return ( @@ -16,13 +22,15 @@ export default function Inquiries() { ); } - if (!myInquiriesData || myInquiriesData?.length === 0) + if (!userActivityData || userActivityData?.length === 0) return ( ); + const myInquiriesData = userActivityData as MyInquiries[]; + return ( diff --git a/src/components/user/mypage/activityLog/inquiries/inquiry/Inquiry.tsx b/src/components/user/mypage/activityLog/inquiries/inquiry/Inquiry.tsx index 832d042a..21881354 100644 --- a/src/components/user/mypage/activityLog/inquiries/inquiry/Inquiry.tsx +++ b/src/components/user/mypage/activityLog/inquiries/inquiry/Inquiry.tsx @@ -1,4 +1,4 @@ -import React, { useRef, useState } from 'react'; +import { useRef, useState } from 'react'; import type { MyInquiries } from '../../../../../../models/activityLog'; import * as S from './Inquiry.styled'; import { My_INQUIRIES_MESSAGE } from '../../../../../../constants/user/customerService'; diff --git a/src/components/user/mypage/myProfile/profile/Profile.tsx b/src/components/user/mypage/myProfile/profile/Profile.tsx index 4035fa7d..5ff6c2b4 100644 --- a/src/components/user/mypage/myProfile/profile/Profile.tsx +++ b/src/components/user/mypage/myProfile/profile/Profile.tsx @@ -9,7 +9,7 @@ import { ROUTES } from '../../../../../constants/routes'; import 'chart.js/auto'; import { chartOptions } from '../../../../../constants/evaluationChartData'; import { formatDate } from '../../../../../util/formatDate'; -import { UserInfoAll } from '../../../../../models/userInfo'; +import type { UserInfoAll } from '../../../../../models/userInfo'; export default function Profile() { const { diff --git a/src/components/user/mypage/notifications/appliedProjects/AppliedProjects.tsx b/src/components/user/mypage/notifications/appliedProjects/AppliedProjects.tsx index 4454ca87..59959a76 100644 --- a/src/components/user/mypage/notifications/appliedProjects/AppliedProjects.tsx +++ b/src/components/user/mypage/notifications/appliedProjects/AppliedProjects.tsx @@ -1,13 +1,17 @@ -import { Link } from 'react-router-dom'; +import { Link, useParams } from 'react-router-dom'; import * as S from './AppliedProjects.styled'; import Spinner from '../../Spinner'; import AppliedProjectsStatus from './appliedProjectsStatus/AppliedProjectsStatus'; import NoContent from '../../../../common/noContent/NoContent'; -import { useMyAppliedStatusList } from '../../../../../hooks/user/useMyInfo'; import { ROUTES } from '../../../../../constants/routes'; +import useGetUserProjectData from '../../../../../hooks/admin/useGetUserProjectData'; export default function AppliedProjects() { - const { myAppliedStatusListData, isLoading } = useMyAppliedStatusList(); + const { userId } = useParams(); + + const { userData: userProjectData, isLoading } = useGetUserProjectData( + Number(userId) + ); if (isLoading) { return ( @@ -17,7 +21,7 @@ export default function AppliedProjects() { ); } - if (!myAppliedStatusListData || myAppliedStatusListData.length === 0) { + if (!userProjectData || userProjectData.length === 0) { return ( @@ -28,7 +32,7 @@ export default function AppliedProjects() { return ( - {myAppliedStatusListData.map((data) => ( + {userProjectData.map((data) => ( diff --git a/src/components/user/notificationLive/NotificationProvider.tsx b/src/components/user/notificationLive/NotificationProvider.tsx index 307dcdf3..b0b0328f 100644 --- a/src/components/user/notificationLive/NotificationProvider.tsx +++ b/src/components/user/notificationLive/NotificationProvider.tsx @@ -1,5 +1,5 @@ import { ReactNode, useState } from 'react'; -import { AlarmLive } from '../../../models/alarm'; +import type { AlarmLive } from '../../../models/alarm'; import { SseContext } from '../../../context/SseContext'; export const NotificationProvider = ({ children }: { children: ReactNode }) => { diff --git a/src/constants/admin/userDetailContentHeader.ts b/src/constants/admin/userDetailContentHeader.ts new file mode 100644 index 00000000..233c9ad0 --- /dev/null +++ b/src/constants/admin/userDetailContentHeader.ts @@ -0,0 +1,19 @@ +import { ADMIN_ROUTE } from '../routes'; + +export const PROJECT_TABS = [ + { + title: '지원한 프로젝트', + url: `${ADMIN_ROUTE.appliedProject}`, + id: 1, + }, + { + title: '참여한 프로젝트', + url: `${ADMIN_ROUTE.joinedProject}`, + id: 2, + }, + { + title: '기획한 프로젝트', + url: `${ADMIN_ROUTE.createdProject}`, + id: 3, + }, +] as const; diff --git a/src/constants/routes.ts b/src/constants/routes.ts index 84c08bf6..a24e135c 100644 --- a/src/constants/routes.ts +++ b/src/constants/routes.ts @@ -47,8 +47,8 @@ export const ADMIN_ROUTE = { detail: 'detail', write: 'write', modification: 'modification', - basic: 'basic', log: 'log', + projects: 'projects', joinedProject: 'joined-project', createdProject: 'created-project', appliedProject: 'applied-project', diff --git a/src/context/SseContext.tsx b/src/context/SseContext.tsx index d30064f8..7ecb7f42 100644 --- a/src/context/SseContext.tsx +++ b/src/context/SseContext.tsx @@ -1,5 +1,5 @@ import { createContext, useContext } from 'react'; -import { AlarmLive } from '../models/alarm'; +import type { AlarmLive } from '../models/alarm'; export interface SseContextProps { signalData: AlarmLive | null; diff --git a/src/hooks/admin/useAdminNotice.ts b/src/hooks/admin/useAdminNotice.ts index e4345710..0b8b8c21 100644 --- a/src/hooks/admin/useAdminNotice.ts +++ b/src/hooks/admin/useAdminNotice.ts @@ -8,7 +8,7 @@ import { AxiosError } from 'axios'; import { CustomerService } from '../queries/keys'; import { useNavigate } from 'react-router-dom'; import { ADMIN_MODAL_MESSAGE } from '../../constants/admin/adminModal'; -import { WriteBody } from '../../models/customerService'; +import type { WriteBody } from '../../models/customerService'; type State = 'success' | 'fail'; diff --git a/src/hooks/admin/useGetAllUserActivity.ts b/src/hooks/admin/useGetAllUserActivity.ts new file mode 100644 index 00000000..ea9ecf8a --- /dev/null +++ b/src/hooks/admin/useGetAllUserActivity.ts @@ -0,0 +1,54 @@ +import { useQuery } from '@tanstack/react-query'; +import { ActivityLog, UserData } from '../queries/keys'; +import { getUserActivityData } from '../../api/admin/userActivity.api'; +import { getMyComments, getMyInquiries } from '../../api/activityLog.api'; +import useAuthStore from '../../store/authStore'; +import type { MyComments, MyInquiries } from '../../models/activityLog'; +import { useLocation } from 'react-router-dom'; + +export function useGetUserActivity( + userId: number, + type: 'comments' | 'inquiries' +) { + const userLoginId = useAuthStore.getState().userData?.id; + const { pathname } = useLocation(); + const isAdmin = pathname.includes('/admin'); + + const getQueryKey = () => { + if (isAdmin) return [UserData.userActivity, userId, type]; + return [ + type === 'comments' ? ActivityLog.myComments : ActivityLog.myInquiries, + userLoginId, + ]; + }; + + const getQueryFn = async () => { + if (isAdmin) { + const response = await getUserActivityData(userId); + return type === 'comments' + ? response.data.comments + : response.data.inquiries; + } + if (type === 'comments') { + return await getMyComments(); + } + return await getMyInquiries(); + }; + + const { data, isLoading, isFetching } = useQuery< + MyComments[] | MyInquiries[] + >({ + queryKey: getQueryKey(), + queryFn: getQueryFn, + staleTime: 60 * 1000, + enabled: isAdmin ? !!userId : true, + }); + + return { + userActivityData: data, + isLoading, + isFetching, + }; +} + +export default useGetUserActivity; diff --git a/src/hooks/admin/useGetUserInfo.ts b/src/hooks/admin/useGetUserInfo.ts index b109461f..cf31679d 100644 --- a/src/hooks/admin/useGetUserInfo.ts +++ b/src/hooks/admin/useGetUserInfo.ts @@ -1,5 +1,5 @@ import { useQuery } from '@tanstack/react-query'; -import { ApiUserInfo } from '../../models/userInfo'; +import type { ApiUserInfo } from '../../models/userInfo'; import { getUserInfo } from '../../api/userpage.api'; import { userInfoKey } from '../queries/keys'; diff --git a/src/hooks/admin/useGetUserProjectData.ts b/src/hooks/admin/useGetUserProjectData.ts new file mode 100644 index 00000000..c29abce1 --- /dev/null +++ b/src/hooks/admin/useGetUserProjectData.ts @@ -0,0 +1,26 @@ +import { useQuery } from '@tanstack/react-query'; +import useAuthStore from '../../store/authStore'; +import { ProjectListKey, UserData } from '../queries/keys'; +import { getUserProjectData } from '../../api/admin/user.api'; +import { useLocation } from 'react-router-dom'; +import { getMyAppliedStatusList } from '../../api/mypage.api'; +import type { AppliedProject } from '../../models/userProject'; + +const useGetUserProjectData = (userId: number) => { + const isAdmin = useLocation().pathname.includes('admin'); + const isLoggedIn = useAuthStore.getState().isLoggedIn; + + const { data, isLoading, isFetching } = useQuery({ + queryKey: isAdmin + ? [UserData.userJoinedProjectList, userId] + : [ProjectListKey.myAppliedStatusList, userId], + queryFn: () => + isAdmin ? getUserProjectData(userId) : getMyAppliedStatusList(), + staleTime: 1 * 60 * 1000, + enabled: isAdmin ? !!userId : isLoggedIn, + }); + + return { userData: data, isLoading, isFetching }; +}; + +export default useGetUserProjectData; diff --git a/src/hooks/queries/keys.ts b/src/hooks/queries/keys.ts index bb4b97a4..8ec9d37d 100644 --- a/src/hooks/queries/keys.ts +++ b/src/hooks/queries/keys.ts @@ -62,12 +62,15 @@ export const CustomerService = { export const ReportData = { allReports: ['AllReports'], + allReportsPreview: ['AllReportsPreview'], } as const; export const UserData = { allUser: ['AllUser'], allUserPreview: ['AllUserPreview'], userInfo: ['userInfo'], + userJoinedProjectList: ['userJoinedProjectList'], + userActivity: ['userActivity'], } as const; export const Tag = { diff --git a/src/hooks/user/ProjectHooks/useApplyProject.ts b/src/hooks/user/ProjectHooks/useApplyProject.ts index 60dfe985..aab99141 100644 --- a/src/hooks/user/ProjectHooks/useApplyProject.ts +++ b/src/hooks/user/ProjectHooks/useApplyProject.ts @@ -2,7 +2,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { ProjectListKey, userInfoKey } from '../../queries/keys'; import { useNavigate } from 'react-router-dom'; import { postApplicantProject } from '../../../api/joinProject.api'; -import { joinProject } from '../../../models/joinProject'; +import type { joinProject } from '../../../models/joinProject'; import { MODAL_MESSAGE } from '../../../constants/user/modalMessage'; import { ROUTES } from '../../../constants/routes'; diff --git a/src/hooks/user/ProjectHooks/useTagSelectors.ts b/src/hooks/user/ProjectHooks/useTagSelectors.ts index 8007ef18..b8217187 100644 --- a/src/hooks/user/ProjectHooks/useTagSelectors.ts +++ b/src/hooks/user/ProjectHooks/useTagSelectors.ts @@ -1,7 +1,7 @@ import { useEffect, useState } from 'react'; import { UseFormSetValue } from 'react-hook-form'; -import { CreateProjectFormValues } from '../../../models/createProject'; -import { PositionTag, SkillTag } from '../../../models/tags'; +import type { CreateProjectFormValues } from '../../../models/createProject'; +import type { PositionTag, SkillTag } from '../../../models/tags'; interface useTagSelectorsProps { apiTagData?: SkillTag[] | PositionTag[] | number; setValue: UseFormSetValue; diff --git a/src/models/admin/userDetail/routing.ts b/src/models/admin/userDetail/routing.ts new file mode 100644 index 00000000..66ab7ac0 --- /dev/null +++ b/src/models/admin/userDetail/routing.ts @@ -0,0 +1 @@ +export type TabKey = 'profile' | 'log' | 'projects'; diff --git a/src/models/admin/userDetail/userActivity.ts b/src/models/admin/userDetail/userActivity.ts new file mode 100644 index 00000000..3ca889c3 --- /dev/null +++ b/src/models/admin/userDetail/userActivity.ts @@ -0,0 +1,19 @@ +import type { MyInquiries } from '../../activityLog'; +import type { ApiCommonType } from '../../apiCommon'; + +export interface UserComment { + id: number; + content: string; + createdAt: string; + projectId: number; + projectTitle: string; +} + +export interface UserActivityData { + inquiries: MyInquiries[]; + comments: UserComment[]; +} + +export interface ApiUserActivityResponse extends ApiCommonType { + data: UserActivityData; +} diff --git a/src/models/admin/userDetail/userDetail.applicants.ts b/src/models/admin/userDetail/userDetail.applicants.ts new file mode 100644 index 00000000..c087b441 --- /dev/null +++ b/src/models/admin/userDetail/userDetail.applicants.ts @@ -0,0 +1,40 @@ +import type { ApiCommonType, User } from '../../apiCommon'; +import type { Career } from '../../applicant'; +import type { SkillTag } from '../../tags'; + +export interface Applicant { + id: number; + userId: number; + projectId: number; + message: string; + email: string; + phoneNumber: string; + career: Career[]; + status: 'WAITING' | 'ACCEPTED' | 'REJECTED'; + createdAt: string; + updatedAt: string; + user: User; +} + +export interface ApplicantDetail { + email: string; + phoneNumber: string; + message: string; + skills: SkillTag[]; + career: Career[]; +} + +export interface ApplicantResult { + accepted: Applicant[]; + rejected: Applicant[]; +} + +export interface UserApplicantsData { + applicants: Applicant[]; + detail: ApplicantDetail; + results: ApplicantResult[]; +} + +export interface ApiUserApplicantsData extends ApiCommonType { + data: UserApplicantsData; +} diff --git a/src/models/admin/userDetail/userProjectData.ts b/src/models/admin/userDetail/userProjectData.ts new file mode 100644 index 00000000..97d4bb9a --- /dev/null +++ b/src/models/admin/userDetail/userProjectData.ts @@ -0,0 +1,40 @@ +import type { ApiCommonType } from '../../apiCommon'; +import type { MethodTag, PositionTag, SkillTag } from '../../tags'; +import type { AppliedProject, JoinedProject } from '../../userProject'; + +export interface BaseProject { + id: number; + title: string; + recruitmentEndDate?: string; + totalMember: number; + isBeginner: boolean; + isDone: boolean; + skills: SkillTag[]; + canEvaluate: boolean; + isAllEvaluated: boolean; +} + +export interface AuthoredProject extends BaseProject { + description: string; + startDate: string; + estimatedPeriod: string; + authorId: number; + views: number; + recruitmentStartDate: string; + createdAt: string; + updatedAt: string; + methodType: MethodTag; + positions: PositionTag[]; +} + +export interface UserProjectData { + authoredProjects: AuthoredProject[]; + ownedProjects: BaseProject[]; + joinedProjects: JoinedProject[]; + appliedProjects: AppliedProject[]; + myJoinedProjects: BaseProject[]; +} + +export interface ApiUserProjectDataResponse extends ApiCommonType { + data: UserProjectData; +} diff --git a/src/models/userProject.ts b/src/models/userProject.ts index d2c67097..6c96fef3 100644 --- a/src/models/userProject.ts +++ b/src/models/userProject.ts @@ -24,7 +24,7 @@ export interface AppliedProject { } export interface ApiAppliedProject extends ApiCommonType { - data: AppliedProject[] | null; + data: AppliedProject[]; } export interface ApiUserProject extends ApiCommonType { diff --git a/src/pages/admin/adminUser/AdminUser.tsx b/src/pages/admin/adminUser/AdminUser.tsx index a1243f5d..224bd1ae 100644 --- a/src/pages/admin/adminUser/AdminUser.tsx +++ b/src/pages/admin/adminUser/AdminUser.tsx @@ -8,7 +8,6 @@ import Pagination from '../../../components/common/pagination/Pagination'; import useSearchBar from '../../../hooks/admin/useSearchBar'; import { ADMIN_MODAL_MESSAGE } from '../../../constants/admin/adminModal'; import { Link } from 'react-router-dom'; -import { ADMIN_ROUTE } from '../../../constants/routes'; import Spinner from '../../../components/user/mypage/Spinner'; const AdminUser = () => { @@ -38,20 +37,13 @@ const AdminUser = () => { - + {allUserData.users.map((userData) => ( - + + + + ); +} diff --git a/src/routes/AdminRoutes.tsx b/src/routes/AdminRoutes.tsx index a961e4fa..725eb2f0 100644 --- a/src/routes/AdminRoutes.tsx +++ b/src/routes/AdminRoutes.tsx @@ -2,9 +2,25 @@ import NotFoundPage from '../pages/notFoundPage/NotFoundPage'; import { lazy, Suspense } from 'react'; import { ADMIN_ROUTE } from '../constants/routes'; import ProtectAdminRoute from './ProtectAdminRoute'; -import { Spinner } from '../components/common/loadingSpinner/LoadingSpinner.styled'; import { Navigate } from 'react-router-dom'; +import { Spinner } from '../components/common/loadingSpinner/LoadingSpinner.styled'; +import AdminUserProjectsLayout from '../pages/admin/adminUser/AdminUserProjectsLayout'; +const AdminUserDetail = lazy( + () => import('../components/admin/adminUserDetail/AdminUserDetail') +); +const UserProjects = lazy( + () => import('../components/user/userPage/userProjectList/UserProjectList') +); +const ActivityLog = lazy( + () => import('../components/user/mypage/activityLog/ActivityLog') +); +const Notifications = lazy( + () => import('../components/user/mypage/notifications/Notifications') +); +const AdminReportDetail = lazy( + () => import('../components/admin/adminUserReport/AdminReportDetail') +); const Sidebar = lazy( () => import('../components/common/admin/sidebar/AdminSidebar') ); @@ -20,21 +36,9 @@ const NoticeWrite = lazy( const NoticeDetail = lazy( () => import('../pages/admin/adminNoticeDetail/AdminNoticeDetail') ); -const AdminUserDetail = lazy( - () => import('../components/admin/adminUserDetail/AdminUserDetail') -); const Profile = lazy( () => import('../components/user/mypage/myProfile/profile/Profile') ); -const ActivityLog = lazy( - () => import('../components/user/mypage/activityLog/ActivityLog') -); -const Notifications = lazy( - () => import('../components/user/mypage/notifications/Notifications') -); -const UserProjects = lazy( - () => import('../components/user/userPage/userProjectList/UserProjectList') -); const FAQ = lazy(() => import('../pages/admin/adminFAQ/AdminFAQ')); const FAQList = lazy( () => import('../pages/admin/adminFAQ/adminFAQList/AdminFAQListPage') @@ -173,10 +177,6 @@ export const AdminRoutes = () => { children: [ { index: true, - element: , - }, - { - path: `${ADMIN_ROUTE.basic}`, element: , }, { @@ -198,41 +198,37 @@ export const AdminRoutes = () => { ], }, { - path: `${ADMIN_ROUTE.appliedProject}`, - element: , + path: `${ADMIN_ROUTE.projects}`, + element: , children: [ { index: true, - element: , + element: , }, { - path: `${ADMIN_ROUTE.checkingApplicant}`, - element: , + path: `${ADMIN_ROUTE.appliedProject}`, + element: , }, { - path: `${ADMIN_ROUTE.comments}`, - element: , + path: `${ADMIN_ROUTE.joinedProject}`, + element: , }, { - path: `${ADMIN_ROUTE.applyingProject}`, - element: , + path: `${ADMIN_ROUTE.createdProject}`, + element: , }, ], }, - { - path: `${ADMIN_ROUTE.joinedProject}`, - element: , - }, - { - path: `${ADMIN_ROUTE.createdProject}`, - element: , - }, ], }, { path: ADMIN_ROUTE.reports, element: , }, + { + path: `${ADMIN_ROUTE.reports}/:id`, + element: , + }, { path: ADMIN_ROUTE.inquiries, element: ,