Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const CardEditViewCard = ({ cardCode }: CardEditViewCardProps) => {
return null; // 카드 정보가 없는 경우 렌더링하지 않음
}

const { period, sizeX } = card;
const { period, sizeX, sizeY } = card;

return (
<li style={{ gridColumn: `span ${sizeX}` }}>
Expand All @@ -41,6 +41,7 @@ export const CardEditViewCard = ({ cardCode }: CardEditViewCardProps) => {
period={period}
className="min-w-full"
sizeX={sizeX}
sizeY={sizeY}
onClickAddButton={handleAddCard}
onClickDeleteButton={handleDeleteCard}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
PeakTimeContent,
RealSalesContent,
SalesByDayContent,
SalesTrendContent,
SalesTypeContent,
} from '@/components/sales';
import type { MetricCardCode } from '@/constants/dashboard';
Expand All @@ -17,6 +18,7 @@ import {
PEAK_TIME,
REAL_SALES,
SALES_BY_DAY,
SALES_TREND,
SALES_TYPE,
} from '@/constants/sales';

Expand Down Expand Up @@ -63,6 +65,7 @@ const {
EXAMPLE_TOP_DAY: SALES_BY_DAY_EXAMPLE_TOP_DAY,
EXAMPLE_IS_SIGNIFICANT: SALES_BY_DAY_EXAMPLE_IS_SIGNIFICANT,
} = SALES_BY_DAY;
const { EXAMPLE_DATA: SALES_TREND_EXAMPLE_DATA } = SALES_TREND;

export const EditCardContent = ({ cardCode }: EditCardContentProps) => {
switch (cardCode) {
Expand Down Expand Up @@ -141,6 +144,15 @@ export const EditCardContent = ({ cardCode }: EditCardContentProps) => {
items={PAYMENT_METHOD_EXAMPLE_PAYMENT_METHOD_DATA}
/>
);
case 'SLS_09_04':
case 'SLS_10_07':
case 'SLS_11_07':
return (
<SalesTrendContent
cardCode={cardCode}
salesTrendData={SALES_TREND_EXAMPLE_DATA[cardCode]}
/>
);
case 'SLS_13_01':
return <PeakTimeContent peakTimeData={PEAK_TIME_EXAMPLE_DATA} />;
case 'SLS_14_06':
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { BarLineChart } from '@/components/shared';
import {
DASHBOARD_METRIC_CARDS,
type DASHBOARD_METRICS,
type ExtractCardCodesFromSection,
} from '@/constants/dashboard';
import { SALES_TREND } from '@/constants/sales';
import { PERIOD_PRESETS } from '@/constants/shared';
import type { BarLineChartSeries } from '@/types/shared';
import { cn } from '@/utils/shared';

type SalesTrendCardCode = ExtractCardCodesFromSection<
typeof DASHBOARD_METRICS.SALES.sections.SALES_TREND
>;

interface SalesTrendContentProps {
cardCode: SalesTrendCardCode;
salesTrendData: BarLineChartSeries;
trendChartWidth?: number;
trendChartHeight?: number;
className?: string;
}

export const SalesTrendContent = ({
cardCode,
salesTrendData,
trendChartWidth,
trendChartHeight,
className,
}: SalesTrendContentProps) => {
const { period, label } = DASHBOARD_METRIC_CARDS[cardCode];
const {
DEFAULT_TREND_CHART_WIDTH,
DEFAULT_TREND_CHART_WIDTH_FOR_RECENT_30_DAYS,
DEFAULT_TREND_CHART_HEIGHT,
} = SALES_TREND;

const trendChartWidthValue =
trendChartWidth ??
(period === PERIOD_PRESETS.recentDays7_14_30.recent30Days
? DEFAULT_TREND_CHART_WIDTH_FOR_RECENT_30_DAYS
: DEFAULT_TREND_CHART_WIDTH);
Comment on lines +38 to +42
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

삼항 연산자와 nullish coalescing 연산자(??)를 함께 사용할 때는 괄호로 우선순위를 명확히 해야 합니다. 현재 코드는 연산자 우선순위로 인해 의도한 대로 동작하지만, 가독성과 명확성을 위해 다음과 같이 수정하는 것을 권장합니다:

trendChartWidth ?? (period === PERIOD_PRESETS.recentDays7_14_30.recent30Days ? DEFAULT_TREND_CHART_WIDTH_FOR_RECENT_30_DAYS : DEFAULT_TREND_CHART_WIDTH)

Copilot uses AI. Check for mistakes.

const trendChartHeightValue = trendChartHeight ?? DEFAULT_TREND_CHART_HEIGHT;

return (
<article
className={cn(
'flex flex-col items-start justify-start gap-4',
period === PERIOD_PRESETS.recentDays7_14_30.recent30Days
? 'w-170'
: 'w-265',
className,
)}
>
<div className="flex items-center gap-3">
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[p2] 제가 생각하기에는 다른 지표카드들과 마찬가지로 "일별 매출 추이"와 같은 제목(label)은 표시하지 않는 게 좋을 것 같습니다!
실매출 주문건수 같은 범례는 기간 옆에 표시될 수는 있을 것 같은데 이건 힘들 것 같고요 ㅎㅎ;;

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이건 디자인이 없어서 임의로 추가했는데, 이미 해당 카드 카테고리에 명시되어 있네요
제거하겠스빈다 ~

<h3 className="body-medium-semibold text-grey-700">{label}</h3>
<div className="caption-large-medium text-grey-600 flex items-center gap-2">
<div className="flex items-center gap-1">
<div className="h-4.5 w-2 rounded-t-[1px] bg-[linear-gradient(180deg,rgba(33,33,33,0.40)_0%,rgba(33,33,33,0.10)_100%)]" />
<span>실매출</span>
</div>
<div className="flex items-center gap-1">
<div className="bg-grey-400 size-1.25 rounded-full" />
<span>주문건수</span>
</div>
</div>
</div>
<BarLineChart
viewBoxWidth={trendChartWidthValue}
viewBoxHeight={trendChartHeightValue}
barLineChartSeries={salesTrendData}
hasXAxis
showYGuideLine
yGuideLineCount={5}
activeTooltip={false}
xAxisType="right-arrow"
/>
</article>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { SalesTrendContent } from './SalesTrendContent';
1 change: 1 addition & 0 deletions frontend/src/components/sales/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ export {
PaymentMethodContent,
} from './dashboard-sales-income';
export { PeakTimeContent, SalesByDayContent } from './dashboard-sales-pattern';
export { SalesTrendContent } from './dashboard-sales-trend';
14 changes: 3 additions & 11 deletions frontend/src/components/shared/bar-line-chart/BarLineSeries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,13 @@ export const BarLineSeries = ({
}: BarLineSeriesProps) => {
return (
<g>
<path
d={interactionPathD}
fill="transparent"
stroke="transparent"
className="peer z-1"
pointerEvents="all"
/>
<path d={interactionPathD} fill="transparent" stroke="transparent" />
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[p4] 툴팁 인터랙션 없애신 건가요? 상세분석에서 필요할 텐데 props로 내리지 않고 날려버리신 이유가 있나요..?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BarLineSeriesRenderer에서 조건부 렌더링하도록 변경ㄹ했습니다 ~

<Dot
x={lineX}
y={lineY}
color={color}
ariaLabel={tooltipContentText}
hasHoverEffect
className="peer-hover:brightness-75 peer-hover:saturate-200"
hasHoverEffect={false}
/>
<Bar
barMiddleX={barX}
Expand All @@ -51,8 +44,7 @@ export const BarLineSeries = ({
bgColor={color}
radius={BAR_CHART.BAR_RADIUS}
hasGradient
barColorChangeOnHover
className="peer-hover:[&_path]:fill-brand-main"
barColorChangeOnHover={false}
/>
</g>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ interface EditCardWrapperProps {
innerClassName?: string; // 자식 컴포넌트 클래스명
period: string; // 오늘, 이번주, 이번달 등 문구
sizeX?: number; // 가로 크기
sizeY?: number; // 세로 크기
onClickDeleteButton?: () => void; // 대시보드에서 삭제하는 버튼 클릭 헨들러
onClickAddButton?: () => void; // 대시보드에 추가하는 버튼 클릭 핸들러
}
Expand All @@ -31,6 +32,7 @@ export const EditCardWrapper = ({
className,
innerClassName,
sizeX = 1,
sizeY = 1,
period = '기간없음',
onClickDeleteButton,
onClickAddButton,
Expand All @@ -47,10 +49,12 @@ export const EditCardWrapper = ({
<div
style={{
width:
Math.max(EDIT_CARD_WRAPPER.MIN_WIDTH, computedCardWidth) * sizeX +
GRID_GAP * (sizeX - 1), // 최소 너비 220px, gap 20px
Math.max(EDIT_CARD_WRAPPER.MIN_WIDTH, computedCardWidth) +
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

width 계산 로직에서 sizeX를 곱하지 않아 여러 그리드 셀을 차지하는 카드의 너비가 잘못 계산됩니다. sizeX > 1인 경우(예: SLS_09_04의 sizeX=3), 카드가 의도한 것보다 훨씬 좁게 표시됩니다.

이전 로직처럼 Math.max(EDIT_CARD_WRAPPER.MIN_WIDTH, computedCardWidth) * sizeX + GRID_GAP * (sizeX - 1)로 수정해야 합니다.

Suggested change
Math.max(EDIT_CARD_WRAPPER.MIN_WIDTH, computedCardWidth) +
Math.max(EDIT_CARD_WRAPPER.MIN_WIDTH, computedCardWidth) * sizeX +

Copilot uses AI. Check for mistakes.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이렇게 하면 높이 계산 때와 마찬가지로 너무 커지는 문제 있어 수정했습니다.

GRID_GAP * (sizeX - 1),
// 최소 너비 220px, gap 20px
height:
Math.max(EDIT_CARD_WRAPPER.MIN_HEIGHT, computedCardHeight) + GRID_GAP, // 최소 높이 147px,
Math.max(EDIT_CARD_WRAPPER.MIN_HEIGHT, computedCardHeight) +
GRID_GAP * (sizeY - 1), // 최소 높이 147px,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[p4] 매출유입구조(도넛차트) 지표 카드에서 아래 height가 조금 남는데 정렬 문제일까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

음...?! 저는 괜찮은 것 같지만.. 내일 다같이 한번 보죠 ?!

}}
className={cn(
'bg-special-card-bg rounded-400 border-grey-300 relative flex flex-col overflow-hidden border p-3',
Expand All @@ -75,13 +79,10 @@ export const EditCardWrapper = ({
<div
style={{
transform: `scale(${EDIT_CARD_WRAPPER.CHANGE_SCALE})`,
transformOrigin:
computedCardHeight < EDIT_CARD_WRAPPER.MIN_HEIGHT
? 'center'
: 'top',
transformOrigin: 'left top',
}}
ref={childRef}
className={cn(isAdded ? 'opacity-10' : 'opacity-100')}
className={cn('w-full', isAdded ? 'opacity-10' : 'opacity-100')}
Comment on lines +82 to +85
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분도 sizeX가 2이상일 때 스타일이 깨지는 문제가 있어서 수정했습니다.

>
{children}
</div>
Expand Down
1 change: 1 addition & 0 deletions frontend/src/components/shared/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ export { PeriodTag, EditCardWrapper } from './edit-card-wrapper';
export { ButtonGroup } from './button-group';
export { PaginationBar } from './pagenation';
export { LineChart } from './line-chart';
export { BarLineChart } from './bar-line-chart';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { SALES_TREND } from './salesTrend';
74 changes: 74 additions & 0 deletions frontend/src/constants/sales/dashboard-sales-trend/salesTrend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
const createXData = (labels: string[]) =>
labels.map((label) => ({ amount: label, unit: '' }));

const createYData = (values: number[]) =>
values.map((value) => ({ amount: value, unit: '' }));

export const SALES_TREND = {
DEFAULT_TREND_CHART_WIDTH: 1040,
DEFAULT_TREND_CHART_WIDTH_FOR_RECENT_30_DAYS: 700,
DEFAULT_TREND_CHART_HEIGHT: 120,
EXAMPLE_DATA: {
SLS_09_04: {
data: {
mainX: createXData([
'1월 15일',
'1월 16일',
'1월 17일',
'1월 18일',
'1월 19일',
'1월 20일',
'오늘',
]),
mainY: createYData([520, 180, 330, 470, 600, 460, 220]),
subX: createXData([
'1월 15일',
'1월 16일',
'1월 17일',
'1월 18일',
'1월 19일',
'1월 20일',
'오늘',
]),
subY: createYData([16, 20, 14, 23, 20, 23, 14]),
},
color: 'var(--color-grey-400)',
},
SLS_10_07: {
data: {
mainX: createXData([
'12월 1~7일',
'12월 8~14일',
'12월 15~21일',
'12월 22~28일',
'12월 29일~1월 4일',
'1월 5~11일',
'1월 12~18일',
'이번주',
]),
mainY: createYData([460, 150, 280, 280, 620, 490, 280, 280]),
subX: createXData([
'12월 1~7일',
'12월 8~14일',
'12월 15~21일',
'12월 22~28일',
'12월 29일~1월 4일',
'1월 5~11일',
'1월 12~18일',
'이번주',
]),
subY: createYData([14, 18, 12, 20, 18, 18, 20, 12]),
},
color: 'var(--color-grey-400)',
},
SLS_11_07: {
data: {
mainX: createXData(['8월', '9월', '10월', '11월', '12월', '이번달']),
mainY: createYData([540, 180, 310, 310, 310, 310]),
subX: createXData(['8월', '9월', '10월', '11월', '12월', '이번달']),
subY: createYData([18, 15, 24, 21, 24, 15]),
},
color: 'var(--color-grey-400)',
},
} as const,
};
1 change: 1 addition & 0 deletions frontend/src/constants/sales/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ export {
SALES_TYPE,
} from './dashboard-sales-income';
export { PEAK_TIME, SALES_BY_DAY } from './dashboard-sales-pattern';
export { SALES_TREND } from './dashboard-sales-trend';
1 change: 1 addition & 0 deletions frontend/src/types/sales/dashboard-sales-trend/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type { SalesTrendItem } from './salesTrend';
5 changes: 5 additions & 0 deletions frontend/src/types/sales/dashboard-sales-trend/salesTrend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface SalesTrendItem {
label: string;
netAmount: number;
orderCount: number;
}
5 changes: 5 additions & 0 deletions frontend/src/types/sales/dto/getSalesTrendDto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import type { SalesTrendItem } from '../dashboard-sales-trend';

export interface GetSalesTrendResponseDto {
items: SalesTrendItem[];
}
1 change: 1 addition & 0 deletions frontend/src/types/sales/dto/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ export type {
GetDetailSalesByDayResponseDto,
GetDashboardSalesByDayResponseDto,
} from './getSalesByDayDto';
export type { GetSalesTrendResponseDto } from './getSalesTrendDto';
1 change: 1 addition & 0 deletions frontend/src/types/sales/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export type {
GetDashboardPeakTimeResponseDto,
GetDetailSalesByDayResponseDto,
GetDashboardSalesByDayResponseDto,
GetSalesTrendResponseDto,
} from './dto';
export type { SalesIncomeStructureInsight } from './dashboard-sales-income';
export type { PeakTimeItem, PeakTimeSummary } from './dashboard-sales-pattern';
Expand Down
Loading