diff --git a/packages/api/src/feature/activity/service/activity.service.ts b/packages/api/src/feature/activity/service/activity.service.ts index e1138bb8f..12966d8ea 100644 --- a/packages/api/src/feature/activity/service/activity.service.ts +++ b/packages/api/src/feature/activity/service/activity.service.ts @@ -173,6 +173,12 @@ export default class ActivityService { ); } + /** + * + * @param enums: ActivityDeadlineEnum의 배열을 받습니다. + * @description 오늘이 해당하는 deadlineEnum이 enums에 포함되어있지 않으면 400 exception을 throw + * @returns void + */ private async checkDeadline(param: { enums: Array }) { const today = getKSTDate(); const todayDeadline = await this.activityRepository @@ -195,6 +201,13 @@ export default class ActivityService { ); } + /** + * + * @param clubId + * @param executiveId + * @description 동아리의 담당 집행부원을 변경합니다. + * 해당 동아리의 활동에 대한 개별 담당 집행부원도 전부 덮어씌웁니다. + */ private async changeClubChargedExecutive(param: { clubId: number; executiveId: number; @@ -968,6 +981,17 @@ export default class ActivityService { "the activity is already approved", HttpStatus.BAD_REQUEST, ); + + const isInsertionSucceed = + await this.activityRepository.insertActivityFeedback({ + activityId: param.param.activityId, + comment: "활동이 승인되었습니다", // feedback에 승인을 기록하기 위한 임의의 문자열ㄴ + executiveId: param.executiveId, + }); + if (!isInsertionSucceed) + throw new HttpException("unreachable", HttpStatus.INTERNAL_SERVER_ERROR); + + return {}; return {}; } diff --git a/packages/api/src/feature/funding/controller/funding.controller.ts b/packages/api/src/feature/funding/controller/funding.controller.ts index 5de88cdee..242cb8c46 100644 --- a/packages/api/src/feature/funding/controller/funding.controller.ts +++ b/packages/api/src/feature/funding/controller/funding.controller.ts @@ -32,6 +32,7 @@ import apiFnd005, { } from "@sparcs-clubs/interface/api/funding/endpoint/apiFnd005"; import apiFnd006, { ApiFnd006RequestParam, + ApiFnd006RequestQuery, ApiFnd006ResponseOk, } from "@sparcs-clubs/interface/api/funding/endpoint/apiFnd006"; import apiFnd007 from "@sparcs-clubs/interface/api/funding/endpoint/apiFnd007"; diff --git a/packages/api/src/feature/funding/service/funding.service.ts b/packages/api/src/feature/funding/service/funding.service.ts index 953053c49..58ea4b4f8 100644 --- a/packages/api/src/feature/funding/service/funding.service.ts +++ b/packages/api/src/feature/funding/service/funding.service.ts @@ -167,31 +167,6 @@ export default class FundingService { ); } - if (funding.jointExpense) { - funding.jointExpense.files = await this.filePublicService.getFilesByIds( - funding.laborContract.files.flatMap(file => file.id), - ); - } - - if (funding.etcExpense) { - funding.etcExpense.files = await this.filePublicService.getFilesByIds( - funding.etcExpense.files.flatMap(file => file.id), - ); - } - - if (funding.publication) { - funding.publication.files = await this.filePublicService.getFilesByIds( - funding.publication.files.flatMap(file => file.id), - ); - } - - if (funding.profitMakingActivity) { - funding.profitMakingActivity.files = - await this.filePublicService.getFilesByIds( - funding.profitMakingActivity.files.flatMap(file => file.id), - ); - } - if (funding.jointExpense) { funding.jointExpense.files = await this.filePublicService.getFilesByIds( funding.jointExpense.files.flatMap(file => file.id), diff --git a/packages/interface/src/api/funding/endpoint/apiFnd006.ts b/packages/interface/src/api/funding/endpoint/apiFnd006.ts index 8455be332..6af637f9b 100644 --- a/packages/interface/src/api/funding/endpoint/apiFnd006.ts +++ b/packages/interface/src/api/funding/endpoint/apiFnd006.ts @@ -48,8 +48,8 @@ type ApiFnd006ResponseOk = z.infer<(typeof apiFnd006.responseBodyMap)[200]>; export default apiFnd006; export type { + ApiFnd006RequestBody, ApiFnd006RequestParam, ApiFnd006RequestQuery, - ApiFnd006RequestBody, ApiFnd006ResponseOk, }; diff --git a/packages/web/src/app/manage-club/funding/[id]/page.tsx b/packages/web/src/app/manage-club/funding/[id]/page.tsx index 08930b617..3ecede07f 100644 --- a/packages/web/src/app/manage-club/funding/[id]/page.tsx +++ b/packages/web/src/app/manage-club/funding/[id]/page.tsx @@ -1,24 +1,59 @@ "use client"; -import React from "react"; +import { useEffect, useState } from "react"; +import AsyncBoundary from "@sparcs-clubs/web/common/components/AsyncBoundary"; import FlexWrapper from "@sparcs-clubs/web/common/components/FlexWrapper"; import PageHead from "@sparcs-clubs/web/common/components/PageHead"; +import LoginRequired from "@sparcs-clubs/web/common/frames/LoginRequired"; +import NoManageClub from "@sparcs-clubs/web/common/frames/NoManageClub"; +import { useAuth } from "@sparcs-clubs/web/common/providers/AuthContext"; import FundingDetailFrame from "@sparcs-clubs/web/features/manage-club/funding/detail/frames/FundingDetailFrame"; +import { useGetMyManageClub } from "@sparcs-clubs/web/features/manage-club/services/getMyManageClub"; -const FundingDetail = () => ( - - - {/* TODO: 기간 맞춰서 isNow 설정 */} - - -); +const FundingDetail = () => { + const { isLoggedIn, login, profile } = useAuth(); + const [loading, setLoading] = useState(true); + const { data, isLoading, isError } = useGetMyManageClub(); + + useEffect(() => { + if (isLoggedIn !== undefined || profile !== undefined) { + setLoading(false); + } + }, [isLoggedIn, profile]); + + if (loading) { + return ; + } + + if (!isLoggedIn) { + return ; + } + + if (profile?.type !== "undergraduate") { + return ; + } + + if (!data || !("clubId" in data)) { + return ; + } + + return ( + + + + {/* TODO: 기간 맞춰서 isNow 설정 */} + + + + ); +}; export default FundingDetail; diff --git a/packages/web/src/app/manage-club/funding/page.tsx b/packages/web/src/app/manage-club/funding/page.tsx index c9fac7540..3fc7ea92b 100644 --- a/packages/web/src/app/manage-club/funding/page.tsx +++ b/packages/web/src/app/manage-club/funding/page.tsx @@ -37,7 +37,7 @@ const Funding: React.FC = () => { /> - + ); diff --git a/packages/web/src/constants/manageClubFunding.ts b/packages/web/src/constants/manageClubFunding.ts index 58780a282..26c19465d 100644 --- a/packages/web/src/constants/manageClubFunding.ts +++ b/packages/web/src/constants/manageClubFunding.ts @@ -22,9 +22,10 @@ const numberToKrWon = (amount: number) => `${amount.toLocaleString("ko-KR")}원` const pastFundingListSectionTitle = "과거 지원금 내역"; const singleSemesterFundingListSectionTitleText = ( + year: number, semester: string, itemCount: number, -) => `${semester} (총 ${itemCount}개)`; +) => `${year}년 ${semester}학기 (총 ${itemCount}개)`; export { manageClubFundingPageBreadCrumbName, diff --git a/packages/web/src/features/activity-report/frames/ActivityReportDetailFrame.tsx b/packages/web/src/features/activity-report/frames/ActivityReportDetailFrame.tsx index 9edbe4211..902611914 100644 --- a/packages/web/src/features/activity-report/frames/ActivityReportDetailFrame.tsx +++ b/packages/web/src/features/activity-report/frames/ActivityReportDetailFrame.tsx @@ -283,10 +283,15 @@ const ActivityReportDetailFrame: React.FC = ({ data.comments.length > 0 && ( ({ - datetime: comment.createdAt, - reason: comment.content, - }))} + reasons={data.comments + .filter( + comment => + comment.content !== "활동이 승인되었습니다", + ) + .map(comment => ({ + datetime: comment.createdAt, + reason: comment.content, + }))} /> ) } diff --git a/packages/web/src/features/manage-club/funding/components/FixtureEvidenceBlock.tsx b/packages/web/src/features/manage-club/funding/components/FixtureEvidenceBlock.tsx index c7464e48c..fe9deb83f 100644 --- a/packages/web/src/features/manage-club/funding/components/FixtureEvidenceBlock.tsx +++ b/packages/web/src/features/manage-club/funding/components/FixtureEvidenceBlock.tsx @@ -170,9 +170,7 @@ const FixtureEvidenceBlock: React.FC = ({ = ({ ) : ( = ({ ( - - - - {/* TODO: ManageClubFundingMainFrame으로부터 주입받은 테이블 데이터 매핑하기 */} - - - - - - -); +const PastFundingListSection: React.FC<{ clubId: number }> = ({ clubId }) => { + const { + data: activityTerms, + isLoading, + isError, + } = useGetActivityTerms({ clubId }); + + if (!activityTerms) { + return null; + } + + return ( + + + + + {activityTerms.terms.toReversed().map(term => ( + + ))} + + + + + ); +}; export default PastFundingListSection; diff --git a/packages/web/src/features/manage-club/funding/components/_atomic/NewFundingListTable.tsx b/packages/web/src/features/manage-club/funding/components/_atomic/NewFundingListTable.tsx index 0c3ef96dd..c00f63456 100644 --- a/packages/web/src/features/manage-club/funding/components/_atomic/NewFundingListTable.tsx +++ b/packages/web/src/features/manage-club/funding/components/_atomic/NewFundingListTable.tsx @@ -6,6 +6,8 @@ import { useReactTable, } from "@tanstack/react-table"; +import { useRouter } from "next/navigation"; + import Table from "@sparcs-clubs/web/common/components/Table"; import TableCell from "@sparcs-clubs/web/common/components/Table/TableCell"; import { TableRow } from "@sparcs-clubs/web/common/components/Table/TableWrapper"; @@ -60,6 +62,7 @@ const columns = [ const NewFundingListTable: React.FC = ({ newFundingList = [], }) => { + const router = useRouter(); const table = useReactTable({ columns, data: newFundingList, @@ -71,6 +74,7 @@ const NewFundingListTable: React.FC = ({ router.push(`/manage-club/funding/${row.id}`)} footer={ diff --git a/packages/web/src/features/manage-club/funding/components/_atomic/PastFundingListTable.tsx b/packages/web/src/features/manage-club/funding/components/_atomic/PastFundingListTable.tsx index d590a4090..29b903ca7 100644 --- a/packages/web/src/features/manage-club/funding/components/_atomic/PastFundingListTable.tsx +++ b/packages/web/src/features/manage-club/funding/components/_atomic/PastFundingListTable.tsx @@ -7,21 +7,16 @@ import { getCoreRowModel, useReactTable, } from "@tanstack/react-table"; +import { useRouter } from "next/navigation"; import styled from "styled-components"; import Table from "@sparcs-clubs/web/common/components/Table"; import TableCell from "@sparcs-clubs/web/common/components/Table/TableCell"; import { TableRow } from "@sparcs-clubs/web/common/components/Table/TableWrapper"; -import Tag from "@sparcs-clubs/web/common/components/Tag"; import { numberToKrWon } from "@sparcs-clubs/web/constants/manageClubFunding"; -import { FundingTagList } from "@sparcs-clubs/web/constants/tableTagList"; -import { - Funding, - mockupPastManageFunding, -} from "@sparcs-clubs/web/features/manage-club/services/_mock/mockManageClub"; -import { getTagDetail } from "@sparcs-clubs/web/utils/getTagDetail"; +import { PastFundingData } from "@sparcs-clubs/web/features/manage-club/funding/types/funding"; const TableWithCount = styled.div` display: flex; @@ -31,39 +26,20 @@ const TableWithCount = styled.div` width: 100%; `; -const CountRow = styled.div` - display: flex; - flex-direction: row; - justify-content: flex-end; - align-items: center; - width: 100%; - font-family: ${({ theme }) => theme.fonts.FAMILY.PRETENDARD}; - font-size: ${({ theme }) => theme.fonts.WEIGHT.REGULAR}; - color: ${({ theme }) => theme.colors.GRAY[600]}; -`; - -const columnHelper = createColumnHelper(); +const columnHelper = createColumnHelper(); const columns = [ - columnHelper.accessor("status", { - header: "상태", - cell: info => { - const { color, text } = getTagDetail(info.getValue(), FundingTagList); - return {text}; - }, - size: 10, - }), - columnHelper.accessor("name", { + columnHelper.accessor("activityName", { header: "활동명", cell: info => info.getValue(), - size: 45, + size: 55, }), - columnHelper.accessor("itemName", { + columnHelper.accessor("name", { header: "항목명", cell: info => info.getValue(), size: 15, }), - columnHelper.accessor("requestedAmount", { + columnHelper.accessor("expenditureAmount", { header: "신청 금액", cell: info => `${info.getValue().toLocaleString("ko-KR")}원`, size: 15, @@ -78,19 +54,24 @@ const columns = [ }), ]; -const PastFundingListTable: React.FC = () => { +const PastFundingListTable: React.FC<{ data: PastFundingData[] }> = ({ + data: fundings, +}) => { + const router = useRouter(); + const table = useReactTable({ columns, - data: mockupPastManageFunding, + data: fundings, getCoreRowModel: getCoreRowModel(), enableSorting: false, }); return ( - 총 {mockupPastManageFunding.length}개
router.push(`/manage-club/funding/${row.id}`)} footer={ @@ -98,15 +79,12 @@ const PastFundingListTable: React.FC = () => { {numberToKrWon( - mockupPastManageFunding.reduce( - (acc, data) => acc + data.requestedAmount, - 0, - ), + fundings.reduce((acc, data) => acc + data.expenditureAmount, 0), )} {numberToKrWon( - mockupPastManageFunding.reduce( + fundings.reduce( (acc, data) => acc + (data.approvedAmount ?? 0), 0, ), diff --git a/packages/web/src/features/manage-club/funding/components/_atomic/PastSingleSemesterFundingListSection.tsx b/packages/web/src/features/manage-club/funding/components/_atomic/PastSingleSemesterFundingListSection.tsx index 8718ceb23..9bbe894f8 100644 --- a/packages/web/src/features/manage-club/funding/components/_atomic/PastSingleSemesterFundingListSection.tsx +++ b/packages/web/src/features/manage-club/funding/components/_atomic/PastSingleSemesterFundingListSection.tsx @@ -1,15 +1,22 @@ "use client"; -import React, { useState } from "react"; +import React from "react"; import styled from "styled-components"; -import FoldUnfoldButton from "@sparcs-clubs/web/common/components/Buttons/FoldUnfoldButton"; -import Typography from "@sparcs-clubs/web/common/components/Typography"; +import AsyncBoundary from "@sparcs-clubs/web/common/components/AsyncBoundary"; +import FoldableSection from "@sparcs-clubs/web/common/components/FoldableSection"; import { singleSemesterFundingListSectionTitleText } from "@sparcs-clubs/web/constants/manageClubFunding"; +import useGetActivityTerm from "@sparcs-clubs/web/features/activity-report/hooks/useGetActivityTerm"; +import useGetPastFundingList from "@sparcs-clubs/web/features/manage-club/funding/hooks/useGetPastFundingList"; + import PastFundingListTable from "./PastFundingListTable"; +interface PastSingleSemesterFundingListSectionProps { + termId: number; + clubId: number; +} const PastSingleSemesterFundingListSectionInner = styled.div` /* Auto layout */ display: flex; @@ -27,90 +34,27 @@ const PastSingleSemesterFundingListSectionInner = styled.div` z-index: 2; */ `; -const SingleSemesterTitleRow = styled.div` - /* layout from FoldableSectionTitleInner */ - display: flex; - align-items: center; - justify-content: space-between; - gap: 20px; +const PastSingleSemesterFundingListSection: React.FC< + PastSingleSemesterFundingListSectionProps +> = ({ clubId, termId }) => { + const { data: activityTerm } = useGetActivityTerm(clubId, termId); - width: 100%; -`; - -const mockData: { - name: string; - fundings: Array<{ - id: number; - status: string; - activityName: string; - contentName: string; - expenditureAmount: number; - approvedAmount?: number; - }>; -} = { - name: "2023년 가을학기", - fundings: [ - { - id: 1, - status: "신청", - activityName: "개발개발한 어떠한 활동", - contentName: "모니터", - expenditureAmount: 300000, - }, - { - id: 2, - status: "신청", - activityName: "개발개발한 어떠한 활동", - contentName: "모니터", - expenditureAmount: 300000, - }, - { - id: 3, - status: "운위", - activityName: "개발개발한 어떠한 활동", - contentName: "모니터", - expenditureAmount: 300000, - }, - { - id: 4, - status: "반려", - activityName: "개발개발한 어떠한 활동", - contentName: "모니터", - expenditureAmount: 300000, - }, - { - id: 5, - status: "승인", - activityName: "개발개발한 어떠한 활동", - contentName: "모니터", - expenditureAmount: 300000, - approvedAmount: 300000, - }, - { - id: 6, - status: "승인", - activityName: "개발개발한 어떠한 활동", - contentName: "모니터", - expenditureAmount: 300000, - approvedAmount: 0, - }, - ], -}; + const { data, isLoading, isError } = useGetPastFundingList(termId, clubId); -const PastSingleSemesterFundingListSection: React.FC = () => { - const [folded, setFolded] = useState(false); return ( - - - {singleSemesterFundingListSectionTitleText( - mockData.name, - mockData.fundings.length, - )} - - - - {folded ? null : } + + + + + ); }; diff --git a/packages/web/src/features/manage-club/funding/constants/fundingProgressStatus.ts b/packages/web/src/features/manage-club/funding/constants/fundingProgressStatus.ts new file mode 100644 index 000000000..991865621 --- /dev/null +++ b/packages/web/src/features/manage-club/funding/constants/fundingProgressStatus.ts @@ -0,0 +1,48 @@ +import { FundingStatusEnum } from "@sparcs-clubs/interface/common/enum/funding.enum"; + +import { StatusAndDate } from "@sparcs-clubs/web/common/components/ProgressCheckSection"; +import { ProgressCheckSectionStatusEnum } from "@sparcs-clubs/web/common/components/ProgressCheckSection/progressCheckStationStatus"; + +interface FundingProgressDetail { + labels: string[]; + progress: StatusAndDate[]; +} + +/** + * 지원금의 신청 상태를 반환합니다. + * @param status 지원금 상태 + * @param date KST(Korea Standard Time) 기준 날짜 + */ +const getFundingProgress = ( + status: FundingStatusEnum, + date: Date, // KST 기준 날짜 +): FundingProgressDetail => { + // date는 이미 KST로 들어오므로 추가 변환 불필요 + switch (status) { + case FundingStatusEnum.Applied: + return { + labels: ["신청 완료", "승인 대기"], + progress: [{ status: ProgressCheckSectionStatusEnum.Approved, date }], + }; + case FundingStatusEnum.Approved: + return { + labels: ["신청 완료", "동아리 연합회 승인 완료"], + progress: [ + { status: ProgressCheckSectionStatusEnum.Approved, date: undefined }, + { status: ProgressCheckSectionStatusEnum.Approved, date }, + ], + }; + case FundingStatusEnum.Rejected: + return { + labels: ["신청 완료", "동아리 연합회 신청 반려"], + progress: [ + { status: ProgressCheckSectionStatusEnum.Approved, date: undefined }, + { status: ProgressCheckSectionStatusEnum.Canceled, date }, + ], + }; + default: + throw new Error("Invalid funding status"); + } +}; + +export { getFundingProgress }; diff --git a/packages/web/src/features/manage-club/funding/detail/components/BasicEvidenceList.tsx b/packages/web/src/features/manage-club/funding/detail/components/BasicEvidenceList.tsx index f993251b9..ac2a78f02 100644 --- a/packages/web/src/features/manage-club/funding/detail/components/BasicEvidenceList.tsx +++ b/packages/web/src/features/manage-club/funding/detail/components/BasicEvidenceList.tsx @@ -1,12 +1,14 @@ import React from "react"; +import { IFundingResponse } from "@sparcs-clubs/interface/api/funding/type/funding.type"; + import ThumbnailPreviewList from "@sparcs-clubs/web/common/components/File/ThumbnailPreviewList"; import FlexWrapper from "@sparcs-clubs/web/common/components/FlexWrapper"; import Typography from "@sparcs-clubs/web/common/components/Typography"; import { ListItem } from "./FundingInfoList"; -const BasicEvidenceList = () => ( +const BasicEvidenceList: React.FC<{ data: IFundingResponse }> = ({ data }) => ( ( > 필수 증빙 - {/* TODO: file이랑 연결 */} + 거래 사실 증빙 - + 거래 세부항목 증빙 - + + {data.tradeDetailExplanation} + + ); diff --git a/packages/web/src/features/manage-club/funding/detail/components/FixtureEvidenceList.tsx b/packages/web/src/features/manage-club/funding/detail/components/FixtureEvidenceList.tsx index 3b92f49a7..2a26edf5d 100644 --- a/packages/web/src/features/manage-club/funding/detail/components/FixtureEvidenceList.tsx +++ b/packages/web/src/features/manage-club/funding/detail/components/FixtureEvidenceList.tsx @@ -1,11 +1,13 @@ -import React from "react"; +import React, { useMemo } from "react"; + +import { IFundingResponse } from "@sparcs-clubs/interface/api/funding/type/funding.type"; + +import { FixtureClassEnum } from "@sparcs-clubs/interface/common/enum/funding.enum"; import ThumbnailPreviewList from "@sparcs-clubs/web/common/components/File/ThumbnailPreviewList"; import FlexWrapper from "@sparcs-clubs/web/common/components/FlexWrapper"; import Typography from "@sparcs-clubs/web/common/components/Typography"; -import mockFundingDetail from "@sparcs-clubs/web/features/manage-club/services/_mock/mockFundingDetail"; - import { classEnumMap, evidenceEnumMap, @@ -15,13 +17,39 @@ import { ListItem } from "./FundingInfoList"; interface FixtureEvidenceListProps { isFixture?: boolean; + data: IFundingResponse; } const FixtureEvidenceList: React.FC = ({ isFixture = false, + data, }) => { const content = isFixture ? "비품" : "동아리 용품"; + const { purpose, fileList } = useMemo(() => { + if (isFixture) { + return data.fixture?.classEnum === FixtureClassEnum.Software + ? { + purpose: data.fixture.softwareEvidence, + fileList: data.fixture.softwareEvidenceFiles, + } + : { + purpose: data.fixture?.purpose, + fileList: data.fixture?.imageFiles, + }; + } + + return data.clubSupplies?.classEnum === FixtureClassEnum.Software + ? { + purpose: data.clubSupplies.softwareEvidence, + fileList: data.clubSupplies.softwareEvidenceFiles, + } + : { + purpose: data.clubSupplies?.purpose, + fileList: data.clubSupplies?.imageFiles, + }; + }, [data, isFixture]); + return ( = ({ 증빙 분류: {content}{" "} {evidenceEnumMap( isFixture - ? mockFundingDetail.fixtureEvidenceEnum - : mockFundingDetail.clubSuppliesEvidenceEnum, + ? data.fixture?.evidenceEnum + : data.clubSupplies?.evidenceEnum, )} {content} 분류:{" "} {classEnumMap( - isFixture - ? mockFundingDetail.fixtureClassEnum - : mockFundingDetail.clubSuppliesClassEnum, + isFixture ? data.fixture?.classEnum : data.clubSupplies?.classEnum, )} - {content}명:{" "} - {isFixture - ? mockFundingDetail.fixtureName - : mockFundingDetail.clubSuppliesName} + {content}명: {isFixture ? data.fixture?.name : data.clubSupplies?.name} {content} 증빙 - {/* TODO: file이랑 연결 */} + - {isFixture - ? mockFundingDetail.fixturePurpose - : mockFundingDetail.clubSuppliesPurpose} + {purpose} - + ); diff --git a/packages/web/src/features/manage-club/funding/detail/components/FundingInfoList.tsx b/packages/web/src/features/manage-club/funding/detail/components/FundingInfoList.tsx index b44944c2e..30e46195c 100644 --- a/packages/web/src/features/manage-club/funding/detail/components/FundingInfoList.tsx +++ b/packages/web/src/features/manage-club/funding/detail/components/FundingInfoList.tsx @@ -1,10 +1,16 @@ +import * as React from "react"; + +import { IFundingResponse } from "@sparcs-clubs/interface/api/funding/type/funding.type"; import styled from "styled-components"; import FlexWrapper from "@sparcs-clubs/web/common/components/FlexWrapper"; import Typography from "@sparcs-clubs/web/common/components/Typography"; -import mockFundingDetail from "@sparcs-clubs/web/features/manage-club/services/_mock/mockFundingDetail"; import { formatDate } from "@sparcs-clubs/web/utils/Date/formatDate"; +interface FundingInfoListProps { + data: IFundingResponse; +} + export const ListItem = styled.div` font-family: ${({ theme }) => theme.fonts.FAMILY.PRETENDARD}; font-weight: ${({ theme }) => theme.fonts.WEIGHT.REGULAR}; @@ -19,7 +25,7 @@ export const ListItem = styled.div` } `; -const FundingInfoList = () => ( +const FundingInfoList: React.FC = ({ data }) => ( ( > 지원금 정보 - 항목명: {mockFundingDetail.name} - {/* TODO: purposeId 연결하기 */} - 지출 목적: ~~~~ - - 지출 일자: {formatDate(mockFundingDetail.expenditureDate)} - - - 지출 금액: {mockFundingDetail.expenditureAmount.toLocaleString()}원 - + 항목명: {data.name} + 지출 목적: {data.purposeActivity?.name} + 지출 일자: {formatDate(data.expenditureDate)} + 지출 금액: {data.expenditureAmount.toLocaleString()}원 ); diff --git a/packages/web/src/features/manage-club/funding/detail/components/NonCorpEvidenceList.tsx b/packages/web/src/features/manage-club/funding/detail/components/NonCorpEvidenceList.tsx index 2db5f27c1..c0ceec9d9 100644 --- a/packages/web/src/features/manage-club/funding/detail/components/NonCorpEvidenceList.tsx +++ b/packages/web/src/features/manage-club/funding/detail/components/NonCorpEvidenceList.tsx @@ -1,13 +1,15 @@ import React from "react"; +import { IFundingResponse } from "@sparcs-clubs/interface/api/funding/type/funding.type"; + import FlexWrapper from "@sparcs-clubs/web/common/components/FlexWrapper"; import Typography from "@sparcs-clubs/web/common/components/Typography"; -import mockFundingDetail from "@sparcs-clubs/web/features/manage-club/services/_mock/mockFundingDetail"; - import { ListItem } from "./FundingInfoList"; -const NonCorpEvidenceList = () => ( +const NonCorpEvidenceList: React.FC<{ data: IFundingResponse }> = ({ + data, +}) => ( ( > 비법인 거래 - {/* TODO: file이랑 연결 */} - 거래자명: {mockFundingDetail.traderName} + + 거래자명: {data.nonCorporateTransaction?.traderName} - 거래자 계좌번호: {mockFundingDetail.traderAccountNumber} + 거래자 계좌번호: {data.nonCorporateTransaction?.traderAccountNumber} - 낭비가 아니라는 소명: {mockFundingDetail.wasteExplanation} + 낭비가 아니라는 소명: {data.nonCorporateTransaction?.wasteExplanation} ); diff --git a/packages/web/src/features/manage-club/funding/detail/components/OtherEvidenceList.tsx b/packages/web/src/features/manage-club/funding/detail/components/OtherEvidenceList.tsx index 323c6c27a..1f91d4ec2 100644 --- a/packages/web/src/features/manage-club/funding/detail/components/OtherEvidenceList.tsx +++ b/packages/web/src/features/manage-club/funding/detail/components/OtherEvidenceList.tsx @@ -1,5 +1,6 @@ import React from "react"; +import { FileDetail } from "@sparcs-clubs/web/common/components/File/attachment"; import ThumbnailPreviewList from "@sparcs-clubs/web/common/components/File/ThumbnailPreviewList"; import FlexWrapper from "@sparcs-clubs/web/common/components/FlexWrapper"; import Typography from "@sparcs-clubs/web/common/components/Typography"; @@ -8,14 +9,14 @@ import { ListItem } from "./FundingInfoList"; interface OtherEvidenceListProps { content: string; - explanation: string; - // fileList: { uid: string; link: string }[]; + explanation?: string; + fileList?: FileDetail[]; } const OtherEvidenceList: React.FC = ({ content, - explanation, - // fileList, + explanation = "", + fileList = [], }) => ( = ({ {content} 증빙: {explanation} - {/* TODO: file 연결 */} - + ); diff --git a/packages/web/src/features/manage-club/funding/detail/components/TransportationEvidenceList.tsx b/packages/web/src/features/manage-club/funding/detail/components/TransportationEvidenceList.tsx index 068cbec8b..606435ec4 100644 --- a/packages/web/src/features/manage-club/funding/detail/components/TransportationEvidenceList.tsx +++ b/packages/web/src/features/manage-club/funding/detail/components/TransportationEvidenceList.tsx @@ -1,13 +1,16 @@ import React from "react"; +import { IFundingResponse } from "@sparcs-clubs/interface/api/funding/type/funding.type"; + import FlexWrapper from "@sparcs-clubs/web/common/components/FlexWrapper"; import Typography from "@sparcs-clubs/web/common/components/Typography"; -import mockFundingDetail from "@sparcs-clubs/web/features/manage-club/services/_mock/mockFundingDetail"; import { transportationEnumMap } from "@sparcs-clubs/web/utils/fundingEnumMap"; import { ListItem } from "./FundingInfoList"; -const TransportationEvidenceList = () => ( +const TransportationEvidenceList: React.FC<{ data: IFundingResponse }> = ({ + data, +}) => ( ( 교통비 - 교통수단: {transportationEnumMap(mockFundingDetail.transportationEnum)} + 교통수단: {transportationEnumMap(data.transportation?.enum)} - 출발지: {mockFundingDetail.origin} - 도착지: {mockFundingDetail.destination} + 출발지: {data.transportation?.origin} + 도착지: {data.transportation?.destination} - 탑승자 명단 ({mockFundingDetail.transportationPassengers.length}명) + 탑승자 명단 ({data.transportation?.passengers.length}명) - {mockFundingDetail.transportationPassengers.map(passenger => ( + {data.transportation?.passengers.map(passenger => ( ( ))} - 이용 목적: {mockFundingDetail.purposeOfTransportation} + 이용 목적: {data.transportation?.purpose} ); diff --git a/packages/web/src/features/manage-club/funding/detail/frames/FundingDetailFrame.tsx b/packages/web/src/features/manage-club/funding/detail/frames/FundingDetailFrame.tsx index a49598441..9210040c1 100644 --- a/packages/web/src/features/manage-club/funding/detail/frames/FundingDetailFrame.tsx +++ b/packages/web/src/features/manage-club/funding/detail/frames/FundingDetailFrame.tsx @@ -1,18 +1,25 @@ import React from "react"; +import { useQueryClient } from "@tanstack/react-query"; import { useParams, useRouter } from "next/navigation"; import { overlay } from "overlay-kit"; import styled from "styled-components"; +import NotFound from "@sparcs-clubs/web/app/not-found"; +import AsyncBoundary from "@sparcs-clubs/web/common/components/AsyncBoundary"; import Button from "@sparcs-clubs/web/common/components/Button"; import Card from "@sparcs-clubs/web/common/components/Card"; import FlexWrapper from "@sparcs-clubs/web/common/components/FlexWrapper"; import Modal from "@sparcs-clubs/web/common/components/Modal"; import CancellableModalContent from "@sparcs-clubs/web/common/components/Modal/CancellableModalContent"; -import { ProgressCheckSectionStatusEnum } from "@sparcs-clubs/web/common/components/ProgressCheckSection/progressCheckStationStatus"; import ProgressStatus from "@sparcs-clubs/web/common/components/ProgressStatus"; import RejectReasonToast from "@sparcs-clubs/web/common/components/RejectReasonToast"; -import mockFundingDetail from "@sparcs-clubs/web/features/manage-club/services/_mock/mockFundingDetail"; + +import { getFundingProgress } from "@sparcs-clubs/web/features/manage-club/funding/constants/fundingProgressStatus"; +import { useDeleteFunding } from "@sparcs-clubs/web/features/manage-club/funding/services/useDeleteFunding"; +import { useGetFunding } from "@sparcs-clubs/web/features/manage-club/funding/services/useGetFunding"; +import { newFundingListQueryKey } from "@sparcs-clubs/web/features/manage-club/funding/services/useGetNewFundingList"; +import { isActivityReportUnverifiable } from "@sparcs-clubs/web/features/manage-club/funding/types/funding"; import BasicEvidenceList from "../components/BasicEvidenceList"; import FixtureEvidenceList from "../components/FixtureEvidenceList"; @@ -23,6 +30,7 @@ import TransportationEvidenceList from "../components/TransportationEvidenceList interface FundingDetailFrameProps { isNow: boolean; + clubId: number; } const ButtonWrapper = styled.div` @@ -30,10 +38,17 @@ const ButtonWrapper = styled.div` justify-content: space-between; `; -const FundingDetailFrame: React.FC = ({ isNow }) => { +const FundingDetailFrame: React.FC = ({ + isNow, + clubId, +}) => { const router = useRouter(); + const queryClient = useQueryClient(); const { id } = useParams<{ id: string }>(); + const { data: funding, isLoading, isError } = useGetFunding(+id); + const { mutate: deleteFunding } = useDeleteFunding(); + const onClick = () => { router.push("/manage-club/funding"); }; @@ -44,7 +59,6 @@ const FundingDetailFrame: React.FC = ({ isNow }) => { { close(); - // TODO: 수정 로직 넣기 router.push(`/manage-club/funding/${id}/edit`); }} onClose={close} @@ -62,9 +76,18 @@ const FundingDetailFrame: React.FC = ({ isNow }) => { { - close(); - // TODO: 삭제 로직 넣기 - router.push("/manage-club/funding"); + deleteFunding( + { requestParam: { id: Number(id) } }, + { + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: newFundingListQueryKey(clubId), + }); + close(); + router.replace("/manage-club/funding"); + }, + }, + ); }} onClose={close} > @@ -76,87 +99,109 @@ const FundingDetailFrame: React.FC = ({ isNow }) => { )); }; + if (isError) { + return ; + } + + if (!funding || !("clubId" in funding)) { + return ; + } + return ( {isNow && ( + labels={ + getFundingProgress( + funding.fundingStatusEnum, + new Date(), // TODO. funding 반환값 date로 수정 + ).labels } - /> - )} - - - {mockFundingDetail.purposeId === 0 && } - {mockFundingDetail.isFixture && } - {mockFundingDetail.isTransportation && } - {mockFundingDetail.isNonCorporateTransaction && } - {mockFundingDetail.isFoodExpense && ( - - )} - {mockFundingDetail.isLaborContract && ( - - )} - {mockFundingDetail.isExternalEventParticipationFee && ( - - )} - {mockFundingDetail.isPublication && ( - - )} - {mockFundingDetail.isProfitMakingActivity && ( - 0 && ( + ({ + datetime: comment.createdAt, + reason: comment.content, + }))} + /> + ) } /> )} - {mockFundingDetail.isJointExpense && ( - - )} - {mockFundingDetail.isEtcExpense && ( - - )} + + + + {funding.purposeActivity && + isActivityReportUnverifiable(funding.purposeActivity.id) && ( + + )} + {funding.isFixture && ( + + )} + {funding.isTransportation && ( + + )} + {funding.isNonCorporateTransaction && ( + + )} + {funding.isFoodExpense && ( + + )} + {funding.isLaborContract && ( + + )} + {funding.isExternalEventParticipationFee && ( + + )} + {funding.isPublication && ( + + )} + {funding.isProfitMakingActivity && ( + + )} + {funding.isJointExpense && ( + + )} + {funding.isEtcExpense && ( + + )} +