diff --git a/.prettierrc b/.prettierrc.json similarity index 100% rename from .prettierrc rename to .prettierrc.json diff --git a/src/app/Router.tsx b/src/app/Router.tsx index d7e38168..23d3a7cd 100644 --- a/src/app/Router.tsx +++ b/src/app/Router.tsx @@ -38,6 +38,11 @@ const LazyMyLinks = lazy(() => default: MyLinksPage, })) ); +const LazyPaymentManagement = lazy(() => + import('@/pages/paymentManagement').then(({ PaymentManagementPage }) => ({ + default: PaymentManagementPage, + })) +); const LazyMyPage = lazy(() => import('@/pages/my').then(({ MyPage }) => ({ default: MyPage, @@ -97,6 +102,10 @@ function AppRouter() { path: ROUTE.myEdit, element: , }, + { + path: ROUTE.paymentManagement, + element: , + }, { path: ROUTE.selectGroup, element: , @@ -123,6 +132,7 @@ function AppRouter() { element: , loader: groupTokenUrlLoader, }, + { path: '*', element: , diff --git a/src/pages/home/HomePage.style.ts b/src/pages/home/HomePage.style.ts index 5eb5e768..2c5c6596 100644 --- a/src/pages/home/HomePage.style.ts +++ b/src/pages/home/HomePage.style.ts @@ -1,45 +1,5 @@ -import { Link } from 'react-router'; import styled from 'styled-components'; -/** @Todo Button 공통 컴포넌트 제작 */ -export const Button = styled.button<{ color?: string; bgColor?: string }>` - display: flex; - justify-content: center; - align-items: center; - background-color: ${({ bgColor }) => bgColor || 'black'}; - color: ${({ color }) => color || 'white'}; - font-weight: 400; - border-radius: 9999px; - padding: 0.5rem 0.75rem; // 8px 12px - height: fit-content; - width: fit-content; - line-height: 1.5; - font-size: 0.875rem; //14px - cursor: pointer; - - &:hover { - filter: brightness(0.9); - transition: filter 0.1s; - } -`; - -export const SelectGroupButton = styled.button` - display: flex; - align-items: center; - gap: ${({ theme }) => theme.unit[4]}; - background: transparent; - color: ${({ theme }) => theme.color.semantic.orange.default}; -`; - -export const MainHeader = styled.header` - display: flex; - justify-content: space-between; - align-items: center; - padding: 1rem 1.25rem; // 20px - width: 100%; - background-color: ${({ theme }) => theme.color.semantic.background.normal}; -`; - export const MainText = styled.h2` font-size: 1.25rem; // 20px font-weight: 700; @@ -52,72 +12,3 @@ export const SubText = styled.p` white-space: pre-wrap; line-height: 1.5; `; - -export const DescriptionImg = styled.img` - width: 9.8rem; - object-fit: contain; - position: absolute; - top: 9%; - right: -0.2rem; - rotate: -2deg; -`; - -export const Hr = styled.hr` - border: 0.5rem solid #edeeee; - width: 100%; -`; - -export const SettlementTitle = styled.h2` - font-size: 1.25rem; // 20px - font-weight: 700; - padding: 0.5rem 1.25rem; // 8px 20px - white-space: nowrap; -`; - -export const SettlementButton = styled(Button)<{ selected?: boolean }>` - background-color: ${({ selected, theme }) => - selected ? theme.color.semantic.primary : '#F1F3F5'}; - color: ${({ selected }) => (selected ? 'white' : 'black')}; -`; - -export const NoSettlementImg = styled.img` - width: 33vw; - max-width: 200px; - object-fit: contain; -`; - -export const BoxButton = styled(Link)` - display: flex; - padding: ${({ theme }) => `${theme.unit[16]} ${theme.unit[20]}`}; - position: relative; - height: 5rem; - background-color: ${({ theme }) => theme.color.semantic.orange.subtle}; - border-radius: ${({ theme }) => theme.radius.default}; - - width: 100%; -`; - -export const SmallImg = styled.img` - width: 2.75rem; - object-fit: contain; - position: absolute; - right: 1rem; - bottom: 0.5rem; -`; - -export const BoxButtonWrapper = styled.div` - display: flex; - max-width: 37.5rem; - margin: ${({ theme }) => - `0 ${theme.unit[20]} ${theme.unit[32]} ${theme.unit[20]}`}; - gap: ${({ theme }) => theme.unit[8]}; -`; - -export const SettlementListWrapper = styled.div` - display: flex; - flex-direction: column; - gap: ${({ theme }) => theme.unit[20]}; - margin: ${({ theme }) => `${theme.unit[20]} 0`}; - overflow-y: auto; - flex: 1; -`; diff --git a/src/pages/home/HomePage.tsx b/src/pages/home/HomePage.tsx index ee2aefcb..2ec4796d 100644 --- a/src/pages/home/HomePage.tsx +++ b/src/pages/home/HomePage.tsx @@ -1,181 +1,18 @@ -import { useTheme } from 'styled-components'; -import { LogoIcon } from '@/shared/assets/svgs'; -import MainHamImg2 from '@/shared/assets/pngs/MainHamImg2.png'; -import Text from '@/shared/ui/Text'; -import { ArrowRight, Bell, Menu, Next } from '@/shared/assets/svgs/icon'; -import { useNavigate } from 'react-router'; -import { ROUTE } from '@/shared/config/route'; -import { useState } from 'react'; -import CoinImg from '@/shared/assets/pngs/CoinImg.png'; -import LinkMain from '@/shared/assets/pngs/link_main.png'; -import CardMain from '@/shared/assets/pngs/card_main.png'; import Divider from '@/shared/ui/Divider'; import Flex from '@/shared/ui/Flex'; -import HomeExpenseItem from './ui/HomeExpenseItem'; -import * as S from './HomePage.style'; - -interface HomeExpenseItemType { - date: string; - groupName: string; - totalAmount: number; - paidMember: number; - totalMember: number; - id: number; -} -/** - * @Todo 진행중인 정산 내역 조회 API 함수 호출 - * 우선 mock data로 대체 - * */ -const settlementList: HomeExpenseItemType[] = [ - { - id: 1, - date: '2025년 2월 22일', - groupName: 'DND 데모데이', - totalAmount: 120000, - paidMember: 3, - totalMember: 6, - }, - { - id: 2, - date: '2025년 1월 14일', - groupName: 'DND 7조 첫모임', - totalAmount: 150000, - paidMember: 5, - totalMember: 6, - }, -]; +import { + MainHeader, + SettlementBanner, + SettlementList, +} from './ui/HomePageSection'; function HomePage() { - const [settlementType, setSettlementType] = useState<'RECEIVE' | 'SEND'>( - 'RECEIVE' - ); - const navigate = useNavigate(); - const theme = useTheme(); - - const handleSettlementTypeButtonClick = (type: 'RECEIVE' | 'SEND') => { - if (settlementType === type) { - return; - } - setSettlementType(type); - }; - return ( - - navigate(ROUTE.login)} - /> - - - - - - - navigate(ROUTE.selectGroup)}> - 정산하기 - - - - 모임은 즐겁게, 정산은 깔끔하게! -
- 모또만 믿고 맡겨줘! -
- -
- - - - 링크 관리 - - - - - - 캐릭터 도감 - - - - + + - - 진행중인 정산 - - - handleSettlementTypeButtonClick('RECEIVE')} - > - 받을 정산 - - handleSettlementTypeButtonClick('SEND')} - > - 보낼 정산 - - - - - 최신순 - - - - - {settlementList.length > 0 && settlementType === 'RECEIVE' && ( - - {settlementList.map((data) => ( - - ))} - - )} - {settlementType === 'SEND' && ( - - - - 아직 진행중인 정산이 없어요. - - - )} - + ); } diff --git a/src/pages/home/ui/HomePageSection/index.style.ts b/src/pages/home/ui/HomePageSection/index.style.ts new file mode 100644 index 00000000..146d4ab4 --- /dev/null +++ b/src/pages/home/ui/HomePageSection/index.style.ts @@ -0,0 +1,61 @@ +import styled from 'styled-components'; +import { Link } from 'react-router'; + +export const SelectGroupButton = styled.button` + display: flex; + align-items: center; + gap: ${({ theme }) => theme.unit[4]}; + background: transparent; + color: ${({ theme }) => theme.color.semantic.orange.default}; +`; + +export const DescriptionImg = styled.img` + width: 9.8rem; + object-fit: contain; + position: absolute; + top: 9%; + right: -0.2rem; + rotate: -2deg; +`; + +export const NoSettlementImg = styled.img` + width: 33vw; + max-width: 200px; + object-fit: contain; +`; + +export const BoxButton = styled(Link)` + display: flex; + padding: ${({ theme }) => `${theme.unit[16]} ${theme.unit[20]}`}; + position: relative; + height: 5rem; + background-color: ${({ theme }) => theme.color.semantic.orange.subtle}; + border-radius: ${({ theme }) => theme.radius.default}; + + width: 100%; +`; + +export const SmallImg = styled.img` + width: 2.75rem; + object-fit: contain; + position: absolute; + right: 1rem; + bottom: 0.5rem; +`; + +export const BoxButtonWrapper = styled.div` + display: flex; + max-width: 37.5rem; + margin: ${({ theme }) => + `0 ${theme.unit[20]} ${theme.unit[32]} ${theme.unit[20]}`}; + gap: ${({ theme }) => theme.unit[8]}; +`; + +export const SettlementListWrapper = styled.div` + display: flex; + flex-direction: column; + gap: ${({ theme }) => theme.unit[20]}; + margin: ${({ theme }) => `${theme.unit[20]} 0`}; + overflow-y: auto; + flex: 1; +`; diff --git a/src/pages/home/ui/HomePageSection/index.tsx b/src/pages/home/ui/HomePageSection/index.tsx new file mode 100644 index 00000000..3f06eb87 --- /dev/null +++ b/src/pages/home/ui/HomePageSection/index.tsx @@ -0,0 +1,221 @@ +import { useTheme } from 'styled-components'; +import { LogoIcon } from '@/shared/assets/svgs'; +import MainHamImg2 from '@/shared/assets/pngs/MainHamImg2.png'; +import Text from '@/shared/ui/Text'; +import { ArrowRight, Bell, Menu, Next } from '@/shared/assets/svgs/icon'; +import { useNavigate } from 'react-router'; +import { ROUTE } from '@/shared/config/route'; +import { useState } from 'react'; +import CoinImg from '@/shared/assets/pngs/CoinImg.png'; +import LinkMain from '@/shared/assets/pngs/link_main.png'; +import CardMain from '@/shared/assets/pngs/card_main.png'; + +import Flex from '@/shared/ui/Flex'; +import Button from '@/shared/ui/Button'; +import Header from '@/shared/ui/Header'; +import Chip from '@/shared/ui/Chip'; +import * as S from './index.style'; +import HomeExpenseItem from '../HomeExpenseItem'; + +type SettlementType = 'IN_PROGRESS' | 'COMPLETED'; + +interface HomeExpenseItemType { + date: string; + groupName: string; + totalAmount: number; + paidMember: number; + totalMember: number; + id: number; +} +/** + * @Todo 진행중인 정산 내역 조회 API 함수 호출 + * 우선 mock data로 대체 + * */ +const settlementListMock: HomeExpenseItemType[] = [ + { + id: 1, + date: '2026년 2월 22일', + groupName: 'DND 데모데이', + totalAmount: 120000, + paidMember: 3, + totalMember: 6, + }, + { + id: 2, + date: '2026년 1월 14일', + groupName: 'DND 7조 첫모임', + totalAmount: 150000, + paidMember: 5, + totalMember: 6, + }, +]; + +export function MainHeader() { + const theme = useTheme(); + return ( +
+ } + // leftButtonOnClick={() => navigate(ROUTE.login)} + rightButtonContent={ + + {/** @Todo 알림 기능 개발 후 변경 */} + + {/** @Todo 마이페이지로 이동하는 핸들러 추가 */} + + + } + bgColor="semantic.background.normal.alternative" + /> + ); +} + +export function SettlementBanner() { + const navigate = useNavigate(); + const theme = useTheme(); + return ( + <> + + navigate(ROUTE.selectGroup)}> + 정산하기 + + + + 모임은 즐겁게, 정산은 깔끔하게! +
+ 모또만 믿고 맡겨줘! +
+ +
+ + + + 링크 관리 + + + + + + 입금 관리 + + + + + + ); +} + +export function SettlementList() { + const [settlementType, setSettlementType] = + useState('IN_PROGRESS'); + const [sortToggle, setSortToggle] = useState(false); + const theme = useTheme(); + + const handleSettlementTypeButtonClick = (type: SettlementType) => { + if (settlementType === type) { + return; + } + setSettlementType(type); + }; + + const handleSortOptionClick = () => { + setSortToggle(!sortToggle); + }; + + const settlementList = sortToggle + ? [...settlementListMock].reverse() + : settlementListMock; + + return ( + + + 정산 내역 + + + + handleSettlementTypeButtonClick('IN_PROGRESS')} + label="진행 중인 정산" + /> + handleSettlementTypeButtonClick('COMPLETED')} + label="완료된 정산" + /> + + {/** @Todo Select 컴포넌트 개발 후 변경 */} + + + {settlementList.length > 0 && settlementType === 'IN_PROGRESS' && ( + + {settlementList.map((data) => ( + + ))} + + )} + {settlementType === 'COMPLETED' && ( + + + + 아직 진행중인 정산이 없어요. + + + )} + + ); +} diff --git a/src/pages/paymentManagement/PaymentManagementPage.tsx b/src/pages/paymentManagement/PaymentManagementPage.tsx new file mode 100644 index 00000000..9caf0550 --- /dev/null +++ b/src/pages/paymentManagement/PaymentManagementPage.tsx @@ -0,0 +1,9 @@ +function PaymentManagementPage() { + return ( +
+

Payment Management

+
+ ); +} + +export default PaymentManagementPage; diff --git a/src/pages/paymentManagement/index.ts b/src/pages/paymentManagement/index.ts new file mode 100644 index 00000000..346e8664 --- /dev/null +++ b/src/pages/paymentManagement/index.ts @@ -0,0 +1 @@ +export { default as PaymentManagementPage } from './PaymentManagementPage'; diff --git a/src/shared/config/route.ts b/src/shared/config/route.ts index 67f54574..0a8061f8 100644 --- a/src/shared/config/route.ts +++ b/src/shared/config/route.ts @@ -4,6 +4,7 @@ export const ROUTE = { login: '/login', home: '/', myLinks: '/my-links', + paymentManagement: '/payment-management', my: '/my', myEdit: '/my/edit', createExpense: '/create-expense/:groupToken', diff --git a/src/shared/ui/Chip/index.styles.ts b/src/shared/ui/Chip/index.styles.ts index 597088e1..3b4d6caa 100644 --- a/src/shared/ui/Chip/index.styles.ts +++ b/src/shared/ui/Chip/index.styles.ts @@ -42,4 +42,5 @@ export const Container = styled.div` ${({ $size }) => chipSizes[$size]}; ${({ $variant }) => chipVariants[$variant]}; white-space: nowrap; + cursor: ${({ onClick }) => (onClick ? 'pointer' : 'default')}; `; diff --git a/src/shared/ui/Chip/index.tsx b/src/shared/ui/Chip/index.tsx index 8f34b947..796e2beb 100644 --- a/src/shared/ui/Chip/index.tsx +++ b/src/shared/ui/Chip/index.tsx @@ -5,11 +5,12 @@ interface ChipProps { label: string; variant?: ChipVariant; size?: ChipSize; + onClick?: () => void; } -function Chip({ label, variant = 'primary', size = 'md' }: ChipProps) { +function Chip({ label, variant = 'primary', size = 'md', onClick }: ChipProps) { return ( - + {label} );