o2o-clinicad-frontend/src/features/plan/ui/assetCollection/AssetCollectionPanel.tsx

123 lines
5.0 KiB
TypeScript

import { useState } from "react";
import ChannelYoutubeIcon from "@/assets/icons/channel-youtube.svg?react";
import { Pill } from "@/components/atoms/Pill";
import { Surface } from "@/components/atoms/Surface";
import { SegmentTabButton } from "@/features/plan/ui/SegmentTabButton";
import type { AssetCollectionData } from "@/features/plan/types/marketingPlan";
import {
ASSET_COLLECTION_FILTER_TABS,
type AssetCollectionFilterKey,
} from "@/features/plan/ui/assetCollection/assetCollectionFilterTabs";
import {
assetSourceBadgeClass,
assetStatusConfig,
assetTypeBadgeClass,
assetTypeDisplayLabel,
formatYoutubeViews,
} from "@/features/plan/ui/assetCollection/assetCollectionBadgeClasses";
type AssetCollectionPanelProps = {
data: AssetCollectionData;
};
export function AssetCollectionPanel({ data }: AssetCollectionPanelProps) {
const [activeFilter, setActiveFilter] = useState<AssetCollectionFilterKey>("all");
const filteredAssets =
activeFilter === "all" ? data.assets : data.assets.filter((a) => a.source === activeFilter);
return (
<div>
<div className="flex flex-wrap gap-2 mb-8" role="tablist" aria-label="에셋 출처 필터">
{ASSET_COLLECTION_FILTER_TABS.map((tab) => {
const isActive = activeFilter === tab.key;
return (
<SegmentTabButton
key={tab.key}
role="tab"
aria-selected={isActive}
active={isActive}
onClick={() => setActiveFilter(tab.key)}
>
{tab.label}
</SegmentTabButton>
);
})}
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mb-12">
{filteredAssets.map((asset) => {
const statusInfo = assetStatusConfig(asset.status);
return (
<Surface key={asset.id}>
<div className="flex items-center gap-2 mb-3 flex-wrap">
<Pill className={`shrink-0 ${assetSourceBadgeClass(asset.source)}`}>{asset.sourceLabel}</Pill>
<Pill className={`shrink-0 ${assetTypeBadgeClass(asset.type)}`}>{assetTypeDisplayLabel(asset.type)}</Pill>
<Pill className={`ml-auto shrink-0 ${statusInfo.className}`}>{statusInfo.label}</Pill>
</div>
<h4 className="title-14 text-navy-900 mb-1 break-keep">{asset.title}</h4>
<p className="body-14 text-neutral-70 mb-3 break-keep">{asset.description}</p>
{asset.repurposingSuggestions.length > 0 ? (
<div>
<p className="label-12 font-semibold text-neutral-60 uppercase mb-2 break-keep">
Repurposing
</p>
<div className="flex flex-wrap gap-2">
{asset.repurposingSuggestions.map((suggestion, j) => (
<Pill key={j} size="sm" className="shrink-0 bg-lavender-100 text-violet-700">
{suggestion}
</Pill>
))}
</div>
</div>
) : null}
</Surface>
);
})}
</div>
{data.youtubeRepurpose.length > 0 ? (
<div>
<h3 className="font-serif headline-24 text-navy-900 mb-4 break-keep">
YouTube Top Videos for Repurposing
</h3>
<div className="flex overflow-x-auto gap-4 pb-4 scrollbar-hide">
{data.youtubeRepurpose.map((video) => (
<Surface key={video.title} className="min-w-[280px] shrink-0">
<div className="flex items-start gap-2 mb-3 min-w-0">
<ChannelYoutubeIcon width={18} height={18} className="text-[var(--color-status-critical-dot)] shrink-0 mt-1" aria-hidden />
<h4 className="title-14 text-navy-900 break-keep min-w-0">{video.title}</h4>
</div>
<div className="flex items-center gap-2 mb-3 flex-wrap">
<Pill className="shrink-0 bg-neutral-10 text-neutral-80">
{formatYoutubeViews(video.views)} views
</Pill>
<Pill
className={
video.type === "Short"
? "shrink-0 bg-lavender-100 text-violet-700 border border-lavender-300"
: "shrink-0 bg-[var(--color-status-info-bg)] text-[var(--color-status-info-text)] border border-[var(--color-status-info-border)]"
}
>
{video.type}
</Pill>
</div>
<p className="label-12 font-semibold text-neutral-60 uppercase mb-2 break-keep">Repurpose As:</p>
<div className="flex flex-wrap gap-2">
{video.repurposeAs.map((suggestion, j) => (
<Pill key={j} size="sm" className="shrink-0 bg-lavender-100 text-violet-700">
{suggestion}
</Pill>
))}
</div>
</Surface>
))}
</div>
</div>
) : null}
</div>
);
}