136 lines
6.0 KiB
TypeScript
136 lines
6.0 KiB
TypeScript
import { useState } from "react";
|
|
import AlertCircleIcon from "@/assets/icons/alert-circle.svg?react";
|
|
import ChevronRightIcon from "@/assets/report/chevron-right.svg?react";
|
|
import CheckCircleIcon from "@/assets/report/check-circle.svg?react";
|
|
import XCircleIcon from "@/assets/report/x-circle.svg?react";
|
|
import type { BrandInconsistency } from "@/types/brandConsistency";
|
|
|
|
export type BrandConsistencyMapProps = {
|
|
inconsistencies: BrandInconsistency[];
|
|
className?: string;
|
|
/** false면 상단 제목·설명을 숨김 (플랜 브랜딩 가이드 탭 등) */
|
|
showHeading?: boolean;
|
|
};
|
|
|
|
export function BrandConsistencyMap({
|
|
inconsistencies,
|
|
className = "",
|
|
showHeading = true,
|
|
}: BrandConsistencyMapProps) {
|
|
const [expanded, setExpanded] = useState<number | null>(0);
|
|
|
|
if (inconsistencies.length === 0) return null;
|
|
|
|
return (
|
|
<div
|
|
className={`${showHeading ? "mt-8 animate-fade-in-up animation-delay-200 " : ""}${className}`.trim()}
|
|
>
|
|
{showHeading ? (
|
|
<>
|
|
<h3 className="font-serif headline-20 text-navy-900 mb-2 break-keep">Brand Consistency Map</h3>
|
|
<p className="body-14 text-neutral-60 mb-5 break-keep">전 채널 브랜드 일관성 분석</p>
|
|
</>
|
|
) : null}
|
|
|
|
<div className="space-y-3">
|
|
{inconsistencies.map((item, i) => {
|
|
const wrongCount = item.values.filter((v) => !v.isCorrect).length;
|
|
const isOpen = expanded === i;
|
|
|
|
return (
|
|
<div
|
|
key={item.field}
|
|
className="rounded-2xl border border-neutral-20 bg-white card-shadow overflow-hidden"
|
|
>
|
|
<button
|
|
type="button"
|
|
onClick={() => setExpanded(isOpen ? null : i)}
|
|
className="w-full flex items-center justify-between gap-3 p-5 text-left hover:bg-neutral-10/80 transition-colors cursor-pointer"
|
|
>
|
|
<div className="flex items-center gap-3 min-w-0">
|
|
<div className="w-8 h-8 rounded-lg bg-navy-900 flex items-center justify-center text-white label-12-semibold shrink-0">
|
|
{wrongCount}
|
|
</div>
|
|
<div className="min-w-0">
|
|
<p className="title-14 text-navy-900 break-keep">{item.field}</p>
|
|
<p className="label-12 text-neutral-60 break-keep">{wrongCount}개 채널 불일치</p>
|
|
</div>
|
|
</div>
|
|
<ChevronRightIcon
|
|
width={16}
|
|
height={16}
|
|
className={`text-neutral-60 shrink-0 transition-transform ${isOpen ? "rotate-90" : ""}`}
|
|
aria-hidden
|
|
/>
|
|
</button>
|
|
|
|
{isOpen ? (
|
|
<div className="px-5 pb-5 border-t border-neutral-20">
|
|
<div className="grid gap-2 mt-4 mb-4">
|
|
{item.values.map((v) => (
|
|
<div
|
|
key={v.channel}
|
|
className={`flex flex-wrap items-center justify-between gap-2 py-3 px-3 rounded-lg body-14 sm:flex-nowrap ${
|
|
v.isCorrect
|
|
? "bg-[var(--color-status-good-bg)]/60"
|
|
: "bg-[var(--color-status-critical-bg)]/60"
|
|
}`}
|
|
>
|
|
<span className="font-medium text-neutral-80 shrink-0 min-w-[100px] break-keep">
|
|
{v.channel}
|
|
</span>
|
|
<span
|
|
className={`flex-1 text-right min-w-0 break-keep ${
|
|
v.isCorrect
|
|
? "text-[var(--color-status-good-text)]"
|
|
: "text-[var(--color-status-critical-text)]"
|
|
}`}
|
|
>
|
|
{v.value}
|
|
</span>
|
|
<span className="ml-auto sm:ml-3 shrink-0">
|
|
{v.isCorrect ? (
|
|
<CheckCircleIcon
|
|
width={15}
|
|
height={15}
|
|
className="text-[var(--color-status-good-dot)]"
|
|
aria-hidden
|
|
/>
|
|
) : (
|
|
<XCircleIcon
|
|
width={15}
|
|
height={15}
|
|
className="text-[var(--color-status-critical-dot)]"
|
|
aria-hidden
|
|
/>
|
|
)}
|
|
</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
<div className="rounded-xl bg-[var(--color-status-warning-bg)] border border-[var(--color-status-warning-border)] p-4 mb-3">
|
|
<p className="label-12-semibold text-[var(--color-status-warning-text)] uppercase tracking-wide mb-1 break-keep">
|
|
<AlertCircleIcon className="inline mr-1 align-text-bottom shrink-0" width={12} height={12} aria-hidden />
|
|
Impact
|
|
</p>
|
|
<p className="body-14 text-[var(--color-status-warning-text)] break-keep">{item.impact}</p>
|
|
</div>
|
|
|
|
<div className="rounded-xl bg-[var(--color-status-good-bg)] border border-[var(--color-status-good-border)] p-4">
|
|
<p className="label-12-semibold text-[var(--color-status-good-text)] uppercase tracking-wide mb-1 break-keep">
|
|
<CheckCircleIcon className="inline mr-1 align-text-bottom shrink-0" width={12} height={12} aria-hidden />
|
|
Recommendation
|
|
</p>
|
|
<p className="body-14 text-[var(--color-status-good-text)] break-keep">{item.recommendation}</p>
|
|
</div>
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|