o2o-clinicad-frontend/src/components/studio/ChannelFormatStep.tsx

129 lines
5.3 KiB
TypeScript

import { type ComponentType } from 'react';
import {
YoutubeFilled,
InstagramFilled,
FacebookFilled,
GlobeFilled,
TiktokFilled,
} from '../icons/FilledIcons';
import { CHANNEL_OPTIONS, type StudioChannel, type ContentFormat } from '../../types/studio';
interface Props {
selectedChannel: StudioChannel | null;
selectedFormat: ContentFormat | null;
onChannelSelect: (ch: StudioChannel | null) => void;
onFormatSelect: (fmt: ContentFormat | null) => void;
}
const iconMap: Record<string, ComponentType<{ size?: number; className?: string }>> = {
youtube: YoutubeFilled,
instagram: InstagramFilled,
facebook: FacebookFilled,
globe: GlobeFilled,
tiktok: TiktokFilled,
};
export default function ChannelFormatStep({ selectedChannel, selectedFormat, onChannelSelect, onFormatSelect }: Props) {
const activeChannel = CHANNEL_OPTIONS.find(c => c.channel === selectedChannel);
return (
<div>
{/* Channel Selection */}
<div className="mb-10">
<h3 className="font-serif font-bold text-2xl text-[#0A1128] mb-2"> </h3>
<p className="text-sm text-slate-500 mb-6"> </p>
<div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 gap-4">
{CHANNEL_OPTIONS.map(ch => {
const Icon = iconMap[ch.icon] ?? GlobeFilled;
const isSelected = selectedChannel === ch.channel;
return (
<button
key={ch.channel}
onClick={() => {
onChannelSelect(isSelected ? null : ch.channel);
onFormatSelect(null);
}}
className={`relative flex flex-col items-center gap-3 p-6 rounded-2xl border-2 transition-all ${
isSelected
? 'border-[#6C5CE7] bg-[#F3F0FF]/30 shadow-[3px_4px_12px_rgba(108,92,231,0.12)]'
: 'border-slate-100 bg-white shadow-[3px_4px_12px_rgba(0,0,0,0.06)] hover:border-[#D5CDF5]'
}`}
>
{isSelected && (
<div className="absolute top-3 right-3 w-5 h-5 rounded-full bg-[#6C5CE7] flex items-center justify-center">
<svg width="10" height="10" viewBox="0 0 14 14" fill="none">
<path d="M2 7L5.5 10.5L12 3.5" stroke="white" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</div>
)}
<div className={`w-12 h-12 rounded-xl flex items-center justify-center ${
isSelected ? 'bg-[#F3F0FF]' : 'bg-slate-50'
}`}>
<Icon size={24} className={isSelected ? 'text-[#6C5CE7]' : 'text-[#9B8AD4]'} />
</div>
<span className={`text-sm font-semibold ${
isSelected ? 'text-[#0A1128]' : 'text-slate-600'
}`}>
{ch.label}
</span>
</button>
);
})}
</div>
</div>
{/* Format Selection */}
{activeChannel && (
<div>
<h3 className="font-serif font-bold text-2xl text-[#0A1128] mb-2"> </h3>
<p className="text-sm text-slate-500 mb-6">{activeChannel.label} </p>
<div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-4">
{activeChannel.formats.map(fmt => {
const isSelected = selectedFormat === fmt.key;
return (
<button
key={fmt.key}
onClick={() => onFormatSelect(isSelected ? null : fmt.key)}
className={`relative flex flex-col items-center gap-3 p-5 rounded-2xl border-2 transition-all ${
isSelected
? 'border-[#6C5CE7] bg-[#F3F0FF]/30 shadow-[3px_4px_12px_rgba(108,92,231,0.12)]'
: 'border-slate-100 bg-white shadow-[3px_4px_12px_rgba(0,0,0,0.06)] hover:border-[#D5CDF5]'
}`}
>
{isSelected && (
<div className="absolute top-3 right-3 w-5 h-5 rounded-full bg-[#6C5CE7] flex items-center justify-center">
<svg width="10" height="10" viewBox="0 0 14 14" fill="none">
<path d="M2 7L5.5 10.5L12 3.5" stroke="white" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</div>
)}
{/* Aspect ratio preview */}
<div className={`rounded-lg bg-slate-100 border border-slate-200 flex items-center justify-center ${
fmt.aspectRatio === '9:16' ? 'w-10 h-16' :
fmt.aspectRatio === '16:9' ? 'w-16 h-10' :
fmt.aspectRatio === '4:5' ? 'w-12 h-14' :
'w-12 h-12'
}`}>
<span className="text-xs text-slate-400 font-medium">{fmt.aspectRatio}</span>
</div>
<span className={`text-sm font-semibold ${
isSelected ? 'text-[#0A1128]' : 'text-slate-600'
}`}>
{fmt.label}
</span>
</button>
);
})}
</div>
</div>
)}
</div>
);
}