Skip to content

Commit

Permalink
Merge pull request #1403 from academic-relations/1402-add-partial-app…
Browse files Browse the repository at this point in the history
…rove-toast-in-funding-page

add partial approve related logic in funding detail frame
  • Loading branch information
jooyeongmee authored Jan 31, 2025
2 parents ff78b90 + 1c4edd6 commit a5b0f4a
Show file tree
Hide file tree
Showing 10 changed files with 333 additions and 65 deletions.
107 changes: 107 additions & 0 deletions packages/web/src/common/components/ApproveReasonToast.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import React from "react";

import styled from "styled-components";

import colors from "@sparcs-clubs/web/styles/themes/colors";
import { formatDotDetailDate } from "@sparcs-clubs/web/utils/Date/formatDate";

import FlexWrapper from "./FlexWrapper";
import Icon from "./Icon";

import Typography from "./Typography";

interface Reason {
datetime: Date;
reason: React.ReactNode;
status?: string;
}

interface ApproveReasonToastProps {
title: string;
reasons: Reason[];
}

const ForceBorderRadius = styled.div`
position: sticky;
top: 0px;
border-radius: 8px;
width: 100%;
overflow: hidden;
border: 1px solid ${({ theme }) => theme.colors.GREEN[600]};
background: ${({ theme }) => theme.colors.GREEN[100]};
z-index: ${({ theme }) => theme.zIndices.toast};
`;

const ApproveReasonToastInner = styled.div`
color: ${({ theme }) => theme.colors.BLACK};
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 8px;
width: 100%;
max-height: 300px;
overflow-y: auto;
.ApproveReasonToast-title {
padding: 12px 16px 0 16px;
position: sticky;
top: 0;
display: flex;
align-items: center;
gap: 8px;
background: ${({ theme }) => theme.colors.GREEN[100]};
z-index: ${({ theme }) => theme.zIndices.toast + 1};
}
.ApproveReasonToast-reasons {
display: flex;
width: 100%;
padding: 0 16px 12px 44px;
flex-direction: column;
gap: 8px;
flex: 1 0 0;
}
.ApproveReasonToast-sticky-title {
position: sticky;
top: 0;
background: ${({ theme }) => theme.colors.GREEN[100]};
z-index: ${({ theme }) => theme.zIndices.toast + 1};
}
`;

const ApproveReasonToast: React.FC<ApproveReasonToastProps> = ({
title,
reasons,
}) => (
<ForceBorderRadius>
<ApproveReasonToastInner>
<div className="ApproveReasonToast-title">
<Icon type="error" size={20} color={colors.GREEN[600]} />
<Typography fs={16} lh={24} fw="MEDIUM">
{title}
</Typography>
</div>
<div className="ApproveReasonToast-reasons">
{reasons.map(reason => (
<FlexWrapper
direction="column"
gap={4}
key={formatDotDetailDate(reason.datetime)}
>
<Typography fs={14} lh={16} fw="REGULAR" color="GRAY.600">
{formatDotDetailDate(reason.datetime)}{" "}
{reason.status && `• ${reason.status}`}
</Typography>
<Typography fs={16} lh={24} fw="REGULAR">
{reason.reason}
</Typography>
</FlexWrapper>
))}
</div>
</ApproveReasonToastInner>
</ForceBorderRadius>
);

export default ApproveReasonToast;
4 changes: 3 additions & 1 deletion packages/web/src/common/components/RejectReasonToast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import Typography from "./Typography";
interface Reason {
datetime: Date;
reason: React.ReactNode;
status?: string;
}

interface RejectReasonToastProps {
Expand Down Expand Up @@ -90,7 +91,8 @@ const RejectReasonToast: React.FC<RejectReasonToastProps> = ({
key={formatDotDetailDate(reason.datetime)}
>
<Typography fs={14} lh={16} fw="REGULAR" color="GRAY.600">
{formatDotDetailDate(reason.datetime)}
{formatDotDetailDate(reason.datetime)}{" "}
{reason.status && `• ${reason.status} 사유`}
</Typography>
<Typography fs={16} lh={24} fw="REGULAR">
{reason.reason}
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/constants/tableTagList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ const FundingTagList: {
[FundingStatusEnum.Committee]: { text: "운위", color: "YELLOW" },
[FundingStatusEnum.Approved]: { text: "승인", color: "GREEN" },
[FundingStatusEnum.Rejected]: { text: "반려", color: "RED" },
[FundingStatusEnum.Partial]: { text: "부분 승인", color: "PURPLE" },
[FundingStatusEnum.Partial]: { text: "부분", color: "PURPLE" },
};

// TODO: interface enum 사용
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ const getFundingProgress = (
},
],
};
// TODO. 부분 승인 케이스 수정 필요
case FundingStatusEnum.Partial:
return {
labels: ["신청 완료", "동아리 연합회 부분 승인"],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React, { useMemo } from "react";

import { IFundingCommentResponse } from "@sparcs-clubs/interface/api/funding/type/funding.type";
import { FundingStatusEnum } from "@sparcs-clubs/interface/common/enum/funding.enum";

import ApproveReasonToast from "@sparcs-clubs/web/common/components/ApproveReasonToast";
import ProgressStatus from "@sparcs-clubs/web/common/components/ProgressStatus";

import RejectReasonToast from "@sparcs-clubs/web/common/components/RejectReasonToast";

import { FundingTagList } from "@sparcs-clubs/web/constants/tableTagList";
import { getFundingProgress } from "@sparcs-clubs/web/features/manage-club/funding/constants/fundingProgressStatus";
import { getTagDetail } from "@sparcs-clubs/web/utils/getTagDetail";

interface FundingStatusSectionProps {
status: FundingStatusEnum;
editedAt: Date;
commentedAt?: Date;
comments: IFundingCommentResponse[];
}

const FundingStatusSection: React.FC<FundingStatusSectionProps> = ({
status,
editedAt,
commentedAt = undefined,
comments,
}) => {
const progressStatus = getFundingProgress(status, editedAt, commentedAt);

const ToastSection = useMemo(() => {
if (status === FundingStatusEnum.Rejected) {
return (
<RejectReasonToast
title="코멘트"
reasons={comments.map(comment => ({
datetime: comment.createdAt,
reason: comment.content,
status: getTagDetail(comment.fundingStatusEnum, FundingTagList)
.text,
}))}
/>
);
}

return (
<ApproveReasonToast
title="코멘트"
reasons={comments.map(comment => ({
datetime: comment.createdAt,
reason: comment.content,
status:
comment.fundingStatusEnum === FundingStatusEnum.Partial
? `${getTagDetail(comment.fundingStatusEnum, FundingTagList).text}승인`
: getTagDetail(comment.fundingStatusEnum, FundingTagList).text,
}))}
/>
);
}, [comments, status]);

return (
<ProgressStatus
labels={progressStatus.labels}
progress={progressStatus.progress}
optional={comments && comments.length > 0 && ToastSection}
/>
);
};

export default FundingStatusSection;
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@ 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 ProgressStatus from "@sparcs-clubs/web/common/components/ProgressStatus";
import RejectReasonToast from "@sparcs-clubs/web/common/components/RejectReasonToast";

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 useGetFundingDeadline from "@sparcs-clubs/web/features/manage-club/funding/services/useGetFundingDeadline";
Expand All @@ -26,6 +23,7 @@ import { isActivityReportUnverifiable } from "@sparcs-clubs/web/features/manage-
import BasicEvidenceList from "../components/BasicEvidenceList";
import FixtureEvidenceList from "../components/FixtureEvidenceList";
import FundingInfoList from "../components/FundingInfoList";
import FundingStatusSection from "../components/FundingStatusSection";
import NonCorpEvidenceList from "../components/NonCorpEvidenceList";
import OtherEvidenceList from "../components/OtherEvidenceList";
import TransportationEvidenceList from "../components/TransportationEvidenceList";
Expand Down Expand Up @@ -135,34 +133,11 @@ const FundingDetailFrame: React.FC<FundingDetailFrameProps> = ({ clubId }) => {
<FlexWrapper direction="column" gap={40}>
<Card outline>
{!isPastFunding && (
// TODO. 부분 승인 케이스 추가
<ProgressStatus
labels={
getFundingProgress(
funding.fundingStatusEnum,
funding.editedAt,
funding.commentedAt,
).labels
}
progress={
getFundingProgress(
funding.fundingStatusEnum,
funding.editedAt,
funding.commentedAt,
).progress
}
optional={
funding.comments &&
funding.comments.length > 0 && (
<RejectReasonToast
title="반려 사유"
reasons={funding.comments.map(comment => ({
datetime: comment.createdAt,
reason: comment.content,
}))}
/>
)
}
<FundingStatusSection
status={funding.fundingStatusEnum}
editedAt={funding.editedAt}
commentedAt={funding.commentedAt}
comments={funding.comments.toReversed()}
/>
)}
<AsyncBoundary isLoading={isLoading} isError={isError}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import apiFnd002, {
ApiFnd002ResponseOk,
} from "@sparcs-clubs/interface/api/funding/endpoint/apiFnd002";
import { FundingStatusEnum } from "@sparcs-clubs/interface/common/enum/funding.enum";
import { useQuery } from "@tanstack/react-query";

import {
Expand All @@ -26,5 +27,114 @@ export const useGetFunding = (fundingId: number) =>
});

defineAxiosMock(mock => {
mock.onGet(apiFnd002.url(1)).reply(() => [200, {}]);
const fundingStatus = FundingStatusEnum.Approved;
const mockFundingDetail = {
id: 1,
club: {
id: 112,
},
activityD: {
id: 5,
},
fundingStatusEnum: fundingStatus,
purposeActivity: {
id: 3335,
activityStatusEnum: 2,
activityTypeEnum: 1,
club: {
id: 112,
name: "술박스",
},
name: "ffsgdgfd",
commentedAt: null,
editedAt: "2025-01-22T23:07:21.000Z",
updatedAt: "2025-01-25T04:35:48.000Z",
},
name: "ㅁㄹㅁㄴㅇㄹㅁㄴㅇㄹ",
expenditureDate: "2024-08-07T09:00:00.000Z",
expenditureAmount: 234234,
approvedAmount: 0,
tradeEvidenceFiles: [
{
id: "b5884fb8-672d-45cf-a4de-5d1ce34a35f4",
name: "websiteplanet-dummy-540X400.png",
extension: "png",
size: 8141,
userId: 8608,
url: "https://ar-002-clubs-v2-dev.s3.ap-northeast-2.amazonaws.com/file/8608.1737884920000.websiteplanet-dummy-540X400.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAZWM3SVKMLN74ARWD%2F20250130%2Fap-northeast-2%2Fs3%2Faws4_request&X-Amz-Date=20250130T133654Z&X-Amz-Expires=600&X-Amz-Signature=210bbac945b25b631498baca8ac0efa1df5cf06768c9287aa4f1d79a04541e9a&X-Amz-SignedHeaders=host&x-id=GetObject",
},
],
tradeDetailFiles: [
{
id: "c0c3fe89-66bd-441f-89e1-069ec53cdfbc",
name: "websiteplanet-dummy-540X400 (1).png",
extension: "png",
size: 8110,
userId: 8608,
url: "https://ar-002-clubs-v2-dev.s3.ap-northeast-2.amazonaws.com/file/8608.1737884926000.websiteplanet-dummy-540X400%20%281%29.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAZWM3SVKMLN74ARWD%2F20250130%2Fap-northeast-2%2Fs3%2Faws4_request&X-Amz-Date=20250130T133654Z&X-Amz-Expires=600&X-Amz-Signature=2cb8499190a6de332edc28793c08f7702e84a596ba033a0bbf1044eab8ffc2e8&X-Amz-SignedHeaders=host&x-id=GetObject",
},
],
tradeDetailExplanation: "ㅇㅎㅇㅎㄹㅇㅎ",
isFixture: false,
isTransportation: false,
isNonCorporateTransaction: false,
isFoodExpense: false,
isLaborContract: true,
laborContract: {
explanation: "ㅇㄴㄹㅇ",
files: [
{
id: "4c991eb5-a5d9-4df9-a5cc-9dbf87d1c67a",
name: "websiteplanet-dummy-540X400 (1).png",
extension: "png",
size: 8110,
userId: 8608,
url: "https://ar-002-clubs-v2-dev.s3.ap-northeast-2.amazonaws.com/file/8608.1737884935000.websiteplanet-dummy-540X400%20%281%29.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAZWM3SVKMLN74ARWD%2F20250130%2Fap-northeast-2%2Fs3%2Faws4_request&X-Amz-Date=20250130T133654Z&X-Amz-Expires=600&X-Amz-Signature=0d291445afba29cc48a30136ca4d9b52ee89a4f4102df8418e4a6002e95ad7c4&X-Amz-SignedHeaders=host&x-id=GetObject",
},
],
},
isExternalEventParticipationFee: false,
isPublication: false,
isProfitMakingActivity: false,
isJointExpense: false,
isEtcExpense: false,
editedAt: "2025-01-26T18:48:59.000Z",
commentedAt: new Date(),
createdAt: "2025-01-26T18:48:59.000Z",
updatedAt: "2025-01-29T22:41:08.000Z",
comments: [
{
id: 1,
funding: { id: 1 },
chargedExecutive: { id: 123 },
content: "대충 어떤 반려 사유",
fundingStatusEnum: FundingStatusEnum.Rejected,
approvedAmount: 1230,
createdAt: new Date(2024, 11, 10),
},
{
id: 1,
funding: { id: 1 },
chargedExecutive: { id: 123 },
content: "대충 어떤 부분 승인 사유",
fundingStatusEnum: FundingStatusEnum.Partial,
approvedAmount: 1230,
createdAt: new Date(2024, 12, 20),
},
{
id: 1,
funding: { id: 1 },
chargedExecutive: { id: 123 },
content: "대충 어떤 승인 사유",
fundingStatusEnum: FundingStatusEnum.Approved,
approvedAmount: 1230,
createdAt: new Date(),
},
],
};

const baseUrl = `/student/fundings/funding/`;
mock
.onGet(new RegExp(`^${baseUrl}\\d+$`))
.reply(() => [200, mockFundingDetail]);
});
Loading

0 comments on commit a5b0f4a

Please sign in to comment.