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 #417

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
93a0e93
chore: 머지 후 브랜치 삭제 github action 추가
withyj-codeit Sep 3, 2023
17be37c
Initial commit from Create Next App
withyj-codeit Sep 8, 2023
4dbede7
fix: 머지 후 브랜치 삭제 github action 수정
hanseulhee Oct 10, 2023
a5cd5f5
env: workflows 폴더로 이동
hanseulhee Oct 10, 2023
37ba32f
Initialize project
kuum97 Mar 29, 2024
4849232
test commit
kuum97 Mar 29, 2024
9f7d6a4
Implemented folder page layout and logic for fetching and processing …
kuum97 Mar 29, 2024
aed2f6b
Modify readme.md
kuum97 Mar 30, 2024
747b93d
Refine network logic and update variable names for clarity
kuum97 Mar 31, 2024
f6f7705
Implement time difference calculation for displaying the elapsed time
kuum97 Mar 31, 2024
fd65ab3
Apply styles using CSS Modules with classnames
kuum97 Mar 31, 2024
1606bd2
Modify readme.md
kuum97 Apr 2, 2024
838b2ec
Refactor dateUtils func and move to utils folder
kuum97 Apr 2, 2024
6ff2afa
Fix hover overlay issue by setting navbar z-index to 1
kuum97 Apr 3, 2024
53ccb17
Merge pull request #299 from kuum97/part2-권주현-week7
Jay-WKJun Apr 4, 2024
b4a120a
Rename GlobalNavgationBar to SharedHeader
kuum97 Apr 4, 2024
1e9d5c5
Adjust card styling according to design specifications
kuum97 Apr 4, 2024
fd68a11
Refactor using useCallback within useEffect for optimized re-rendering
kuum97 Apr 4, 2024
5dafc45
Initialize multi-page setup using react-router-dom
kuum97 Apr 4, 2024
03efd6b
Rename .js to .jsx for clarity
kuum97 Apr 5, 2024
fc9ca7f
Create pre-structured Folder page configuration for future enhancements
kuum97 Apr 5, 2024
9b62603
Refine certain styles in the codebase
kuum97 Apr 6, 2024
54a908c
Refactor api logic using useAsync as custum hook
kuum97 Apr 7, 2024
1d7d779
Finish folder page logic
kuum97 Apr 7, 2024
828cc16
Refactor flex style
kuum97 Apr 8, 2024
d6a01ba
Add star icon and modal with kebab button
kuum97 Apr 8, 2024
9f89409
Add folderAddButton mobile version
kuum97 Apr 8, 2024
603f5d3
Merge pull request #341 from kuum97/part2-권주현-week8
Jay-WKJun Apr 10, 2024
109cbb7
Create Modal component
kuum97 Apr 27, 2024
6b6affe
Rename card's modal to dropdown
kuum97 Apr 27, 2024
4d0ad60
Styled Modal component and add children prop
kuum97 Apr 27, 2024
9699195
Add Modal button event handler
kuum97 Apr 28, 2024
b0fece2
Add folder adding modal
kuum97 Apr 28, 2024
d412c83
Add FoldersList modal by several actionTypes
kuum97 Apr 28, 2024
c19a637
Add LinkCard modal
kuum97 Apr 28, 2024
8d4d9cf
Repactor share card style
kuum97 Apr 29, 2024
bec021d
Repactor FoldersList modal
kuum97 Apr 29, 2024
a9d870b
Repactor modal's children
kuum97 Apr 29, 2024
8895927
Repactor FolderLinkCard modal
kuum97 Apr 29, 2024
6ea9077
Repactor component architecture
kuum97 Apr 29, 2024
b46ea0f
Add FoldersList modal's children component
kuum97 Apr 29, 2024
9e28bd0
Add FolderLinkCard modal's children components
kuum97 Apr 29, 2024
73821a0
Style LinkAddToFolderForm
kuum97 Apr 29, 2024
cc0a6da
Add clipboard copy logic for SocialShareBox
kuum97 Apr 29, 2024
e2c0b25
Add kakao share feat
kuum97 Apr 29, 2024
49b8898
Add facebook share feat
kuum97 Apr 29, 2024
830f1ef
Override typescript on my project
kuum97 May 1, 2024
5e5fa6e
Reinstall ts for cra
kuum97 May 1, 2024
d6e1d65
Add outdir to tsconfig
kuum97 May 2, 2024
aca998b
Add routes folder for Router
kuum97 May 2, 2024
4261358
Repactor api interfaces
kuum97 May 2, 2024
40a9efe
Repactor useAsync for type
kuum97 May 3, 2024
249f803
Add react-app-env.d.ts for css module in typescript
kuum97 May 3, 2024
8afb411
Add tsconfig paths for assets
kuum97 May 3, 2024
a89eee2
Refactor shared page index for type
kuum97 May 3, 2024
d1236cf
Repactor global components for type
kuum97 May 3, 2024
2fc37fb
Repactor shared page's card list for type
kuum97 May 3, 2024
c951caa
Repactor shared card for type
kuum97 May 4, 2024
76025f2
Repactor folder page's index for type
kuum97 May 6, 2024
12fa203
Repactor folder controller for type
kuum97 May 6, 2024
98a463b
Repactor folders list for type
kuum97 May 6, 2024
4c78a5f
Repactor all files import order and paths alias
kuum97 May 6, 2024
bc6695b
Repactor share box for type
kuum97 May 6, 2024
64ce634
Add react app rewired for assets
kuum97 May 6, 2024
6bcd74c
Fix webpack path setting for type alias
kuum97 May 6, 2024
c8abf20
Initialize next migration
kuum97 May 7, 2024
dee5f48
Migrate project
kuum97 May 7, 2024
68c8885
Migrate components
kuum97 May 7, 2024
0bb9342
Migrate components
kuum97 May 9, 2024
a195768
Repactor shared page for migration
kuum97 May 9, 2024
affae19
Repactor shared avatar and header img for next Image
kuum97 May 10, 2024
4041cf3
Repactor folder page's img for next Image
kuum97 May 10, 2024
7a4b0e4
modify readme
kuum97 May 10, 2024
870bd89
Repactor components for import alias
kuum97 May 10, 2024
35318bc
Modify readme
kuum97 May 11, 2024
6060e85
Repactor folder list for button click event issue
kuum97 May 11, 2024
17f3c5e
Add footer to _app
kuum97 May 11, 2024
980ba3a
Repactor next Image tag
kuum97 May 11, 2024
40591ce
Repactor next Image tag
kuum97 May 11, 2024
d64b3e2
Implement search bar logic
kuum97 May 11, 2024
eb17fa2
Fix folder card style
kuum97 May 11, 2024
6aa71af
Implement global input component
kuum97 May 11, 2024
3193bb7
Modify readme
kuum97 May 11, 2024
8c16330
Remove lock.json for conflict
kuum97 May 11, 2024
86876a7
Move modal's childrens to Modal folder
kuum97 May 12, 2024
3d43f61
Merge branch 'part3-권주현' into part3-권주현-week13
kuum97 May 12, 2024
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz

# testing
/coverage
Expand Down
96 changes: 56 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,40 +1,56 @@
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).

## Getting Started

First, run the development server:

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.

[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.

The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.

This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.

## Learn More

To learn more about Next.js, take a look at the following resources:

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!

## Deploy on Vercel

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.

Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
## Styles

### Week7

1. 상단 네비게이션 바, 푸터를 랜딩 페이지와 동일한 스타일과 규칙으로 만듭니다. (week 1 ~ 3 요구사항 참고)
2. Static, no image, Hover 상태 디자인을 보여주는 카드 컴포넌트를 만듭니다.
3. Hover 상태에서 이미지가 기본 크기의 130%로 변합니다.
4. 카드 컴포넌트를 클릭하면 해당 링크로 새로운 창을 띄워서 이동합니다.
5. Tablet에서 카드 리스트가 화면의 너비 1124px를 기준으로 이상일 때는 3열로 작을 때는 2열로 배치됩니다.
6. Tablet, Mobile에서 좌우 최소 여백은 32px 입니다.

### Week8

1. 반응형을 위한 스타일코드들을 리팩토링 합니다.

## API & Logic

### Week7

1. 상단 네비게이션 바에 프로필 영역의 데이터는 https://bootcamp-api.codeit.kr/docs 에 명세된 “/api/sample/user”를 활용합니다.
2. 상단 네비게이션 바에 프로필 영역의 데이터가 없는 경우 “로그인” 버튼이 보이도록 만듭니다.
3. 폴더 소유자, 폴더 이름 영역, 링크들에 대한 데이터는 “/api/sample/folder”를 활용합니다.
4. 커스텀 hook을 만들어 사용합니다.(선택)
5. shared 페이지는 외부 유저에게 자신의 폴더 데이터 하나를 공유할 때 유저가 보게되는 화면 입니다.
6. 카드 컴포넌트에서 createdAt 데이터 기준으로 현재 Date와 차이에 따라 아래와 같은 규칙으로 설정해 주세요.
- 2분 미만은 “1 minute ago”
- 59분 이하는 “OO minutes ago”
- 60분 이상은 “1 hour ago”
- 23시간 이하는 “OO hours ago”
- 24시간 이상은 “1 day ago”
- 30일 이하는 “OO days ago”
- 31일 이상은 “1 month ago”
- 11달 이하는 “OO months ago”
- 12달 이상은 “1 year ago”
- OO달 이상은 “{OO/12(소수점 버린 정수)} years ago”

### Week8

1. useEffect 훅의 내부로직과 디펜던스를 올바르게 수정합니다.
2. api와 util함수들을 디렉토리를 따로 생성해 관리를 쉽게 합니다.
3. GNB의 네이밍을 더 목적에 맞게 수정합니다.
4. 시간경과 함수의 하드코딩이 된 부분을 더 명확하고 깔끔하게 수정합니다.
5. 시간경과 함수의 조건문을 수정합니다.

### Week13

1. 기존의 프로젝트를 nextjs로 마이그레이션 합니다.
2. 폴더페이지에서 아이디가 있는 폴더로 변경했을 때 전체폴더로 돌아갈 수 없는 문제를 해결합니다.
3. 검색바의 기능을 구현합니다.
4. 로그인, 회원가입에 필요한 인풋을 구현합니다.
5. 기존의 react-router-dom으로 구현했던 쿼리파라미터 관련 로직을 next의 useRouter로 변경합니다.

### Week14

1. 소셜공유 로직을 분리합니다.
2. 모달 컴포넌트를 더 명확하고 간단하게 리팩토링합니다.
18 changes: 18 additions & 0 deletions components/Avatar/Avatar.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
.avatar {
position: relative;
margin-right: 6px;
}

.avatar img {
border-radius: 100%;
}

.small {
width: 35px;
height: 35px;
}

.medium {
width: 100px;
height: 100px;
}
23 changes: 23 additions & 0 deletions components/Avatar/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import Image from "next/image";
import classNames from "classnames";
import styles from "@/components/Avatar/Avatar.module.css";

interface AvatarProps {
src: string;
size: string;
}

function Avatar({ src, size }: AvatarProps) {
const avatarClass = classNames(styles.avatar, {
[styles.small]: size === "small",
[styles.medium]: size === "medium",
});

return (
<div className={avatarClass}>
<Image fill src={src} alt="avatar" />
</div>
);
}

export default Avatar;
16 changes: 16 additions & 0 deletions components/FolderController/FoldersController.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.container {
width: 1060px;
margin: 0 auto;
}

@media (min-width: 768px) and (max-width: 1199px) {
.container {
width: 705px;
}
}

@media (min-width: 375px) and (max-width: 767px) {
.container {
width: 325px;
}
}
74 changes: 74 additions & 0 deletions components/FolderController/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { useState } from "react";
import { useRouter } from "next/router";
import useAsync from "@/lib/useAsync";
import { FolderData, LinkData, getLinksByUserIdAndFolderId } from "@/lib/api";
import SearchBar from "@/components/SearchBar";
import FoldersList from "@/components/FoldersList";
import FolderLinkCards from "@/components/FolderLinkCards";
import styles from "@/components/FolderController/FoldersController.module.css";

interface FoldersControllerProps {
folders: FolderData[];
userId: number;
}

function FoldersController({ folders, userId }: FoldersControllerProps) {
const [searchedLinks, setSearchedLinks] = useState<LinkData[] | null>(null);
const [selectedFolderId, setSelectedFolderId] = useState<number | null>(null);
const router = useRouter();
const {
value: links,
isLoading,
error,
} = useAsync<LinkData[]>(
getLinksByUserIdAndFolderId,
userId,
selectedFolderId
);

const handleClickFolder = (folderId: number | null) => {
router.push({
pathname: router.pathname,
query: { ...router.query, folderId },
});

if (!folderId) {
router.push({
pathname: router.pathname,
});
}

setSelectedFolderId(folderId);
};

const handleSearchByKeyword = (keyword: string) => {
if (!links) return console.log("링크가 존재하지 않습니다!");
const searchedLink = links?.filter((link) => link.title?.includes(keyword));
if (!searchedLink) return console.log("해당 링크가 존재하지 않습니다!");
setSearchedLinks(searchedLink);
};

return (
<section className={styles.container}>
<SearchBar onSearch={handleSearchByKeyword} />
{isLoading ? (
<div>Loading...</div>
) : error ? (
<div>Error loading data.</div>
) : (
links && (
<>
<FoldersList
handleClick={handleClickFolder}
folders={folders}
selectedFolderId={selectedFolderId}
/>
<FolderLinkCards links={links} searchedLinks={searchedLinks} />
</>
)
)}
</section>
);
}

export default FoldersController;
94 changes: 94 additions & 0 deletions components/FolderLinkCard/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { ReactElement, useState } from "react";
import Image from "next/image";
import { LinkData } from "@/lib/api";
import { displayCreatedTime, formatDateString } from "@/lib/dateUtils";
import Modal from "@/components/Modal";
import styles from "@/components/LinkCard.module.css";
import { FaRegStar } from "react-icons/fa";
import { GoKebabHorizontal } from "react-icons/go";
import LinkDeleteForm from "../Modal/childrens/LinkDeleteForm";
import LinkAddToFolderForm from "../Modal/childrens/LinkAddToFolderForm";

interface FolderLinkCardProps {
link: LinkData;
}

interface ActionTypes {
[actionType: string]: ReactElement;
}

function FolderLinkCard({ link }: FolderLinkCardProps) {
const [onModal, setOnModal] = useState(false);
const [modalContent, setModalContent] = useState<ReactElement | null>(null);

const handleClickModal = (actionType: string) => {
const actionTypes: ActionTypes = {
deleteLink: <LinkDeleteForm />,
addLink: <LinkAddToFolderForm />,
};

setModalContent(actionTypes[actionType]);
setOnModal(true);
};

const { url, description, title, created_at, image_source } = link;

const createdTime = displayCreatedTime(created_at);
const createdAtFormat = formatDateString(created_at);

const src = image_source || "/card-default.png";

const handleToggleDropDown = (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault();
e.stopPropagation();

const button = e.currentTarget;
const linkInfo = button.closest(`.${styles.linkInfo}`);
const dropdown = linkInfo?.querySelector(`.${styles.dropdown}`);

dropdown?.classList.toggle(styles.hidden);
};

return (
<div className={styles.linkContainer}>
<a href={url} target="_blank" rel="noreferrer">
<div className={styles.imageWrapper}>
<div className={styles.linkImage}>
<Image
fill
src={src}
alt={title || "링크 카드"}
style={{ objectFit: "cover" }}
sizes="(max-width: 767px) 325px, 340px"
priority
/>
</div>
<FaRegStar className={styles.starIcon} />
</div>
</a>
<div className={styles.linkInfo}>
<div className={styles.linkInfoContent}>
<div>{createdTime}</div>
<button onClick={handleToggleDropDown} className={styles.kebabButton}>
<GoKebabHorizontal />
</button>
</div>
<div className={styles.linkInfoContent}>{description}</div>
<div className={styles.linkInfoContent}>{createdAtFormat}</div>
<div className={`${styles.dropdown} ${styles.hidden}`}>
<button onClick={() => handleClickModal("deleteLink")}>
삭제하기
</button>
<button onClick={() => handleClickModal("addLink")}>
폴더에 추가
</button>
</div>
</div>
{onModal && (
<Modal onClick={() => setOnModal(false)}>{modalContent}</Modal>
)}
</div>
);
}

export default FolderLinkCard;
32 changes: 32 additions & 0 deletions components/FolderLinkCards/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { LinkData } from "@/lib/api";
import FolderLinkCard from "@/components/FolderLinkCard";
import styles from "@/components/LinkCards.module.css";

interface FolderLinkCardsProps {
links: LinkData[];
searchedLinks: LinkData[] | null;
}

function FolderLinkCards({ links, searchedLinks }: FolderLinkCardsProps) {
return (
<>
{links.length > 0 ? (
<section className={styles.container}>
<ul className={styles.linkList}>
{(searchedLinks ? searchedLinks : links).map((link) => (
<li key={link.id}>
<FolderLinkCard link={link} />
</li>
))}
</ul>
</section>
) : (
<div className={`${styles.container} ${styles.empty}`}>
저장된 링크가 없습니다.
</div>
)}
</>
);
}

export default FolderLinkCards;
Loading
Loading