Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat-fe: posts 라우트 List컴포넌트 빌드 #349

Merged
merged 7 commits into from
Aug 8, 2024
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
24 changes: 24 additions & 0 deletions frontend/src/api/dashboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,30 @@ import { convertParamsToQueryString } from './utils';
import ApiError from './ApiError';

const dashboardApis = {
get: async ({ dashboardId }: { dashboardId: string }) => {
const queryParams = {
clubId: dashboardId,
};

const response = await fetch(`${DASHBOARDS}?${convertParamsToQueryString(queryParams)}`, {
headers: {
Accept: 'application/json',
},
});

if (!response.ok) {
throw new ApiError({
method: 'GET',
statusCode: response.status,
message: '공고 리스트의 정보를 불러오지 못했습니다.',
});
}

const data = await response.json();

return data;
},

create: async ({ clubId, dashboardFormInfo }: { clubId: number; dashboardFormInfo: DashboardFormInfo }) => {
const queryParams = {
clubId: String(clubId),
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/api/process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { createParams } from './utils';
import ApiError from './ApiError';

const processApis = {
get: async ({ id }: { id: number }) => {
const response = await fetch(`${PROCESSES}?${createParams({ dashboard_id: String(id) })}`, {
get: async ({ id }: { id: string }) => {
const response = await fetch(`${PROCESSES}?${createParams({ dashboard_id: id })}`, {
headers: {
Accept: 'application/json',
},
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/components/appModal/ApplicantBaseInfo/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { useParams } from 'react-router-dom';

import Dropdown from '@components/common/Dropdown';
import Button from '@components/common/Button';
import useProcess from '@hooks/useProcess';
Expand All @@ -14,7 +16,8 @@ interface ApplicantBaseInfoProps {
export default function ApplicantBaseInfo({ applicantId }: ApplicantBaseInfoProps) {
const { data: applicantBaseDetail } = specificApplicant.useGetBaseInfo({ applicantId });
const { mutate: rejectMutate } = specificApplicant.useRejectApplicant();
const { processList } = useProcess();
const { dashboardId, postId } = useParams() as { dashboardId: string; postId: string };
const { processList } = useProcess({ dashboardId, postId });
const { moveApplicantProcess } = useApplicant({ applicantId });
const { close } = useModal();

Expand Down
5 changes: 4 additions & 1 deletion frontend/src/components/dashboard/ProcessColumn/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { useParams } from 'react-router-dom';

import { useSpecificApplicantId } from '@contexts/SpecificApplicnatIdContext';
import { Process } from '@customTypes/process';
import useProcess from '@hooks/useProcess';
Expand All @@ -12,7 +14,8 @@ interface ProcessColumnProps {
}

export default function ProcessColumn({ process }: ProcessColumnProps) {
const { processList } = useProcess();
const { dashboardId, postId } = useParams() as { dashboardId: string; postId: string };
const { processList } = useProcess({ dashboardId, postId });
const { moveApplicantProcess } = useApplicant({});
const { setApplicantId } = useSpecificApplicantId();
const { open } = useModal();
Expand Down
17 changes: 17 additions & 0 deletions frontend/src/hooks/useGetDashboards/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import dashboardApis from '@api/dashboard';
import type { Club } from '@customTypes/dashboard';
import QUERY_KEYS from '@hooks/queryKeys';
import { useQuery } from '@tanstack/react-query';

interface UseGetDashboardsProps {
dashboardId: string;
}

export default function useGetDashboards({ dashboardId }: UseGetDashboardsProps) {
const { data, error, isLoading } = useQuery<Club>({
queryKey: [QUERY_KEYS.DASHBOARD, dashboardId],
queryFn: () => dashboardApis.get({ dashboardId }),
});

return { data, error, isLoading };
}
12 changes: 8 additions & 4 deletions frontend/src/hooks/useProcess/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,29 @@ import { useQuery } from '@tanstack/react-query';
import type { Process } from '@customTypes/process';

import processApis from '@api/process';
import { DASHBOARD_ID } from '@constants/constants';
import QUERY_KEYS from '@hooks/queryKeys';

interface SimpleProcess {
processName: string;
processId: number;
}

interface UseProcessProps {
dashboardId: string;
postId: string;
}

interface UseProcessReturn {
processes: Process[];
processList: SimpleProcess[];
error: Error | null;
isLoading: boolean;
}

export default function useProcess(): UseProcessReturn {
export default function useProcess({ dashboardId, postId }: UseProcessProps): UseProcessReturn {
const { data, error, isLoading } = useQuery<{ processes: Process[] }>({
queryKey: [QUERY_KEYS.DASHBOARD, DASHBOARD_ID],
queryFn: () => processApis.get({ id: DASHBOARD_ID }),
queryKey: [QUERY_KEYS.DASHBOARD, dashboardId, postId],
queryFn: () => processApis.get({ id: postId }),
});

const processes = data?.processes || [];
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/hooks/useProcess/useProcess.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const wrapper = ({ children }: PropsWithChildren) => (

describe('useProcess', () => {
it('should return processes and processNameList when data is loaded', async () => {
const { result } = renderHook(() => useProcess(), { wrapper });
const { result } = renderHook(() => useProcess({ dashboardId: '1', postId: '1' }), { wrapper });

await waitFor(() => expect(result.current.isLoading).toBe(false));

Expand Down
44 changes: 44 additions & 0 deletions frontend/src/mocks/dashboardList.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"clubName": "크루루",
"dashboards": [
{
"dashboardId": "1",
"title": "프론트엔드 7기 모집",
"stats": {
"accept": 3,
"fail": 3,
"inProgress": 9,
"total": 15
},
"postUrl": "https://www.cruru.kr/123543920/recruit",
"startDate": "1900-01-21T00:00:00",
"endDate": "2024-07-24T18:00:00"
},
{
"dashboardId": "2",
"title": "백엔드 7기 모집",
"stats": {
"accept": 3,
"fail": 3,
"inProgress": 9,
"total": 15
},
"postUrl": "https://www.cruru.kr/98765101/recruit",
"startDate": "1900-01-21T00:00:00",
"endDate": "2024-07-24T20:30:00"
},
{
"dashboardId": "3",
"title": "안드로이드 7기 모집",
"stats": {
"accept": 3,
"fail": 3,
"inProgress": 9,
"total": 15
},
"postUrl": "https://www.cruru.kr/7777890/recruit",
"startDate": "1900-01-21T00:00:00",
"endDate": "2024-07-24T21:00:00"
}
]
}
17 changes: 16 additions & 1 deletion frontend/src/mocks/handlers/dashboardHandlers.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { http } from 'msw';
import { http, HttpResponse } from 'msw';

import { DASHBOARDS } from '@api/endPoint';
import { DashboardFormInfo } from '@customTypes/dashboard';
import DASHBOARD_LIST from '../dashboardList.json';

const dashboardHandlers = [
http.post(DASHBOARDS, async ({ request }) => {
Expand All @@ -21,6 +22,20 @@ const dashboardHandlers = [
statusText: 'Created',
});
}),

http.get(DASHBOARDS, ({ request }) => {
const url = new URL(request.url);
const clubId = url.searchParams.get('clubId');

if (!clubId) {
return new Response(null, {
status: 400,
statusText: 'The request Param is missing required information.',
});
}

return HttpResponse.json(DASHBOARD_LIST);
}),
];

export default dashboardHandlers;
36 changes: 36 additions & 0 deletions frontend/src/pages/DashBoardList/DashboardList.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { reactRouterParameters } from 'storybook-addon-remix-react-router';
import type { Meta, StoryObj } from '@storybook/react';
import DashboardList from '.';

const meta: Meta<typeof DashboardList> = {
title: 'Components/DashboardList',
component: DashboardList,
parameters: {
layout: 'centered',
docs: {
description: {
component: 'DashboardList 컴포넌트는 여러 대시보드를 리스트 형태로 보여줍니다.',
},
},
reactRouter: reactRouterParameters({
location: {
pathParams: { dashboardId: '1' },
},
routing: { path: '/dashboardId/:dashboardId' },
}),
},
tags: ['autodocs'],
};

export default meta;
type Story = StoryObj<typeof DashboardList>;

export const Default: Story = {
decorators: [
(Child) => (
<div style={{ width: '900px', border: '1px solid gray' }}>
<Child />
</div>
),
],
};
40 changes: 34 additions & 6 deletions frontend/src/pages/DashBoardList/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,38 @@
import RecruitmentCard from '@components/recruitment/RecruitmentCard';
import { useNavigate, useParams } from 'react-router-dom';
import useGetDashboards from '@hooks/useGetDashboards';
import S from './style';

export default function DashboardList() {
const { dashboardId } = useParams() as { dashboardId: string };
const { data } = useGetDashboards({ dashboardId });
const navigate = useNavigate();

const handleCardClick = (postId: number) => {
navigate(`/dashboard/${dashboardId}/${postId}`);
};

return (
<div>
List
{/*
TODO: 여기에 PostList를 넣어주세요!!!!!!
*/}
</div>
<S.Container>
<S.Title>{data?.clubName}</S.Title>
<S.CardGrid>
{data?.dashboards.map((dashboard) => (
<RecruitmentCard
key={dashboard.dashboardId}
// TODO: dashboardId -> postId로 변경
dashboardId={Number(dashboard.dashboardId)}
title={dashboard.title}
postStats={dashboard.stats}
startDate={dashboard.startDate}
endDate={dashboard.endDate}
onClick={handleCardClick}
/>
))}
<S.AddCard onClick={() => navigate(`/dashboard/${dashboardId}/create`)}>
<div>+</div>
<span>새 공고 추가</span>
</S.AddCard>
</S.CardGrid>
</S.Container>
);
}
58 changes: 58 additions & 0 deletions frontend/src/pages/DashBoardList/style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import styled from '@emotion/styled';

const Container = styled.div`
display: flex;
flex-direction: column;
padding: 3.8rem 2.4rem;
gap: 2.4rem;
`;

const Title = styled.h1`
${({ theme }) => theme.typography.heading[700]}
padding-bottom: 2.4rem;
border-bottom: 1px solid ${({ theme }) => theme.baseColors.grayscale[300]};
`;

const CardGrid = styled.div`
display: grid;
grid-template-columns: repeat(auto-fill, 37.8rem);
gap: 2.4rem;
`;

const AddCard = styled.div`
display: flex;
height: 20rem;

flex-direction: column;
align-items: center;
justify-content: center;

border: 0.2rem dashed ${({ theme }) => theme.baseColors.grayscale[600]};
border-radius: 0.5rem;
padding: 2rem;

cursor: pointer;
text-align: center;

&:hover {
background-color: ${({ theme }) => theme.baseColors.grayscale[200]};
}

div {
font-size: 6rem;
color: ${({ theme }) => theme.colors.brand.primary};
}
span {
${({ theme }) => theme.typography.common.large}
color: ${({ theme }) => theme.baseColors.grayscale[800]};
}
`;

const S = {
Container,
Title,
CardGrid,
AddCard,
};

export default S;
7 changes: 5 additions & 2 deletions frontend/src/pages/Dashboard/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { useParams } from 'react-router-dom';

import ProcessManageBoard from '@components/processManagement/ProcessManageBoard';
import Tab from '@components/common/Tab';
import KanbanBoard from '@components/dashboard/KanbanBoard';
import ProcessManageBoard from '@components/processManagement/ProcessManageBoard';

import useTab from '@components/common/Tab/useTab';
import useProcess from '@hooks/useProcess';
Expand All @@ -13,7 +15,8 @@ import S from './style';
export type DashboardTabItems = '지원자 관리' | '모집 과정 관리';

export default function Dashboard() {
const { processes } = useProcess();
const { dashboardId, postId } = useParams() as { dashboardId: string; postId: string };
const { processes } = useProcess({ dashboardId, postId });

const { currentMenu, moveTab } = useTab<DashboardTabItems>({ defaultValue: '지원자 관리' });

Expand Down
Loading
Loading