Skip to content

Commit

Permalink
Merge pull request #84 from Duri-Salon/feat(salon)/statistics-page
Browse files Browse the repository at this point in the history
[feat] 미용사 통계페이지 반려견 부분 구현
  • Loading branch information
cksquf98 authored Dec 19, 2024
2 parents ad7ba1c + 4943cb7 commit 1536ecd
Show file tree
Hide file tree
Showing 6 changed files with 229 additions and 32 deletions.
8 changes: 6 additions & 2 deletions apps/salon/src/components/home/DailyScheduleItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ const DailyScheduleItem = ({

</WidthFitFlex>

<Flex justify="space-between">
<NameWrapper justify="space-between">
<Text typo="Body2">{petName}</Text>
<Text typo="Body4" colorCode={theme.palette.Gray500}>{petInfoStr}</Text>
<Text typo="Body4">{groomerName}</Text>
</Flex>
</NameWrapper>
</ItemWrapper>
)
}
Expand All @@ -55,4 +55,8 @@ const ItemWrapper = styled(Flex)`
flex-shrink: 0;
`

const NameWrapper = styled(Flex)`
flex-wrap: wrap;
`

export default DailyScheduleItem;
146 changes: 144 additions & 2 deletions apps/salon/src/components/income/PetStatistic.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
import { useState } from 'react';

import { Flex, Text, theme, WidthFitFlex } from '@duri-fe/ui';
import styled from '@emotion/styled';
import { ResponsivePie } from '@nivo/pie';
import { PET_STATISTIC_MENT } from '@salon/constants/statistic';

import { TabBarItem } from '../quotation/TabBarItem';

interface PetStatisticProps {
agePetStatistic?: {
standard: string;
Expand All @@ -18,11 +27,144 @@ interface PetStatisticProps {
}[];
}

const colorScheme = [
`${theme.palette.Normal200}`,
`${theme.palette.Normal500}`,
`${theme.palette.Normal600}`,
`${theme.palette.Normal800}`,
`${theme.palette.Normal900}`,
`${theme.palette.Gray300}`,
];

export const PetStatistic = ({
agePetStatistic,
characterPetStatistic,
diseasePetStatistic,
}: PetStatisticProps) => {
console.log(agePetStatistic, diseasePetStatistic, characterPetStatistic);
return <div>PetStatistic</div>;
const [selectedTab, setSelectedTab] = useState<'나이' | '질환' | '성격'>(
'나이',
);
// 각 탭에 해당하는 데이터 (Props로 전달된 데이터를 사용)
const dataMap = {
나이: agePetStatistic,
질환: diseasePetStatistic,
성격: characterPetStatistic,
};

// 선택된 탭에 맞는 데이터가 없으면 빈 배열 반환
const chartData = (dataMap[selectedTab] ?? []).map((item, index) => ({
id: item.standard,
label: item.standard,
value: item.count,
ratio: item.ratio,
color: colorScheme[index % colorScheme.length], // 순환하여 색상 적용
}));

const handleToggleTab = (currTab: '나이' | '질환' | '성격') => {
setSelectedTab(currTab);
};

return (
<Flex direction="column">
<Flex direction="column" align="flex-start" gap={8} padding="0 20px">
<Text typo="Title2">반려견 통계</Text>
<Text typo="Caption1">우리 매장에는 이런 아이들이 자주 왔어요.</Text>
</Flex>
<Flex
height={37}
justify="flex-start"
backgroundColor={theme.palette.White}
margin="24px 0 0"
>
<TabBarItem
label="나이"
selected={selectedTab === '나이'}
typo={selectedTab === '나이' ? 'Title3' : 'Label3'}
onClick={() => handleToggleTab('나이')}
/>
<TabBarItem
label="질환"
selected={selectedTab === '질환'}
typo={selectedTab === '질환' ? 'Title3' : 'Label3'}
onClick={() => handleToggleTab('질환')}
/>
<TabBarItem
label="성격"
selected={selectedTab === '성격'}
typo={selectedTab === '성격' ? 'Title3' : 'Label3'}
onClick={() => handleToggleTab('성격')}
/>
</Flex>

{/* 통계 상단 타이틀 */}
<Flex justify="flex-start" padding="0 20px" margin="24px 0 0">
{dataMap[selectedTab] && dataMap[selectedTab]?.length > 0 ? (
<RelativeText typo="Title3">
{PET_STATISTIC_MENT[selectedTab]?.[
dataMap[selectedTab][0].standard
] ?? '다양한 아이들이 자주 와요!'}
<TextHighlight width={selectedTab === '성격' ? '50px' : '62px'} />
</RelativeText>
) : (
<Text typo="Title3">아직 방문한 고객이 없어요😞</Text>
)}
</Flex>

<Flex padding="0 20px" margin="28px 0 0" gap={48}>
<Flex width={150} height={150}>
<ResponsivePie
data={chartData}
innerRadius={0.9} // 파이 차트 안쪽 반지름 크기
padAngle={1} // 조각 간의 간격
cornerRadius={1} // 조각 모서리의 둥글기
colors={({ data }) => data.color}
enableArcLinkLabels={false} // 링크 레이블 표시 여부
enableArcLabels={true} // 차트 안에 레이블 표시 여부
arcLinkLabelsSkipAngle={10} // 링크 레이블이 보이려면 이 각도 이상일 때만 표시
arcLabelsRadiusOffset={0.5} // 레이블이 원에서 떨어지는 거리
arcLinkLabelsThickness={2} // 링크 레이블 두께
arcLinkLabelsColor={{ from: 'color', modifiers: [['darker', 0.6]] }} // 링크 레이블 색상
arcLabelsTextColor="transparent" // 레이블 텍스트 색상
tooltip={() => null} // 툴팁 비활성화
/>
</Flex>
<WidthFitFlex direction="column" gap={6}>
{chartData &&
chartData.map((item) => (
<BarColorFlex
justify="space-between"
color={item.color}
key={item.label}
width={130}
height={22}
padding="10px 10px 10px 16px"
>
<Text typo="Label3">{item.label}</Text>
<Text typo="Label2" colorCode={theme.palette.Gray300}>
{item.ratio}%
</Text>
</BarColorFlex>
))}
</WidthFitFlex>
</Flex>
</Flex>
);
};

const BarColorFlex = styled(Flex)<{ color: string }>`
border-left: 3px solid ${({ color }) => color};
`;

const RelativeText = styled(Text)`
position: relative;
`;

const TextHighlight = styled.div<{ width: string }>`
position: absolute;
width: ${({ width }) => (width ? width : '62px')};
height: 9px;
background-color: ${theme.palette.Normal500};
opacity: 0.5;
left: -3px;
top: 8px;
`;
39 changes: 39 additions & 0 deletions apps/salon/src/constants/statistic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
export const DEFAULT_AGE_LABEL = ['~3세', '3세~7세', '7세~'];

type PetStatisticMentType = {
[key: string]: Record<string, string>;
};

export const PET_STATISTIC_MENT: PetStatisticMentType = {
나이: {
'~3세': '3세 미만인 아이들이 가장 자주 와요!',

'3세~7세': '3세~7세 아이들이 가장 자주 와요!',

'7세~': '7세 이상 아이들이 가장 자주 와요!',
},
질환: {
귀질환: '귀 질환이 있는 아이들이 가장 자주 와요!',

관절질환: '관절질환이 있는 아이들이 가장 자주 와요!',

기저질환: '기저질환이 있는 아이들이 가장 자주 와요!',

피부질환: '피부질환이 있는 아이들이 가장 자주 와요!',

해당없음: '다양한 아이들이 자주 와요!',
},
성격: {
입질: '입질이 있는 아이들이 가장 자주 와요!',

낯가리는: '낯가리는 아이들이 가장 자주 와요!',

예민한: '예민한 아이들이 가장 자주 와요!',

친화적: '친화적인 아이들이 가장 자주 와요!',

얌전한: '얌전한 아이들이 가장 자주 와요!',

겁많은: '겁많은 아이들이 가장 자주 와요!',
},
};
42 changes: 23 additions & 19 deletions apps/salon/src/pages/Income/Income.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useNavigate } from 'react-router-dom';

import { Header, MobileLayout, SalonNavbar } from '@duri-fe/ui';
import { Flex, Header, MobileLayout, SalonNavbar } from '@duri-fe/ui';
import {
useGetAgeStatistic,
useGetCharacterStatistic,
Expand All @@ -19,7 +19,9 @@ const IncomePage = () => {
};

const { data: monthIncomeData } = useGetThisMonthIncome({});
const { data: selectedIncomeData } = useGetSelectedMonthIncome({ month: "2024-12" });
const { data: selectedIncomeData } = useGetSelectedMonthIncome({
month: '2024-12',
});
const { data: agePetStatistic } = useGetAgeStatistic({});
const { data: diseasePetStatistic } = useGetDiseaseStatistic({});
const { data: characterPetStatistic } = useGetCharacterStatistic({});
Expand All @@ -32,25 +34,27 @@ const IncomePage = () => {
backIcon
onClickBack={handleClickBack}
/>
{monthIncomeData && (
<MonthIncomeStatistic
beforeRatio={monthIncomeData.beforeRatio}
incomeMonthList={monthIncomeData.incomeMonthList}
/>
)}
<Flex direction="column">
{monthIncomeData && (
<MonthIncomeStatistic
beforeRatio={monthIncomeData.beforeRatio}
incomeMonthList={monthIncomeData.incomeMonthList}
/>
)}

{selectedIncomeData && (
<RecentIncomeStatistic
incomeMonthList={selectedIncomeData.incomeMonthList}
beforeRatio={selectedIncomeData.beforeRatio}
nowRatio={selectedIncomeData.nowRatio}
{selectedIncomeData && (
<RecentIncomeStatistic
incomeMonthList={selectedIncomeData.incomeMonthList}
beforeRatio={selectedIncomeData.beforeRatio}
nowRatio={selectedIncomeData.nowRatio}
/>
)}
<PetStatistic
agePetStatistic={agePetStatistic?.ageList}
diseasePetStatistic={diseasePetStatistic?.diseaseList}
characterPetStatistic={characterPetStatistic?.characterList}
/>
)}
{<PetStatistic
agePetStatistic={agePetStatistic?.ageList}
diseasePetStatistic={diseasePetStatistic?.diseaseList}
characterPetStatistic={characterPetStatistic?.characterList}
/>}
</Flex>
<SalonNavbar />
</MobileLayout>
);
Expand Down
14 changes: 11 additions & 3 deletions packages/utils/src/apis/duri/hooks/my.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { useMutation, useQuery } from '@tanstack/react-query';
import {
useMutation,
useQuery,
useQueryClient,
} from '@tanstack/react-query';

import {
deletePetInfo,
Expand All @@ -25,6 +29,7 @@ export const useGetPetListInfo = () => {
return useQuery({
queryKey: ['getPetListInfo'],
queryFn: () => getPetListInfo(),
refetchOnWindowFocus: true,
staleTime: 1000 * 60 * 30,
select: (data) =>
data.petProfileList.map((pet) => ({
Expand All @@ -36,20 +41,23 @@ export const useGetPetListInfo = () => {
});
};
export const useGetPetDetailInfo = (petId: number) => {
const { data, isError } = useQuery({
return useQuery({
queryKey: ['getPetDetailInfo'],
queryFn: () => getPetDetailInfo(petId),
staleTime: 1000 * 60 * 30,
});
return { data, isError };
};

export const usePutPetInfo = () => {
const queryClient = useQueryClient();

return useMutation({
mutationKey: ['putPetInfo'],
mutationFn: ({ petId, formData }: { petId: number; formData: FormData }) =>
putPetInfo(petId, formData),
onSuccess: () => {
// 데이터가 성공적으로 변경되었을 때 refetch
queryClient.invalidateQueries({ queryKey: ['getPetListInfo'] });
alert('펫 정보가 수정되었습니다.');
},
onError: () => {
Expand Down
12 changes: 6 additions & 6 deletions packages/utils/src/apis/salon/income.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const getThisMonthIncome = async (): Promise<
GetThisMonthIncomeResponse['response']
> => {
const { data } = await salonInstance.get('statistics/income/five-month');
return data;
return data.response;
};

export const getSelectedMonthIncome = async (month: string): Promise<
Expand All @@ -21,33 +21,33 @@ export const getSelectedMonthIncome = async (month: string): Promise<
const { data } = await salonInstance.get('statistics/income/month', {
params: { month: month },
});
return data;
return data.response;
};

export const getRecentDaysIncome = async (): Promise<
GetRecentDaysResponse['response']
> => {
const { data } = await salonInstance.get('statistics/age');
return data;
return data.response;
};

export const getAgeStatistic = async (): Promise<
GetAgeStatisticResponse['response']
> => {
const { data } = await salonInstance.get('statistics/age');
return data;
return data.response;
};

export const getCharacterStatistic = async (): Promise<
GetCharacterStatisticResponse['response']
> => {
const { data } = await salonInstance.get('statistics/character');
return data;
return data.response;
};

export const getDiseaseStatistic = async (): Promise<
GetDiseaseStatisticResponse['response']
> => {
const { data } = await salonInstance.get('statistics/disease');
return data;
return data.response;
};

0 comments on commit 1536ecd

Please sign in to comment.