-
Notifications
You must be signed in to change notification settings - Fork 51
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #490 from Accept77/part3-양진수
- Loading branch information
Showing
56 changed files
with
2,797 additions
and
536 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"presets": ["next/babel"], | ||
"plugins": [ | ||
[ | ||
"styled-components", | ||
{ | ||
"ssr": true, | ||
"displayName": true, | ||
"preprocess": true | ||
} | ||
] | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import axios from "axios"; | ||
|
||
const apiUrl = "https://bootcamp-api.codeit.kr/api"; | ||
|
||
export default async function getApi(path: string) { | ||
try { | ||
const res = await axios.get(`${apiUrl}${path}`); | ||
if (res.status === 200) { | ||
return res.data; | ||
} | ||
} catch (e) { | ||
throw new Error("Unknown error"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
import styled from "styled-components"; | ||
import star from "../../public/images/card/star.png"; | ||
import kebab from "../../public/images/card/kebab.svg"; | ||
import { useState } from "react"; | ||
import Image from "next/image"; | ||
import Link from "next/link"; | ||
import dateCalculator from "@/utils/dateCalculator"; | ||
import { CardProps, CardsProps } from "../../types/type"; | ||
import filterItems from "./filterItems"; | ||
import { NoLink } from "@/pages/folder"; | ||
|
||
function Card({ item, setClose, setTag, close }: CardProps) { | ||
const [status, setStatus] = useState(true); | ||
|
||
function handlePopOver(tag: string) { | ||
setStatus(!status); | ||
if (setClose !== undefined && setTag !== undefined) { | ||
setClose(!close); | ||
setTag(tag); | ||
} | ||
} | ||
|
||
const apiDate = new Date(item.createdAt as string); | ||
const elapsedTime = dateCalculator(apiDate); | ||
const year = apiDate.getFullYear(); | ||
const month = apiDate.getMonth() + 1; | ||
const days = apiDate.getDate(); | ||
return ( | ||
<StyledCardBox> | ||
<StyledHoverImg> | ||
<Image src={star} alt="star" /> | ||
</StyledHoverImg> | ||
<Link href={item.url} target="_blank" rel="noopener noreferrer"> | ||
<StyledCardImg | ||
$bg={item.imageSource ?? "/images/card/no-image.svg"} | ||
/> | ||
</Link> | ||
<StyledTextBox> | ||
<StyledHoverImg onClick={() => setStatus(!status)}> | ||
<Image src={kebab} alt="kebab" /> | ||
</StyledHoverImg> | ||
<StyledPopOverBox $status={status}> | ||
<StyledPopOver onClick={() => handlePopOver("deleteLink")}> | ||
삭제하기 | ||
</StyledPopOver> | ||
<StyledPopOver onClick={() => handlePopOver("add")}> | ||
폴더에 추가 | ||
</StyledPopOver> | ||
</StyledPopOverBox> | ||
<div>{elapsedTime}</div> | ||
<StyledDescription>{item.description}</StyledDescription> | ||
<div>{`${year}. ${month}. ${days}`}</div> | ||
</StyledTextBox> | ||
</StyledCardBox> | ||
); | ||
} | ||
|
||
export default function Cards({ | ||
items, | ||
setClose, | ||
setTag, | ||
close, | ||
search, | ||
}: CardsProps) { | ||
if (items === undefined) return; | ||
const newItems = filterItems(items, search); | ||
return ( | ||
<StyledCardGrid> | ||
{newItems.length === 0 ? ( | ||
<NoLink /> | ||
) : ( | ||
newItems.map((item) => ( | ||
<Card | ||
key={item.id} | ||
item={item} | ||
setClose={setClose} | ||
setTag={setTag} | ||
close={close} | ||
/> | ||
)) | ||
)} | ||
</StyledCardGrid> | ||
); | ||
} | ||
|
||
const StyledHoverImg = styled.div` | ||
position: absolute; | ||
top: 15px; | ||
right: 15px; | ||
cursor: pointer; | ||
`; | ||
|
||
const StyledDescription = styled.div` | ||
text-overflow: ellipsis; | ||
white-space: normal; | ||
display: -webkit-box; | ||
-webkit-line-clamp: 2; | ||
-webkit-box-orient: vertical; | ||
overflow: hidden; | ||
color: #000; | ||
`; | ||
|
||
const StyledTextBox = styled.div` | ||
display: flex; | ||
padding: 15px 20px; | ||
flex-direction: column; | ||
gap: 10px; | ||
position: relative; | ||
`; | ||
|
||
const StyledCardBox = styled.div` | ||
width: 340px; | ||
background-color: #fff; | ||
border-radius: 15px; | ||
position: relative; | ||
`; | ||
|
||
const StyledCardImg = styled.div<{ $bg: string }>` | ||
overflow: hidden; | ||
width: 340px; | ||
height: 253.746px; | ||
background-image: url(${({ $bg }) => $bg}); | ||
background-size: cover; | ||
background-position: center; | ||
background-repeat: no-repeat; | ||
border-radius: 15px 15px 0px 0px; | ||
&:hover { | ||
background-size: 160%; | ||
} | ||
`; | ||
|
||
const StyledCardGrid = styled.div` | ||
display: grid; | ||
grid-template-columns: repeat(3, 1fr); | ||
gap: 25px 20px; | ||
@media (max-width: 767px) { | ||
grid-template-columns: 1fr; | ||
gap: 20px 0; | ||
margin: 0 32px; | ||
align-items: center; | ||
justify-items: center; | ||
} | ||
@media (min-width: 768px) and (max-width: 1124px) { | ||
grid-template-columns: repeat(2, 1fr); | ||
gap: 24px 25px; | ||
margin: 0 32px; | ||
align-items: center; | ||
justify-items: center; | ||
} | ||
`; | ||
|
||
const StyledPopOverBox = styled.div<{ $status: boolean }>` | ||
background-color: #fff; | ||
display: flex; | ||
width: 100px; | ||
flex-direction: column; | ||
gap: 2px; | ||
box-shadow: 0px 2px 8px 0px rgba(51, 50, 54, 0.1); | ||
position: absolute; | ||
top: 30px; | ||
right: -60px; | ||
z-index: 1; | ||
text-align: center; | ||
display: ${({ $status }) => ($status ? "none" : "")}; | ||
`; | ||
|
||
const StyledPopOver = styled.div` | ||
padding: 7px 12px; | ||
font-size: 14px; | ||
&:hover { | ||
background-color: #e7effb; | ||
color: #6d6afe; | ||
} | ||
cursor: pointer; | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { CardItem } from "@/types/type"; | ||
|
||
export default function filterItems( | ||
items: CardItem[], | ||
search: string | undefined | ||
) { | ||
const filterItems = items.map((item) => { | ||
const newItem = { ...item }; | ||
if ("created_at" in newItem && "image_source" in newItem) { | ||
if ( | ||
typeof newItem.created_at === "string" || | ||
typeof newItem.image_source === "string" | ||
) { | ||
newItem.createdAt = newItem.created_at; | ||
delete newItem.created_at; | ||
newItem.imageSource = newItem.image_source!; | ||
delete newItem.image_source; | ||
} | ||
} | ||
return newItem; | ||
}); | ||
if (search !== undefined && search !== "") { | ||
const searchItems = filterItems.filter( | ||
(item) => | ||
(item.title !== null && item.title.search(search) !== -1) || | ||
(item.url !== null && item.url.search(search) !== -1) || | ||
(item.description !== null && | ||
item.description.search(search) !== -1) | ||
); | ||
return searchItems; | ||
} | ||
return filterItems; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import facebookIcon from "../../public/images/footer/facebook-fill.png"; | ||
import twitterIcon from "../../public/images/footer/twitter-fill.png"; | ||
import youtubeIcon from "../../public/images/footer/youtube-fill.png"; | ||
import instagramIcon from "../../public/images/footer/instagram-filled.png"; | ||
import styled from "styled-components"; | ||
import Image from "next/image"; | ||
import Link from "next/link"; | ||
|
||
export default function Footer() { | ||
return ( | ||
<StyledFooterBox> | ||
©codeit - 2023 | ||
<StyledFaq> | ||
<Link href="/privacy">Privacy Policy</Link> | ||
<Link href="/faq">FAQ</Link> | ||
</StyledFaq> | ||
<StyledSns> | ||
<Link href="https://www.facebook.com/"> | ||
<Image src={facebookIcon} alt="facebook" /> | ||
</Link> | ||
<Link href="https://twitter.com/"> | ||
<Image src={twitterIcon} alt="twitter" /> | ||
</Link> | ||
<Link href="https://www.youtube.com/"> | ||
<Image src={youtubeIcon} alt="youtube" /> | ||
</Link> | ||
<Link href="https://www.instagram.com/"> | ||
<Image src={instagramIcon} alt="instagram" /> | ||
</Link> | ||
</StyledSns> | ||
</StyledFooterBox> | ||
); | ||
} | ||
|
||
const StyledFooterBox = styled.div` | ||
height: 160px; | ||
padding: 32px 104px 108px; | ||
display: flex; | ||
justify-content: space-between; | ||
align-items: center; | ||
background: #111322; | ||
color: #676767; | ||
`; | ||
|
||
const StyledFaq = styled.div` | ||
display: flex; | ||
gap: 30px; | ||
a { | ||
color: #676767; | ||
} | ||
`; | ||
|
||
const StyledSns = styled.div` | ||
display: flex; | ||
gap: 12px; | ||
`; |
Oops, something went wrong.