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

[송민혁] week13 #483

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
19 changes: 19 additions & 0 deletions components/AddLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import styles from '@/styles/addLink.module.css';

function AddLink(): JSX.Element {
Copy link
Collaborator

Choose a reason for hiding this comment

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

굳이 returntype을 명시하지 않으셔도됩니다. 컴포넌트는 알아서 타입이 추론되게 하는게 좋습니다.

return (
<>
Copy link
Collaborator

Choose a reason for hiding this comment

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

불필요한 Fragment입니다. 단일 Wrapper만 있는경우에는 제거해주세요

<div className={styles.container}>
<div className={styles.input_container}>
<div className={styles.add_box}>
<img src="/assets/image/linkIcon.svg" alt="링크 아이콘" />
<input className={styles.link_input} placeholder="링크를 추가해 보세요" />
<button className={styles.cta}>추가하기</button>
</div>
</div>
</div>
</>
);
}

export default AddLink;
42 changes: 42 additions & 0 deletions components/Card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import Moment from 'react-moment';
Copy link
Collaborator

Choose a reason for hiding this comment

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

Moment보다는 date-fns 같은 유틸리티 모듈을 사용하시는 것이 좋습니다. moment는 브라우저에서 사용하기에 너무 기능이 무겁습니다.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

이 말을 듣고 라이브러리를 비교해서 쓸 필요가 있다고 느껴서 라이브러리를 정리하는 시간을 가져봤어요!

import '@/styles/card.module.css';
import { useState } from 'react';
import { Popover } from 'react-tiny-popover';

type OpenPopoverFunc = (e: any) => void;

function Card({ card }: { card: any }): JSX.Element {
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
Copy link
Collaborator

Choose a reason for hiding this comment

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

popover는 아직 구현이 안된것이겠쥬?


const OpenPopover: OpenPopoverFunc = (e) => {
e.stopPropagation();
setIsPopoverOpen(!isPopoverOpen);
};
return (
<>
<div className="card-wrapper" ref={card.url} rel="noreferrer noopener">
<div className="card-image-box">
<img
className="card-image"
src={card.image_source ? card.image_source : `/assets/image/no-img-card.svg`}
alt={card.title}
/>
<button className="star-mark-button">
<img className="star-mark" src="/assets/image/star.png" alt="카드 즐겨찾기 버튼" />
</button>
</div>
<div className="card-info-box">
<div className="card-info-top">
<Moment className="card-passed-time" fromNow>
{card.createdAt ? card.createdAt : card.created_at}
</Moment>
</div>
<p className="card-description">{card.description}</p>
<Moment format="YYYY.MM.DD">{card.createdAt}</Moment>
</div>
</div>
</>
);
}

export default Card;
29 changes: 29 additions & 0 deletions components/CardList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import Card from './Card';
import '@/styles/cardList.module.css';
import NoSavedLinks from './NoSavedLinks';

function CardList({ cards }: { cards: any }): JSX.Element {
Copy link
Collaborator

Choose a reason for hiding this comment

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

any보다도 파일에 빈 타입이라도 만들어두고 Card[] 라는 타입 을 기입해주시는것이 좋습니다.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

any를 계속 지우는 연습해보겠습니다

if (!cards) {
return <NoSavedLinks />;
}
if (cards.length === 0) {
return <NoSavedLinks />;
}
return (
<>
<div className="container">
<div className="card-list">
{cards.map((card: any) => {
return (
<li key={card.id}>
<Card card={card} />
</li>
);
})}
</div>
</div>
</>
);
}

export default CardList;
7 changes: 7 additions & 0 deletions components/Cta.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import '@/styles/cta.module.css';

function Cta({ name }: { name: string }): JSX.Element {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Cta 처럼 약어는 웬만하면 자제해주시고 풀네임을 쓰시는 것이 좋습니다.

return <button className="cta-btn">{name}</button>;
}

export default Cta;
80 changes: 80 additions & 0 deletions components/Folder.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import '@/styles/folder.module.css';
import CardList from './CardList';
import { useEffect, useState } from 'react';
import { getLinks } from '@/pages/api/api';
import FolderButton from './FolderButton';
import OptionBtn from './OptionBtn';
import PenIcon from '@/public/images/pen.svg';
import ShareIcon from '@/public/images/share.svg';
import TrashIcon from '@/public/images/trash.svg';

const INITIAL_FOLDER = {
id: '',
name: '전체',
};

function FolderList({ folderList = null, getCardList }): JSX.Element {
Copy link
Collaborator

Choose a reason for hiding this comment

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

별도의 FolderList.tsx 로 분리해주세요

const [folderName, setFolderName] = useState('전체');

const handleButton = (name: string, id: string) => {
setFolderName(name);
getCardList(id);
};

return (
<div>
<div className="folderlist-container">
<FolderButton folder={INITIAL_FOLDER} handleButton={handleButton} />
{folderList && (
<>
{folderList.map((folder) => {
return (
<div key={folder.id}>
<FolderButton folder={folder} handleButton={handleButton} />
</div>
);
})}
</>
)}
</div>
<div className="folder-title-container">
<div className="folder-title">{folderName}</div>
<div className="option-container">
<OptionBtn src={ShareIcon} alt="공유">
공유
</OptionBtn>
<OptionBtn src={PenIcon} alt="이름 변경">
이름 변경
</OptionBtn>
<OptionBtn src={TrashIcon} alt="삭제">
삭제
</OptionBtn>
</div>
</div>
</div>
);
}

function Folder({ folderList = null }) {
const [cards, setCards] = useState();

const getCardList = async (id = '') => {
const result = await getLinks(id);
setCards(() => {
return [...result?.data];
});
};

useEffect(() => {
getLinks();
}, []);

return (
<div className="container">
<FolderList folderList={folderList} getCardList={getCardList} />
<CardList cards={cards} />
</div>
);
}

export default Folder;
22 changes: 22 additions & 0 deletions components/FolderButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
type Props = {
Copy link
Collaborator

Choose a reason for hiding this comment

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

컴포넌트props 타입은 언제나 ${ComponentName}Props 로 지어주시면 좋습니다.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

FolderProps, CardProps ... 이런 식으로 작성하도록 하겠습니다.

folder: any;
Copy link
Collaborator

Choose a reason for hiding this comment

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

제 ts 프로젝트에서 any를 쓰는 일은 거의 없습니다

handleButton: (name: string, id: string) => void;
};

function FolderButton({ folder, handleButton }: Props): JSX.Element {
const { id = '', name = '전체' } = folder;

const handleButtonClick = (e: any): void => {
handleButton(name, id);
};

return (
<>
<button className="button" onClick={handleButtonClick}>
{name}
</button>
</>
);
}

export default FolderButton;
35 changes: 35 additions & 0 deletions components/Footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import styles from '.@/styles/footer.module.css';

function Footer(): JSX.Element {
return (
<div className="footer">
<div className="footer_box">
<span className="copyright">©codeit - 2023</span>
<div className="footer_links">
<a className="footer_link" href="privacy.html">
Privacy Policy
</a>
<a className="footer_link" href="faq.html">
FAQ
</a>
</div>
<div className="sns">
<a href="https://www.facebook.com/" target="_blank" rel="noopener noreferrer">
<img src="../assets/image/facebook.svg" alt="facebook 홈페이지로 연결된 facebook 로고" />
Copy link
Collaborator

Choose a reason for hiding this comment

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

next의 Image 컴포넌트를 사용해주세요

</a>
<a href="https://twitter.com/" target="_blank" rel="noopener noreferrer">
<img src="../assets/image/twitter.svg" alt="twitter 홈페이지로 연결된 twitter 로고" />
</a>
<a href="https://www.youtube.com/" target="_blank" rel="noopener noreferrer">
<img src="../assets/image/youtube.svg" alt="youtube 홈페이지로 연결된 youtube 로고" />
</a>
<a href="https://www.instagram.com/" target="_blank" rel="noopener noreferrer">
<img src="../assets/image/instagram.svg" alt="instagram 홈페이지로 연결된 instagram 로고" />
</a>
</div>
</div>
</div>
);
}

export default Footer;
15 changes: 15 additions & 0 deletions components/Header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import '@/styles/header.css';

function Header({ folder }: { folder: any }): JSX.Element {
return (
<div className="header">
<div className="header-info">
<img className="header-avatar" src={folder?.avatar} alt="avatar" />
Copy link
Collaborator

Choose a reason for hiding this comment

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

folder가 없다면 에러가 발생할텐데요. foder가 없을때 그려질 ui를 정의해주는것이 좋습니다.

<span className="header-user-name">@{folder?.ownerName}</span>
</div>
<span className="header-folder-name">{folder?.folderName}</span>
</div>
);
}

export default Header;
7 changes: 7 additions & 0 deletions components/MobileFolderButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import styles from '@/styles/mobileFolderButton.module.css';

function MobileFolderButton(): JSX.Element {
return <button className={styles.button}>폴더 추가 +</button>;
}

export default MobileFolderButton;
17 changes: 17 additions & 0 deletions components/Nav.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import styles from '@/styles/nav.module.css';
import UserProfile from './UserProfile';

function Nav({ userProfile }: { userProfile: any }): JSX.Element {
return (
<div className={styles.nav_wrapper}>
<div className={styles.gnb}>
<a href="index.html">
<img className={styles.logo} src="@/public/images/linkbrary.svg" alt="홈으로 연결된 Linkbrary 로고" />
</a>
<UserProfile userProfile={userProfile} />
</div>
</div>
);
}

export default Nav;
9 changes: 9 additions & 0 deletions components/NoSavedLinks.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
function NoSavedLinks(): JSX.Element {
return (
<div className="container">
<p>저장된 링크가 없습니다.</p>
</div>
);
}

export default NoSavedLinks;
21 changes: 21 additions & 0 deletions components/OptionBtn.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Image from 'next/image';
import { ReactNode } from 'react';

interface Props {
src: any;
alt: string;
children: ReactNode;
}

function OptionBtn({ src, alt, children, onClick }: Props): JSX.Element {
return (
<div className="container">
<Image src={src} alt={alt} fill />
<button className="button" onClick={onClick}>
{children}
</button>
</div>
);
}

export default OptionBtn;
15 changes: 15 additions & 0 deletions components/SearchBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import styles from '@/styles/searchBar.module.css';
import SearchIcon from '@/public/images/search.svg';

function SearchBar(): JSX.Element {
return (
<>
<form className={styles.search_form}>
<img className={styles.search_icon} src={SearchIcon} alt="검색 아이콘" />
<input className={styles.search_bar} type="text" placeholder="링크를 검색해 보세요." />
</form>
</>
);
}

export default SearchBar;
18 changes: 18 additions & 0 deletions components/UserProfile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { UserRawData } from '@/pages/api/type';
import styles from '@/styles/userProfile.module.css';

function UserProfile({ userProfile }: any): JSX.Element {
const userProfileImage = userProfile?.image_source;
const userEmail = userProfile?.email;

return (
<>
<div className={styles.user_profile}>
<img className={styles.user_profile_image} src={userProfileImage} alt="유저 프로필 이미지" />
<p className={styles.user_profile_email}>{userEmail}</p>
</div>
</>
);
}

export default UserProfile;
26 changes: 26 additions & 0 deletions hooks/useAsync.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { FolderRawData } from '@/pages/api/type';
import { useCallback, useState } from 'react';

function useAsync(asyncFunction: any) {
const [pending, setPending] = useState(false);
const [error, setError] = useState(null);
Copy link
Collaborator

Choose a reason for hiding this comment

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

data까지 제공해주고 hook 호출 시점에 내부에서 호출하는게 더 낫지 않을까요?


const wrappedFunction = useCallback(
async (...args: any[]) => {
setPending(true);
setError(null);
try {
return (await asyncFunction)(...args);
} catch (error: any) {
setError(error);
} finally {
setPending(false);
}
},
[asyncFunction],
);

return [pending, error, wrappedFunction];
Copy link
Collaborator

Choose a reason for hiding this comment

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

2개가 넘어가버리면 tuple보다는 객체를 반환해주는것이 좋습니다.

}

export default useAsync;
Loading