diff --git a/component/Button/Button.styled.ts b/component/Button/Button.styled.ts new file mode 100644 index 0000000..7185683 --- /dev/null +++ b/component/Button/Button.styled.ts @@ -0,0 +1,53 @@ +import styled from '@emotion/styled'; +import { css } from '@emotion/react'; +import { ButtonProps } from './Button.types'; + +const getSize = ({ size }: Pick) => { + switch (size) { + case 'XS': + return css` + width: 72px; + height: 24px; + `; + case 'S': + return css` + width: 84px; + height: 37px; + `; + case 'L': + return css` + width: 320px; + height: 54px; + `; + } +}; + +export const ButtonWrapper = styled.button< + Omit +>` + ${(props) => getSize(props)}; + padding-block: 0; + padding-inline: 0; + border: 0; + border-radius: ${(props) => (props.radius ? '12px' : 0)}; + + background-color: ${(props) => props.backgroundColor}; + font-weight: 700; + + &:hover { + opacity: 80%; + box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.24); + } + + &:active { + opacity: 40%; + } + + &:disabled { + pointer-events: none; + } + + input[type='submit'] { + display: none; + } +`; diff --git a/component/Button/Button.tsx b/component/Button/Button.tsx new file mode 100644 index 0000000..e5dddbd --- /dev/null +++ b/component/Button/Button.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import classNames from 'classnames'; + +import { ButtonProps } from './Button.types'; +import { ButtonWrapper } from './Button.styled'; +import { SerializedStyles, css } from '@emotion/react'; + +export const Button = ({ + type = 'button', + className, + size, + disabled = false, + radius = true, + backgroundColor, + onClick, + style, + children, +}: ButtonProps) => { + return ( + <> + { + if (onClick) { + onClick(e); + } + }} + backgroundColor={backgroundColor} + css={style as SerializedStyles} + > + {children} + + + ); +}; diff --git a/component/Button/Button.types.ts b/component/Button/Button.types.ts new file mode 100644 index 0000000..c62b4e0 --- /dev/null +++ b/component/Button/Button.types.ts @@ -0,0 +1,15 @@ +import { SerializedStyles } from '@emotion/react'; +import { Property } from 'csstype'; + +export interface ButtonProps { + type?: 'submit' | 'reset' | 'button' | undefined; + className?: string; + size: 'XS' | 'S' | 'L'; + disabled?: boolean; + radius?: boolean; + backgroundColor: Property.BackgroundColor; + onClick?: React.MouseEventHandler; + style?: SerializedStyles | React.CSSProperties; + children?: React.ReactNode; + isSubmit?: boolean; +} diff --git a/component/Button/index.ts b/component/Button/index.ts new file mode 100644 index 0000000..0e5e7bc --- /dev/null +++ b/component/Button/index.ts @@ -0,0 +1,5 @@ +import { Button } from './Button'; +import { ButtonProps } from './Button.types'; + +export { Button }; +export type { ButtonProps }; diff --git a/component/Card.tsx b/component/Card.tsx index 9a80d75..b6bc565 100644 --- a/component/Card.tsx +++ b/component/Card.tsx @@ -1,5 +1,5 @@ import { pallete } from '@/styles/Color'; -import { BodyMedium, Headline_02, Subtitle_02 } from '@/styles/Typography'; +import { BodyMedium, Headline_02, Subtitle_02 } from '@/component/Typography'; import Image from 'next/image'; import React, { forwardRef } from 'react'; diff --git a/component/Collapse/Collapse.styled.ts b/component/Collapse/Collapse.styled.ts new file mode 100644 index 0000000..6e2df80 --- /dev/null +++ b/component/Collapse/Collapse.styled.ts @@ -0,0 +1,5 @@ +import styled from '@emotion/styled'; + +export const CollapseWrapper = styled.div` + width: 100%; +`; diff --git a/component/Collapse/Collapse.tsx b/component/Collapse/Collapse.tsx new file mode 100644 index 0000000..ce169c3 --- /dev/null +++ b/component/Collapse/Collapse.tsx @@ -0,0 +1,53 @@ +import React, { useRef, useState } from 'react'; +import { SerializedStyles, css } from '@emotion/react'; +import Image from 'next/image'; + +import { Button } from '../Button'; +import { Icon } from '../Icon'; +import chevronDown from '../Icon/asset/chevron-down-gray400.svg'; + +import { CollapseProps } from './Collapse.types'; +import { CollapseWrapper } from './Collapse.styled'; + +export const Collapse = ({ + className, + title, + children, + style, +}: CollapseProps) => { + const [isOpen, setIsOpen] = useState(false); + const contentRef = useRef(null); + + const handleClick = () => setIsOpen(!isOpen); + + return ( + + +
+ {children} +
+
+ ); +}; diff --git a/component/Collapse/Collapse.types.ts b/component/Collapse/Collapse.types.ts new file mode 100644 index 0000000..38e806b --- /dev/null +++ b/component/Collapse/Collapse.types.ts @@ -0,0 +1,8 @@ +import { SerializedStyles } from '@emotion/react'; + +export interface CollapseProps { + className?: string; + title: React.ReactNode; + children: React.ReactNode; + style?: SerializedStyles | React.CSSProperties; +} diff --git a/component/Collapse/index.ts b/component/Collapse/index.ts new file mode 100644 index 0000000..82aaa21 --- /dev/null +++ b/component/Collapse/index.ts @@ -0,0 +1,3 @@ +import { Collapse } from './Collapse'; + +export { Collapse }; diff --git a/component/Icon/Icon.styled.ts b/component/Icon/Icon.styled.ts new file mode 100644 index 0000000..660f08e --- /dev/null +++ b/component/Icon/Icon.styled.ts @@ -0,0 +1,11 @@ +import styled from '@emotion/styled'; +import { IconProps } from '.'; + +export const IconWrapper = styled.div>` + width: fit-content; + height: fit-content; + + display: flex; + justify-content: center; + align-items: center; +`; diff --git a/component/Icon/Icon.tsx b/component/Icon/Icon.tsx new file mode 100644 index 0000000..dfb2d9b --- /dev/null +++ b/component/Icon/Icon.tsx @@ -0,0 +1,83 @@ +import React from 'react'; +import { SerializedStyles } from '@emotion/react'; +import parse, { Element, HTMLReactParserOptions } from 'html-react-parser'; + +import { IconWrapper } from './Icon.styled'; +import { IconProps, SvgProps } from './Icon.types'; + +/** + * If you set 'type' as 'svg', you should pass raw svg string data to 'src' + * for vite environment, add postfix '?raw' at asset importing + * ref (https://ko.vitejs.dev/guide/assets.html#importing-asset-as-string) + */ +export const Icon = ({ + className, + type, + color, + src, + alt, + width, + height, + style, +}: IconProps) => { + return ( + + {type === 'svg' ? ( + + ) : ( + {alt} + )} + + ); +}; + +const Svg = ({ color, width, height, src }: SvgProps) => { + const convertSrc = () => { + const widthToken = 'width'; + const heightToken = 'height'; + const fillToken = 'fill'; + + const convertedColor = color; + + const option: HTMLReactParserOptions = { + replace: (domNode) => { + if ( + domNode instanceof Element && + domNode.attribs && + domNode.name === 'svg' + ) { + let widthFlag = false; + let heightFlag = false; + let fillFlag = false; + + domNode.attributes.forEach((value) => { + if (value.name === widthToken && width && !widthFlag) { + domNode.attribs[widthToken] = String(width); + widthFlag = true; + } + if (value.name === heightToken && height && !heightFlag) { + domNode.attribs[heightToken] = String(height); + heightFlag = true; + } + if (value.name === fillToken && convertedColor && !fillFlag) { + domNode.attribs[fillToken] = convertedColor; + fillFlag = true; + } + }); + + if (!widthFlag && width) domNode.attribs[widthToken] = String(width); + if (!heightFlag && height) + domNode.attribs[heightToken] = String(height); + if (!fillFlag && convertedColor) + domNode.attribs[fillToken] = convertedColor; + } + + return domNode; + }, + }; + + return parse(src, option); + }; + + return convertSrc(); +}; diff --git a/component/Icon/Icon.types.ts b/component/Icon/Icon.types.ts new file mode 100644 index 0000000..cda848b --- /dev/null +++ b/component/Icon/Icon.types.ts @@ -0,0 +1,20 @@ +import { Property } from 'csstype'; +import { SerializedStyles } from '@emotion/react'; + +export interface IconProps { + className?: string; + type: 'svg' | 'png' | 'jpg'; + src: string; + alt: string; + width?: number | string; + height?: number | string; + color?: Property.BackgroundColor; + style?: SerializedStyles | React.CSSProperties; +} + +export interface SvgProps { + color?: Property.BackgroundColor; + width?: number | string; + height?: number | string; + src: string; +} diff --git a/component/Icon/asset/add-and-share.svg b/component/Icon/asset/add-and-share.svg new file mode 100644 index 0000000..ad3ec12 --- /dev/null +++ b/component/Icon/asset/add-and-share.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/component/Icon/asset/bell.svg b/component/Icon/asset/bell.svg new file mode 100644 index 0000000..4607115 --- /dev/null +++ b/component/Icon/asset/bell.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/component/Icon/asset/check.svg b/component/Icon/asset/check.svg new file mode 100644 index 0000000..d5f9eca --- /dev/null +++ b/component/Icon/asset/check.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/component/Icon/asset/chevron-down-gray400.svg b/component/Icon/asset/chevron-down-gray400.svg new file mode 100644 index 0000000..fd21d8f --- /dev/null +++ b/component/Icon/asset/chevron-down-gray400.svg @@ -0,0 +1,3 @@ + + + diff --git a/component/Icon/asset/chevron-down.svg b/component/Icon/asset/chevron-down.svg new file mode 100644 index 0000000..5af6f9a --- /dev/null +++ b/component/Icon/asset/chevron-down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/component/Icon/asset/chevron-left.svg b/component/Icon/asset/chevron-left.svg new file mode 100644 index 0000000..6742acc --- /dev/null +++ b/component/Icon/asset/chevron-left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/component/Icon/asset/chevron-right.jpg b/component/Icon/asset/chevron-right.jpg new file mode 100644 index 0000000..07a7c34 Binary files /dev/null and b/component/Icon/asset/chevron-right.jpg differ diff --git a/component/Icon/asset/chevron-right.png b/component/Icon/asset/chevron-right.png new file mode 100644 index 0000000..9d02580 Binary files /dev/null and b/component/Icon/asset/chevron-right.png differ diff --git a/component/Icon/asset/chevron-right.svg b/component/Icon/asset/chevron-right.svg new file mode 100644 index 0000000..5ad1387 --- /dev/null +++ b/component/Icon/asset/chevron-right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/component/Icon/asset/dash.svg b/component/Icon/asset/dash.svg new file mode 100644 index 0000000..a962682 --- /dev/null +++ b/component/Icon/asset/dash.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/component/Icon/asset/discussion-outdated.svg b/component/Icon/asset/discussion-outdated.svg new file mode 100644 index 0000000..38b6c2a --- /dev/null +++ b/component/Icon/asset/discussion-outdated.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/component/Icon/asset/gift.svg b/component/Icon/asset/gift.svg new file mode 100644 index 0000000..5c88d37 --- /dev/null +++ b/component/Icon/asset/gift.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/component/Icon/asset/megaphone.svg b/component/Icon/asset/megaphone.svg new file mode 100644 index 0000000..af0d1b7 --- /dev/null +++ b/component/Icon/asset/megaphone.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/component/Icon/asset/message-square.svg b/component/Icon/asset/message-square.svg new file mode 100644 index 0000000..593a492 --- /dev/null +++ b/component/Icon/asset/message-square.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/component/Icon/asset/person.svg b/component/Icon/asset/person.svg new file mode 100644 index 0000000..19e1b5f --- /dev/null +++ b/component/Icon/asset/person.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/component/Icon/asset/plus.svg b/component/Icon/asset/plus.svg new file mode 100644 index 0000000..cf7b9b3 --- /dev/null +++ b/component/Icon/asset/plus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/component/Icon/asset/project-roadmap.svg b/component/Icon/asset/project-roadmap.svg new file mode 100644 index 0000000..37e81d9 --- /dev/null +++ b/component/Icon/asset/project-roadmap.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/component/Icon/asset/ranking-1.svg b/component/Icon/asset/ranking-1.svg new file mode 100644 index 0000000..9b6ecad --- /dev/null +++ b/component/Icon/asset/ranking-1.svg @@ -0,0 +1,4 @@ + + + + diff --git a/component/Icon/asset/ranking-2.svg b/component/Icon/asset/ranking-2.svg new file mode 100644 index 0000000..d1bcaa8 --- /dev/null +++ b/component/Icon/asset/ranking-2.svg @@ -0,0 +1,4 @@ + + + + diff --git a/component/Icon/asset/ranking-3.svg b/component/Icon/asset/ranking-3.svg new file mode 100644 index 0000000..60e9169 --- /dev/null +++ b/component/Icon/asset/ranking-3.svg @@ -0,0 +1,4 @@ + + + + diff --git a/component/Icon/asset/ranking-down.svg b/component/Icon/asset/ranking-down.svg new file mode 100644 index 0000000..23686a1 --- /dev/null +++ b/component/Icon/asset/ranking-down.svg @@ -0,0 +1,3 @@ + + + diff --git a/component/Icon/asset/ranking-up.svg b/component/Icon/asset/ranking-up.svg new file mode 100644 index 0000000..7aa97b2 --- /dev/null +++ b/component/Icon/asset/ranking-up.svg @@ -0,0 +1,3 @@ + + + diff --git a/component/Icon/asset/report.svg b/component/Icon/asset/report.svg new file mode 100644 index 0000000..66a48dd --- /dev/null +++ b/component/Icon/asset/report.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/component/Icon/asset/search-white.svg b/component/Icon/asset/search-white.svg new file mode 100644 index 0000000..5525cf5 --- /dev/null +++ b/component/Icon/asset/search-white.svg @@ -0,0 +1,4 @@ + + + + diff --git a/component/Icon/asset/search.svg b/component/Icon/asset/search.svg new file mode 100644 index 0000000..e964f24 --- /dev/null +++ b/component/Icon/asset/search.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/component/Icon/asset/shield-lock.svg b/component/Icon/asset/shield-lock.svg new file mode 100644 index 0000000..9760482 --- /dev/null +++ b/component/Icon/asset/shield-lock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/component/Icon/asset/three-bars.svg b/component/Icon/asset/three-bars.svg new file mode 100644 index 0000000..d5dc974 --- /dev/null +++ b/component/Icon/asset/three-bars.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/component/Icon/index.ts b/component/Icon/index.ts new file mode 100644 index 0000000..5e625c0 --- /dev/null +++ b/component/Icon/index.ts @@ -0,0 +1,6 @@ +import { Icon } from './Icon'; +import { IconProps } from './Icon.types'; + +export { Icon }; + +export type { IconProps }; diff --git a/component/Menu.tsx b/component/Menu.tsx index 10186db..17e3e92 100644 --- a/component/Menu.tsx +++ b/component/Menu.tsx @@ -1,5 +1,5 @@ import { pallete } from '@/styles/Color'; -import { BodyLarge } from '@/styles/Typography'; +import { BodyLarge } from '@/component/Typography'; import { ActionList } from '@primer/react'; import Link from 'next/link'; import React from 'react'; @@ -8,10 +8,11 @@ const Menu = () => { return ( <>
diff --git a/component/Spacing/Spacing.tsx b/component/Spacing/Spacing.tsx new file mode 100644 index 0000000..2bc780f --- /dev/null +++ b/component/Spacing/Spacing.tsx @@ -0,0 +1,5 @@ +import React from 'react'; + +export const Spacing = ({ size }: { size: number }) => { + return
; +}; diff --git a/component/Spacing/index.ts b/component/Spacing/index.ts new file mode 100644 index 0000000..e60f421 --- /dev/null +++ b/component/Spacing/index.ts @@ -0,0 +1,3 @@ +import { Spacing } from './Spacing'; + +export { Spacing }; diff --git a/component/TopBar.tsx b/component/TopBar.tsx index 1974d56..1b4ee64 100644 --- a/component/TopBar.tsx +++ b/component/TopBar.tsx @@ -2,7 +2,7 @@ import { Button, Header } from '@primer/react'; import Image from 'next/image'; import logo from '../public/app_icon.svg'; -import { Headline_00 } from '@/styles/Typography'; +import { Headline_00 } from '@/component/Typography'; import { pallete } from '@/styles/Color'; import Link from 'next/link'; import { useSearchParams } from 'next/navigation'; @@ -98,7 +98,7 @@ const TopBar = ({ router }: { router: NextRouter }) => { return ( <> -
+
logo diff --git a/styles/Typography.ts b/component/Typography.ts similarity index 95% rename from styles/Typography.ts rename to component/Typography.ts index d8d2066..bc624f0 100644 --- a/styles/Typography.ts +++ b/component/Typography.ts @@ -24,6 +24,14 @@ export const Headline_02 = styled.div` line-height: 28px; `; +export const Headline_02_Light = styled.div` + font-family: 'Pretendard'; + font-style: normal; + font-weight: 500; + font-size: 20px; + line-height: 28px; +`; + export const Subtitle_01 = styled.div` font-family: 'Pretendard'; font-style: normal; @@ -103,11 +111,3 @@ export const Body_01 = styled.div` font-size: 15px; line-height: 24px; `; - -export const Button = styled.div` - font-family: 'Pretendard'; - font-style: normal; - font-weight: 600; - font-size: 13px; - line-height: 18px; -`; diff --git a/component/index.ts b/component/index.ts new file mode 100644 index 0000000..4aba312 --- /dev/null +++ b/component/index.ts @@ -0,0 +1,37 @@ +import { Spacing } from './Spacing'; +import { + Headline_00, + Headline_01, + Headline_02, + Headline_02_Light, + Subtitle_01, + Subtitle_02, + BodyLarge, + BodyMedium, + BodySmall, + Body_02, + Body_01, + LabelLarge, + LabelMedium, + LabelSmall, + Button, +} from './Typography'; + +export { + Spacing, + Headline_00, + Headline_01, + Headline_02, + Headline_02_Light, + Subtitle_01, + Subtitle_02, + BodyLarge, + BodyMedium, + BodySmall, + Body_02, + Body_01, + LabelLarge, + LabelMedium, + LabelSmall, + Button, +}; diff --git a/next.config.js b/next.config.js index 50f8420..6106a92 100644 --- a/next.config.js +++ b/next.config.js @@ -4,6 +4,15 @@ const nextConfig = { images: { unoptimized: true, }, + webpack: (config, { isServer }) => { + if (!isServer) { + config.resolve.fallback = { + fs: false, + }; + } + + return config; + }, }; module.exports = nextConfig; diff --git a/package.json b/package.json index 841c362..0f9e245 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,10 @@ "@tanstack/react-query-devtools": "^5.17.21", "autoprefixer": "10.4.15", "axios": "^1.5.0", + "classnames": "^2.5.1", "dayjs": "^1.11.9", + "fs": "^0.0.1-security", + "html-react-parser": "^5.1.6", "next": "13.4.19", "postcss": "8.4.28", "react": "^18.2.0", @@ -36,6 +39,7 @@ "@types/react-dom": "18.2.7", "@typescript-eslint/eslint-plugin": "^6.14.0", "@typescript-eslint/parser": "^6.14.0", + "csstype": "^3.1.3", "eslint": "8.47.0", "eslint-config-next": "13.4.19", "eslint-config-prettier": "^9.1.0", diff --git a/pages/_app.tsx b/pages/_app.tsx index 582fe3e..f4a571b 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -4,9 +4,9 @@ import type { AppProps } from 'next/app'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; -const queryClient = new QueryClient(); - export default function App({ Component, pageProps }: AppProps) { + const queryClient = new QueryClient(); + return ( <> diff --git a/pages/admin/cooldown/index.tsx b/pages/admin/cooldown/index.tsx index 613d426..2a06f09 100644 --- a/pages/admin/cooldown/index.tsx +++ b/pages/admin/cooldown/index.tsx @@ -9,7 +9,7 @@ import { Headline_02, Subtitle_01, Subtitle_02, -} from '@/styles/Typography'; +} from '@/component/Typography'; import { SearchIcon, TrashIcon } from '@primer/octicons-react'; import { ActionList, @@ -122,7 +122,7 @@ const CooldownPagination = ({ <> ) : ( <> - +
{ return ( <> -
+
+
-
+
{'Yello 어드민에 오신걸 환영합니다.'}
- +
); } diff --git a/pages/admin/notification/index.tsx b/pages/admin/notification/index.tsx index 0920804..5482a59 100644 --- a/pages/admin/notification/index.tsx +++ b/pages/admin/notification/index.tsx @@ -3,7 +3,7 @@ import Card from '@/component/Card'; import Menu from '@/component/Menu'; import TopBar from '@/component/TopBar'; import { pallete } from '@/styles/Color'; -import { Headline_00, Subtitle_01 } from '@/styles/Typography'; +import { Headline_00, Subtitle_01 } from '@/component/Typography'; import { Button, Spinner, TextInput } from '@primer/react'; import axios, { AxiosError } from 'axios'; import { useRouter } from 'next/router'; @@ -133,13 +133,13 @@ const index = () => { <>
에러 - + {error?.response?.data?.message}
@@ -150,7 +150,7 @@ const index = () => { return ( <> -
+
{ style={{ display: 'flex', flexDirection: 'column', - backgroundColor: pallete.grayscales_500, + backgroundColor: pallete['grayscales-500'], padding: '50px 50px 50px', borderRadius: '20px', }} @@ -176,7 +176,7 @@ const index = () => { style={{ display: 'flex', flexDirection: 'column', - backgroundColor: pallete.semantic_gender_male_500, + backgroundColor: pallete['semantic-gender-male-500'], }} > {'유저'} @@ -211,7 +211,7 @@ const index = () => { style={{ display: 'flex', flexDirection: 'column', - backgroundColor: pallete.semantic_gender_female_500, + backgroundColor: pallete['semantic-gender-female-500'], marginLeft: '50px', }} > @@ -244,7 +244,7 @@ const index = () => {
@@ -284,7 +284,7 @@ const index = () => { />
*/}
+ } + /> + {searchKey && ( +
+ {searchQuery.data?.pages.map((page, index) => ( + + {page.data.data.statisticsList.map((statistics, index) => { + return ( + + ); + })} + + ))} +
+ )} + +
+ + {'전국 중/고등학교 TOP 10'} + +
+
+ + {'순위'} + + + {'학교 이름'} + + + {'총점'} + +
+
+
+ {statisticsQuery.data?.pages.map((page, index) => ( + + {page.data.data.statisticsList.map((statistics, index) => { + return ( + <> + + + ); + })} + + ))} +
+
+ + + + ); +} + +const ListItem = ({ + groupName, + rank, + diffRank, + title, + score, +}: { + groupName: string; + rank: number; + diffRank?: number; + title: string; + score: number; +}) => { + const router = useRouter(); + const getRankingImage = () => { + if (rank === 1) return ranking_1_svg; + else if (rank === 2) return ranking_2_svg; + else if (rank === 3) return ranking_3_svg; + }; + return ( +
{ + e.preventDefault(); + router.push(`/school-attack/${groupName}`); + }} + > +
+ {rank === 1 || rank === 2 || rank === 3 ? ( + rank + ) : ( +
+ + {rank} + + {diffRank && ( +
+ 0 ? ranking_up_svg : ranking_down_svg} + alt='rank-diff' + /> + {diffRank} +
+ )} +
+ )} +
+ {title} +
+ + {score} + + {'점'} +
+
+ ); +}; diff --git a/pages/school-attack/index.tsx b/pages/school-attack/index.tsx new file mode 100644 index 0000000..5ea5c8f --- /dev/null +++ b/pages/school-attack/index.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import SchoolAttack from './[groupId]'; + +const index = () => { + return ( + <> + + + ); +}; + +export default index; diff --git a/pages/school-attack/styled.ts b/pages/school-attack/styled.ts new file mode 100644 index 0000000..f9767cf --- /dev/null +++ b/pages/school-attack/styled.ts @@ -0,0 +1,19 @@ +import emotionStyled from '@emotion/styled'; + +export const SystemLayout = emotionStyled.div` + width: 100vw; + height: content-fit; + + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; +`; + +export const MainLayout = emotionStyled.main<{ maxWidth: number }>` + display: flex; + flex-direction: column; + + max-width: ${(props) => props.maxWidth}px; + width: 100vw; +`; diff --git a/public/background_main.svg b/public/background_main.svg new file mode 100644 index 0000000..e521477 --- /dev/null +++ b/public/background_main.svg @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/repository/schema.ts b/repository/schema.ts new file mode 100644 index 0000000..f8f7c14 --- /dev/null +++ b/repository/schema.ts @@ -0,0 +1,24 @@ +export interface BaseResponse { + status: number; + message: string; + data: T; +} + +export interface SchoolAttackStatistics { + userGroupName: string; + userCount: number; + voteCount: number; + score: number; + rankNumber: number; + prevUserCount: number; + prevVoteCount: number; + prevScore: number; + prevRankNumber: number; +} + +export interface GetSchoolAttackStatistics { + pageCount: number; + totalCount: number; + updatedAt: string; + statisticsList: SchoolAttackStatistics[]; +} diff --git a/repository/statistics.ts b/repository/statistics.ts new file mode 100644 index 0000000..ed7fd0a --- /dev/null +++ b/repository/statistics.ts @@ -0,0 +1,32 @@ +import axios from 'axios'; +import { + BaseResponse, + GetSchoolAttackStatistics, + SchoolAttackStatistics, +} from './schema'; + +const BASE_URL = process.env.NEXT_PUBLIC_SERVER_URL; + +export const getSchoolAttackStatistics = async (page: number) => { + return await axios>({ + method: 'GET', + url: `${BASE_URL}/api/v1/statistics/user-group/school-attack?page=${page}`, + }); +}; + +export const getSchoolAttackStatisticsDetail = async (groupId: string) => { + return await axios>({ + method: 'GET', + url: `${BASE_URL}/api/v1/statistics/user-group/school-attack/${groupId}`, + }); +}; + +export const getSchoolAttackStatisticsLikeGroupName = async ( + groupId: string, + page: number, +) => { + return await axios>({ + method: 'GET', + url: `${BASE_URL}/api/v1/statistics/user-group/school-attack/group-name/${groupId}?page=${page}`, + }); +}; diff --git a/styles/Color.ts b/styles/Color.ts index 56ca057..7518527 100644 --- a/styles/Color.ts +++ b/styles/Color.ts @@ -1,61 +1,64 @@ -type ColorIndex = { - [key: string]: string | undefined; -}; - -export const pallete = { +export const pallete: Record = { white: '#FFFFFF', black: '#191919', // Grayscales - grayscales_50: '#FBFBFB', - grayscales_100: '#F1F3F5', - grayscales_200: '#E9ECEF', - grayscales_300: '#DEE2E6', - grayscales_400: '#CED4DA', - grayscales_500: '#ADB5BD', - grayscales_600: '#868E96', - grayscales_700: '#495057', - grayscales_800: '#343A40', - graysacles_900: '#212529', + 'grayscales-50': '#FBFBFB', + 'grayscales-100': '#F1F3F5', + 'grayscales-200': '#E9ECEF', + 'grayscales-300': '#DEE2E6', + 'grayscales-400': '#CED4DA', + 'grayscales-500': '#ADB5BD', + 'grayscales-600': '#868E96', + 'grayscales-700': '#495057', + 'grayscales-800': '#343A40', + 'graysacles-900': '#212529', - // Yello_Main - yello_main_100: '#FFFFF4', - yello_main_200: '#FEFFDE', - yello_main_300: '#FDFFA8', - yello_main_400: '#FCFF7E', - yello_main_500: '#FBFF3E', - yello_main_600: '#F1F513', - yello_main_700: '#E7EB1A', - yello_main_800: '#D1D412', - yello_main_900: '#B8BC17', + // Yello-Main + 'yello-main-100': '#FFFFF4', + 'yello-main-200': '#FEFFDE', + 'yello-main-300': '#FDFFA8', + 'yello-main-400': '#FCFF7E', + 'yello-main-500': '#FBFF3E', + 'yello-main-600': '#F1F513', + 'yello-main-700': '#E7EB1A', + 'yello-main-800': '#D1D412', + 'yello-main-900': '#B8BC17', - // yello_sub - yello_sub_50: '#F5F3FF', - yello_sub_100: '#E6E2FF', - yello_sub_200: '#C7C2FF', - yello_sub_300: '#A799FF', - yello_sub_400: '#8364FF', - yello_sub_500: '#6437FF', - yello_sub_600: '#5837DC', - yello_sub_700: '#4B25B8', - yello_sub_800: '#40139F', - yello_sub_900: '#33117C', + // yello-sub + 'yello-sub-50': '#F5F3FF', + 'yello-sub-100': '#E6E2FF', + 'yello-sub-200': '#C7C2FF', + 'yello-sub-300': '#A799FF', + 'yello-sub-400': '#8364FF', + 'yello-sub-500': '#6437FF', + 'yello-sub-600': '#5837DC', + 'yello-sub-700': '#4B25B8', + 'yello-sub-800': '#40139F', + 'yello-sub-900': '#33117C', // semantic - semantic_red_100: '#FFF1F1', - semantic_red_500: '#F04646', - semantic_yellow_100: '#FEF7E7', - semantic_yellow_500: '#FFD84D', - semantic_green_100: '#F6FFF3', - semantic_green_500: '#68D44C', - semantic_gender_female_100: '#FFBAE8', - semantic_gender_female_500: '#F262C1', - semantic_gender_female_700: '#7B245E', - semantic_gender_male_300: '#ACCEFF', - semantic_gender_male_500: '#579AFF', - semantic_gender_male_700: '#1D498E', + 'semantic-red-100': '#FFF1F1', + 'semantic-red-500': '#F04646', + 'semantic-yellow-100': '#FEF7E7', + 'semantic-yellow-500': '#FFD84D', + 'semantic-green-100': '#F6FFF3', + 'semantic-green-500': '#68D44C', + 'semantic-gender-female-100': '#FFBAE8', + 'semantic-gender-female-500': '#F262C1', + 'semantic-gender-female-700': '#7B245E', + 'semantic-gender-male-300': '#ACCEFF', + 'semantic-gender-male-500': '#579AFF', + 'semantic-gender-male-700': '#1D498E', + + // Ranking + 'rank-1': '#D4AD20', + 'rank-2': '#838383', + 'rank-3': '#C6832F', +}; - vote_color: [ +export const palleteArray: Record = { + 'vote-color': [ '', '#6437FF', '#62DFAF', diff --git a/styles/globals.css b/styles/globals.css index 4093dc4..5a70290 100644 --- a/styles/globals.css +++ b/styles/globals.css @@ -3,3 +3,12 @@ @tailwind base; @tailwind components; @tailwind utilities; + +div { + display: block; + white-space: pre-wrap; +} + +body { + background-color: black; +} diff --git a/tailwind.config.ts b/tailwind.config.ts index 2686392..2df9581 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -1,4 +1,5 @@ import type { Config } from 'tailwindcss'; +import { pallete } from './styles/Color'; const config: Config = { content: [ @@ -13,6 +14,9 @@ const config: Config = { 'gradient-conic': 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', }, + colors: { + ...pallete, + }, }, }, plugins: [], diff --git a/util/string.ts b/util/string.ts index 36c6749..949ba34 100644 --- a/util/string.ts +++ b/util/string.ts @@ -2,3 +2,9 @@ export const GOOGLE_PLAY_URL = 'https://play.google.com/store/apps/details?id=com.el.yello&hl=ko&gl=KR'; export const APP_STORE_URL = 'https://apps.apple.com/app/id6451451050'; export const LANDING_PAGE_URL = 'https://yello.imweb.me/'; + +export const QUERY_KEY = { + SCHOOL_ATTACK_STATISTICS: 'SCHOOL_ATTACK_STATISTICS', + SCHOOL_ATTACK_STATISTICS_DETAIl: 'SCHOOL_ATTACK_STATISTICS_DETAIl', + SCHOOL_ATTACK_SEARCH: 'SCHOOL_ATTACK_SEARCH', +}; diff --git a/yarn.lock b/yarn.lock index e47f045..e5b1d02 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1076,6 +1076,17 @@ __metadata: languageName: node linkType: hard +"@types/react@npm:18.2.55": + version: 18.2.55 + resolution: "@types/react@npm:18.2.55" + dependencies: + "@types/prop-types": "*" + "@types/scheduler": "*" + csstype: ^3.0.2 + checksum: a8eb4fa77f73831b9112d4f11a7006217dc0740361649b9b0da3fd441d151a9cd415d5d68b91c0af4e430e063424d301c77489e5edaddc9f711c4e46cf9818a5 + languageName: node + linkType: hard + "@types/scheduler@npm:*": version: 0.16.3 resolution: "@types/scheduler@npm:0.16.3" @@ -1875,6 +1886,13 @@ __metadata: languageName: node linkType: hard +"classnames@npm:^2.5.1": + version: 2.5.1 + resolution: "classnames@npm:2.5.1" + checksum: da424a8a6f3a96a2e87d01a432ba19315503294ac7e025f9fece656db6b6a0f7b5003bb1fbb51cbb0d9624d964f1b9bb35a51c73af9b2434c7b292c42231c1e5 + languageName: node + linkType: hard + "clean-stack@npm:^2.0.0": version: 2.2.0 resolution: "clean-stack@npm:2.2.0" @@ -2053,6 +2071,13 @@ __metadata: languageName: node linkType: hard +"csstype@npm:^3.1.3": + version: 3.1.3 + resolution: "csstype@npm:3.1.3" + checksum: 8db785cc92d259102725b3c694ec0c823f5619a84741b5c7991b8ad135dfaa66093038a1cc63e03361a6cd28d122be48f2106ae72334e067dd619a51f49eddf7 + languageName: node + linkType: hard + "damerau-levenshtein@npm:^1.0.8": version: 1.0.8 resolution: "damerau-levenshtein@npm:1.0.8" @@ -2183,6 +2208,44 @@ __metadata: languageName: node linkType: hard +"dom-serializer@npm:^2.0.0": + version: 2.0.0 + resolution: "dom-serializer@npm:2.0.0" + dependencies: + domelementtype: ^2.3.0 + domhandler: ^5.0.2 + entities: ^4.2.0 + checksum: cd1810544fd8cdfbd51fa2c0c1128ec3a13ba92f14e61b7650b5de421b88205fd2e3f0cc6ace82f13334114addb90ed1c2f23074a51770a8e9c1273acbc7f3e6 + languageName: node + linkType: hard + +"domelementtype@npm:^2.3.0": + version: 2.3.0 + resolution: "domelementtype@npm:2.3.0" + checksum: ee837a318ff702622f383409d1f5b25dd1024b692ef64d3096ff702e26339f8e345820f29a68bcdcea8cfee3531776b3382651232fbeae95612d6f0a75efb4f6 + languageName: node + linkType: hard + +"domhandler@npm:5.0.3, domhandler@npm:^5.0.2, domhandler@npm:^5.0.3": + version: 5.0.3 + resolution: "domhandler@npm:5.0.3" + dependencies: + domelementtype: ^2.3.0 + checksum: 0f58f4a6af63e6f3a4320aa446d28b5790a009018707bce2859dcb1d21144c7876482b5188395a188dfa974238c019e0a1e610d2fc269a12b2c192ea2b0b131c + languageName: node + linkType: hard + +"domutils@npm:^3.1.0": + version: 3.1.0 + resolution: "domutils@npm:3.1.0" + dependencies: + dom-serializer: ^2.0.0 + domelementtype: ^2.3.0 + domhandler: ^5.0.3 + checksum: e5757456ddd173caa411cfc02c2bb64133c65546d2c4081381a3bafc8a57411a41eed70494551aa58030be9e58574fcc489828bebd673863d39924fb4878f416 + languageName: node + linkType: hard + "dotenv@npm:^16.4.2": version: 16.4.4 resolution: "dotenv@npm:16.4.4" @@ -2244,6 +2307,13 @@ __metadata: languageName: node linkType: hard +"entities@npm:^4.2.0, entities@npm:^4.5.0": + version: 4.5.0 + resolution: "entities@npm:4.5.0" + checksum: 853f8ebd5b425d350bffa97dd6958143179a5938352ccae092c62d1267c4e392a039be1bae7d51b6e4ffad25f51f9617531fedf5237f15df302ccfb452cbf2d7 + languageName: node + linkType: hard + "env-paths@npm:^2.2.0": version: 2.2.1 resolution: "env-paths@npm:2.2.1" @@ -2943,6 +3013,13 @@ __metadata: languageName: node linkType: hard +"fs@npm:^0.0.1-security": + version: 0.0.1-security + resolution: "fs@npm:0.0.1-security" + checksum: 53c6230e1faae9fa32c1df82c16a84b51b1243d20f3da2b64bd110bb472b73b9185169b703e008356e3cdc92d155088b617d9d39a63b5227a30b3621baad7f5d + languageName: node + linkType: hard + "fsevents@npm:~2.3.2": version: 2.3.3 resolution: "fsevents@npm:2.3.3" @@ -3288,6 +3365,43 @@ __metadata: languageName: node linkType: hard +"html-dom-parser@npm:5.0.8": + version: 5.0.8 + resolution: "html-dom-parser@npm:5.0.8" + dependencies: + domhandler: 5.0.3 + htmlparser2: 9.1.0 + checksum: f264d58665ab1c49e84b9aa28aad4366b5661a9ed742d40827315ba2b8a2915d16624ec517c743421e5543cc7eeb62c0cadfb07b1399d5f11fecf6611861b04d + languageName: node + linkType: hard + +"html-react-parser@npm:^5.1.6": + version: 5.1.6 + resolution: "html-react-parser@npm:5.1.6" + dependencies: + "@types/react": 18.2.55 + domhandler: 5.0.3 + html-dom-parser: 5.0.8 + react-property: 2.0.2 + style-to-js: 1.1.10 + peerDependencies: + react: 0.14 || 15 || 16 || 17 || 18 + checksum: 8c3e8f21ae0b0fa4378ca6cc1b1b19d5f5d100f9973eec22909c62d64218246d7408539437755df2b12b445adce99dd38d9ce0b9f12cab465e415b2c1020a9ef + languageName: node + linkType: hard + +"htmlparser2@npm:9.1.0": + version: 9.1.0 + resolution: "htmlparser2@npm:9.1.0" + dependencies: + domelementtype: ^2.3.0 + domhandler: ^5.0.3 + domutils: ^3.1.0 + entities: ^4.5.0 + checksum: e5f8d5193967e4a500226f37bdf2c0f858cecb39dde14d0439f24bf2c461a4342778740d988fbaba652b0e4cb6052f7f2e99e69fc1a329a86c629032bb76e7c8 + languageName: node + linkType: hard + "http-cache-semantics@npm:^4.1.1": version: 4.1.1 resolution: "http-cache-semantics@npm:4.1.1" @@ -3411,6 +3525,13 @@ __metadata: languageName: node linkType: hard +"inline-style-parser@npm:0.2.2": + version: 0.2.2 + resolution: "inline-style-parser@npm:0.2.2" + checksum: 698893d6542d4e7c0377936a1c7daec34a197765bd77c5599384756a95ce8804e6b79347b783aa591d5e9c6f3d33dae74c6d4cad3a94647eb05f3a785e927a3f + languageName: node + linkType: hard + "internal-slot@npm:^1.0.3, internal-slot@npm:^1.0.5": version: 1.0.5 resolution: "internal-slot@npm:1.0.5" @@ -5317,6 +5438,13 @@ __metadata: languageName: node linkType: hard +"react-property@npm:2.0.2": + version: 2.0.2 + resolution: "react-property@npm:2.0.2" + checksum: c67fb2590dad9102239b2e574fbc078b472e227b66c3d64e1949426e33889d78c2ac5369cc4c740bb8fe61665505940e39a78d113261ac52410f7538089fe367 + languageName: node + linkType: hard + "react@npm:^18.2.0": version: 18.2.0 resolution: "react@npm:18.2.0" @@ -5886,6 +6014,24 @@ __metadata: languageName: node linkType: hard +"style-to-js@npm:1.1.10": + version: 1.1.10 + resolution: "style-to-js@npm:1.1.10" + dependencies: + style-to-object: 1.0.5 + checksum: 2db181d838b0076161f1ac0959e64841efd91ad9f03d80aabd11769e88f7d979a6b4d73c59810dfb47a2e87543fb02693447a03c385dda86d8750e8cf1802d62 + languageName: node + linkType: hard + +"style-to-object@npm:1.0.5": + version: 1.0.5 + resolution: "style-to-object@npm:1.0.5" + dependencies: + inline-style-parser: 0.2.2 + checksum: 6201063204b6a94645f81b189452b2ca3e63d61867ec48523f4d52609c81e96176739fa12020d97fbbf023efb57a6f7ec3a15fb3a7fb7eb3ffea0b52b9dd6b8c + languageName: node + linkType: hard + "style-to-object@npm:^0.4.0": version: 0.4.2 resolution: "style-to-object@npm:0.4.2" @@ -6639,6 +6785,8 @@ __metadata: "@typescript-eslint/parser": ^6.14.0 autoprefixer: 10.4.15 axios: ^1.5.0 + classnames: ^2.5.1 + csstype: ^3.1.3 dayjs: ^1.11.9 eslint: 8.47.0 eslint-config-next: 13.4.19 @@ -6646,7 +6794,9 @@ __metadata: eslint-plugin-prettier: ^5.1.3 eslint-plugin-react-hooks: ^4.6.0 eslint-plugin-react-refresh: ^0.4.5 + fs: ^0.0.1-security gh-pages: ^6.0.0 + html-react-parser: ^5.1.6 next: 13.4.19 postcss: 8.4.28 prettier: 3.0.2