Skip to content

[클린코드 리액트 3기 한재성] 페이먼츠 미션 Step2 #140

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

Open
wants to merge 6 commits into
base: han-d-peter
Choose a base branch
from
Open
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
Binary file modified .yarn/install-state.gz
Binary file not shown.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -17,7 +17,8 @@
"react-dom": "^18.2.0",
"react-merge-refs": "^2.1.1",
"react-router-dom": "^6.22.2",
"react-scripts": "^5.0.1"
"react-scripts": "^5.0.1",
"uuid": "^9.0.1"
},
"devDependencies": {
"@storybook/addon-essentials": "^7.6.17",
4 changes: 4 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Link, Route, Routes } from "react-router-dom";
import MyCards from "./domains/MyCards/MyCards";
import RegisterPage from "./domains/RegisterPage/RegisterPage";
import ModifyPage from "./domains/MyCards/ModifyPage/ModifyPage";

function App() {
return (
@@ -15,6 +16,9 @@ function App() {
<Route path="register">
<Route index element={<RegisterPage />} />
</Route>
<Route path="modify">
<Route index element={<ModifyPage />} />
</Route>
</Route>
</Routes>
</div>
35 changes: 35 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { v4 } from "uuid";
import { Card } from "./domains/RegisterPage/CardRegister/types";

export const initialCards: Card[] = [
{
uuid: v4(),
cardType: "윤호",
cardNumber: {
firstNumber: "1234",
secondNumber: "4321",
thirdNumber: "2121",
fourthNumber: "1111",
},
cardHolder: "SUN",
cvc: "111",
holderName: "SUM",
expiration: { month: "12", year: "12" },
createdAt: new Date(),
},
{
uuid: v4(),
cardType: "은규",
cardNumber: {
firstNumber: "2222",
secondNumber: "2222",
thirdNumber: "2222",
fourthNumber: "2222",
},
cardHolder: "SUN",
cvc: "111",
holderName: "KYU",
expiration: { month: "11", year: "11" },
createdAt: new Date(),
},
];
40 changes: 40 additions & 0 deletions src/domains/MyCards/ModifyPage/ModifyPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { useNavigate, useSearchParams } from "react-router-dom";
import { initialCards } from "../../../constants";
import useLocalStorage from "../../../hooks/useLocalStorage";
import CardNaming, {
NameQuery,
} from "../../RegisterPage/CardNaming/CardNaming";
import { Card } from "../../RegisterPage/CardRegister/types";
import { omitObj } from "../../../utils";

export default function ModifyPage() {
const [cards, setCards] = useLocalStorage<Card[]>("mycards", initialCards);
const [param] = useSearchParams();
const navigate = useNavigate();
const uuid = param.get("uuid");

if (!uuid) throw new Error("index search 파라미터가 필수입니다.");

function isThis(value: Card) {
return value.uuid === uuid;
}

const targetCard = cards.find(isThis);

if (!targetCard) return <div>존재하지 않은 카드입니다.</div>;

function changeName(name: NameQuery) {
const copied = [...cards];
const targetCard = copied.find(isThis);
if (targetCard) targetCard["holderName"] = name.cardName;
setCards(copied);
navigate("/mycards");
Comment on lines +27 to +31

Choose a reason for hiding this comment

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

지금의 방식도 좋지만 Array.prototype.map같은 내장 메서드를 활용하면 더 선언적으로 작성할 수 있을 것 같아요!

const newCards = cards.map(card => 
  isThis(card)
    ? { ...card, holderName: name.cardName }
    : card
)
setCards(newCards)

}

return (
<CardNaming
card={omitObj<Omit<Card, "createdAt">>(targetCard, ["createdAt"])}
onSubmit={changeName}
/>
);
}
16 changes: 16 additions & 0 deletions src/domains/MyCards/MyCards.module.css
Original file line number Diff line number Diff line change
@@ -28,6 +28,22 @@
font-size: 20px;
}

.card__box {
position: relative;
}

.card__delBtn {
position: absolute;
z-index: 2;
right: -15px;
top: -5px;
padding: 3px 5px;

border: 1px solid #04c09e;
background-color: white;
border-radius: 20px;
}

a {
color: #000;
text-decoration: none;
61 changes: 39 additions & 22 deletions src/domains/MyCards/MyCards.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,53 @@
import { Link } from "react-router-dom";
import PlasticCard from "../component/PlaticCard/PlaticCard";
import styles from "./MyCards.module.css";
import Button from "../../components/Button/Button";
import useCards from "../../hooks/useCards";

export default function MyCards() {
const { cards, setCards } = useCards({
sortByKey: "createdAt",
sortMethod: "asc",
});

const latest = cards.sort((a, b) => {

Choose a reason for hiding this comment

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

sort 메서드는 원본도 바꿉니다 사용에 유의해주세요

return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
});
Comment on lines +8 to +15

Choose a reason for hiding this comment

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

오 useCards에 sort option을 주고있어서 알아서 정렬을 할줄 알았는데 한 번 더 해야하나요?


function deleteCard(uuid: string) {
return function deleteCard() {
const filtered = latest.filter((value) => uuid !== value.uuid);
setCards(filtered);
};
}

return (
<section className={styles.cards__layout}>
<div className={styles.cards__list}>
<PlasticCard
cardType="윤호"
cardNumber={{
firstNumber: "1234",
secondNumber: "1234",
thirdNumber: "1234",
fourthNumber: "1234",
}}
holderName="SUN"
expiration={{ month: "12", year: "12" }}
/>
<PlasticCard
cardType="은규"
cardNumber={{
firstNumber: "1234",
secondNumber: "1234",
thirdNumber: "1234",
fourthNumber: "1234",
}}
holderName="SUN"
expiration={{ month: "12", year: "12" }}
/>
<Link to="/mycards/register">
<div className={styles.card__link}>+</div>
</Link>
{latest.map((card) => {
return (
<div
key={JSON.stringify(card.cardNumber)}
className={styles.card__box}
>
<div className={styles.card__delBtn}>
<Button onClick={deleteCard(card.uuid)}>지우기</Button>
</div>
<Link to={`modify?uuid=${card.uuid}`}>
<PlasticCard
cardType={card.cardType}
cardNumber={card.cardNumber}
holderName={card.cardHolder}
expiration={card.expiration}
/>
</Link>
<div>{card.holderName}</div>
</div>
);
})}
</div>
</section>
);
20 changes: 13 additions & 7 deletions src/domains/RegisterPage/CardNaming/CardNaming.tsx
Original file line number Diff line number Diff line change
@@ -9,18 +9,22 @@ import PlasticCard from "../../component/PlaticCard/PlaticCard";
import Input from "../../../components/Input/Input";
import { ChangeEvent, useState } from "react";

type CardQuery = {
export type CardQuery = {
cardNumber: CardNumber;
expiration: ExpirationDate;
holder: string;
cardHolder: string;
cvc: string;
password: TwoPasswordDigits;
cardType: CardType;
password?: TwoPasswordDigits;
};

export type NameQuery = {
cardName: string;
};

interface CardNamingProps {
card?: CardQuery;
onSubmit: (value: { cardName: string }) => void;
card?: Omit<CardQuery, "password">;
onSubmit: (value: NameQuery) => void;
}

export default function CardNaming({
@@ -29,7 +33,7 @@ export default function CardNaming({
}: Readonly<CardNamingProps>) {
const [cardName, setCardName] = useState<string>("");
function completeRegist() {
onSubmit({ cardName });
onSubmit({ cardName: cardName ?? card?.cardType });
}

function changeCardName(event: ChangeEvent<HTMLInputElement>) {
@@ -46,12 +50,14 @@ export default function CardNaming({
<PlasticCard
cardNumber={card?.cardNumber}
cardType={card?.cardType}
holderName={card?.holder}
holderName={card?.cardHolder}
expiration={card?.expiration}
/>
</div>
<div className={styles.result__name}>
<Input
maxLength={10}
placeholder="카드 별칭 (선택)"
textAlign="center"
hasUnderbar
value={cardName}
6 changes: 3 additions & 3 deletions src/domains/RegisterPage/CardRegister/CardRegister.tsx
Original file line number Diff line number Diff line change
@@ -17,13 +17,13 @@ import SelectCardType from "./Components/SelectCardtype/SelectCardType";
import Button from "../../../components/Button/Button";
import { useNavigate } from "react-router-dom";

type CardRegistration = {
export type CardRegistration = {
cardType: CardType;
cardNumber: CardNumber;
expirationDate: ExpirationDate;
cardHolder: string;
cvc: string;
expirationDate: ExpirationDate;
password: TwoPasswordDigits;
cardType: CardType;
};

interface CardRegisterProps {
11 changes: 11 additions & 0 deletions src/domains/RegisterPage/CardRegister/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
export type Card = {
uuid: string;
cardType: CardType;
cardNumber: CardNumber;
holderName: string;
cardHolder: string;
cvc: string;
expiration: ExpirationDate;
createdAt: Date;
};

export type CardNumber = {
firstNumber?: string;
secondNumber?: string;
24 changes: 22 additions & 2 deletions src/domains/RegisterPage/CardResult/CardResult.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,37 @@
import { useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { CardRegistration } from "../CardRegister/CardRegister";
import { NameQuery } from "../CardNaming/CardNaming";
import useLocalStorage from "../../../hooks/useLocalStorage";
import { Card } from "../CardRegister/types";
import { initialCards } from "../../../constants";
import { v4 } from "uuid";

interface CardResultProps {
store: Record<string, unknown>;
}

export default function CardResult({ store }: CardResultProps) {
const [cards, setCards] = useLocalStorage<Card[]>("mycards", initialCards);
const navigate = useNavigate();
useEffect(() => {
console.log(store);
const registration = store["register"] as CardRegistration;
const naming = store["naming"] as NameQuery;

const newCard: Card = {
uuid: v4(),
...registration,
holderName: naming.cardName || registration.cardType,
createdAt: new Date(),
expiration: registration.expirationDate,
};

console.log("newCard", newCard);

cards ? setCards([...cards, newCard]) : setCards([newCard]);

navigate("/mycards");
}, []);
}, [cards]);

return <></>;
}
3 changes: 2 additions & 1 deletion src/domains/RegisterPage/RegisterPage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import useFunnel from "../../hooks/useFunnel/useFunnel";
import CardRegister from "./CardRegister/CardRegister";
import CardNaming from "./CardNaming/CardNaming";
import CardNaming, { CardQuery } from "./CardNaming/CardNaming";
import CardResult from "./CardResult/CardResult";

export default function RegisterPage() {
@@ -21,6 +21,7 @@ export default function RegisterPage() {
</Funnel.Page>
<Funnel.Page step="naming">
<CardNaming
card={stepStore["register"] as CardQuery}
onSubmit={(value) => {
setStep("result", value);
}}
1 change: 1 addition & 0 deletions src/domains/component/PlaticCard/PlasticCard.module.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.card__container {
position: relative;
width: 185px;
height: 115px;
border-radius: 4px;
42 changes: 42 additions & 0 deletions src/hooks/useCards.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { useMemo } from "react";
import { initialCards } from "../constants";
import { Card } from "../domains/RegisterPage/CardRegister/types";
import useLocalStorage from "./useLocalStorage";

type useCardsArgs = {
sortByKey?: keyof Card;
sortMethod?: "asc" | "dcs";
};

export default function useCards({
sortByKey = "createdAt",
sortMethod = "asc",
}: useCardsArgs) {
const [cards, setCards] = useLocalStorage<Card[]>("mycards", initialCards);

const sortedCards = useMemo(() => {
const isAscended = sortMethod === "asc";
return cards.sort((a, b) => {
const keyA = isAscended
? a[sortByKey].toString()
: b[sortByKey].toString();
const keyB = isAscended
? b[sortByKey].toString()
: a[sortByKey].toString();

if (keyA < keyB) return -1;
if (keyA > keyB) return 1;

return 0;
});
}, [cards, sortByKey, sortMethod]);

function deleteCard(uuid: string) {
return function deleteCard() {
const filtered = sortedCards.filter((value) => uuid !== value.uuid);
setCards(filtered);
};
}

return { cards: sortedCards, setCards, deleteCard };
}
Loading