diff --git a/CLAUDE.md b/CLAUDE.md index ca070ed4..e7fed7c5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -17,6 +17,68 @@ sam_project: --- +## Git Workflow +**Priority**: ๐Ÿ”ด + +### ๋ธŒ๋žœ์น˜ ๊ตฌ์กฐ +| ๋ธŒ๋žœ์น˜ | ์—ญํ•  | ์ปค๋ฐ‹ ์ƒํƒœ | +|--------|------|-----------| +| `develop` | ํ‰์†Œ ์ž‘์—… ๋ธŒ๋žœ์น˜ (์ž์œ ๋กญ๊ฒŒ) | ์ง€์ €๋ถ„ํ•ด๋„ OK | +| `stage` | QA/ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ | ๊ธฐ๋Šฅ๋ณ„ squash ์ •๋ฆฌ | +| `main` | ๋ฐฐํฌ์šฉ (๊ธฐ๋ณธ ๋ธŒ๋žœ์น˜) | ๊ฒ€์ฆ๋œ ๊ฒƒ๋งŒ | +| `feature/*` | ํฐ ๊ธฐ๋Šฅ/์‹คํ—˜์  ์ž‘์—… ์‹œ | ์„ ํƒ์  ์‚ฌ์šฉ | + +### "git ์˜ฌ๋ ค์ค˜" ๋‹จ์ถ• ๋ช…๋ น์–ด +`git ์˜ฌ๋ ค์ค˜` ์ž…๋ ฅ ์‹œ **develop์— push**: +1. `git status` โ†’ 2. `git diff --stat` โ†’ 3. `git add -A` โ†’ 4. `git commit` (์ž๋™ ๋ฉ”์‹œ์ง€) โ†’ 5. `git push origin develop` + +- `snapshot.txt`, `.DS_Store` ํŒŒ์ผ์€ ํ•ญ์ƒ ์ œ์™ธ +- develop์—์„œ ์ž์œ ๋กญ๊ฒŒ ์ปค๋ฐ‹ (์ปค๋ฐ‹ ๋ฉ”์‹œ์ง€ ์ •๋ฆฌ ๋ถˆํ•„์š”) + +### main์— ์˜ฌ๋ฆฌ๊ธฐ (๊ธฐ๋Šฅ๋ณ„ squash merge) +์‚ฌ์šฉ์ž๊ฐ€ "main์— ์˜ฌ๋ ค์ค˜" ๋˜๋Š” ํŠน์ • ๊ธฐ๋Šฅ์„ main์— ์˜ฌ๋ฆฌ๋ผ๊ณ  ์ง€์‹œํ•  ๋•Œ๋งŒ ์‹คํ–‰. +**์ ˆ๋Œ€ ์ž๋™์œผ๋กœ main์— pushํ•˜์ง€ ์•Š์Œ.** + +```bash +# ๊ธฐ๋Šฅ๋ณ„๋กœ squash merge +git checkout main +git merge --squash develop # ๋˜๋Š” cherry-pick์œผ๋กœ ํŠน์ • ์ปค๋ฐ‹๋งŒ ์„ ๋ณ„ +git commit -m "feat: [๊ธฐ๋Šฅ๋ช…]" +git push origin main +git checkout develop +``` + +๊ธฐ๋Šฅ๋ณ„๋กœ ๋‚˜๋ˆ ์„œ ์˜ฌ๋ฆฌ๋Š” ๊ฒฝ์šฐ: +```bash +# ์˜ˆ: "๋Œ€์‹œ๋ณด๋“œ๋ž‘ ๊ฑฐ๋ž˜์ฒ˜ main์— ์˜ฌ๋ ค์ค˜" +git checkout main +git cherry-pick --no-commit <๋Œ€์‹œ๋ณด๋“œ์ปค๋ฐ‹1> <๋Œ€์‹œ๋ณด๋“œ์ปค๋ฐ‹2> +git commit -m "feat: CEO ๋Œ€์‹œ๋ณด๋“œ ์บ˜๋ฆฐ๋” ๊ธฐ๋Šฅ ๊ตฌํ˜„" + +git cherry-pick --no-commit <๊ฑฐ๋ž˜์ฒ˜์ปค๋ฐ‹1> <๊ฑฐ๋ž˜์ฒ˜์ปค๋ฐ‹2> +git commit -m "feat: ๊ฑฐ๋ž˜์ฒ˜ ๊ด€๋ฆฌ ๊ฐœ์„ " + +git push origin main +git checkout develop +``` + +**ํ•ต์‹ฌ: main์—๋Š” ๊ธฐ๋Šฅ ๋‹จ์œ„ ์ปค๋ฐ‹๋งŒ โ†’ ๋ฌธ์ œ ์‹œ `git revert`๋กœ ํ•ด๋‹น ๊ธฐ๋Šฅ๋งŒ ๋กค๋ฐฑ ๊ฐ€๋Šฅ** + +### feature ๋ธŒ๋žœ์น˜ ์‚ฌ์šฉ ๊ธฐ์ค€ +| ์ƒํ™ฉ | ๋ฐฉ๋ฒ• | +|------|------| +| ์ผ๋ฐ˜ ์ž‘์—… | develop์—์„œ ๋ฐ”๋กœ | +| 1์ฃผ์ผ+ ๊ฑธ๋ฆฌ๋Š” ํฐ ๊ธฐ๋Šฅ | feature/* ๋”ฐ์„œ ์ž‘์—… | +| ์‹คํ—˜์  ์‹œ๋„ | feature/* ๋”ฐ์„œ ์ž‘์—… | +| ๋ฐฑ์—”๋“œ์™€ ๋™์‹œ ์ˆ˜์ • ๊ฑด | ๊ฐ์ž feature/* ๊ถŒ์žฅ | + +### ๊ธˆ์ง€ ์‚ฌํ•ญ +- โŒ main์— ์ง์ ‘ ์ปค๋ฐ‹/push +- โŒ `git push --force` (main/develop) +- โŒ ์‚ฌ์šฉ์ž ์ง€์‹œ ์—†์ด main์— merge + +--- + ## Client Component ์‚ฌ์šฉ ์›์น™ **Priority**: ๐Ÿ”ด diff --git a/src/components/business/CEODashboard/components.tsx b/src/components/business/CEODashboard/components.tsx index 924e9523..52c4d1c2 100644 --- a/src/components/business/CEODashboard/components.tsx +++ b/src/components/business/CEODashboard/components.tsx @@ -1,5 +1,6 @@ 'use client'; +import { useState } from 'react'; import { Check, AlertTriangle, @@ -7,6 +8,7 @@ import { AlertCircle, TrendingUp, TrendingDown, + ChevronDown, type LucideIcon, } from 'lucide-react'; import { Card, CardContent } from '@/components/ui/card'; @@ -18,18 +20,18 @@ import type { CheckPoint, CheckPointType, AmountCard, HighlightColor } from './t // ์„น์…˜๋ณ„ ์ปฌ๋Ÿฌ ํ…Œ๋งˆ ํƒ€์ž… export type SectionColorTheme = 'blue' | 'purple' | 'orange' | 'green' | 'red' | 'amber' | 'cyan' | 'pink' | 'emerald' | 'indigo'; -// ์ปฌ๋Ÿฌ ํ…Œ๋งˆ๋ณ„ ์Šคํƒ€์ผ -export const SECTION_THEME_STYLES: Record = { - blue: { bg: '#eff6ff', border: '#bfdbfe', iconBg: '#3b82f6', labelColor: '#1d4ed8', accentColor: '#3b82f6' }, - purple: { bg: '#faf5ff', border: '#e9d5ff', iconBg: '#a855f7', labelColor: '#7c3aed', accentColor: '#a855f7' }, - orange: { bg: '#fff7ed', border: '#fed7aa', iconBg: '#f97316', labelColor: '#ea580c', accentColor: '#f97316' }, - green: { bg: '#f0fdf4', border: '#bbf7d0', iconBg: '#22c55e', labelColor: '#16a34a', accentColor: '#22c55e' }, - red: { bg: '#fef2f2', border: '#fecaca', iconBg: '#ef4444', labelColor: '#dc2626', accentColor: '#ef4444' }, - amber: { bg: '#fffbeb', border: '#fde68a', iconBg: '#f59e0b', labelColor: '#d97706', accentColor: '#f59e0b' }, - cyan: { bg: '#ecfeff', border: '#a5f3fc', iconBg: '#06b6d4', labelColor: '#0891b2', accentColor: '#06b6d4' }, - pink: { bg: '#fdf2f8', border: '#fbcfe8', iconBg: '#ec4899', labelColor: '#db2777', accentColor: '#ec4899' }, - emerald: { bg: '#ecfdf5', border: '#a7f3d0', iconBg: '#10b981', labelColor: '#059669', accentColor: '#10b981' }, - indigo: { bg: '#eef2ff', border: '#c7d2fe', iconBg: '#6366f1', labelColor: '#4f46e5', accentColor: '#6366f1' }, +// ์ปฌ๋Ÿฌ ํ…Œ๋งˆ๋ณ„ ์Šคํƒ€์ผ (๋‹คํฌ๋ชจ๋“œ ์ง€์› Tailwind ํด๋ž˜์Šค) +export const SECTION_THEME_STYLES: Record = { + blue: { bgClass: 'bg-blue-50 dark:bg-blue-900/30', borderClass: 'border-blue-200 dark:border-blue-800', iconBg: '#3b82f6', labelClass: 'text-blue-700 dark:text-blue-300', accentColor: '#3b82f6' }, + purple: { bgClass: 'bg-purple-50 dark:bg-purple-900/30', borderClass: 'border-purple-200 dark:border-purple-800', iconBg: '#a855f7', labelClass: 'text-purple-700 dark:text-purple-300', accentColor: '#a855f7' }, + orange: { bgClass: 'bg-orange-50 dark:bg-orange-900/30', borderClass: 'border-orange-200 dark:border-orange-800', iconBg: '#f97316', labelClass: 'text-orange-700 dark:text-orange-300', accentColor: '#f97316' }, + green: { bgClass: 'bg-green-50 dark:bg-green-900/30', borderClass: 'border-green-200 dark:border-green-800', iconBg: '#22c55e', labelClass: 'text-green-700 dark:text-green-300', accentColor: '#22c55e' }, + red: { bgClass: 'bg-red-50 dark:bg-red-900/30', borderClass: 'border-red-200 dark:border-red-800', iconBg: '#ef4444', labelClass: 'text-red-700 dark:text-red-300', accentColor: '#ef4444' }, + amber: { bgClass: 'bg-amber-50 dark:bg-amber-900/30', borderClass: 'border-amber-200 dark:border-amber-800', iconBg: '#f59e0b', labelClass: 'text-amber-700 dark:text-amber-300', accentColor: '#f59e0b' }, + cyan: { bgClass: 'bg-cyan-50 dark:bg-cyan-900/30', borderClass: 'border-cyan-200 dark:border-cyan-800', iconBg: '#06b6d4', labelClass: 'text-cyan-700 dark:text-cyan-300', accentColor: '#06b6d4' }, + pink: { bgClass: 'bg-pink-50 dark:bg-pink-900/30', borderClass: 'border-pink-200 dark:border-pink-800', iconBg: '#ec4899', labelClass: 'text-pink-700 dark:text-pink-300', accentColor: '#ec4899' }, + emerald: { bgClass: 'bg-emerald-50 dark:bg-emerald-900/30', borderClass: 'border-emerald-200 dark:border-emerald-800', iconBg: '#10b981', labelClass: 'text-emerald-700 dark:text-emerald-300', accentColor: '#10b981' }, + indigo: { bgClass: 'bg-indigo-50 dark:bg-indigo-900/30', borderClass: 'border-indigo-200 dark:border-indigo-800', iconBg: '#6366f1', labelClass: 'text-indigo-700 dark:text-indigo-300', accentColor: '#6366f1' }, }; /** @@ -249,31 +251,21 @@ export const AmountCardItem = ({ return formatKoreanAmount(amount); }; - // ํ…Œ๋งˆ ์ ์šฉ ์‹œ ์Šคํƒ€์ผ - const cardStyle = themeStyle && !card.isHighlighted ? { - backgroundColor: themeStyle.bg, - borderColor: themeStyle.border, - } : undefined; - return ( {/* ๊ฑด์ˆ˜ ๋ฑƒ์ง€ (์˜ค๋ฅธ์ชฝ ์ƒ๋‹จ) */} {showCountBadge && card.subLabel && (
{card.subLabel}
@@ -299,9 +291,8 @@ export const AmountCardItem = ({

{card.label}

@@ -309,7 +300,7 @@ export const AmountCardItem = ({ {/* ๊ธˆ์•ก */}

{formatCardAmount(card.amount)} @@ -318,11 +309,12 @@ export const AmountCardItem = ({ {/* ํŠธ๋ Œ๋“œ ํ‘œ์‹œ (pill ํ˜•ํƒœ, ๊ธˆ์•ก ์•„๋ž˜์— ๋ฐฐ์น˜) */} {showTrend && trendValue && (

{trendDirection === 'up' ? ( @@ -360,10 +352,12 @@ export const AmountCardItem = ({ {card.subLabel && card.subAmount === undefined && !card.previousLabel && ( subLabelAsBadge && themeStyle ? ( @@ -431,4 +425,69 @@ export const IssueCardItem = ({ ); -}; \ No newline at end of file +}; + +/** + * ์ ‘๊ธฐ/ํŽผ์น˜๊ธฐ ๊ฐ€๋Šฅํ•œ ๋Œ€์‹œ๋ณด๋“œ ์นด๋“œ + * - ๋‹คํฌ ํ—ค๋” + ํฐ์ƒ‰ ๋ฐ”๋”” ํŒจํ„ด์˜ ๊ณตํ†ต ์ปดํฌ๋„ŒํŠธ + * - ํ—ค๋” ํด๋ฆญ ์‹œ ๋ฐ”๋”” ํ† ๊ธ€ + */ +interface CollapsibleDashboardCardProps { + icon: React.ReactNode; + title: string; + subtitle?: string; + rightElement?: React.ReactNode; + children: React.ReactNode; + defaultOpen?: boolean; + bodyClassName?: string; +} + +export function CollapsibleDashboardCard({ + icon, + title, + subtitle, + rightElement, + children, + defaultOpen = true, + bodyClassName, +}: CollapsibleDashboardCardProps) { + const [isOpen, setIsOpen] = useState(defaultOpen); + + return ( +
+
setIsOpen(!isOpen)} + > +
+
+
+ {icon} +
+
+

{title}

+ {subtitle &&

{subtitle}

} +
+
+
+ {rightElement} + +
+
+
+ {isOpen && ( +
+ {children} +
+ )} +
+ ); +} \ No newline at end of file diff --git a/src/components/business/CEODashboard/sections/CalendarSection.tsx b/src/components/business/CEODashboard/sections/CalendarSection.tsx index 32e327f7..e7a823b9 100644 --- a/src/components/business/CEODashboard/sections/CalendarSection.tsx +++ b/src/components/business/CEODashboard/sections/CalendarSection.tsx @@ -2,7 +2,6 @@ import { useState, useMemo, useEffect } from 'react'; import { useRouter } from 'next/navigation'; -import { Card, CardContent } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { @@ -12,11 +11,12 @@ import { SelectTrigger, SelectValue, } from '@/components/ui/select'; -import { Plus, ExternalLink, ChevronLeft, ChevronRight } from 'lucide-react'; +import { Plus, ExternalLink, ChevronLeft, ChevronRight, CalendarDays } from 'lucide-react'; import { ScheduleCalendar } from '@/components/common/ScheduleCalendar'; import type { ScheduleEvent } from '@/components/common/ScheduleCalendar/types'; import { getCalendarEventsForYear, type CalendarEvent } from '@/constants/calendarEvents'; import { useCalendarScheduleStore } from '@/stores/useCalendarScheduleStore'; +import { CollapsibleDashboardCard } from '../components'; import type { CalendarScheduleItem, CalendarViewType, @@ -46,16 +46,16 @@ const SCHEDULE_TYPE_COLORS: Record = { // ์ด์Šˆ ๋ฑƒ์ง€๋ณ„ ์ƒ‰์ƒ const ISSUE_BADGE_COLORS: Record = { - '์ˆ˜์ฃผ๋“ฑ๋ก': 'bg-blue-100 text-blue-700', - '์ถ”์‹ฌ์ด์Šˆ': 'bg-purple-100 text-purple-700', - '์•ˆ์ „์žฌ๊ณ ': 'bg-orange-100 text-orange-700', - '์ง€์ถœ ์Šน์ธ๋Œ€๊ธฐ': 'bg-green-100 text-green-700', - '์„ธ๊ธˆ ์‹ ๊ณ ': 'bg-red-100 text-red-700', - '๊ฒฐ์žฌ ์š”์ฒญ': 'bg-yellow-100 text-yellow-700', - '์‹ ๊ทœ๊ฑฐ๋ž˜์ฒ˜': 'bg-emerald-100 text-emerald-700', - '์ž…๊ธˆ': 'bg-teal-100 text-teal-700', - '์ถœ๊ธˆ': 'bg-pink-100 text-pink-700', - '๊ธฐํƒ€': 'bg-gray-100 text-gray-700', + '์ˆ˜์ฃผ๋“ฑ๋ก': 'bg-blue-100 text-blue-700 dark:bg-blue-900/40 dark:text-blue-300', + '์ถ”์‹ฌ์ด์Šˆ': 'bg-purple-100 text-purple-700 dark:bg-purple-900/40 dark:text-purple-300', + '์•ˆ์ „์žฌ๊ณ ': 'bg-orange-100 text-orange-700 dark:bg-orange-900/40 dark:text-orange-300', + '์ง€์ถœ ์Šน์ธ๋Œ€๊ธฐ': 'bg-green-100 text-green-700 dark:bg-green-900/40 dark:text-green-300', + '์„ธ๊ธˆ ์‹ ๊ณ ': 'bg-red-100 text-red-700 dark:bg-red-900/40 dark:text-red-300', + '๊ฒฐ์žฌ ์š”์ฒญ': 'bg-yellow-100 text-yellow-700 dark:bg-yellow-900/40 dark:text-yellow-300', + '์‹ ๊ทœ๊ฑฐ๋ž˜์ฒ˜': 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-300', + '์ž…๊ธˆ': 'bg-teal-100 text-teal-700 dark:bg-teal-900/40 dark:text-teal-300', + '์ถœ๊ธˆ': 'bg-pink-100 text-pink-700 dark:bg-pink-900/40 dark:text-pink-300', + '๊ธฐํƒ€': 'bg-gray-100 text-gray-700 dark:bg-gray-800 dark:text-gray-300', }; // ๋ถ€์„œ ํ•„ํ„ฐ ์˜ต์…˜ @@ -295,67 +295,15 @@ export function CalendarSection({ }; return ( - - - {/* ๋ชจ๋ฐ”์ผ: ์Šคํ‹ฐํ‚ค ํ—ค๋” (ํƒ€์ดํ‹€+ํ•„ํ„ฐ+์›”๋„ค๋น„) */} -
-
-

์บ˜๋ฆฐ๋”

-
- {/* ๋ถ€์„œ ํ•„ํ„ฐ */} - - - {/* ์—…๋ฌด ํ•„ํ„ฐ */} - -
-
- - {/* ์›” ๋„ค๋น„๊ฒŒ์ด์…˜ */} -
- - - {currentDate.getFullYear()}๋…„ {currentDate.getMonth() + 1}์›” - - -
-
- - {/* ๋ฐ์Šคํฌํƒ‘: ์ผ๋ฐ˜ ํ—ค๋” */} -
-

์บ˜๋ฆฐ๋”

-
+ } + title="์บ˜๋ฆฐ๋”" + subtitle="์ผ์ • ๊ด€๋ฆฌ" + > + {/* ๋ชจ๋ฐ”์ผ: ํ•„ํ„ฐ+์›”๋„ค๋น„ */} +
+
+ {/* ๋ถ€์„œ ํ•„ํ„ฐ */} + {/* ์—…๋ฌด ํ•„ํ„ฐ */}
+ + {/* ์›” ๋„ค๋น„๊ฒŒ์ด์…˜ */} +
+ + + {currentDate.getFullYear()}๋…„ {currentDate.getMonth() + 1}์›” + + +
+
+ + {/* ๋ฐ์Šคํฌํƒ‘: ํ•„ํ„ฐ */} +
+ + +
{/* ๋ชจ๋ฐ”์ผ: ๋ฆฌ์ŠคํŠธ๋ทฐ */}
{/* ์ผ๋ณ„ ๋ฆฌ์ŠคํŠธ */} -
+
{monthDaysWithEvents.map((day) => { const hasEvents = day.events.length > 0; const isSelected = selectedDate && day.date.getTime() === selectedDate.getTime(); return (
handleDateClick(day.date)} > {/* ๋‚ ์งœ + ์ผ์ •๋“ฑ๋ก ๋ฒ„ํŠผ */}
{day.label} - {day.isToday && (์˜ค๋Š˜)} + {day.isToday && (์˜ค๋Š˜)}
{isSelected && (
{/* ์„ ํƒ๋œ ๋‚ ์งœ ์ผ์ • + ์ด์Šˆ ๋ชฉ๋ก */} -
+
-

+

{selectedDate ? formatSelectedDate(selectedDate) : '๋‚ ์งœ๋ฅผ ์„ ํƒํ•˜์„ธ์š”'}

-
-
- -
+ } + title="๋‹น์ผ ๊ทผํƒœ ํ˜„ํ™ฉ" + subtitle="์˜ค๋Š˜์˜ ์ถœ๊ทผ ํ˜„ํ™ฉ" + rightElement={ + + } + > {/* ์š”์•ฝ ์นด๋“œ 4๊ฐœ */}
-
+
- ์ถœ๊ทผ + ์ถœ๊ทผ
- {data.present}๋ช… + {data.present}๋ช…
-
+
- ํœด๊ฐ€ + ํœด๊ฐ€
- {data.onLeave}๋ช… + {data.onLeave}๋ช…
-
+
- ์ง€๊ฐ + ์ง€๊ฐ
- {data.late}๋ช… + {data.late}๋ช…
-
+
- ๊ฒฐ๊ทผ + ๊ฒฐ๊ทผ
- {data.absent}๋ช… + {data.absent}๋ช…
{/* ํ…Œ์ด๋ธ” */} -
-
-

์ง์› ๊ทผํƒœ ๋ชฉ๋ก

+
+
+

์ง์› ๊ทผํƒœ ๋ชฉ๋ก

- - - - - ์ตœ์‹ ์ˆœ - ์˜ค๋ž˜๋œ์ˆœ - ๊ธˆ์•ก ๋†’์€์ˆœ - ๊ธˆ์•ก ๋‚ฎ์€์ˆœ - - -
+ } + title="๋‹น์›” ๋งค์ž… ๋‚ด์—ญ" + subtitle="๋‹น์›” ๋งค์ž… ๊ฑฐ๋ž˜ ์ƒ์„ธ" + bodyClassName="p-0" + > +
+
์ด {filteredItems.length}๊ฑด
+ ({ value: s, label: s }))} + value={supplierFilter} + onChange={setSupplierFilter} + placeholder="์ „์ฒด ๊ณต๊ธ‰์ฒ˜" + className="w-full h-8 text-xs" + /> +
- - - - - - + + + + + + {filteredItems.map((item, idx) => ( - - - - - + + + + - - - + +
๋‚ ์งœ๊ณต๊ธ‰์ฒ˜ํ’ˆ๋ชฉ๊ธˆ์•ก์ƒํƒœ
๋‚ ์งœ๊ณต๊ธ‰์ฒ˜ํ’ˆ๋ชฉ๊ธˆ์•ก์ƒํƒœ
{item.date}{item.supplier}{item.item} +
{item.date}{item.supplier}{item.item} {item.amount.toLocaleString()}์› @@ -253,10 +226,10 @@ export function PurchaseStatusSection({ data }: PurchaseStatusSectionProps) { variant="outline" className={ item.status === '๊ฒฐ์ œ์™„๋ฃŒ' - ? 'text-green-600 border-green-200 bg-green-50' + ? 'text-green-600 border-green-200 bg-green-50 dark:text-green-400 dark:border-green-800 dark:bg-green-900/30' : item.status === '๋ฏธ๊ฒฐ์ œ' - ? 'text-red-600 border-red-200 bg-red-50' - : 'text-orange-600 border-orange-200 bg-orange-50' + ? 'text-red-600 border-red-200 bg-red-50 dark:text-red-400 dark:border-red-800 dark:bg-red-900/30' + : 'text-orange-600 border-orange-200 bg-orange-50 dark:text-orange-400 dark:border-orange-800 dark:bg-orange-900/30' } > {item.status} @@ -266,9 +239,9 @@ export function PurchaseStatusSection({ data }: PurchaseStatusSectionProps) { ))}
ํ•ฉ๊ณ„ +
ํ•ฉ๊ณ„ {filteredItems.reduce((sum, item) => sum + item.amount, 0).toLocaleString()}์› @@ -276,7 +249,7 @@ export function PurchaseStatusSection({ data }: PurchaseStatusSectionProps) {
-
+
); } diff --git a/src/components/business/CEODashboard/sections/ReceivableSection.tsx b/src/components/business/CEODashboard/sections/ReceivableSection.tsx index d7eb27a9..0bc73907 100644 --- a/src/components/business/CEODashboard/sections/ReceivableSection.tsx +++ b/src/components/business/CEODashboard/sections/ReceivableSection.tsx @@ -1,9 +1,9 @@ 'use client'; import { useRouter } from 'next/navigation'; -import { Banknote, Clock, AlertTriangle, CircleDollarSign } from 'lucide-react'; -import { Card, CardContent } from '@/components/ui/card'; -import { SectionTitle, AmountCardItem, CheckPointItem, type SectionColorTheme } from '../components'; +import { Banknote, Clock, AlertTriangle, CircleDollarSign, ChevronRight } from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import { AmountCardItem, CheckPointItem, CollapsibleDashboardCard, type SectionColorTheme } from '../components'; import type { ReceivableData } from '../types'; // ์นด๋“œ๋ณ„ ์•„์ด์ฝ˜ ๋งคํ•‘ (๋ฏธ์ˆ˜๊ธˆ ํ•ฉ๊ณ„, 30์ผ ์ด๋‚ด, 30~90์ผ, 90์ผ ์ดˆ๊ณผ) @@ -24,46 +24,46 @@ export function ReceivableSection({ data }: ReceivableSectionProps) { }; return ( - - - + } + title="๋ฏธ์ˆ˜๊ธˆ ํ˜„ํ™ฉ" + subtitle="๋ฏธ์ˆ˜๊ธˆ ๊ด€๋ฆฌ ํ˜„ํ™ฉ" + rightElement={ + data.detailButtonLabel ? ( + + ) : undefined + } + > +
+ {data.cards.map((card, idx) => ( + + ))} +
-
- {data.cards.map((card, idx) => ( - + {data.checkPoints.length > 0 && ( +
+ {data.checkPoints.map((cp) => ( + ))}
- - {data.checkPoints.length > 0 && ( -
- {data.checkPoints.map((cp) => ( - - ))} -
- )} - - + )} + ); -} \ No newline at end of file +} diff --git a/src/components/business/CEODashboard/sections/SalesStatusSection.tsx b/src/components/business/CEODashboard/sections/SalesStatusSection.tsx index b0af22a1..f55ab179 100644 --- a/src/components/business/CEODashboard/sections/SalesStatusSection.tsx +++ b/src/components/business/CEODashboard/sections/SalesStatusSection.tsx @@ -30,6 +30,7 @@ import { ResponsiveContainer, } from 'recharts'; import { formatKoreanAmount } from '@/lib/utils/amount'; +import { CollapsibleDashboardCard } from '../components'; import type { SalesStatusData } from '../types'; interface SalesStatusSectionProps { @@ -59,81 +60,58 @@ export function SalesStatusSection({ data }: SalesStatusSectionProps) { return (
-
- {/* ๋‹คํฌ ํ—ค๋” */} -
-
-
-
- -
-
-

๋งค์ถœ ํ˜„ํ™ฉ

-

๋‹น์›” ๋งค์ถœ ์‹ค์ 

-
-
- - ๋‹น์›” - -
-
- -
+ } + title="๋งค์ถœ ํ˜„ํ™ฉ" + subtitle="๋‹น์›” ๋งค์ถœ ์‹ค์ " + rightElement={ + + ๋‹น์›” + + } + > {/* ํ†ต๊ณ„์นด๋“œ 4๊ฐœ */}
{/* ๋ˆ„์  ๋งค์ถœ */} -
+
- +
- ๋ˆ„์  ๋งค์ถœ + ๋ˆ„์  ๋งค์ถœ
- + {formatKoreanAmount(data.cumulativeSales)}
{/* ๋‹ฌ์„ฑ๋ฅ  */} -
+
- +
- ๋‹ฌ์„ฑ๋ฅ  + ๋‹ฌ์„ฑ๋ฅ 
- + {data.achievementRate}%
{/* ์ „๋…„ ๋™๊ธฐ ๋Œ€๋น„ */}
= 0 ? '#fef2f2' : '#eff6ff', - borderColor: data.yoyChange >= 0 ? '#fecaca' : '#bfdbfe', - }} - className="rounded-xl p-4 border" + className={`rounded-xl p-4 border ${data.yoyChange >= 0 ? 'bg-red-50 border-red-200 dark:bg-red-900/30 dark:border-red-800' : 'bg-blue-50 border-blue-200 dark:bg-blue-900/30 dark:border-blue-800'}`} >
= 0 ? '#ef4444' : '#3b82f6' }} className="p-1.5 rounded-lg"> {data.yoyChange >= 0 - ? - : } + ? + : }
- = 0 ? '#dc2626' : '#1d4ed8' }} className="text-sm font-medium">์ „๋…„ ๋™๊ธฐ ๋Œ€๋น„ + = 0 ? 'text-red-700 dark:text-red-300' : 'text-blue-700 dark:text-blue-300'}`}>์ „๋…„ ๋™๊ธฐ ๋Œ€๋น„
- + {data.yoyChange >= 0 ? '+' : ''}{data.yoyChange}% {data.yoyChange >= 0 @@ -143,17 +121,14 @@ export function SalesStatusSection({ data }: SalesStatusSectionProps) {
{/* ๋‹น์›” ๋งค์ถœ */} -
+
- +
- ๋‹น์›” ๋งค์ถœ + ๋‹น์›” ๋งค์ถœ
- + {formatKoreanAmount(data.monthlySales)}
@@ -162,8 +137,8 @@ export function SalesStatusSection({ data }: SalesStatusSectionProps) { {/* ์ฐจํŠธ 2์—ด */}
{/* ์›”๋ณ„ ๋งค์ถœ ์ถ”์ด */} -
-

์›”๋ณ„ ๋งค์ถœ ์ถ”์ด

+
+

์›”๋ณ„ ๋งค์ถœ ์ถ”์ด

@@ -178,8 +153,8 @@ export function SalesStatusSection({ data }: SalesStatusSectionProps) {
{/* ๊ฑฐ๋ž˜์ฒ˜๋ณ„ ๋งค์ถœ (์ˆ˜ํ‰ Bar) */} -
-

๊ฑฐ๋ž˜์ฒ˜๋ณ„ ๋งค์ถœ

+
+

๊ฑฐ๋ž˜์ฒ˜๋ณ„ ๋งค์ถœ

@@ -194,63 +169,54 @@ export function SalesStatusSection({ data }: SalesStatusSectionProps) {
-
-
+ {/* ๋‹น์›” ๋งค์ถœ ๋‚ด์—ญ (๋ณ„๋„ ์นด๋“œ) */} -
-
-
-
- -
-
-

๋‹น์›” ๋งค์ถœ ๋‚ด์—ญ

-

๋‹น์›” ๋งค์ถœ ๊ฑฐ๋ž˜ ์ƒ์„ธ

-
-
-
-
-
์ด {filteredItems.length}๊ฑด
-
- ({ value: c, label: c }))} - value={clientFilter} - onChange={setClientFilter} - placeholder="์ „์ฒด ๊ฑฐ๋ž˜์ฒ˜" - className="w-[160px] h-8 text-xs" - /> - -
+ } + title="๋‹น์›” ๋งค์ถœ ๋‚ด์—ญ" + subtitle="๋‹น์›” ๋งค์ถœ ๊ฑฐ๋ž˜ ์ƒ์„ธ" + bodyClassName="p-0" + > +
+
์ด {filteredItems.length}๊ฑด
+ ({ value: c, label: c }))} + value={clientFilter} + onChange={setClientFilter} + placeholder="์ „์ฒด ๊ฑฐ๋ž˜์ฒ˜" + className="w-full h-8 text-xs" + /> +
- - - - - - + + + + + + {filteredItems.map((item, idx) => ( - - - - - + + + + - - - + +
๋‚ ์งœ๊ฑฐ๋ž˜์ฒ˜ํ’ˆ๋ชฉ๊ธˆ์•ก์ƒํƒœ
๋‚ ์งœ๊ฑฐ๋ž˜์ฒ˜ํ’ˆ๋ชฉ๊ธˆ์•ก์ƒํƒœ
{item.date}{item.client}{item.item} +
{item.date}{item.client}{item.item} {item.amount.toLocaleString()}์› @@ -258,10 +224,10 @@ export function SalesStatusSection({ data }: SalesStatusSectionProps) { variant="outline" className={ item.status === '์ž…๊ธˆ์™„๋ฃŒ' - ? 'text-green-600 border-green-200 bg-green-50' + ? 'text-green-600 border-green-200 bg-green-50 dark:text-green-400 dark:border-green-800 dark:bg-green-900/30' : item.status === '๋ฏธ์ž…๊ธˆ' - ? 'text-red-600 border-red-200 bg-red-50' - : 'text-orange-600 border-orange-200 bg-orange-50' + ? 'text-red-600 border-red-200 bg-red-50 dark:text-red-400 dark:border-red-800 dark:bg-red-900/30' + : 'text-orange-600 border-orange-200 bg-orange-50 dark:text-orange-400 dark:border-orange-800 dark:bg-orange-900/30' } > {item.status} @@ -271,9 +237,9 @@ export function SalesStatusSection({ data }: SalesStatusSectionProps) { ))}
ํ•ฉ๊ณ„ +
ํ•ฉ๊ณ„ {filteredItems.reduce((sum, item) => sum + item.amount, 0).toLocaleString()}์› @@ -281,7 +247,7 @@ export function SalesStatusSection({ data }: SalesStatusSectionProps) {
-
+
); } diff --git a/src/components/business/CEODashboard/sections/StatusBoardSection.tsx b/src/components/business/CEODashboard/sections/StatusBoardSection.tsx index 3f90e560..44acfb0e 100644 --- a/src/components/business/CEODashboard/sections/StatusBoardSection.tsx +++ b/src/components/business/CEODashboard/sections/StatusBoardSection.tsx @@ -1,8 +1,8 @@ 'use client'; import { useRouter } from 'next/navigation'; -import { Card, CardContent } from '@/components/ui/card'; -import { SectionTitle, IssueCardItem } from '../components'; +import { LayoutGrid } from 'lucide-react'; +import { IssueCardItem, CollapsibleDashboardCard } from '../components'; import type { TodayIssueItem, TodayIssueSettings } from '../types'; // ๋ผ๋ฒจ โ†’ ์„ค์ •ํ‚ค ๋งคํ•‘ @@ -40,10 +40,11 @@ export function StatusBoardSection({ items, itemSettings }: StatusBoardSectionPr : items; return ( - - - - + } + title="ํ˜„ํ™ฉํŒ" + subtitle="์ฃผ์š” ํ˜„ํ™ฉ ์š”์•ฝ" + >
{filteredItems.map((item) => ( ))}
-
-
+ ); -} \ No newline at end of file +} diff --git a/src/components/business/CEODashboard/sections/TodayIssueSection.tsx b/src/components/business/CEODashboard/sections/TodayIssueSection.tsx index 72e1591f..89490dd3 100644 --- a/src/components/business/CEODashboard/sections/TodayIssueSection.tsx +++ b/src/components/business/CEODashboard/sections/TodayIssueSection.tsx @@ -2,7 +2,6 @@ import { useState, useMemo, useCallback, useRef, useLayoutEffect } from 'react'; import { useRouter } from 'next/navigation'; -import { Card, CardContent } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'; @@ -31,6 +30,7 @@ import { Loader2, type LucideIcon, } from 'lucide-react'; +import { CollapsibleDashboardCard } from '../components'; import { usePastIssue } from '@/hooks/useCEODashboard'; import type { TodayIssueListItem, TodayIssueNotificationType } from '../types'; @@ -44,16 +44,16 @@ interface BadgeStyle { // notification_type ์ฝ”๋“œ ๊ธฐ๋ฐ˜ ์Šคํƒ€์ผ ๋งคํ•‘ (API ๊ณ ์ •๊ฐ’ ์‚ฌ์šฉ) const NOTIFICATION_STYLES: Record = { - sales_order: { bg: 'bg-blue-50', text: 'text-blue-700', iconBg: 'bg-blue-500', Icon: ShoppingCart }, - bad_debt: { bg: 'bg-purple-50', text: 'text-purple-700', iconBg: 'bg-purple-500', Icon: AlertCircle }, - safety_stock: { bg: 'bg-orange-50', text: 'text-orange-700', iconBg: 'bg-orange-500', Icon: Package }, - expected_expense: { bg: 'bg-green-50', text: 'text-green-700', iconBg: 'bg-green-500', Icon: Receipt }, - vat_report: { bg: 'bg-red-50', text: 'text-red-700', iconBg: 'bg-red-500', Icon: FileText }, - approval_request: { bg: 'bg-amber-50', text: 'text-amber-700', iconBg: 'bg-amber-500', Icon: CheckCircle2 }, - new_vendor: { bg: 'bg-emerald-50', text: 'text-emerald-700', iconBg: 'bg-emerald-500', Icon: Building2 }, - deposit: { bg: 'bg-cyan-50', text: 'text-cyan-700', iconBg: 'bg-cyan-500', Icon: TrendingUp }, - withdrawal: { bg: 'bg-pink-50', text: 'text-pink-700', iconBg: 'bg-pink-500', Icon: TrendingDown }, - other: { bg: 'bg-gray-50', text: 'text-gray-700', iconBg: 'bg-gray-500', Icon: Info }, + sales_order: { bg: 'bg-blue-50 dark:bg-blue-900/30', text: 'text-blue-700 dark:text-blue-300', iconBg: 'bg-blue-500', Icon: ShoppingCart }, + bad_debt: { bg: 'bg-purple-50 dark:bg-purple-900/30', text: 'text-purple-700 dark:text-purple-300', iconBg: 'bg-purple-500', Icon: AlertCircle }, + safety_stock: { bg: 'bg-orange-50 dark:bg-orange-900/30', text: 'text-orange-700 dark:text-orange-300', iconBg: 'bg-orange-500', Icon: Package }, + expected_expense: { bg: 'bg-green-50 dark:bg-green-900/30', text: 'text-green-700 dark:text-green-300', iconBg: 'bg-green-500', Icon: Receipt }, + vat_report: { bg: 'bg-red-50 dark:bg-red-900/30', text: 'text-red-700 dark:text-red-300', iconBg: 'bg-red-500', Icon: FileText }, + approval_request: { bg: 'bg-amber-50 dark:bg-amber-900/30', text: 'text-amber-700 dark:text-amber-300', iconBg: 'bg-amber-500', Icon: CheckCircle2 }, + new_vendor: { bg: 'bg-emerald-50 dark:bg-emerald-900/30', text: 'text-emerald-700 dark:text-emerald-300', iconBg: 'bg-emerald-500', Icon: Building2 }, + deposit: { bg: 'bg-cyan-50 dark:bg-cyan-900/30', text: 'text-cyan-700 dark:text-cyan-300', iconBg: 'bg-cyan-500', Icon: TrendingUp }, + withdrawal: { bg: 'bg-pink-50 dark:bg-pink-900/30', text: 'text-pink-700 dark:text-pink-300', iconBg: 'bg-pink-500', Icon: TrendingDown }, + other: { bg: 'bg-muted/50', text: 'text-muted-foreground', iconBg: 'bg-gray-500', Icon: Info }, }; // ์‹ ์šฉ๋“ฑ๊ธ‰ ์ƒ‰์ƒ ๋งคํ•‘ (A=๋…น์ƒ‰, B=๋…ธ๋ž‘, C=์ฃผํ™ฉ, D=๋นจ๊ฐ•) @@ -277,11 +277,13 @@ export function TodayIssueSection({ items }: TodayIssueSectionProps) { }; return ( - - - {/* ํ—ค๋” */} + } + title="์˜ค๋Š˜์˜ ์ด์Šˆ" + subtitle="์ฃผ์š” ์•Œ๋ฆผ ๋ฐ ์ด์Šˆ ํ˜„ํ™ฉ" + > + {/* ํ•„ํ„ฐ/ํƒญ ์˜์—ญ */}
-

์˜ค๋Š˜์˜ ์ด์Šˆ

{/* ํƒญ */} @@ -334,7 +336,7 @@ export function TodayIssueSection({ items }: TodayIssueSectionProps) {
{option.label} - + {option.count}
@@ -348,11 +350,11 @@ export function TodayIssueSection({ items }: TodayIssueSectionProps) {
{activeTab === 'past' && pastLoading ? (
- - ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘... + + ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘...
) : filteredItems.length === 0 ? ( -
+
{activeTab === 'past' ? `${formatDateDisplay(pastDate)}์— ์ด์Šˆ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.` : 'ํ‘œ์‹œํ•  ์ด์Šˆ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.'} @@ -368,7 +370,7 @@ export function TodayIssueSection({ items }: TodayIssueSectionProps) { return (
handleItemClick(item)} > {/* ์•„์ด์ฝ˜ + ๋ฑƒ์ง€ */} @@ -382,7 +384,7 @@ export function TodayIssueSection({ items }: TodayIssueSectionProps) {
{/* ๋‚ด์šฉ */} - + {item.content} @@ -404,7 +406,7 @@ export function TodayIssueSection({ items }: TodayIssueSectionProps) { )} {/* ์‹œ๊ฐ„ */} - + {item.time} @@ -434,7 +436,7 @@ export function TodayIssueSection({ items }: TodayIssueSectionProps) {
- - + ); -} \ No newline at end of file +} diff --git a/src/components/business/CEODashboard/sections/UnshippedSection.tsx b/src/components/business/CEODashboard/sections/UnshippedSection.tsx index b135cda7..01591190 100644 --- a/src/components/business/CEODashboard/sections/UnshippedSection.tsx +++ b/src/components/business/CEODashboard/sections/UnshippedSection.tsx @@ -11,6 +11,7 @@ import { SelectValue, } from '@/components/ui/select'; import { MultiSelectCombobox } from '@/components/ui/multi-select-combobox'; +import { CollapsibleDashboardCard } from '../components'; import type { UnshippedData } from '../types'; interface UnshippedSectionProps { @@ -31,81 +32,68 @@ export function UnshippedSection({ data }: UnshippedSectionProps) { }); return ( -
- {/* ๋‹คํฌ ํ—ค๋” */} -
-
-
-
- -
-
-

๋ฏธ์ถœ๊ณ  ๋‚ด์—ญ

-

๋‚ฉ๊ธฐ์ผ ๊ธฐ์ค€ ๋ฏธ์ถœ๊ณ  ํ˜„ํ™ฉ

-
-
- - {data.items.length}๊ฑด - -
-
- -
+ } + title="๋ฏธ์ถœ๊ณ  ๋‚ด์—ญ" + subtitle="๋‚ฉ๊ธฐ์ผ ๊ธฐ์ค€ ๋ฏธ์ถœ๊ณ  ํ˜„ํ™ฉ" + rightElement={ + + {data.items.length}๊ฑด + + } + > {/* ๋ฏธ์ถœ๊ณ  ํ…Œ์ด๋ธ” */} -
-
-

๋ฏธ์ถœ๊ณ  ๋ชฉ๋ก

-
- ({ value: c, label: c }))} - value={clientFilter} - onChange={setClientFilter} - placeholder="์ „์ฒด ๊ฑฐ๋ž˜์ฒ˜" - className="w-[160px] h-8 text-xs" - /> - -
+
+
+

๋ฏธ์ถœ๊ณ  ๋ชฉ๋ก

+ ({ value: c, label: c }))} + value={clientFilter} + onChange={setClientFilter} + placeholder="์ „์ฒด ๊ฑฐ๋ž˜์ฒ˜" + className="w-full h-8 text-xs" + /> +
- - - - - - - + + + + + + + {filteredItems.map((item, idx) => ( - - - - - - + + + + + +
NoํฌํŠธ๋ฒˆํ˜ธํ˜„์žฅ๋ช…์ˆ˜์ฃผ์ฒ˜๋‚ฉ๊ธฐ์ผ๋‚จ์€์ผ
NoํฌํŠธ๋ฒˆํ˜ธํ˜„์žฅ๋ช…์ˆ˜์ฃผ์ฒ˜๋‚ฉ๊ธฐ์ผ๋‚จ์€์ผ
{idx + 1}{item.portNo}{item.siteName}{item.orderClient}{item.dueDate}
{idx + 1}{item.portNo}{item.siteName}{item.orderClient}{item.dueDate} D-{item.daysLeft} @@ -117,7 +105,6 @@ export function UnshippedSection({ data }: UnshippedSectionProps) {
-
-
+
); } diff --git a/src/components/business/CEODashboard/sections/VatSection.tsx b/src/components/business/CEODashboard/sections/VatSection.tsx index 2a230f58..eb5c45ab 100644 --- a/src/components/business/CEODashboard/sections/VatSection.tsx +++ b/src/components/business/CEODashboard/sections/VatSection.tsx @@ -1,7 +1,7 @@ 'use client'; -import { Card, CardContent } from '@/components/ui/card'; -import { SectionTitle, AmountCardItem, CheckPointItem } from '../components'; +import { Calculator } from 'lucide-react'; +import { AmountCardItem, CheckPointItem, CollapsibleDashboardCard } from '../components'; import type { VatData } from '../types'; interface VatSectionProps { @@ -11,28 +11,28 @@ interface VatSectionProps { export function VatSection({ data, onClick }: VatSectionProps) { return ( - - - + } + title="๋ถ€๊ฐ€์„ธ ํ˜„ํ™ฉ" + subtitle="๋ถ€๊ฐ€์„ธ ๋‚ฉ๋ถ€ ์ •๋ณด" + > +
+ {data.cards.map((card) => ( + + ))} +
-
- {data.cards.map((card) => ( - + {data.checkPoints.length > 0 && ( +
+ {data.checkPoints.map((cp) => ( + ))}
- - {data.checkPoints.length > 0 && ( -
- {data.checkPoints.map((cp) => ( - - ))} -
- )} - - + )} + ); -} \ No newline at end of file +} diff --git a/src/components/business/CEODashboard/sections/WelfareSection.tsx b/src/components/business/CEODashboard/sections/WelfareSection.tsx index f94a6af4..d9e3f733 100644 --- a/src/components/business/CEODashboard/sections/WelfareSection.tsx +++ b/src/components/business/CEODashboard/sections/WelfareSection.tsx @@ -1,8 +1,7 @@ 'use client'; import { Heart, Gift, Coffee, Smile } from 'lucide-react'; -import { Card, CardContent } from '@/components/ui/card'; -import { SectionTitle, AmountCardItem, CheckPointItem, type SectionColorTheme } from '../components'; +import { AmountCardItem, CheckPointItem, CollapsibleDashboardCard, type SectionColorTheme } from '../components'; import type { WelfareData } from '../types'; // ์นด๋“œ๋ณ„ ์•„์ด์ฝ˜ ๋งคํ•‘ @@ -16,38 +15,33 @@ interface WelfareSectionProps { export function WelfareSection({ data, onCardClick }: WelfareSectionProps) { return ( - - - + } + title="๋ณต๋ฆฌํ›„์ƒ๋น„ ํ˜„ํ™ฉ" + subtitle="๋ณต๋ฆฌํ›„์ƒ๋น„ ์‚ฌ์šฉ ํ˜„ํ™ฉ" + > +
+ {data.cards.map((card, idx) => ( + onCardClick(card.id) : undefined} + icon={CARD_ICONS[idx] || Heart} + colorTheme={CARD_THEMES[idx] || 'emerald'} + showTrend={!!card.previousLabel} + trendValue={card.previousLabel} + trendDirection="up" + /> + ))} +
-
- {data.cards.map((card, idx) => ( - onCardClick(card.id) : undefined} - icon={CARD_ICONS[idx] || Heart} - colorTheme={CARD_THEMES[idx] || 'emerald'} - showTrend={!!card.previousLabel} - trendValue={card.previousLabel} - trendDirection="up" - /> + {data.checkPoints.length > 0 && ( +
+ {data.checkPoints.map((cp) => ( + ))}
- - {data.checkPoints.length > 0 && ( -
- {data.checkPoints.map((cp) => ( - - ))} -
- )} - - + )} + ); -} \ No newline at end of file +}