-
Notifications
You must be signed in to change notification settings - Fork 26
[정상인] sprint9 #142
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
The head ref may contain hidden characters: "Next-\uC815\uC0C1\uC778-sprint9"
[정상인] sprint9 #142
Changes from all commits
8740806
2e5abc1
eb8d439
5cb8004
daacec4
7530b5b
7b362a1
40998da
6ec6662
941126e
80ec748
1be6363
255c564
b558d6a
bc55513
7f712b0
315e4f9
1fbfd58
d56106a
8d41f5e
f11ad7a
560d7e2
55879e0
ad8bc68
78ee62b
d1c050c
b479700
249a133
83aea8a
525a2bd
39367ec
1c39f9e
2fc9fd8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| import { instance } from "./instance"; | ||
|
|
||
| interface DeleteTodoRequest { | ||
| itemId: number; | ||
| } | ||
|
|
||
| export interface DeleteTodoResponse { | ||
| message: string; | ||
| } | ||
|
|
||
| export const deleteTodo = async ({ | ||
| itemId, | ||
| }: DeleteTodoRequest): Promise<DeleteTodoResponse> => { | ||
| return instance.delete(`/items/${itemId}`); | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| import { instance } from "./instance"; | ||
| import { TodoItem } from "@/types/global"; | ||
|
|
||
| interface GetTodosRequest { | ||
| page?: number; | ||
| pageSize?: number; | ||
| } | ||
|
|
||
| export type GetTodosResponse = TodoItem[]; | ||
|
|
||
| export const getTodos = async ({ | ||
| page = 1, | ||
| pageSize = 10, | ||
| }: GetTodosRequest = {}): Promise<GetTodosResponse> => { | ||
| return instance.get("/items", { params: { page, pageSize } }); | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| import axios, { AxiosResponse } from "axios"; | ||
|
|
||
| const NEXT_PUBLIC_BACKEND_URL = process.env.NEXT_PUBLIC_BACKEND_URL; | ||
| const NEXT_PUBLIC_TENANTID = process.env.NEXT_PUBLIC_TENANTID; | ||
|
|
||
| export const instance = axios.create({ | ||
| baseURL: NEXT_PUBLIC_BACKEND_URL, | ||
| headers: { | ||
| "Content-Type": "application/json", | ||
| }, | ||
| }); | ||
|
|
||
| instance.interceptors.request.use((config) => { | ||
| config.baseURL = `${NEXT_PUBLIC_BACKEND_URL}/${NEXT_PUBLIC_TENANTID}`; | ||
|
|
||
| return config; | ||
| }); | ||
|
|
||
| instance.interceptors.response.use( | ||
| (response: AxiosResponse) => { | ||
| return response.data; | ||
| }, | ||
| (error) => { | ||
| if (axios.isAxiosError(error)) { | ||
| console.error( | ||
| "status: ", | ||
| error.response?.status, | ||
| "message: ", | ||
| error.message | ||
| ); | ||
| } else if (error instanceof Error) { | ||
| console.error(error.message); | ||
| } | ||
|
|
||
| return Promise.reject(error); | ||
| } | ||
| ); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| import { TodoItem } from "@/types/global"; | ||
| import { instance } from "./instance"; | ||
|
|
||
| interface PatchTodoRequest { | ||
| itemId: number; | ||
| name?: string; | ||
| isCompleted?: boolean; | ||
| } | ||
|
|
||
| export interface PatchTodoResponse extends TodoItem { | ||
| tenantId: string; | ||
| memo: string; | ||
| imageUrl: string; | ||
| } | ||
|
|
||
| export const patchTodo = async ({ | ||
| itemId, | ||
| name, | ||
| isCompleted, | ||
| }: PatchTodoRequest): Promise<PatchTodoResponse> => { | ||
| return instance.patch(`/items/${itemId}`, { name, isCompleted }); | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| import { TodoItem } from "@/types/global"; | ||
| import { instance } from "./instance"; | ||
|
|
||
| interface PostTodoRequest { | ||
| name: string; | ||
| } | ||
|
|
||
| export interface PostTodoResponse extends TodoItem { | ||
| tenantId: string; | ||
| memo: string; | ||
| imageUrl: string; | ||
| } | ||
|
|
||
| export const postTodo = async ({ | ||
| name, | ||
| }: PostTodoRequest): Promise<PostTodoResponse> => { | ||
| return instance.post("/items", { name }); | ||
| }; | ||
|
Comment on lines
+4
to
+18
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 굿굿 ! 응답과 요청 타입을 정의하셨군요 !이제 타입스크립트에 완전 적응하신게 느껴집니다 👍👍👍👍 |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| .badge { | ||
| display: inline-block; | ||
| padding: 4px 27px; | ||
| border-radius: 23px; | ||
| font-size: 18px; | ||
| font-weight: 400; | ||
| } | ||
|
|
||
| .todo { | ||
| background-color: var(--lime-300); | ||
| color: var(--green-700); | ||
| } | ||
|
|
||
| .done { | ||
| background-color: var(--green-700); | ||
| color: var(--amber-300); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| import styles from "./Badge.module.css"; | ||
|
|
||
| const variantsToLabel = { | ||
| todo: "TO DO", | ||
| done: "DONE", | ||
| }; | ||
|
|
||
| interface BadgeProps { | ||
| variants: keyof typeof variantsToLabel; | ||
| className?: string; | ||
| } | ||
|
|
||
| const Badge = ({ variants, className }: BadgeProps) => { | ||
|
Comment on lines
+3
to
+13
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (의견/선택)
|
||
| return ( | ||
| <span | ||
| className={`${styles.badge} ${styles[variants]} ${className ?? ""}`} | ||
| aria-label={variantsToLabel[variants]} | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오호
|
||
| > | ||
| {variantsToLabel[variants]} | ||
| </span> | ||
| ); | ||
| }; | ||
|
|
||
| export default Badge; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| .button { | ||
| border: 2px solid var(--state-900); | ||
| border-radius: 24px; | ||
| width: 164.35px; | ||
| height: 52px; | ||
| box-shadow: 4px 4px var(--black); | ||
| cursor: pointer; | ||
| } | ||
|
|
||
| .button:disabled { | ||
| background-color: var(--state-500); | ||
| cursor: default; | ||
| } | ||
|
|
||
| .append { | ||
| background-color: var(--violet-600); | ||
| color: var(--white); | ||
| } | ||
|
|
||
| .delete { | ||
| background-color: var(--rose-500); | ||
| color: var(--white); | ||
| } | ||
|
|
||
| .update { | ||
| background-color: var(--lime-300); | ||
| } | ||
|
|
||
| .content { | ||
| display: flex; | ||
| justify-content: center; | ||
| align-items: center; | ||
| gap: 4px; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| import { ButtonHTMLAttributes } from "react"; | ||
| import styles from "./Button.module.css"; | ||
| import Image from "next/image"; | ||
| import IcPlus from "@/public/images/ic_plus.svg"; | ||
| import IcDelete from "@/public/images/ic_x.svg"; | ||
| import IcUpdate from "@/public/images/ic_check.svg"; | ||
|
|
||
| const variantsToLabel = { | ||
| append: "추가하기", | ||
| delete: "삭제하기", | ||
| update: "수정완료", | ||
| }; | ||
|
|
||
| const variantsToIcon = { | ||
| append: <Image src={IcPlus} alt="추가하기" aria-label="항목 추가하기" />, | ||
| delete: <Image src={IcDelete} alt="삭제하기" aria-label="항목 삭제하기" />, | ||
| update: <Image src={IcUpdate} alt="수정완료" aria-label="항목 수정완료" />, | ||
| }; | ||
|
|
||
| interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> { | ||
| variants: keyof typeof variantsToLabel; | ||
| } | ||
|
|
||
| const Button = ({ variants, ...props }: ButtonProps) => { | ||
| return ( | ||
| <button | ||
| className={`${styles.button} ${styles[variants]} font-bold-16`} | ||
| aria-label={variantsToLabel[variants]} | ||
| {...props} | ||
| > | ||
| <span className={styles.content}> | ||
| {variantsToIcon[variants]} | ||
| <span>{variantsToLabel[variants]}</span> | ||
| </span> | ||
| </button> | ||
| ); | ||
| }; | ||
|
|
||
| export default Button; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| .button { | ||
| background: none; | ||
| border: none; | ||
| cursor: pointer; | ||
| padding: 0; | ||
| width: 32px; | ||
| height: 32px; | ||
| position: relative; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| import { ButtonHTMLAttributes } from "react"; | ||
| import IcCheckTrue from "@/public/images/ic_check-true.svg"; | ||
| import IcCheckFalse from "@/public/images/ic_check-false.svg"; | ||
| import Image from "next/image"; | ||
| import styles from "./CheckBox.module.css"; | ||
|
|
||
| interface CheckBoxProps extends ButtonHTMLAttributes<HTMLButtonElement> { | ||
| isCompleted: boolean; | ||
| } | ||
|
|
||
| const CheckBox = ({ isCompleted, ...props }: CheckBoxProps) => { | ||
| return ( | ||
| <button className={styles.button} onClick={props.onClick}> | ||
| {isCompleted ? ( | ||
| <Image | ||
| src={IcCheckTrue} | ||
| alt="완료" | ||
| aria-label="done" | ||
| fill | ||
| quality={100} | ||
| /> | ||
| ) : ( | ||
| <Image | ||
| src={IcCheckFalse} | ||
| alt="미완료" | ||
| aria-label="not-done" | ||
| fill | ||
| quality={100} | ||
| /> | ||
| )} | ||
| </button> | ||
| ); | ||
| }; | ||
|
|
||
| export default CheckBox; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| .header { | ||
| border-bottom: 1px solid var(--state-200); | ||
| height: 60px; | ||
| } | ||
|
|
||
| .nav { | ||
| max-width: 1200px; | ||
| margin: 0 auto; | ||
| height: 100%; | ||
| display: flex; | ||
| align-items: center; | ||
| } | ||
|
|
||
| .logo-small { | ||
| display: none; | ||
| } | ||
|
|
||
| @media screen and (max-width: 720px) { | ||
| .logo-large { | ||
| display: none; | ||
| } | ||
| .logo-small { | ||
| display: block; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| import ImgLogoLarge from "@/public/images/img_logo-large.svg"; | ||
| import ImgLogoSmall from "@/public/images/img_logo-small.svg"; | ||
| import Image from "next/image"; | ||
| import Link from "next/link"; | ||
| import styles from "./Header.module.css"; | ||
|
|
||
| const Header = () => { | ||
| return ( | ||
| <header className={styles.header}> | ||
| <nav className={styles.nav}> | ||
| <Link href="/"> | ||
| <Image | ||
| src={ImgLogoLarge} | ||
| alt="헤더 로고" | ||
| quality={100} | ||
| className={styles["logo-large"]} | ||
| /> | ||
| <Image | ||
| src={ImgLogoSmall} | ||
| alt="헤더 로고" | ||
| quality={100} | ||
| className={styles["logo-small"]} | ||
| /> | ||
| </Link> | ||
| </nav> | ||
| </header> | ||
| ); | ||
| }; | ||
|
|
||
| export default Header; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| .layout { | ||
| max-width: 1200px; | ||
| margin: 0 auto; | ||
| padding-top: 24px; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| import { ReactNode } from "react"; | ||
| import Header from "../Header"; | ||
| import styles from "./Layout.module.css"; | ||
|
|
||
| interface LayoutProps { | ||
| children: ReactNode; | ||
| } | ||
|
|
||
| const Layout = ({ children }: LayoutProps) => { | ||
| return ( | ||
| <> | ||
| <Header /> | ||
| <div className={styles.layout}>{children}</div> | ||
| </> | ||
| ); | ||
| }; | ||
|
|
||
| export default Layout; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| .todo { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 16px; | ||
| border: 2px solid var(--state-900); | ||
| border-radius: 27px; | ||
| padding: 9px; | ||
| } | ||
|
|
||
| .done { | ||
| background-color: var(--violet-100); | ||
| text-decoration-line: line-through; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
크으 ~ Axios 세팅이 깔끔하고 좋네요 👍