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

Implement /credits #26

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
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
65 changes: 65 additions & 0 deletions packages/web/src/app/credits/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
"use client";

import React from "react";

import styled from "styled-components";

import FlexWrapper from "@sparcs-students/web/common/components/FlexWrapper";
import FoldableSectionTitle from "@sparcs-students/web/common/components/FoldableSectionTitle";
import PageHead from "@sparcs-students/web/common/components/PageHead";

import SectionTitle from "@sparcs-students/web/common/components/SectionTitle";
import MemberCardSection from "@sparcs-students/web/features/credits/components/MemberCardSection";
import credits from "@sparcs-students/web/features/credits/credits";

const CreditCardsFlexWrapper = styled(FlexWrapper)`
gap: 40px;

@media (max-width: ${({ theme }) => theme.responsive.BREAKPOINT.sm}) {
gap: 20px;
}
`;

const ResponsiveMemberCardSectionWrapper = styled.div`
@media (max-width: ${({ theme }) => theme.responsive.BREAKPOINT.sm}) {
margin-left: 16px;
}
`;

const Credits: React.FC = () => {
const [toggleFold, setToggleFold] = React.useState(false);
return (
<FlexWrapper direction="column" gap={60}>
<PageHead title="만든 사람들" />
{credits.map((credit, index) => (
<CreditCardsFlexWrapper
direction="column"
gap={40}
key={credit.semester}
>
{index === 0 ? (
<>
<SectionTitle size="lg">{credit.semester}</SectionTitle>
<ResponsiveMemberCardSectionWrapper>
<MemberCardSection semesterCredit={credit} leftMargin={24} />
</ResponsiveMemberCardSectionWrapper>
</>
) : (
<FoldableSectionTitle
title={credit.semester}
toggle={toggleFold}
toggleHandler={() => {
setToggleFold(!toggleFold);
}}
>
<ResponsiveMemberCardSectionWrapper>
<MemberCardSection semesterCredit={credit} />
</ResponsiveMemberCardSectionWrapper>
</FoldableSectionTitle>
)}
</CreditCardsFlexWrapper>
))}
</FlexWrapper>
);
};
export default Credits;
6 changes: 6 additions & 0 deletions packages/web/src/assets/sparcs-orange.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 14 additions & 4 deletions packages/web/src/common/components/Card.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
"use client";

import React from "react";
import isPropValid from "@emotion/is-prop-valid";
import styled from "styled-components";

const Card: React.FC<React.PropsWithChildren> = styled.div<{
interface CardProps {
outline?: boolean;
}>`
padding?: string;
gap?: number;
}

const Card = styled.div.withConfig({
shouldForwardProp: prop => isPropValid(prop),
})<CardProps>`
display: flex;
flex-direction: column;
position: relative;
padding: 16px 20px;
align-self: stretch;
padding: ${({ padding }) => padding ?? `32px`};
gap: ${({ gap }) => (gap ? `${gap}px` : "inherit")};

font-family: ${({ theme }) => theme.fonts.FAMILY.PRETENDARD};
font-size: 16px;
line-height: 20px;
font-weight: ${({ theme }) => theme.fonts.WEIGHT.REGULAR};

color: ${({ theme }) => theme.colors.BLACK};
background-color: ${({ theme }) => theme.colors.WHITE};
border-radius: ${({ theme }) => theme.round.md};
Expand Down
22 changes: 22 additions & 0 deletions packages/web/src/common/components/FlexWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import isPropValid from "@emotion/is-prop-valid";
import styled from "styled-components";

interface FlexWrapperProps {
direction: "row" | "column";
gap: number;
justify?: string;
padding?: string;
}

const FlexWrapper = styled.div.withConfig({
shouldForwardProp: prop => isPropValid(prop),
})<FlexWrapperProps>`
display: flex;
position: relative;
flex-direction: ${({ direction }) => direction};
gap: ${({ gap }) => `${gap}px`};
justify-content: ${({ justify }) => justify ?? "flex-start"};
padding: ${({ padding }) => padding ?? 0};
`;

export default FlexWrapper;
63 changes: 46 additions & 17 deletions packages/web/src/common/components/FoldableSectionTitle.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,67 @@
"use client";

import React from "react";

import isPropValid from "@emotion/is-prop-valid";
import styled from "styled-components";

import TextButton from "@sparcs-students/web/common/components/Buttons/TextButton";
import SectionTitle from "@sparcs-students/web/common/components/SectionTitle";
import Icon from "@sparcs-students/web/common/components/Icon";

const FoldableSectionOuter = styled.div`
width: 100%;
max-width: calc(100vw + (100% - 100vw));
`;

const FoldableSectionTitleInner = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
gap: 20px;

width: 100%;
`;

export const MoreInfo = styled.div`
font-family: ${({ theme }) => theme.fonts.FAMILY.PRETENDARD};
font-size: 14px;
line-height: 20px;
font-weight: ${({ theme }) => theme.fonts.WEIGHT.REGULAR};
color: ${({ theme }) => theme.colors.BLACK};
text-decoration-line: underline;
cursor: pointer;
const ChildrenOuter = styled.div.withConfig({
shouldForwardProp: prop => isPropValid(prop),
})<{ margin?: string }>`
margin-top: ${({ margin }) => margin};
margin-left: 24px;
`;

const FoldableSectionTitle: React.FC<{
iconType?: string;
title: string;
toggle: boolean;
toggleHandler: () => void;
}> = ({ title, toggle, toggleHandler }) => (
<FoldableSectionTitleInner>
<SectionTitle size="lg">{title}</SectionTitle>
<MoreInfo onClick={toggleHandler}>{toggle ? `접기` : `펼치기`}</MoreInfo>
</FoldableSectionTitleInner>
);
toggle?: boolean;
toggleHandler?: () => void;
children?: React.ReactNode;
childrenMargin?: string;
}> = ({
iconType = null,
title,
toggle = null,
toggleHandler = null,
children = null,
childrenMargin = "40px",
}) => {
const [open, setOpen] = React.useState<boolean>(true);
const openHandler = () => setOpen(!open);

return (
<FoldableSectionOuter>
<FoldableSectionTitleInner>
{iconType && <Icon type={iconType} size={16} color="white" />}
<SectionTitle size="lg">{title}</SectionTitle>
<TextButton
text={toggle ?? open ? `접기` : `펼치기`}
onClick={toggleHandler ?? openHandler}
/>
</FoldableSectionTitleInner>
{(toggle ?? open) && children && (
<ChildrenOuter margin={childrenMargin}>{children}</ChildrenOuter>
)}
</FoldableSectionOuter>
);
};

export default FoldableSectionTitle;
25 changes: 25 additions & 0 deletions packages/web/src/common/components/PageHead/_atomic/PageTitle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"use client";

import React from "react";

import styled from "styled-components";

const PageTitleInner = styled.div`
position: relative;
width: fit-content;
font-family: ${({ theme }) => theme.fonts.FAMILY.PRETENDARD};
font-size: 30px;
line-height: 24px;
@media (max-width: ${({ theme }) => theme.responsive.BREAKPOINT.sm}) {
font-size: 24px;
line-height: 36px;
}
font-weight: ${({ theme }) => theme.fonts.WEIGHT.SEMIBOLD};
color: ${({ theme }) => theme.colors.BLACK};
`;

const PageTitle: React.FC<React.PropsWithChildren> = ({
children = <div />,
}) => <PageTitleInner>{children}</PageTitleInner>;

export default PageTitle;
53 changes: 53 additions & 0 deletions packages/web/src/common/components/PageHead/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React from "react";

import styled from "styled-components";

import PageTitle from "./_atomic/PageTitle";

// 주석 처리된 부분은 BreadCrumb 컴포넌트를 쓰게 된다면 다시 추가
interface PageHeadProps {
// items: { name: string; path: string }[];
title: string;
// enableLast?: boolean;
action?: React.ReactNode;
}

const PageHeadWrapper = styled.div`
display: flex;
flex-direction: column;
gap: 20px;
@media (max-width: ${({ theme }) => theme.responsive.BREAKPOINT.sm}) {
gap: 12px;
}
align-items: flex-start;
align-self: stretch;
`;

const TitleWrapper = styled.div`
display: flex;
width: 100%;
justify-content: space-between;
align-items: center;
@media (max-width: ${({ theme }) => theme.responsive.BREAKPOINT.sm}) {
flex-direction: column;
align-items: flex-start;
gap: 20px;
}
`;

const PageHead: React.FC<PageHeadProps> = ({
// items,
title,
// enableLast = false,
action = null,
}) => (
<PageHeadWrapper>
{/* <BreadCrumb items={items} enableLast={enableLast} /> */}
<TitleWrapper>
<PageTitle>{title}</PageTitle>
{action && <div>{action}</div>}
</TitleWrapper>
</PageHeadWrapper>
);

export default PageHead;
5 changes: 3 additions & 2 deletions packages/web/src/common/components/SectionTitle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ const IdentityBar = styled.div`

const Title = styled.p<{ size: Size }>`
font-family: ${({ theme }) => theme.fonts.FAMILY.PRETENDARD};
font-size: ${({ size }) => (size === "sm" ? "20px" : "24px")};
line-height: ${({ size }) => (size === "sm" ? "28px" : "32px")};
font-size: ${({ size }) =>
size === "sm" ? "20px" : "20px"}; // TODO: 반응형 사이즈 재확인
line-height: ${({ size }) => (size === "sm" ? "24px" : "24px")};
font-weight: ${({ theme }) => theme.fonts.WEIGHT.MEDIUM};
color: ${({ theme }) => theme.colors.BLACK};
`;
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/constants/paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const paths = {
},
],
},
MADE_BY: { name: "만든 사람들", path: "/" },
MADE_BY: { name: "만든 사람들", path: "/credits" },
LICENSE: { name: "라이센스", path: "/" },
TERMS_OF_SERVICE: { name: "이용 약관", path: "/" },
LOGIN: { name: "로그인", path: "/login" },
Expand Down
70 changes: 70 additions & 0 deletions packages/web/src/features/credits/components/MemberCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { useState } from "react";

import Image from "next/image";
import styled from "styled-components";

import SparcsLogo from "@sparcs-students/web/assets/sparcs-orange.svg";
import Card from "@sparcs-students/web/common/components/Card";
import Typography from "@sparcs-students/web/common/components/Typography";

import type { Member } from "../credits";

const MemberWrapper = styled.div`
display: flex;
flex-direction: row;
align-items: flex-end;
gap: 6px;
`;

const MemberCard = ({ member }: { member: Member }) => {
const [displayText, setDisplayText] = useState(member.role);

const handleMouseEnter = () => {
if (member.comment) {
setDisplayText(member.comment);
}
};

const handleMouseLeave = () => {
setDisplayText(member.role);
};

return (
<Card padding="10px 15px 15px 15px" gap={5}>
<MemberWrapper>
<Image src={SparcsLogo} alt="SPARCS Logo" height={20} />
<Typography
ff="RALEWAY"
fw="EXTRABOLD"
fs={14}
lh={20}
color="SPARCS.main"
>
{member.nickname}
</Typography>
<Typography
ff="NANUM_SQUARE"
fw="EXTRABOLD" // TODO: 현재 NANUM_SQUARE는 EXTRABOLD만 존재함
fs={10}
lh={16}
color="SPARCS.member"
>
{member.name}
</Typography>
</MemberWrapper>
<Typography
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
ff="RALEWAY"
fw="EXTRABOLD"
fs={10}
lh={20}
color="GRAY.900"
>
{displayText}
</Typography>
</Card>
);
};

export default MemberCard;
Loading
Loading