Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { SearchFilteringProvider } from './context/SearchFilteringContext';
const queryClient = new QueryClient({
defaultOptions: {
queries: {
throwOnError: true,
//retry: 0,
gcTime: 20000,
},
},
Expand Down
23 changes: 14 additions & 9 deletions src/api/applicant.api.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { ApplicantInfo } from '../models/applicant';
import { ApiApplicantInfo, ApiApplicants } from '../models/applicant';
import { httpClient } from './http.api';

export const getApplicantList = async (projectId: number) => {
const response = await httpClient.get<ApplicantInfo[]>(
`/project/${projectId}/applicant`
const response = await httpClient.get<ApiApplicants>(
`/project/${projectId}/applicants`
);
return response.data;
};

export const getApplicantInfo = async (projectId: number, userId: number) => {
const response = await httpClient.get<ApplicantInfo>(
`/project/${projectId}/applicant/${userId}`
const response = await httpClient.get<ApiApplicantInfo>(
`/project/${projectId}/applicants/${userId}`
);
return response.data;
};
Expand All @@ -20,16 +20,21 @@ export const patchPassNonPassStatus = async (
projectId: number,
userId: number
) => {
const response = await httpClient.patch(
`/project/${projectId}/applicant/${userId}/status`,
data
const requestBody = {
applicantUserId: userId,
status: data.status,
};
const response = await httpClient.put(
`/project/${projectId}/applicant`,
requestBody
);

return response.data;
};

export const getPassNonPassList = async (projectId: number) => {
const response = await httpClient.get(
`/project/${projectId}/applicant/summary`
`/project/${projectId}/applicants/results`
);
return response.data;
};
8 changes: 4 additions & 4 deletions src/api/myProjectList.api.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { ManagedProject } from '../models/manageMyProject';
import { ManagedProject, ApiManagedProjects } from '../models/manageMyProject';
import { httpClient } from './http.api';

export const getMyProjectLists = async () => {
const response = await httpClient.get<ManagedProject[]>(`/project/my`);
const response = await httpClient.get<ApiManagedProjects>(`/project/my`);
return response.data;
};

export const patchSendResult = async (projectId: number) => {
const response = await httpClient.patch<ManagedProject>(
`/project/${projectId}/is-done`
const response = await httpClient.put<ManagedProject>(
`/project/${projectId}/close`
);
return response.data;
};
3 changes: 3 additions & 0 deletions src/assets/wifi.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 4 additions & 5 deletions src/components/common/avatar/AvatarList.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import * as S from './AvatarList.styled';
import { ProjectSkillTag } from '../../../models/manageMyProject';
import Avatar from './Avatar';
import { UserSkillTag } from '../../../models/applicant';
import { formatImgPath } from '../../../util/formatImgPath';
import { SkillTag } from '../../../models/tags';
export interface AvatarListProps {
avatars: ProjectSkillTag[] | UserSkillTag[] | null;
avatars: SkillTag[] | null;
size?: string;
maxCount?: number;
}
Expand All @@ -17,10 +16,10 @@ function AvatarList({ avatars, size = '33px', maxCount = 8 }: AvatarListProps) {
<S.Wrapper>
{displayAvatars.map((avatar) => (
<Avatar
key={avatar.skillTagId}
key={avatar.id}
size={size}
image={`${import.meta.env.VITE_APP_IMAGE_CDN_URL}/${formatImgPath(
avatar.SkillTag.img
avatar.img
)}?w=62&h=62&fit=crop&crop=entropy&q=60`}
/>
))}
Expand Down
63 changes: 63 additions & 0 deletions src/components/common/error/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { Component, createElement, ReactNode } from 'react';
import { ErrorBoundaryContext } from '../../../context/ErrorBoundaryContext';

/* eslint-disable @typescript-eslint/no-explicit-any */

interface ErrorBoundaryProps {
children: ReactNode;
fallback: React.ElementType;
onReset?: () => void;
}

interface ErrorBoundaryState {
hasError: boolean;
error: any;
}

const initialState = {
hasError: false,
error: null,
};

class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
constructor(props: ErrorBoundaryProps) {
super(props);

this.resetErrorBoundary = this.resetErrorBoundary.bind(this);
this.state = initialState;
}

static getDerivedStateFromError(error: Error) {
return { hasError: true, error };
}

componentDidCatch(error: Error, errorInfo: any) {
console.error('error', error, errorInfo);
}

resetErrorBoundary() {
const { onReset } = this.props;
onReset?.();
this.setState(initialState);
}

render() {
const { hasError, error } = this.state;
const { children, fallback } = this.props;
let childrenToRender: ReactNode = children;

if (hasError) {
const FallbackComponent = fallback;
childrenToRender = <FallbackComponent error={error} />;
}

return createElement(
ErrorBoundaryContext.Provider,
{
value: { hasError, error, resetErrorBoundary: this.resetErrorBoundary },
},
childrenToRender
);
}
}
export default ErrorBoundary;
25 changes: 25 additions & 0 deletions src/components/common/error/ErrorFallBack.styled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import styled from 'styled-components';

export const Container = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
`;

export const ErrorMessage = styled.h1`
font-size: 24px;
font-weight: 700;
color: ${({ theme }) => theme.color.red};
`;

export const ErrorDescription = styled.p`
font-size: 16px;
color: ${({ theme }) => theme.color.deepGrey};
margin-bottom: 32px;
`;

export const Button = styled.button`
margin-top: 16px;
`;
46 changes: 46 additions & 0 deletions src/components/common/error/ErrorFallback.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { useContext } from 'react';
import { ErrorBoundaryContext } from '../../../context/ErrorBoundaryContext';
import * as S from './ErrorFallBack.styled';
import wifi from '../../../assets/wifi.svg';
import Button from '../Button/Button';
import { HTTP_ERROR_MESSAGES } from '../../../constants/httpMessage';
import { AxiosError } from 'axios';

interface ErrorFallBackProps {
error: AxiosError;
}

const getErrorMessage = (statusCode: number | 'unknown') => {
const errorMessage = HTTP_ERROR_MESSAGES[statusCode];
return errorMessage ?? HTTP_ERROR_MESSAGES.unknown;
};

const ErrorFallback = ({ error }: ErrorFallBackProps) => {
const { title, description } = getErrorMessage(
error.response?.status ?? 'unknown'
);

const context = useContext(ErrorBoundaryContext);
if (!context) return null;

const { resetErrorBoundary } = context;

return (
<S.Container>
<img src={wifi} alt='wifi' />
<S.ErrorMessage>{title}</S.ErrorMessage>
<S.ErrorDescription>{description}</S.ErrorDescription>

<Button
size='primary'
schema='primary'
radius='primary'
onClick={resetErrorBoundary}
>
재시도하기
</Button>
</S.Container>
);
};

export default ErrorFallback;
20 changes: 20 additions & 0 deletions src/components/common/error/QueryErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { useQueryErrorResetBoundary } from '@tanstack/react-query';
import type { PropsWithChildren } from 'react';
import ErrorBoundary from './ErrorBoundary';
import ErrorFallback from './ErrorFallback';

export default function QueryErrorBoundary({ children }: PropsWithChildren) {
const { reset } = useQueryErrorResetBoundary();

return (
<ErrorBoundary
onReset={reset}
fallback={(props) => {
const { error } = props;
return <ErrorFallback error={error} />;
}}
>
{children}
</ErrorBoundary>
);
}
2 changes: 1 addition & 1 deletion src/components/manageProjects/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ function Card({ project }: CardProps) {
<S.CardTitle>{project.title}</S.CardTitle>
<S.RecruitmentDate>{formatEndDate}까지</S.RecruitmentDate>
<S.TotalMember>{project.totalMember}명</S.TotalMember>
<AvatarList maxCount={5} avatars={project.ProjectSkillTag} />
<AvatarList maxCount={5} avatars={project.skills} />

<S.ButtonWrapper>
{project.isDone && <S.RecruitmentEnd>모집 종료</S.RecruitmentEnd>}
Expand Down
4 changes: 2 additions & 2 deletions src/components/manageProjects/ProjectHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import * as S from './ProjectHeader.styled';
import Title from '../common/title/Title';
import { ProjectDetailExtended } from '../../models/projectDetail';
import { ProjectDetailPlusExtended } from '../../models/projectDetail';
import RecruitmentDate from './RecruitmentDate';
import React from 'react';
interface ProjectHeaderProps {
projectData: ProjectDetailExtended;
projectData: ProjectDetailPlusExtended;
}

function ProjectHeader({ projectData }: ProjectHeaderProps) {
Expand Down
4 changes: 2 additions & 2 deletions src/components/manageProjects/RecruitmentDate.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { ProjectDetail } from '../../models/projectDetail';
import { ProjectDetailPlus } from '../../models/projectDetail';
import { formatDate } from '../../util/formatDate';
import * as S from './RecruitmentDate.styled';
interface RecruitmentDateProps {
ProjectData: ProjectDetail;
ProjectData: ProjectDetailPlus;
}

function RecruitmentDate({ ProjectData }: RecruitmentDateProps) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,11 @@ interface ApplicantInfoProps {
const ApplicantInfo = ({ applicantInfo }: ApplicantInfoProps) => {
return (
<S.Container>
<S.Title>{applicantInfo.User.nickname}</S.Title>
<LabelWithContent label='이메일' content={applicantInfo.email} />
<LabelWithContent label='휴대폰' content={applicantInfo.phoneNumber} />
<S.SkillSetWrapper>
<S.Label>스킬셋</S.Label>
<AvatarList avatars={applicantInfo.User.UserSkillTag} />
<AvatarList avatars={applicantInfo.skills} />
</S.SkillSetWrapper>

<S.Label>경력</S.Label>
Expand All @@ -27,7 +26,6 @@ const ApplicantInfo = ({ applicantInfo }: ApplicantInfoProps) => {
{data.name} - {data.role}
</S.Text>
))}
<LabelWithContent label='자기 소개' content={applicantInfo.User.bio} />
<LabelWithContent label='하고 싶은 말' content={applicantInfo.message} />
</S.Container>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ function ApplicantItem({
onClick={() => onClick(applicantData.userId)}
$passStatus={applicantData.status}
>
{applicantData.User.nickname}
{applicantData.user.nickname}
</S.Button>
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ function ApplicantList({
}: ApplicantListProps) {
return (
<S.Container>
{applicantsData.map((data) => (
{applicantsData?.map((data) => (
<ApplicantItem
key={data.userId}
selectedApplicant={selectedApplicant}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { useMutationParams } from '../../../hooks/usePassNonPassMutation';
import { ApplicantInfo } from '../../../models/applicant';
import { ProjectDetailExtended } from '../../../models/projectDetail';
import { ProjectDetailPlusExtended } from '../../../models/projectDetail';
import DeleteButton from './DeleteButton';
import * as S from './PassNonPassItem.styled';

interface PassNonPassItemProps {
userInfo: ApplicantInfo;
projectData: ProjectDetailExtended;
projectData: ProjectDetailPlusExtended;
hanldeStatus: ({ status, userId }: useMutationParams) => void;
handleUserInfo: (userId: number) => void;
}
Expand All @@ -20,7 +20,7 @@ function PassNonPassItem({
return (
<S.ItemWrapper>
<S.NickName onClick={() => handleUserInfo(userInfo.userId)}>
{userInfo.User.nickname}
{userInfo.user.nickname}
</S.NickName>
<DeleteButton
disabled={projectData.isDone}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { useNavigate } from 'react-router-dom';
import { useMutationParams } from '../../../hooks/usePassNonPassMutation';
import { ApplicantInfo } from '../../../models/applicant';
import { ProjectDetailExtended } from '../../../models/projectDetail';
import { ProjectDetailPlusExtended } from '../../../models/projectDetail';
import PassNonPassItem from './PassNonPassItem';
import * as S from './PassNonPassList.styled';

interface PassNonPassListProps {
passNonPassListData: ApplicantInfo[];
projectData: ProjectDetailExtended;
projectData: ProjectDetailPlusExtended;
handleStatus: ({ status, userId }: useMutationParams) => void;
}

Expand Down
Loading