Skip to content

Commit 610ab44

Browse files
committed
Merge branch 'develop' of https://github.com/codeit9-temporary/linkbrary into feature/Header
2 parents 77bb226 + 8f8bf27 commit 610ab44

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+1134
-64
lines changed

components/Auth/AuthInput.tsx

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import Image from "next/image";
2+
import React, { InputHTMLAttributes, useState } from "react";
3+
4+
interface AuthInputProps extends InputHTMLAttributes<HTMLInputElement> {
5+
text: string;
6+
name: string;
7+
}
8+
9+
const AuthInput = ({ text, name, type, ...props }: AuthInputProps) => {
10+
const [isPasswordVisible, setIsPasswordVisible] = useState(false);
11+
12+
const toggleClick = () => {
13+
setIsPasswordVisible((prev) => !prev);
14+
};
15+
16+
return (
17+
<div className="mb-6">
18+
<label htmlFor={name} className="text-[14px] block mb-[12px]">
19+
{text}
20+
</label>
21+
<div className="relative">
22+
<input
23+
{...props}
24+
type={isPasswordVisible ? "text" : type}
25+
className="w-full h-[60px] rounded-lg border border-gray300 px-[15px] py-[18px] pr-[40px] outline-purple100"
26+
/>
27+
{type === "password" && (
28+
<Image
29+
src={
30+
isPasswordVisible
31+
? "/icons/eyes_open.svg"
32+
: "/icons/eyes_close.svg"
33+
}
34+
width={16}
35+
height={16}
36+
alt={isPasswordVisible ? "비밀번호 숨기기" : "비밀번호 보기"}
37+
onClick={toggleClick}
38+
className="absolute top-1/2 right-4 transform -translate-y-1/2 cursor-pointer"
39+
/>
40+
)}
41+
</div>
42+
</div>
43+
);
44+
};
45+
46+
export default AuthInput;

components/Auth/SnsLogin.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import Image from "next/image";
2+
3+
const SnsLogin = () => {
4+
return (
5+
<div className="flex items-center justify-between bg-gray300 rounded-lg px-6 py-3 mt-8">
6+
<span>소셜 로그인</span>
7+
<div className="flex gap-4">
8+
<Image src="/icons/google.svg" width="42" height="42" alt="구글" />
9+
<Image
10+
src="/icons/kakaotalk.svg"
11+
width="42"
12+
height="42"
13+
alt="카카오톡"
14+
/>
15+
</div>
16+
</div>
17+
);
18+
};
19+
20+
export default SnsLogin;

components/Button.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const Button = ({
1616
radius = "8px",
1717
color = "positive",
1818
size = "18px",
19+
className = "",
1920
...props
2021
}: ButtonProps) => {
2122
const backgroundStyle =
@@ -29,7 +30,7 @@ const Button = ({
2930
borderRadius: radius,
3031
background: backgroundStyle,
3132
}}
32-
className={`flex justify-center ${width} ${height} ${size} items-center text-white font-[600] whitespace-nowrap hover:opacity-90`}
33+
className={`flex justify-center ${width} ${height} ${size} ${className} items-center text-white font-[600] whitespace-nowrap hover:opacity-90`}
3334
{...props}
3435
>
3536
{children}

components/FolderTag.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
interface FolderData {
2+
id: number;
3+
createdAt: string;
4+
name: string;
5+
linkCount: number;
6+
}
7+
8+
const FolderTag = (list: FolderData[]) => {
9+
const folderStyle = "py-[8px] px-[12px] border border-purple-100 rounded-md";
10+
11+
return (
12+
<div className="flex flex-wrap gap-[8px]">
13+
<div className={folderStyle}>전체</div>
14+
{list.map((folder) => (
15+
<div key={folder.id} className={folderStyle}>
16+
{folder.name}
17+
</div>
18+
))}
19+
</div>
20+
);
21+
};
22+
23+
export default FolderTag;

components/Layout/AuthLayout.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import Image from "next/image";
2+
import { useRouter } from "next/router";
3+
import { ReactNode } from "react";
4+
5+
const AuthLayout = ({ children }: { children: ReactNode }) => {
6+
const router = useRouter();
7+
return (
8+
<div className="mx-auto bg-gray100 flex flex-col items-center justify-center h-screen py-32">
9+
<div>
10+
<Image
11+
className="cursor-pointer"
12+
src="/icons/logo.svg"
13+
width="211"
14+
height="38"
15+
alt="로고"
16+
onClick={() => {
17+
router.push("/");
18+
}}
19+
/>
20+
</div>
21+
{children}
22+
</div>
23+
);
24+
};
25+
26+
export default AuthLayout;
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import React from "react";
22

3-
interface ContentLayoutProps {
3+
interface ContainerProps {
44
children: React.ReactNode;
55
}
66

7-
const ContentLayout = ({ children }: ContentLayoutProps) => {
7+
const Container = ({ children }: ContainerProps) => {
88
return (
99
<div className="w-full max-w-[1125px] mx-auto p-[10px] md:p-10 px-[32.5px]">
1010
{children}
1111
</div>
1212
);
1313
};
1414

15-
export default ContentLayout;
15+
export default Container;

components/Link/AddLinkInput.tsx

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { ChangeEvent, FormEvent, useState } from "react";
2+
import { postLink } from "@/lib/api/link";
3+
import Image from "next/image";
4+
import Button from "../Button";
5+
6+
const AddLinkInput = (folderId: number) => {
7+
const [value, setValue] = useState("");
8+
9+
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
10+
setValue(e.target.value);
11+
};
12+
13+
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
14+
e.preventDefault();
15+
await postLink({ url: value, folderId });
16+
// postLink 하고 추가된 link가 보이도록 하는 로직 구현해야 함.
17+
};
18+
19+
return (
20+
<form
21+
onSubmit={handleSubmit}
22+
className="flex gap-[12px] items-center w-[800px] h-[69px] py-[16px] px-[20px] border border-blue-500 rounded-[10px] md:w-[704px] sm:w-[325px] sm:h-[53px] transition-all"
23+
>
24+
<Image src="/icons/link.svg" width={20} height={20} alt="link icon" />
25+
<input
26+
onChange={handleChange}
27+
value={value}
28+
placeholder="링크를 추가해 보세요."
29+
className="flex-grow"
30+
/>
31+
<Button color="positive" className="w-[80px] h-[37px]">
32+
추가하기
33+
</Button>
34+
</form>
35+
);
36+
};
37+
38+
export default AddLinkInput;

components/LinkCard.tsx

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { useState } from "react";
2+
import timeAgo from "@/util/timAgo";
3+
import Image from "next/image";
4+
5+
interface linkDataType {
6+
id: number;
7+
title: string;
8+
description: string;
9+
favorite: boolean;
10+
imageSource: string;
11+
url: string;
12+
createdAt: string;
13+
}
14+
15+
const LinkCard = (info: linkDataType) => {
16+
const [isSubscribed, seIsSubscribed] = useState(false);
17+
const [isOpen, setIsOpen] = useState(false);
18+
19+
const formattedDate = info.createdAt?.slice(0, 10).replace(/-/g, ".");
20+
const createdTime = timeAgo(info.createdAt);
21+
22+
return (
23+
<div className="w-[340px] h-[344px] rounded-[12px] shadow-lg mt-20 ml-20 overflow-hidden cursor-pointer hover:scale-105 hover:duration-300">
24+
<section className="relative w-full h-[60%]">
25+
<Image
26+
src={info.imageSource || `/images/no-content.svg`}
27+
objectFit="cover"
28+
alt="링크 미리보기"
29+
fill
30+
/>
31+
{isSubscribed ? (
32+
<div
33+
onClick={() => seIsSubscribed(!isSubscribed)}
34+
className="absolute top-[15px] right-[15px] z-1"
35+
>
36+
<Image
37+
src="/icons/star-fill.svg"
38+
width={32}
39+
height={32}
40+
alt="subscripe button"
41+
/>
42+
</div>
43+
) : (
44+
<div
45+
onClick={() => seIsSubscribed(!isSubscribed)}
46+
className="absolute top-[15px] right-[15px] z-1"
47+
>
48+
<Image
49+
src="/icons/star-empty.svg"
50+
width={32}
51+
height={32}
52+
alt="subscripe button"
53+
/>
54+
</div>
55+
)}
56+
</section>
57+
58+
<section className="w-full h-[40%] flex flex-col justify-between gap-[10px] pt-[15px] px-[20px] pb-[10px]">
59+
<div className="flex justify-between">
60+
<span className="text-sm text-gray-400">
61+
{createdTime || "1일 전"}
62+
</span>
63+
<div
64+
className="relative w-[21px] h-[17px]"
65+
onClick={(state) => setIsOpen(!state)}
66+
>
67+
<Image src="/icons/kebab.svg" alt="kebab button" fill />
68+
</div>
69+
</div>
70+
<div className="text-[black100] text-lg ">
71+
{info.description || "설명"}
72+
</div>
73+
<div className="text-sm text-[black200]">
74+
{formattedDate || "2024.11.06"}
75+
</div>
76+
</section>
77+
</div>
78+
);
79+
};
80+
81+
export default LinkCard;

components/Search/SearchInput.tsx

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { ChangeEvent, FormEvent, useState } from "react";
2+
import { useRouter } from "next/router";
3+
import Image from "next/image";
4+
5+
export const SearchInput = () => {
6+
const router = useRouter();
7+
const [value, setValue] = useState("");
8+
9+
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
10+
setValue(e.target.value);
11+
};
12+
13+
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
14+
e.preventDefault();
15+
router.push({
16+
pathname: router.pathname,
17+
query: { ...router.query, search: value },
18+
});
19+
};
20+
21+
return (
22+
<form
23+
onSubmit={handleSubmit}
24+
className="flex gap-[8px] w-[1024px] h-[54px] items-center px-[16px] py-[15px] bg-gray-100 rounded-[10px] md:w-[704px] md:h-[54px] sm:w-[325px] sm:h-[43px] transition-all"
25+
>
26+
<Image
27+
src="/icons/search.svg"
28+
width={16}
29+
height={16}
30+
alt="search button"
31+
/>
32+
<input
33+
value={value}
34+
onChange={handleChange}
35+
placeholder="링크를 검색해 보세요."
36+
className="flex-grow bg-transparent placeholder:text-gray-500"
37+
/>
38+
</form>
39+
);
40+
};
41+
42+
export default SearchInput;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import ModalContainer from "./modalComponents/ModalContainer";
2+
import ModalInput from "./modalComponents/ModalInput";
3+
4+
const AddFolderModal = ({ folderName }: { folderName: string }) => {
5+
return (
6+
<ModalContainer title="폴더 추가" buttonText="추가하기">
7+
<ModalInput placeholder="내용 입력" name={folderName} />
8+
</ModalContainer>
9+
);
10+
};
11+
12+
export default AddFolderModal;

0 commit comments

Comments
 (0)