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

taken lectures api/#35 #36

Merged
merged 19 commits into from
Feb 29, 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
9 changes: 9 additions & 0 deletions app/__test__/ui/lecture/taken-lecture-list.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import TakenLectureList from '@/app/ui/lecture/taken-lecture-list';
import { render, screen } from '@testing-library/react';

describe('Taken lecture list', () => {
it('๊ธฐ์ด์ˆ˜ ๊ณผ๋ชฉ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ณด์—ฌ์ค€๋‹ค.', async () => {
render(await TakenLectureList());
expect(await screen.findByTestId('table-data'));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[comment]

  • test-id๋กœ ํ…Œ์ŠคํŠธ ์ž‘์„ฑํ•˜์‹  ์ด์œ ๊ฐ€ ์žˆ์„๊นŒ์š”?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

๊ธฐ์ด์ˆ˜ ๊ณผ๋ชฉ ๋ฆฌ์ŠคํŠธ๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ํ˜ธ์ถœํ•ด์„œ ์ž˜ ๋ Œ๋”๋ง ํ•˜๋Š” ์ง€ ํ™•์ธํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค!
๋‹ค๋ฅธ ํ…Œ์ŠคํŠธ์ฒ˜๋Ÿผ findByText ๋ฅผ ์ด์šฉํ•˜๊ธฐ์—” ์–ด๋–ค ๋ฐ์ดํ„ฐ๊ฐ€ ๋“ค์–ด์˜ฌ ์ง€ ๋ชจ๋ฅด๊ธฐ ๋•Œ๋ฌธ์— test id๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค!
๋ฐ์ดํ„ฐ๊ฐ€ ๋ฌธ์ œ ์—†์ด ํ˜ธ์ถœ๋˜๋ฉด table์ด ์ •์ƒ์ ์œผ๋กœ ๋ Œ๋”๋ง ๋  ๊ฒƒ์ด๋ผ๊ณ  ํŒ๋‹จํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค~!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • ๋„ค, ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ๋ง์”€ํ•˜์‹ ๋Œ€๋กœ test-id๊ฐ€ ์ ์ ˆํ•ด ๋ณด์ด๋„ค์š”!
  • ์•„๋ž˜๋Š” ํ…Œ์ŠคํŠธ์—์„œ ์š”์†Œ๋ฅผ ์„ ํƒํ•˜๋Š” ๋ฒ ์ŠคํŠธ ํ”„๋ž™ํ‹ฐ์Šค๋ฅผ ์ •๋ฆฌํ•œ ๋ฌธ์„œ์ž…๋‹ˆ๋‹ค. ์ฐธ๊ณ ํ•˜์‹œ๋ฉด ๋„์›€์ด ๋  ๊ฒƒ ๊ฐ™์•„ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค
    https://docs.cypress.io/guides/references/best-practices#Selecting-Elements

});
});
1 change: 1 addition & 0 deletions app/business/api-path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ const BASE_URL = process.env.API_MOCKING === 'enable' ? 'http://localhost:9090'

export const API_PATH = {
revenue: `${BASE_URL}/revenue`,
takenLectures: `${BASE_URL}/taken-lectures`,
};
20 changes: 20 additions & 0 deletions app/business/lecture/taken-lecture-list.query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { API_PATH } from '../api-path';

type LectureInfo = {
id: number;
year: string;
semester: string;
lectureCode: string;
lectureName: string;
credit: number;
};
type TakenLectures = {
totalCredit: number;
takenLectures: LectureInfo[];
};

export const fetchTakenLectures = async (): Promise<TakenLectures> => {
const response = await fetch(API_PATH.takenLectures);
const data = await response.json();
return data;
};
62 changes: 62 additions & 0 deletions app/mocks/data.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,65 @@ export const revenue = [
{ month: 'Nov', revenue: 3000 },
{ month: 'Dec', revenue: 4800 },
];

export const takenLectures = JSON.parse(`{
"totalCredit": 115,
"takenLectures": [
{
"id": 140,
"year": "CUSTOM",
"semester": "CUSTOM",
"lectureCode": "HED01407",
"lectureName": "๋”ฅ๋Ÿฌ๋‹",
"credit": 3
},
{
"id": 139,
"year": "CUSTOM",
"semester": "CUSTOM",
"lectureCode": "HED01311",
"lectureName": "์ž๊ธฐ์ฃผ๋„ํ•™์Šต",
"credit": 2
},
{
"id": 135,
"year": "2023",
"semester": "1ํ•™๊ธฐ",
"lectureCode": "HED01413",
"lectureName": "์บก์Šคํ†ค๋””์ž์ธ",
"credit": 3
},
{
"id": 128,
"year": "2023",
"semester": "1ํ•™๊ธฐ",
"lectureCode": "HED01404",
"lectureName": "๋น…๋ฐ์ดํ„ฐ๊ธฐ์ˆ ํŠน๋ก 1",
"credit": 3
},
{
"id": 126,
"year": "2023",
"semester": "1ํ•™๊ธฐ",
"lectureCode": "HED01318",
"lectureName": "๋ชจ๋ฐ”์ผ์ปดํ“จํŒ…",
"credit": 3
},
{
"id": 125,
"year": "2023",
"semester": "1ํ•™๊ธฐ",
"lectureCode": "HED01407",
"lectureName": "๋”ฅ๋Ÿฌ๋‹",
"credit": 3
},
{
"id": 124,
"year": "2023",
"semester": "1ํ•™๊ธฐ",
"lectureCode": "HED01317",
"lectureName": "๋ฐ์ดํ„ฐ๋ฒ ์ด์Šคํ”„๋กœ์ ํŠธ",
"credit": 3
}
]
}`);
5 changes: 4 additions & 1 deletion app/mocks/handlers.mock.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { HttpResponse, http, delay } from 'msw';
import { revenue } from './data.mock';
import { revenue, takenLectures } from './data.mock';
import { API_PATH } from '../business/api-path';

export const handlers = [
Expand All @@ -8,4 +8,7 @@ export const handlers = [
console.log(revenue);
return HttpResponse.json(revenue);
}),
http.get(API_PATH.takenLectures, () => {
return HttpResponse.json(takenLectures);
}),
];
19 changes: 19 additions & 0 deletions app/ui/lecture/taken-lecture-label.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import Link from 'next/link';
import Button from '../view/atom/button/button';
import LabelContainer from '../view/atom/label-container/label-container';

export default function TakenLectureLabel() {
return (
<LabelContainer
label="๋‚ด ๊ธฐ์ด์ˆ˜ ๊ณผ๋ชฉ"
rightElement={
<div className="flex gap-2">
<Button label="์ปค์Šคํ…€ํ•˜๊ธฐ" variant="secondary" size="md" />
<Link href="/file-upload">
<Button label="์—…๋ฐ์ดํŠธ" variant="secondary" size="md" />
</Link>
</div>
}
/>
);
}
15 changes: 15 additions & 0 deletions app/ui/lecture/taken-lecture-list.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { fetchTakenLectures } from '@/app/business/lecture/taken-lecture-list.query';
import { Table } from '../view/molecule/table';
import TakenLectureLabel from './taken-lecture-label';

const headerInfo = ['์ˆ˜๊ฐ•๋…„๋„', '์ˆ˜๊ฐ•ํ•™๊ธฐ', '๊ณผ๋ชฉ์ฝ”๋“œ', '๊ณผ๋ชฉ๋ช…', 'ํ•™์ '];

export default async function TakenLectureList() {
const data = await fetchTakenLectures();
return (
<div className="w-[800px] flex flex-col gap-2">
<TakenLectureLabel />
<Table headerInfo={headerInfo} data={data.takenLectures} />
</div>
);
}
29 changes: 0 additions & 29 deletions app/ui/sample.stories.tsx

This file was deleted.

40 changes: 40 additions & 0 deletions app/ui/view/atom/label-container/label-container.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react';
import type { Meta, StoryObj } from '@storybook/react';
import LabelContainer from './label-container';
import Button from '../button/button';

const meta = {
title: 'ui/view/atom/Button',
component: LabelContainer,
tags: ['autodocs'],
parameters: {
componentSubtitle: 'LabelContainer๋Š” ๊ธฐ์ด์ˆ˜, ๋ฏธ์ด์ˆ˜ ๊ณผ๋ชฉ์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ๋‚˜ํƒ€๋‚ผ ๋•Œ ์‚ฌ์šฉ๋˜๋Š” view component์ž…๋‹ˆ๋‹ค.',
docs: {
description: {
component: `
- label ๊ฐ’์œผ๋กœ ํ™”๋ฉด์— ์ถœ๋ ฅํ•˜๊ณ  ์‹ถ์€ text๋ฅผ ํ• ๋‹นํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. \n
- right element ๊ฐ’์œผ๋กœ ์˜ค๋ฅธ์ชฝ์— ๋ Œ๋”๋ง ๋  ์š”์†Œ๋ฅผ ํ• ๋‹นํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. \n
`,
},
},
},
} satisfies Meta<typeof LabelContainer>;

export default meta;

export const TakenLectureLabel: StoryObj<typeof LabelContainer> = {
args: {
label: '๋‚ด ๊ธฐ์ด์ˆ˜ ๊ณผ๋ชฉ',
rightElement: (
<div className="flex gap-2">
<Button label="์ปค์Šคํ…€ํ•˜๊ธฐ" variant="secondary" size="md" />
<Button label="์—…๋ฐ์ดํŠธ" variant="secondary" size="md" />
</div>
),
},
render: (args) => (
<div className="w-[800px]">
<LabelContainer {...args} />
</div>
),
};
15 changes: 15 additions & 0 deletions app/ui/view/atom/label-container/label-container.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { ReactNode } from 'react';

type LabelContainerProps = {
label: string;
rightElement: ReactNode;
};

export default function LabelContainer({ label, rightElement }: LabelContainerProps) {
return (
<div className="flex justify-between items-center w-full">
<div className="rounded-[100px] bg-light-blue-6 p-2.5 text-white text-lg font-bold">{label}</div>
{rightElement}
</div>
);
}
15 changes: 10 additions & 5 deletions app/ui/view/molecule/list/list-root.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import { ReactNode } from 'react';

type ListRootProps<T> = {
data: T[];
render: (item: T) => ReactNode;
export type ListRow = { [key: string]: string | number; id: number };
type ListRootProps = {
data: ListRow[];
render: (item: ListRow, index: number) => ReactNode;
};

export function ListRoot<T>({ data, render }: ListRootProps<T>) {
return <div className="rounded-2xl border-[1px] border-black-2 w-full">{data.map((item) => render(item))}</div>;
export function ListRoot({ data, render }: ListRootProps) {
return (
<div className="rounded-2xl border-[1px] border-black-2 w-full">
{data.map((item, index) => render(item, index))}
</div>
);
}
45 changes: 40 additions & 5 deletions app/ui/view/molecule/table/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,43 @@
import { TableHeader } from './table-header';
import { TableRoot } from './table-root';
import { ColType } from '../grid/grid-root';
import List from '../list';
import Grid from '../grid';
import { ListRow } from '../list/list-root';

const Table = Object.assign(TableRoot, {
Header: TableHeader,
});
type TableProps = {
headerInfo: string[];
data: ListRow[];
renderActionButton?: (id: number) => JSX.Element;
};

export default Table;
function isCol(cols: number): cols is ColType {
if (cols === 3 || cols === 4 || cols === 5 || cols === 6) {
return true;
}
return false;
}

export function Table({ data, headerInfo, renderActionButton }: TableProps) {
const cols = renderActionButton ? headerInfo.length + 1 : headerInfo.length;

const render = (item: ListRow, index: number) => {
return (
<List.Row key={index}>
<Grid cols={isCol(cols) ? cols : 6}>
{Object.keys(item).map((key, index) => {
if (key === 'id') return null;
return <Grid.Column key={index}>{item[key]}</Grid.Column>;
})}
{renderActionButton ? <Grid.Column>{renderActionButton(item.id)}</Grid.Column> : null}
</Grid>
</List.Row>
);
};

return (
<div className="flex flex-col gap-2.5 w-full" data-testid="table-data">
<TableHeader headerInfo={headerInfo} cols={isCol(cols) ? cols : 6} />
<List data={data} render={render} />
</div>
);
}
41 changes: 0 additions & 41 deletions app/ui/view/molecule/table/table-root.tsx

This file was deleted.

2 changes: 1 addition & 1 deletion app/ui/view/molecule/table/table.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Meta, StoryObj } from '@storybook/react';
import Table from '.';
import Button from '../../atom/button/button';
import { Table } from '.';

const meta = {
title: 'ui/view/molecule/Table',
Expand Down
Loading