From 885c27531de53b4cba7c9f4b43308018dca82ee4 Mon Sep 17 00:00:00 2001 From: juuhye Date: Sat, 2 May 2026 21:22:07 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat(channel):=20=EC=8B=9C=EC=B2=AD?= =?UTF-8?q?=EC=9E=90=20=EB=B6=84=ED=8F=AC=20=EC=98=81=EC=97=AD=20=EC=A0=9C?= =?UTF-8?q?=EC=9E=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit src/entities/channel/subscriberDistribution/ui/DistributionChart.tsx -> 연령, 국가별 막대차트 영역 src/entities/channel/subscriberDistribution/ui/GenderChart.tsx -> 성별 파이 차트 src/entities/channel/subscriberDistribution/ui/SubscriberChart.tsx -> 기존/ 신규 구독자 파이 차트 src/features/channel/contentType/ui/ContentType.tsx -> 기존 숏/롱 filter -> 범용 사용으로 수정 src/features/channel/subscriberDistribution/api/DistributionChartApi.ts src/features/channel/subscriberDistribution/api/subscriberChartApi.ts src/features/channel/subscriberDistribution/model/useDistributionChart.ts src/features/channel/subscriberDistribution/model/useSubscriberChart.ts -> api, React Query src/shared/ui/chart/barChart/BaseBarChart.tsx -> bar 차트 공통 UI --- .../channel/subscriberDistribution/index.ts | 14 +++ .../mock/mockSubscriberDistribution.tsx | 32 +++++++ .../subscriberDistribution/model/types.ts | 16 ++++ .../ui/DistributionChart.tsx | 44 +++++++++ .../subscriberDistribution/ui/GenderChart.tsx | 37 ++++++++ .../ui/SubscriberChart.tsx | 44 +++++++++ .../channel/contentType/ui/ContentType.tsx | 37 +++++--- .../api/DistributionChartApi.ts | 19 ++++ .../api/subscriberChartApi.ts | 13 +++ .../channel/subscriberDistribution/index.ts | 2 + .../model/useDistributionChart.ts | 17 ++++ .../model/useSubscriberChart.ts | 10 +++ src/pages/channel/ui/ChannelPage.tsx | 2 + .../msw/handlers/distributionChartHandlers.ts | 30 +++++++ src/shared/api/msw/handlers/index.ts | 5 +- .../msw/handlers/subscriberChartHandlers.ts | 15 ++++ .../chart/barChart/BaseBarChart.stories.tsx | 33 +++++++ src/shared/ui/chart/barChart/BaseBarChart.tsx | 36 ++++++++ src/shared/ui/chart/barChart/index.ts | 1 + .../channel/newInflow/ui/NewInflowSection.tsx | 9 +- .../channel/subscriberDemographics/index.ts | 1 + .../ui/SubscriberDemographicsSection.tsx | 90 +++++++++++++++++++ .../trendingVideo/ui/TrendingVideoSection.tsx | 9 +- .../ui/ChannelProfileSection.tsx | 2 + 24 files changed, 502 insertions(+), 16 deletions(-) create mode 100644 src/entities/channel/subscriberDistribution/index.ts create mode 100644 src/entities/channel/subscriberDistribution/mock/mockSubscriberDistribution.tsx create mode 100644 src/entities/channel/subscriberDistribution/model/types.ts create mode 100644 src/entities/channel/subscriberDistribution/ui/DistributionChart.tsx create mode 100644 src/entities/channel/subscriberDistribution/ui/GenderChart.tsx create mode 100644 src/entities/channel/subscriberDistribution/ui/SubscriberChart.tsx create mode 100644 src/features/channel/subscriberDistribution/api/DistributionChartApi.ts create mode 100644 src/features/channel/subscriberDistribution/api/subscriberChartApi.ts create mode 100644 src/features/channel/subscriberDistribution/index.ts create mode 100644 src/features/channel/subscriberDistribution/model/useDistributionChart.ts create mode 100644 src/features/channel/subscriberDistribution/model/useSubscriberChart.ts create mode 100644 src/shared/api/msw/handlers/distributionChartHandlers.ts create mode 100644 src/shared/api/msw/handlers/subscriberChartHandlers.ts create mode 100644 src/shared/ui/chart/barChart/BaseBarChart.stories.tsx create mode 100644 src/shared/ui/chart/barChart/BaseBarChart.tsx create mode 100644 src/shared/ui/chart/barChart/index.ts create mode 100644 src/widgets/channel/subscriberDemographics/index.ts create mode 100644 src/widgets/channel/subscriberDemographics/ui/SubscriberDemographicsSection.tsx diff --git a/src/entities/channel/subscriberDistribution/index.ts b/src/entities/channel/subscriberDistribution/index.ts new file mode 100644 index 00000000..4c76c26a --- /dev/null +++ b/src/entities/channel/subscriberDistribution/index.ts @@ -0,0 +1,14 @@ +export { + mockSubscriberDistribution, + mockSubscriber, +} from './mock/mockSubscriberDistribution' +export type { + SubscriberRatioDto, + DistributionItem, + SubscriberDistributionsResponseDto, + DistributionsFilter, +} from './model/types' + +export { DistributionChart } from './ui/DistributionChart' +export { GenderChart } from './ui/GenderChart' +export { SubscriberChart } from './ui/SubscriberChart' diff --git a/src/entities/channel/subscriberDistribution/mock/mockSubscriberDistribution.tsx b/src/entities/channel/subscriberDistribution/mock/mockSubscriberDistribution.tsx new file mode 100644 index 00000000..00d0b304 --- /dev/null +++ b/src/entities/channel/subscriberDistribution/mock/mockSubscriberDistribution.tsx @@ -0,0 +1,32 @@ +import type { + SubscriberDistributionsResponseDto, + SubscriberRatioDto, +} from '../model/types' + +export const mockSubscriberDistribution: SubscriberDistributionsResponseDto = { + gender: [ + { label: '남성', percentage: 62.4 }, + { label: '여성', percentage: 37.6 }, + ], + age: [ + { label: '13-17', percentage: 70 }, + { label: '18-24', percentage: 20.1 }, + { label: '25-34', percentage: 7.9 }, + { label: '35-44', percentage: 2.5 }, + { label: '45-54', percentage: 1.2 }, + { label: '55-64', percentage: 1.2 }, + { label: '65+', percentage: 1.2 }, + ], + country: [ + { label: '대한민국', percentage: 70 }, + { label: '일본', percentage: 20.1 }, + { label: '미국', percentage: 7.9 }, + { label: '남아프리카 공화국', percentage: 2.5 }, + { label: '중앙 아프리카 공화국', percentage: 1.2 }, + ], +} + +export const mockSubscriber: SubscriberRatioDto = { + count: 1240, + ratio: 62.5, +} diff --git a/src/entities/channel/subscriberDistribution/model/types.ts b/src/entities/channel/subscriberDistribution/model/types.ts new file mode 100644 index 00000000..f876ea97 --- /dev/null +++ b/src/entities/channel/subscriberDistribution/model/types.ts @@ -0,0 +1,16 @@ +export interface DistributionItem { + label: string + percentage: number +} + +export interface SubscriberDistributionsResponseDto { + gender: DistributionItem[] + age: DistributionItem[] + country: DistributionItem[] +} +export interface SubscriberRatioDto { + count: number + ratio: number +} + +export type DistributionsFilter = 'countries' | 'ages' | 'genders' diff --git a/src/entities/channel/subscriberDistribution/ui/DistributionChart.tsx b/src/entities/channel/subscriberDistribution/ui/DistributionChart.tsx new file mode 100644 index 00000000..66f819b8 --- /dev/null +++ b/src/entities/channel/subscriberDistribution/ui/DistributionChart.tsx @@ -0,0 +1,44 @@ +'use client' + +import { BaseBarChart } from '@/shared/ui/chart/barChart' +import { DistributionItem } from '../model/types' + +export function DistributionChart({ data, type }: { + data: DistributionItem[] + type: string +}) { + return ( +
+ {/* 순위 번호 + 항목 라벨 */} +
+ {data.map((item, idx) => ( +
+ {idx + 1} + + {item.label} + {type === 'ages' + ? idx === data.length - 1 + ? '세 이상' + : '세' + : ''} + +
+ ))} +
+ {/* 가로 막대 차트 */} + + {/* 항목별 (%) */} +
+ {data.map((item, idx) => ( +
+ {item.percentage}% +
+ ))} +
+
+ ) +} diff --git a/src/entities/channel/subscriberDistribution/ui/GenderChart.tsx b/src/entities/channel/subscriberDistribution/ui/GenderChart.tsx new file mode 100644 index 00000000..c1db47af --- /dev/null +++ b/src/entities/channel/subscriberDistribution/ui/GenderChart.tsx @@ -0,0 +1,37 @@ +'use client' + +import { ChartLegend } from '@/features/channel/chartLegend' +import { DistributionItem } from '../model/types' +import { BasePieChart, type PieDataPoint } from '@/shared/ui/chart' + +export function GenderChart({ data }: { data: DistributionItem[] }) { + const pieData: PieDataPoint[] = data.map((item, index) => ({ + name: item.label, + value: item.percentage, + color: index === 0 ? 'bg-brand-primary' : 'bg-btn-primary-filled-disabled', + })) + + return ( +
+ {/* 여성 / 남성 차트 */} + + data={pieData} + dataKey='value' + nameKey='name' + tooltipFormatter={(value) => `${value}%`} + /> + + {/* 여성 / 남성 차트 범례 */} +
+ {pieData.map((item) => ( + + ))} +
+
+ ) +} diff --git a/src/entities/channel/subscriberDistribution/ui/SubscriberChart.tsx b/src/entities/channel/subscriberDistribution/ui/SubscriberChart.tsx new file mode 100644 index 00000000..62e41f51 --- /dev/null +++ b/src/entities/channel/subscriberDistribution/ui/SubscriberChart.tsx @@ -0,0 +1,44 @@ +'use client' + +import { ChartLegend } from '@/features/channel/chartLegend' +import { SubscriberRatioDto } from '../model/types' +import { BasePieChart, type PieDataPoint } from '@/shared/ui/chart' + +export function SubscriberChart({ data }: { data: SubscriberRatioDto }) { + const { ratio } = data + + const pieData: PieDataPoint[] = [ + { + name: '기존 구독자', + value: 100 - ratio, + color: 'bg-brand-primary', + }, + { + name: '신규 구독자', + value: ratio, + color: 'bg-btn-primary-filled-disabled', + }, + ] + return ( +
+ {/* 기존 / 신규 구독자 차트 */} + + data={pieData} + dataKey='value' + nameKey='name' + tooltipFormatter={(value) => `${value}%`} + /> + {/* 기존 / 신규 구독자 차트 범례 */} +
+ {pieData.map((item) => ( + + ))} +
+
+ ) +} diff --git a/src/features/channel/contentType/ui/ContentType.tsx b/src/features/channel/contentType/ui/ContentType.tsx index 38004d49..1e0815c3 100644 --- a/src/features/channel/contentType/ui/ContentType.tsx +++ b/src/features/channel/contentType/ui/ContentType.tsx @@ -2,26 +2,37 @@ import { cn } from '@/shared/lib/utils' -const FILTER_OPTIONS = [ - { label: '롱폼', isShort: false }, - { label: '숏폼', isShort: true }, -] as const +interface FilterOption { + label: string + filter: T +} -interface Props { - isShort: boolean - onIsShortChange: (isShort: boolean) => void +interface Props { + options: FilterOption[] + filter: T + onFilterChange: (filter: T) => void + className?: string } -export function ContentType({ isShort, onIsShortChange }: Props) { +export function ContentType({ + options, + filter, + onFilterChange, + className, +}: Props) { return ( -
- {FILTER_OPTIONS.map((option, index) => ( +
+ {options.map((option, index) => (
diff --git a/src/shared/api/msw/handlers/distributionChartHandlers.ts b/src/shared/api/msw/handlers/distributionChartHandlers.ts new file mode 100644 index 00000000..b9e02db7 --- /dev/null +++ b/src/shared/api/msw/handlers/distributionChartHandlers.ts @@ -0,0 +1,30 @@ +import { http, HttpResponse } from 'msw' +import { mockSubscriberDistribution } from '@/entities/channel/subscriberDistribution/mock/mockSubscriberDistribution' + +export const distributionChartHandlers = [ + http.get( + `${process.env.NEXT_PUBLIC_API_URL}/channels/:id/subscriber-distribution`, + ({ request }) => { + const url = new URL(request.url) + const filters = url.searchParams.get('filter')?.split(',') ?? [] + + const responseDto = { + ...(filters.includes('genders') && { + gender: mockSubscriberDistribution.gender, + }), + ...(filters.includes('ages') && { + age: mockSubscriberDistribution.age, + }), + ...(filters.includes('countries') && { + country: mockSubscriberDistribution.country, + }), + } + + return HttpResponse.json({ + success: true, + responseDto, + error: null, + }) + } + ), +] diff --git a/src/shared/api/msw/handlers/index.ts b/src/shared/api/msw/handlers/index.ts index 31a7f69f..cc99029e 100644 --- a/src/shared/api/msw/handlers/index.ts +++ b/src/shared/api/msw/handlers/index.ts @@ -11,7 +11,8 @@ import { videoStatsHandlers } from './videoStatsHandlers' import { channelTrendingVideoHandlers } from './channelTrendingVideoHandlers' import { newInflowHandlers } from './newInflowHandlers' import { typeEngagementHandlers } from './typeEngagementHandlers' - +import { distributionChartHandlers } from './distributionChartHandlers' +import { subscriberChartHandlers } from './subscriberChartHandlers' export const handlers = [ ...authHandlers, @@ -27,4 +28,6 @@ export const handlers = [ ...channelTrendingVideoHandlers, ...newInflowHandlers, ...typeEngagementHandlers, + ...subscriberChartHandlers, + ...distributionChartHandlers, ] diff --git a/src/shared/api/msw/handlers/subscriberChartHandlers.ts b/src/shared/api/msw/handlers/subscriberChartHandlers.ts new file mode 100644 index 00000000..f3f74f72 --- /dev/null +++ b/src/shared/api/msw/handlers/subscriberChartHandlers.ts @@ -0,0 +1,15 @@ +import { http, HttpResponse } from 'msw' +import { mockSubscriber } from '@/entities/channel/subscriberDistribution/mock/mockSubscriberDistribution' + +export const subscriberChartHandlers = [ + http.get( + `${process.env.NEXT_PUBLIC_API_URL}/channels/:id/subscriber-pattern`, + () => { + return HttpResponse.json({ + success: true, + responseDto: mockSubscriber, + error: null, + }) + } + ), +] diff --git a/src/shared/ui/chart/barChart/BaseBarChart.stories.tsx b/src/shared/ui/chart/barChart/BaseBarChart.stories.tsx new file mode 100644 index 00000000..62b856dd --- /dev/null +++ b/src/shared/ui/chart/barChart/BaseBarChart.stories.tsx @@ -0,0 +1,33 @@ +import type { Meta, StoryObj } from '@storybook/nextjs-vite' +import { BaseBarChart } from './BaseBarChart' + +const meta: Meta = { + title: 'Shared/Chart/BaseBarChart', + component: BaseBarChart, + tags: ['autodocs'], + parameters: { + layout: 'centered', + }, +} + +export default meta +type Story = StoryObj + +export const Default: Story = { + args: { + data: [ + { label: '18-24', percentage: 45 }, + { label: '25-34', percentage: 78 }, + { label: '35-44', percentage: 62 }, + { label: '45-54', percentage: 30 }, + { label: '55+', percentage: 15 }, + ], + }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], +} diff --git a/src/shared/ui/chart/barChart/BaseBarChart.tsx b/src/shared/ui/chart/barChart/BaseBarChart.tsx new file mode 100644 index 00000000..98daa0de --- /dev/null +++ b/src/shared/ui/chart/barChart/BaseBarChart.tsx @@ -0,0 +1,36 @@ +import { BarChart, Bar, ResponsiveContainer, XAxis, YAxis } from 'recharts' + +interface BarChartItem { + label: string + percentage: number +} + +interface BaseBarChartProps { + data: BarChartItem[] +} + +export function BaseBarChart({ data }: BaseBarChartProps) { + const itemHeight = 56 + + return ( +
+ + + + + {/* 막대 차트 */} + + + +
+ ) +} diff --git a/src/shared/ui/chart/barChart/index.ts b/src/shared/ui/chart/barChart/index.ts new file mode 100644 index 00000000..6738e39c --- /dev/null +++ b/src/shared/ui/chart/barChart/index.ts @@ -0,0 +1 @@ +export { BaseBarChart } from './BaseBarChart' diff --git a/src/widgets/channel/newInflow/ui/NewInflowSection.tsx b/src/widgets/channel/newInflow/ui/NewInflowSection.tsx index 1a74c800..6d7535d4 100644 --- a/src/widgets/channel/newInflow/ui/NewInflowSection.tsx +++ b/src/widgets/channel/newInflow/ui/NewInflowSection.tsx @@ -38,7 +38,14 @@ export function NewInflowSection({ channelId }: { channelId: string }) { 신규 유입 비율 TOP 5 - +
diff --git a/src/widgets/channel/subscriberDemographics/index.ts b/src/widgets/channel/subscriberDemographics/index.ts new file mode 100644 index 00000000..b397756b --- /dev/null +++ b/src/widgets/channel/subscriberDemographics/index.ts @@ -0,0 +1 @@ +export { SubscriberDemographicsSection } from './ui/SubscriberDemographicsSection' diff --git a/src/widgets/channel/subscriberDemographics/ui/SubscriberDemographicsSection.tsx b/src/widgets/channel/subscriberDemographics/ui/SubscriberDemographicsSection.tsx new file mode 100644 index 00000000..ed238ff3 --- /dev/null +++ b/src/widgets/channel/subscriberDemographics/ui/SubscriberDemographicsSection.tsx @@ -0,0 +1,90 @@ +'use client' + +import { + DistributionChart, + GenderChart, + SubscriberChart, +} from '@/entities/channel/subscriberDistribution' +import { ContentType } from '@/features/channel/contentType' +import { + useSubscriberChart, + useDistributionChart, +} from '@/features/channel/subscriberDistribution' + +import IconUsers from '@/shared/assets/users-bold.svg' +import { Skeleton } from '@/shared/ui/shadcn/skeleton' + +export function SubscriberDemographicsSection({ channelId }: { channelId: string }) { + // 기존 / 신규 구독자 + const { + data: subscriberData, isFetching: isSubscriberFetching, isError: isSubscriberError } = useSubscriberChart(channelId) + // 성별, 국가, 연령 + const { + data: distributionData, + filter, + setFilter, + isFetching: isDistributionFetching, + isError: isDistributionError + } = useDistributionChart(channelId) + + const isFetching = isSubscriberFetching || isDistributionFetching + const isError = isSubscriberError || isDistributionError + + const distributionChartData = + filter === 'countries' + ? (distributionData?.country ?? []) + : (distributionData?.age ?? []) + + if (isFetching || isError) { + return ( +
+
+
+ + + + 구독자 분포 +
+
+
+ +
+
+ ) + } + + return ( +
+
+
+ + + + 구독자 분포 +
+
+
+
+ {/* 기존 / 신규 구독자 Pie Chart */} + + {/* 여성 / 남성 평균 Pie Chart */} + +
+
+ {/* 국가별 / 연령별 filter 선택 */} + + {/* 국가별 / 연령별 Bar Chart */} + +
+
+
+ ) +} diff --git a/src/widgets/channel/trendingVideo/ui/TrendingVideoSection.tsx b/src/widgets/channel/trendingVideo/ui/TrendingVideoSection.tsx index aea2f43c..e7998a8d 100644 --- a/src/widgets/channel/trendingVideo/ui/TrendingVideoSection.tsx +++ b/src/widgets/channel/trendingVideo/ui/TrendingVideoSection.tsx @@ -50,7 +50,14 @@ export function TrendingVideoSection({ channelId }: { channelId: string }) {
- +
diff --git a/src/widgets/main/channelProfile/ui/ChannelProfileSection.tsx b/src/widgets/main/channelProfile/ui/ChannelProfileSection.tsx index 408932ff..4ab9ffed 100644 --- a/src/widgets/main/channelProfile/ui/ChannelProfileSection.tsx +++ b/src/widgets/main/channelProfile/ui/ChannelProfileSection.tsx @@ -123,6 +123,8 @@ export function ChannelProfileSection({ queryClient.invalidateQueries({ queryKey: ['trendingVideo', channelId] }) queryClient.invalidateQueries({ queryKey: ['newInflow', channelId] }) queryClient.invalidateQueries({ queryKey: ['typeEngagement', channelId] }) + queryClient.invalidateQueries({ queryKey: ['subscriberChart', channelId]}) + queryClient.invalidateQueries({ queryKey: ['distributionChart', channelId] }) }} /> )} From 6457f68fe78e05dd2d715a749e8d6a966cbb8685 Mon Sep 17 00:00:00 2001 From: juuhye Date: Sun, 10 May 2026 00:19:31 +0900 Subject: [PATCH 2/3] =?UTF-8?q?feat(IN-181):=20=EC=B0=A8=ED=8A=B8=20?= =?UTF-8?q?=EB=B2=94=EB=A1=80=20=EB=8B=A8=EC=9C=84=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../channel/subscriberDistribution/ui/GenderChart.tsx | 1 + .../channel/subscriberDistribution/ui/SubscriberChart.tsx | 1 + .../channel/typeEngagement/ui/TypeEngagementChart.tsx | 7 ++++++- src/features/channel/chartLegend/ui/ChartLegend.tsx | 8 ++++++-- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/entities/channel/subscriberDistribution/ui/GenderChart.tsx b/src/entities/channel/subscriberDistribution/ui/GenderChart.tsx index c1db47af..5a4c30ca 100644 --- a/src/entities/channel/subscriberDistribution/ui/GenderChart.tsx +++ b/src/entities/channel/subscriberDistribution/ui/GenderChart.tsx @@ -29,6 +29,7 @@ export function GenderChart({ data }: { data: DistributionItem[] }) { value={item.value} label={item.name} variant={item.color} + unit='%' /> ))}
diff --git a/src/entities/channel/subscriberDistribution/ui/SubscriberChart.tsx b/src/entities/channel/subscriberDistribution/ui/SubscriberChart.tsx index 62e41f51..b7c509d4 100644 --- a/src/entities/channel/subscriberDistribution/ui/SubscriberChart.tsx +++ b/src/entities/channel/subscriberDistribution/ui/SubscriberChart.tsx @@ -36,6 +36,7 @@ export function SubscriberChart({ data }: { data: SubscriberRatioDto }) { value={item.value} label={item.name} variant={item.color} + unit='%' /> ))} diff --git a/src/entities/channel/typeEngagement/ui/TypeEngagementChart.tsx b/src/entities/channel/typeEngagement/ui/TypeEngagementChart.tsx index 65089bf3..e7fb8737 100644 --- a/src/entities/channel/typeEngagement/ui/TypeEngagementChart.tsx +++ b/src/entities/channel/typeEngagement/ui/TypeEngagementChart.tsx @@ -4,7 +4,11 @@ import { ChartLegend } from '@/features/channel/chartLegend' import type { TypeEngagementSummaryDto } from '../model/types' import { BasePieChart, type PieDataPoint } from '@/shared/ui/chart' -export function TypeEngagementChart({ data }: { data: TypeEngagementSummaryDto }) { +export function TypeEngagementChart({ + data, +}: { + data: TypeEngagementSummaryDto +}) { const { longFormEngagementRate, shortFormEngagementRate } = data const pieData: PieDataPoint[] = [ @@ -37,6 +41,7 @@ export function TypeEngagementChart({ data }: { data: TypeEngagementSummaryDto } value={item.value} label={item.name} variant={item.color} + unit='%' /> ))} diff --git a/src/features/channel/chartLegend/ui/ChartLegend.tsx b/src/features/channel/chartLegend/ui/ChartLegend.tsx index 424ce06b..9d54c0be 100644 --- a/src/features/channel/chartLegend/ui/ChartLegend.tsx +++ b/src/features/channel/chartLegend/ui/ChartLegend.tsx @@ -4,9 +4,10 @@ interface ChartLegendProps { label: string value: number | string variant?: string + unit?: string } -export function ChartLegend({ label, value, variant }: ChartLegendProps) { +export function ChartLegend({ label, value, variant, unit }: ChartLegendProps) { return (
@@ -14,7 +15,10 @@ export function ChartLegend({ label, value, variant }: ChartLegendProps) {
{label} - {value} + + {value} + {unit} +
) From a41753a49a5f5d4c4b1c2124fe148ac44f284250 Mon Sep 17 00:00:00 2001 From: juuhye Date: Sun, 10 May 2026 00:56:12 +0900 Subject: [PATCH 3/3] =?UTF-8?q?refactor=20(IN-181):=20=EB=A6=AC=EB=B7=B0?= =?UTF-8?q?=20=EB=B0=98=EC=98=81=20(key=20=EA=B0=92,=20import=20=EA=B2=BD?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/entities/channel/kpiCard/ui/KpiCard.tsx | 2 +- .../subscriberDistribution/ui/DistributionChart.tsx | 11 +++++++---- .../api/msw/handlers/distributionChartHandlers.ts | 2 +- .../api/msw/handlers/subscriberChartHandlers.ts | 2 +- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/entities/channel/kpiCard/ui/KpiCard.tsx b/src/entities/channel/kpiCard/ui/KpiCard.tsx index b889b416..20814970 100644 --- a/src/entities/channel/kpiCard/ui/KpiCard.tsx +++ b/src/entities/channel/kpiCard/ui/KpiCard.tsx @@ -20,7 +20,7 @@ export function KpiCard({ icon, label, prefix, value, unit }: KpiCardProps) { )} {label} -
+
{prefix && {prefix}} {value} {unit} diff --git a/src/entities/channel/subscriberDistribution/ui/DistributionChart.tsx b/src/entities/channel/subscriberDistribution/ui/DistributionChart.tsx index 66f819b8..29ae7682 100644 --- a/src/entities/channel/subscriberDistribution/ui/DistributionChart.tsx +++ b/src/entities/channel/subscriberDistribution/ui/DistributionChart.tsx @@ -3,7 +3,10 @@ import { BaseBarChart } from '@/shared/ui/chart/barChart' import { DistributionItem } from '../model/types' -export function DistributionChart({ data, type }: { +export function DistributionChart({ + data, + type, +}: { data: DistributionItem[] type: string }) { @@ -14,7 +17,7 @@ export function DistributionChart({ data, type }: { {data.map((item, idx) => (
+ key={item.label}> {idx + 1} {item.label} @@ -31,10 +34,10 @@ export function DistributionChart({ data, type }: { {/* 항목별 (%) */}
- {data.map((item, idx) => ( + {data.map((item) => (
+ key={item.label}> {item.percentage}%
))} diff --git a/src/shared/api/msw/handlers/distributionChartHandlers.ts b/src/shared/api/msw/handlers/distributionChartHandlers.ts index b9e02db7..e7cc43ae 100644 --- a/src/shared/api/msw/handlers/distributionChartHandlers.ts +++ b/src/shared/api/msw/handlers/distributionChartHandlers.ts @@ -1,5 +1,5 @@ import { http, HttpResponse } from 'msw' -import { mockSubscriberDistribution } from '@/entities/channel/subscriberDistribution/mock/mockSubscriberDistribution' +import { mockSubscriberDistribution } from '@/entities/channel/subscriberDistribution' export const distributionChartHandlers = [ http.get( diff --git a/src/shared/api/msw/handlers/subscriberChartHandlers.ts b/src/shared/api/msw/handlers/subscriberChartHandlers.ts index f3f74f72..ca268300 100644 --- a/src/shared/api/msw/handlers/subscriberChartHandlers.ts +++ b/src/shared/api/msw/handlers/subscriberChartHandlers.ts @@ -1,5 +1,5 @@ import { http, HttpResponse } from 'msw' -import { mockSubscriber } from '@/entities/channel/subscriberDistribution/mock/mockSubscriberDistribution' +import { mockSubscriber } from '@/entities/channel/subscriberDistribution' export const subscriberChartHandlers = [ http.get(