diff --git a/packages/web/public/temp/student-fee.svg b/packages/web/public/temp/student-fee.svg new file mode 100644 index 0000000..574ab7b --- /dev/null +++ b/packages/web/public/temp/student-fee.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/packages/web/src/app/my/student-fee/page.tsx b/packages/web/src/app/my/student-fee/page.tsx new file mode 100644 index 0000000..867f396 --- /dev/null +++ b/packages/web/src/app/my/student-fee/page.tsx @@ -0,0 +1,85 @@ +"use client"; + +import React, { useState } from "react"; + +import Image from "next/image"; + +import FlexWrapper from "@sparcs-students/web/common/components/FlexWrapper"; +import Typography from "@sparcs-students/web/common/components/Typography"; +import StudentFeeChange from "@sparcs-students/web/features/studentFee/components/StudentFeeChange"; +import StudentFeeStatus from "@sparcs-students/web/features/studentFee/components/StudentFeeStatus"; + +const mockSemesters = [ + { + year: 2023, + semester: "봄", + }, + { + year: 2023, + semester: "가을", + }, + { + year: 2024, + semester: "봄", + }, + { + year: 2024, + semester: "가을", + }, +]; + +const mockChangeDate = new Date("2025-09-08"); +const mockSemesterNow = { + year: 2024, + semester: "가을", +}; + +const StudentFee = () => { + const [payment, setPayment] = useState(true); + + return ( + + + 학생회비 납부 조회 + + + + 학생회비 납부 학기 + + + + 2015년 가을까지 모든 재적학기는 회비를 납부한 것으로 인정되며, 현재 + 기록에 포함되어 있지 않습니다.
그 외 기록이 누락 된 경우 + kaistua@kaist.ac.kr로 문의해 주십시오.
+ 8회 학생회비를 납부 완료하신 학우분들은 별도의 공제 신청 없이 자동 + 공제가 적용됩니다.
+
+
+ + + 학생회비 공제 / 미공제 변경 + + = 8} + setPayment={setPayment} + /> + + + + 학생회비 관련 공지사항 + + {/* TODO: 임시 이미지 */} + student-fee notice + +
+ ); +}; +export default StudentFee; diff --git a/packages/web/src/common/components/FlexWrapper.tsx b/packages/web/src/common/components/FlexWrapper.tsx new file mode 100644 index 0000000..ac7bad3 --- /dev/null +++ b/packages/web/src/common/components/FlexWrapper.tsx @@ -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), +})` + 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; diff --git a/packages/web/src/common/components/Typography.tsx b/packages/web/src/common/components/Typography.tsx index 6cd310d..9eb5e84 100644 --- a/packages/web/src/common/components/Typography.tsx +++ b/packages/web/src/common/components/Typography.tsx @@ -1,7 +1,9 @@ -import { Theme } from "@sparcs-students/web/styles/themes"; import React from "react"; + import styled from "styled-components"; +import { Theme } from "@sparcs-students/web/styles/themes"; + interface TypographyPropsBase extends React.HTMLAttributes { children: React.ReactNode; } @@ -45,7 +47,8 @@ interface TypographyProps extends TypographyPropsBase { const TypographyInner = styled.div` color: ${({ color, theme }) => color ? getColorFromTheme(theme, color) : "inherit"}; - font-family: ${({ theme, ff }) => (ff ? theme.fonts.FAMILY[ff] : "inherit")}; + font-family: ${({ theme, ff }) => + ff ? theme.fonts.FAMILY[ff] : theme.fonts.FAMILY.PRETENDARD}; font-size: ${({ fs }) => (fs ? `${fs}px` : "inherit")}; line-height: ${({ lh }) => (lh ? `${lh}px` : "inherit")}; font-weight: ${({ fw, theme }) => (fw ? theme.fonts.WEIGHT[fw] : "inherit")}; diff --git a/packages/web/src/features/studentFee/components/StudentFeeChange.tsx b/packages/web/src/features/studentFee/components/StudentFeeChange.tsx new file mode 100644 index 0000000..3aba15a --- /dev/null +++ b/packages/web/src/features/studentFee/components/StudentFeeChange.tsx @@ -0,0 +1,87 @@ +import React from "react"; +import Button from "@sparcs-students/web/common/components/Buttons/Button"; +import FlexWrapper from "@sparcs-students/web/common/components/FlexWrapper"; +import Typography from "@sparcs-students/web/common/components/Typography"; +import { formatSimpleDate } from "@sparcs-students/web/utils/Date/formatDate"; +import styled from "styled-components"; +import { Semester } from "@sparcs-students/web/types/semester.type"; + +interface StudentFeeChangeProp { + changeDate: Date; + semesterNow: Semester; + paymentStatus: boolean; + finishPayment: boolean; + setPayment: (payment: boolean) => void; +} + +const StudentFeeChangeWrapper = styled.div` + width: 840px; // TODO: 반응형 + display: flex; + flex-direction: row; + justify-content: space-between; + gap: 24px; + border-top: 1px solid ${({ theme }) => theme.colors.GRAY[400]}; + border-bottom: 1px solid ${({ theme }) => theme.colors.GRAY[400]}; + padding: 20px 12px; + align-items: center; +`; + +const ButtonWrapper = styled.div` + width: 100px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; +`; + +const StudentFeeChange: React.FC = ({ + changeDate, + semesterNow, + paymentStatus, + finishPayment, + setPayment, +}) => { + const isChangeable = changeDate >= new Date(); + + return ( + + + + {semesterNow.year} {semesterNow.semester}학기 + + {isChangeable ? ( + + {formatSimpleDate(changeDate)}까지 변경 가능 + + ) : ( + + 변경 불가 기간 + + )} + + {finishPayment ? ( + + 학생회비 8회 납부 완료자 + + ) : ( + + 학생회비 {paymentStatus ? "납부 (공제)" : "미납부 (미공제)"}{" "} + {isChangeable ? "예정" : ""} + + )} + + {isChangeable && !finishPayment ? ( + + ) : ( +
+ )} + + + ); +}; +export default StudentFeeChange; diff --git a/packages/web/src/features/studentFee/components/StudentFeeStatus.tsx b/packages/web/src/features/studentFee/components/StudentFeeStatus.tsx new file mode 100644 index 0000000..7056bd9 --- /dev/null +++ b/packages/web/src/features/studentFee/components/StudentFeeStatus.tsx @@ -0,0 +1,33 @@ +import React from "react"; +import styled from "styled-components"; +import { Semester } from "@sparcs-students/web/types/semester.type"; +import StudentFeePaid from "./_atomic/StudentFeePaid"; +import StudentFeeNotPaid from "./_atomic/StudentFeeNotPaid"; + +interface StudentFeeStatusProp { + semesters: Semester[]; +} + +const StudentFeeStatusWrapper = styled.div` + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + // TODO: 반응형 + gap: 24px; +`; + +const StudentFeeStatus: React.FC = ({ semesters }) => ( + + {semesters.map(semester => ( + + ))} + {Array.from({ length: 8 - semesters.length }).map(_ => ( + + ))} + +); + +export default StudentFeeStatus; diff --git a/packages/web/src/features/studentFee/components/_atomic/StudentFeeNotPaid.tsx b/packages/web/src/features/studentFee/components/_atomic/StudentFeeNotPaid.tsx new file mode 100644 index 0000000..a808dc0 --- /dev/null +++ b/packages/web/src/features/studentFee/components/_atomic/StudentFeeNotPaid.tsx @@ -0,0 +1,23 @@ +import React from "react"; + +import Typography from "@sparcs-students/web/common/components/Typography"; +import styled from "styled-components"; + +const StudentFeeNotPaidWrapper = styled.div` + width: 192px; + height: 60px; + display: flex; + justify-content: center; + align-items: center; + background-color: ${({ theme }) => theme.colors.GRAY[400]}; + border-radius: 20px; +`; + +const StudentFeeNotPaid: React.FC = () => ( + + + - + + +); +export default StudentFeeNotPaid; diff --git a/packages/web/src/features/studentFee/components/_atomic/StudentFeePaid.tsx b/packages/web/src/features/studentFee/components/_atomic/StudentFeePaid.tsx new file mode 100644 index 0000000..e7965b5 --- /dev/null +++ b/packages/web/src/features/studentFee/components/_atomic/StudentFeePaid.tsx @@ -0,0 +1,24 @@ +import React from "react"; + +import Typography from "@sparcs-students/web/common/components/Typography"; +import { Semester } from "@sparcs-students/web/types/semester.type"; +import styled from "styled-components"; + +const StudentFeePaidWrapper = styled.div` + width: 192px; + height: 60px; + display: flex; + justify-content: center; + align-items: center; + background-color: ${({ theme }) => theme.colors.GREEN[100]}; + border-radius: 20px; +`; + +const StudentFeePaid: React.FC = ({ year, semester }) => ( + + + {year} {semester}학기 + + +); +export default StudentFeePaid; diff --git a/packages/web/src/styles/themes/colors.ts b/packages/web/src/styles/themes/colors.ts index be6d447..a6f6007 100644 --- a/packages/web/src/styles/themes/colors.ts +++ b/packages/web/src/styles/themes/colors.ts @@ -27,6 +27,10 @@ const colors = { 200: "#F5A3A8", 700: "#B7202A", }, + + BLUE: { + 900: "#3A2682", + }, }; export default colors; diff --git a/packages/web/src/types/semester.type.ts b/packages/web/src/types/semester.type.ts new file mode 100644 index 0000000..cf351a2 --- /dev/null +++ b/packages/web/src/types/semester.type.ts @@ -0,0 +1,4 @@ +export type Semester = { + year: number; + semester: string; +}; diff --git a/packages/web/src/utils/Date/formatDate.ts b/packages/web/src/utils/Date/formatDate.ts new file mode 100644 index 0000000..f35ffaf --- /dev/null +++ b/packages/web/src/utils/Date/formatDate.ts @@ -0,0 +1,7 @@ +import { format } from "date-fns"; +import { ko } from "date-fns/locale"; + +const formatSimpleDate = (date: Date) => + format(date, "M월 d일", { locale: ko }); + +export { formatSimpleDate };