Skip to content

Commit

Permalink
Merge pull request #91 from IT-Cotato/feat/#90-deposit
Browse files Browse the repository at this point in the history
[Feat] 입금일 UI 및 기능
  • Loading branch information
yongaricode authored Feb 16, 2025
2 parents c8b9a05 + f94e3fc commit 058ebb4
Show file tree
Hide file tree
Showing 20 changed files with 517 additions and 22 deletions.
50 changes: 50 additions & 0 deletions src/api/deposit.api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { BankInfo, DepositInfo } from '../models/deposit.model';
import instance from './axios';

// 은행 목록 조회
export const getBankList = async () => {
try {
const response = await instance.get(`/api/v1/banks`);
if (response.status == 200) {
return response.data;
}
} catch (error) {
console.log(error);
}
};

// 입금일 생성 및 수정
export const putDeposit = async (roomId: string, payload: DepositInfo) => {
try {
const response = await instance.put(`/api/v1/rooms/${roomId}/deposit/modify`, payload);
if (response.status == 200) {
return response.data;
}
} catch (error) {
console.log(error);
}
};

// 입금일 상세 조회
export const getDeposit = async (roomId: string) => {
try {
const response = await instance.get(`/api/v1/rooms/${roomId}/deposit`);
if (response.status == 200) {
return response.data;
}
} catch (error) {
console.log(error);
}
};

// 입금일 수정을 위한 상세조회
export const getDepositDetail = async (roomId: string) => {
try {
const response = await instance.get(`/api/v1/rooms/${roomId}/deposit/modify`);
if (response.status == 200) {
return response.data;
}
} catch (error) {
console.log(error);
}
};
12 changes: 12 additions & 0 deletions src/assets/images/RoomDetail/Edit copy.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 19 additions & 0 deletions src/components/Bank.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
interface BankProps {
bankUrl: string;
bankName: string;
onClick: (event: React.MouseEvent<HTMLDivElement>) => void;
}

const Bank = ({ bankName, bankUrl, onClick }: BankProps) => {
return (
<div
className="p-2 bg-gray-50 flex flex-col items-center justify-center rounded-2xl aspect-square"
onClick={onClick}
>
<img src={bankUrl} className="p-[5px]"></img>
<p className="text-body4 leading-[25px] tracking-[-0.042px] text-gray-900">{bankName}</p>
</div>
);
};

export default Bank;
58 changes: 58 additions & 0 deletions src/components/BottomSheet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { useEffect, useState } from 'react';
import { getBankList } from '../api/deposit.api';
import Bank from './Bank';
import { BankInfo } from '../models/deposit.model';
import Loading from '../pages/Loading';

interface BottomSheetProps {
setBottomSheetOpen: (value: boolean) => void;
setBank: (value: BankInfo) => void;
}

const BottomSheet = ({ setBottomSheetOpen, setBank }: BottomSheetProps) => {
const [bankList, setBankList] = useState<BankInfo[]>([]);

const getBanks = () => {
getBankList().then((data) => {
setBankList(data.data.banks);
});
};

useEffect(() => {
getBanks();
}, []);

const handleBank = (bank: BankInfo) => {
setBank(bank);
setBottomSheetOpen(false);
};

if (!bankList) {
return <Loading text="은행 정보를 불러오는 데 실패하였습니다!" />;
}

return (
<div
className={`fixed left-0 right-0 bottom-0 z-20 mx-auto flex max-w-[500px] flex-col
rounded-t-lg bg-white shadow-[0_-6px_10px_-5px_rgba(0,0,0,0.6)]
transition-transform duration-500 ease-out translate-y-full
animate-slide-up`}
>
{/* 드래그 핸들 */}
<div className="h-6 relative pt-3 pb-1 rounded-t-lg rounded-br-lg">
<div className="w-10 h-1 rounded bg-gray-300 mx-auto"></div>
</div>

{/* 내용 */}
<div className="overflow-auto overscroll-contain scrollbar-hide h-[calc(100vh-10rem)] px-4 py-6">
<div className="grid grid-cols-3 gap-2">
{bankList.map((bank) => (
<Bank key={bank.bankId} bankName={bank.bankName} bankUrl={bank.bankUrl} onClick={() => handleBank(bank)} />
))}
</div>
</div>
</div>
);
};

export default BottomSheet;
8 changes: 6 additions & 2 deletions src/components/Calendar/CalendarDeposit.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useNavigate } from 'react-router-dom';
import money from '../../assets/images/money.png';
import vector from '../../assets/images/vector.png';
import { getClosestFutureDate } from '../../utils/getCloseDate';

type DepositProps = {
roomname: string[];
Expand All @@ -12,7 +13,10 @@ const CalendarDeposit = ({ roomname, nextDeopsit, isPermission }: DepositProps)
const nav = useNavigate();

const handleClick = () => {
if (nextDeopsit && isPermission) nav('payment');
if (nextDeopsit == '0') nav('payment/create');
// 입금일이 등록되지 않은 경우
else if (nextDeopsit && isPermission) nav('payment');
// 입금일 등록됐을 경우
};

return (
Expand All @@ -39,7 +43,7 @@ const CalendarDeposit = ({ roomname, nextDeopsit, isPermission }: DepositProps)
<div className="text-sm leading-6 tracking-[-0.042px] text-gray-800">{item} 입금일 입니다.</div>
) : (
<div className="text-sm leading-6 tracking-[-0.042px] text-gray-800">
다음 입금일은 {nextDeopsit} 입니다.
다음 입금일은 {getClosestFutureDate(nextDeopsit)} 입니다.
</div>
)}
</div>
Expand Down
38 changes: 38 additions & 0 deletions src/components/ChooseBank.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { useState } from 'react';
import { FaAngleDown } from 'react-icons/fa6';
import BottomSheet from './BottomSheet';
import Modal from './Modal/Modal';
import { BankInfo } from '../models/deposit.model';

interface ChooseBankProps {
setBank: (value: BankInfo) => void;
bank: BankInfo | undefined;
}

const ChooseBank = ({ setBank, bank }: ChooseBankProps) => {
const [bottomSheetOpen, setBottomSheetOpen] = useState(false);

return (
<>
<div
className="py-2 px-3 border-gray-300 border-[1px] rounded-md text-body3 flex justify-between items-center h-full"
onClick={() => setBottomSheetOpen(true)}
>
{bank ? (
<p className="text-gray-900 leading-7-">{bank.bankName}</p>
) : (
<p className="text-gray-400 leading-7-">은행 선택</p>
)}

<FaAngleDown fill="#C6C4C1" />
</div>
{bottomSheetOpen && (
<Modal onClose={() => setBottomSheetOpen(false)}>
<BottomSheet setBottomSheetOpen={setBottomSheetOpen} setBank={setBank} />
</Modal>
)}
</>
);
};

export default ChooseBank;
53 changes: 53 additions & 0 deletions src/components/DepositDetail.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { useNavigate } from 'react-router-dom';
import Edit from '../assets/images/RoomDetail/Edit copy.svg?react';
import { DepositInfo } from '../models/deposit.model';
import { getClosestFutureDate } from '../utils/getCloseDate';

interface DepositDetailProps {
depositInfo: DepositInfo;
}

const DepositDetail = ({ depositInfo }: DepositDetailProps) => {
const nav = useNavigate();

const depositDate = getClosestFutureDate(depositInfo.depositAt.toString()).split('-'); // 19일 -> 2025-05-19로 변환 -> [2025, 05, 19]
const month = depositDate[1].startsWith('0') ? depositDate[1].slice(1) : depositDate[1]; // 만약 0으로 시작한다면(05) 0 제거
const day = depositDate[2].startsWith('0') ? depositDate[2].slice(1) : depositDate[2]; // 만약 0으로 시작한다면(05) 0 제거

const amount = depositInfo.depositAmount.toLocaleString('ko-KR');

const goToEdit = () => {
nav('create?isEdit=true');
};

return (
<div className="py-[17px] px-4 rounded-xl border-2 border-solid border-gray-100 gap-[10px]">
{/* 정보 */}
<div className="flex flex-col text-body4 leading-[25px] tracking-[-0.042px]">
<div className="flex gap-1">
<p className="px-1 bg-primary_100 text-primary_700 rounded-[4px]">입금</p>
<p className="text-gray-900 flex-1">{depositInfo.bankName}</p>
<Edit onClick={goToEdit} />
</div>
<p className="text-gray-500 underline">{depositInfo.accountNumber}</p>
</div>
{/* 예정 입금액 */}
<div className="pt-4 pb-2 flex-col flex justify-center items-center">
<p className="text-body3 leading-7 font-semibold tracking-[-0.048px]">예정 입금액</p>
<div className="flex items-center gap-[1px]">
<span className="text-heading6 font-bold leading-10">{amount}</span>
<span className="text-base leading-7 tracking-[-0.048px] font-semibold"></span>
</div>
</div>
{/* 예정 입금일 */}
<div className="py-2 px-4 rounded-lg bg-gray-50 flex justify-between items-center">
<p className="text-body4 leading-[25px] tracking-[-0.042px]">예정 입금일</p>
<p className="font-semibold leading-7 tracking-[-0.048px]">
{month}{day}
</p>
</div>
</div>
);
};

export default DepositDetail;
11 changes: 11 additions & 0 deletions src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,17 @@ const Header = () => {
return { title: '상담 일지', left: `/user/${roomId}`, hasBorder: false };
}

// 입금 관련 경로
if (location.includes('/payment')) {
if (location.endsWith('/create')) {
return { title: '입금일 생성하기', left: true, hasBorder: false };
}
if (location.endsWith('/edit')) {
return { title: '입금일 정보 수정', left: true, hasBorder: false };
}
return { title: '입금일', left: `/user/${roomId}`, hasBorder: false };
}

return { title, left: true, hasBorder: false };
}
}
Expand Down
12 changes: 7 additions & 5 deletions src/components/Room/Input.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ChangeEvent } from 'react';

interface InputProps {
name: string;
name?: string;
placeholder: string;
setDesc: (value: string) => void;
desc: string;
Expand All @@ -15,10 +15,12 @@ const Input = ({ name, placeholder, setDesc, desc, isAble }: InputProps) => {

return (
<div className="flex flex-col gap-[6px]">
<div className="text-body4 leading-[26px] font-medium flex gap-1">
<span className="text-gray-900 ">{name}</span>
<span className="text-primary_700">(필수)</span>
</div>
{name && (
<div className="text-body4 leading-[26px] font-medium flex gap-1">
<span className="text-gray-900 ">{name}</span>
{name !== '입금 금액' && <span className="text-primary_700">(필수)</span>}
</div>
)}
<input
placeholder={placeholder}
className="py-2 px-3 border-gray-300 border-[1px] focus:outline-none focus:outline-2 focus:outline-gray-500 rounded-md text-body3"
Expand Down
5 changes: 3 additions & 2 deletions src/components/Room/PickDate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ interface PickDateProps {
setDeadline: (value: string) => void;
isAble: boolean;
text: string;
onlyDate?: boolean;
}

const PickDate = ({ deadline, setDeadline, isAble, text }: PickDateProps) => {
const PickDate = ({ deadline, setDeadline, isAble, text, onlyDate }: PickDateProps) => {
const [showDatePicker, setShowDatePicker] = useState(false);

const handleClick = () => {
Expand All @@ -26,7 +27,7 @@ const PickDate = ({ deadline, setDeadline, isAble, text }: PickDateProps) => {
<Calendar onClick={handleClick} className="cursor-pointer" />
<input
className="text-gray-400 text-body3 leading-7 tracking-[-0.048px] flex-1 bg-transparent pointer-events-none"
value={deadline}
value={onlyDate ? deadline.slice(8) : deadline}
placeholder={text}
></input>
<MdKeyboardArrowRight size={24} fill="#6A6966" />
Expand Down
23 changes: 23 additions & 0 deletions src/components/Toggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
interface ToggleProps {
isChecked: boolean;
setIsChecked: (value: boolean) => void;
}

const Toggle = ({ isChecked, setIsChecked }: ToggleProps) => {
return (
<div className="flex w-full justify-between font-normal text-base leading-7 mt-2 tracking-[-0.048px]">
<h1 className={`${isChecked ? 'text-gray-900' : 'text-gray-500'}`}>입금일 알림</h1>
<label
className={`relative block w-10 h-6 bg-gray-100 rounded-full cursor-pointer transition ${isChecked ? 'bg-primary_700' : 'bg-gray-300'}
before:content-['']
before:absolute before:top-0.5 before:left-0.5
before:w-5 before:h-5 before:bg-white before:rounded-full before:transition
${isChecked ? 'before:translate-x-4' : ''}`}
>
<input type="checkbox" checked={isChecked} className="hidden" onChange={() => setIsChecked(!isChecked)} />
</label>
</div>
);
};

export default Toggle;
15 changes: 15 additions & 0 deletions src/models/deposit.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// 은행 목록 조회
export interface BankInfo {
bankId: number;
bankName: string;
bankUrl: string;
}

// 입금일 생성 및 수정 및 조회
export interface DepositInfo {
bankId?: number;
accountNumber: string;
depositAmount: number;
depositAt: number;
bankName?: string;
}
Loading

0 comments on commit 058ebb4

Please sign in to comment.