'use client'; import { useState, useEffect } from 'react'; import { useRouter } from 'next/navigation'; import { fetchApi } from '@/lib/utils/client'; import { useStudioContext } from '@/app/studio/context'; import { useCrewWidgetConfigContext } from '../context'; import { CREW_WIDGET_THEMES, CREW_PERIODS, FONT_FAMILIES } from '../constants'; import { type FormState, createEmptyForm, formatInput, parseInput } from '../types'; import type { CrewWidgetConfigItem } from '@/types/response/crew/widgetConfig'; import { Checkbox } from '@/components/ui/checkbox'; /** 색상 입력 (color picker + hex text) */ function ColorInput({ value, onChange }: { value: string; onChange: (v: string) => void }) { return (
onChange(e.target.value)} /> onChange(e.target.value)} maxLength={7} />
); } type Props = { editItem?: CrewWidgetConfigItem; form?: FormState; onFormChange?: (form: FormState) => void; onSaved?: () => void; onCancel?: () => void; channelID?: number; saving?: boolean; setSaving?: (v: boolean) => void; }; export default function CrewWidgetFormPanel({ editItem, form: externalForm, onFormChange, onSaved, onCancel, channelID: directChannelID, saving: directSaving, setSaving: directSetSaving }: Props) { const router = useRouter(); const studio = useStudioContext(); const channelID = directChannelID ?? studio.channelID; // context 사용 가능 여부에 따라 분기 let ctxSaving = false; let ctxSetSaving: (v: boolean) => void = () => {}; let ctxFetchList: () => void = () => {}; try { const ctx = useCrewWidgetConfigContext(); ctxSaving = ctx.saving; ctxSetSaving = ctx.setSaving; ctxFetchList = ctx.fetchList; } catch { } const saving = directSaving ?? ctxSaving; const setSaving = directSetSaving ?? ctxSetSaving; const fetchList = ctxFetchList; const [internalForm, setInternalForm] = useState(createEmptyForm()); const form = externalForm ?? internalForm; useEffect(() => { if (!editItem) { return; } const data: FormState = { title: editItem.title, theme: editItem.theme, period: editItem.period, startAt: editItem.startAt, endAt: editItem.endAt, maxDisplayCount: editItem.maxDisplayCount, isShowAmount: editItem.isShowAmount, isShowDonationCount: editItem.isShowDonationCount, isShowContributionRate: editItem.isShowContributionRate, isShowMemberIcon: editItem.isShowMemberIcon, isActive: editItem.isActive, bgColor: editItem.bgColor, titleFontFamily: editItem.titleFontFamily, titleFontSizePx: editItem.titleFontSizePx, titleFontColor: editItem.titleFontColor, rank1FontFamily: editItem.rank1FontFamily, rank1FontSizePx: editItem.rank1FontSizePx, rank1FontColor: editItem.rank1FontColor, rank2FontFamily: editItem.rank2FontFamily, rank2FontSizePx: editItem.rank2FontSizePx, rank2FontColor: editItem.rank2FontColor, rank3FontFamily: editItem.rank3FontFamily, rank3FontSizePx: editItem.rank3FontSizePx, rank3FontColor: editItem.rank3FontColor, rowFontFamily: editItem.rowFontFamily, rowFontSizePx: editItem.rowFontSizePx, rowFontColor: editItem.rowFontColor }; if (onFormChange) { onFormChange(data); } else { setInternalForm(data); } }, [editItem]); const set = (key: keyof FormState, value: FormState[keyof FormState]) => { const updater = (f: FormState): FormState => ({ ...f, [key]: value }); if (onFormChange) { onFormChange(updater(form)); } else { setInternalForm(updater); } }; const handleSave = async () => { if (!form.title.trim()) { alert('제목을 입력해 주세요.'); return; } if (form.period === 5) { if (!form.startAt || !form.endAt) { alert('사용자 지정 기간을 입력해 주세요.'); return; } } setSaving(true); try { await fetchApi('/api/studio/crew/widget/config', { method: 'POST', body: { ...form, channelID, id: editItem?.id ?? undefined } }); fetchList(); if (onSaved) { onSaved(); } else { router.push('/studio/donation/crew/widget/list'); } } catch (err: unknown) { alert(err instanceof Error ? err.message : '저장에 실패했습니다.'); } finally { setSaving(false); } }; const renderFontFields = (prefix: string) => { const familyKey = `${prefix}FontFamily` as keyof FormState; const sizeKey = `${prefix}FontSizePx` as keyof FormState; const colorKey = `${prefix}FontColor` as keyof FormState; return ( <>
set(sizeKey, Number(e.target.value))} />
set(colorKey, v)} />
); }; const renderFontDetails = (label: string, prefix: string) => { return (
{label} 폰트 설정
{renderFontFields(prefix)}
); }; return (
{/* 기본 설정 */}
기본 설정
set('title', e.target.value)} maxLength={300} />
{form.period === 5 && (
set('startAt', parseInput(e.target.value) || null)} />
set('endAt', parseInput(e.target.value) || null)} />
)}
set('maxDisplayCount', Number(e.target.value))} />
set('bgColor', v)} />
{/* 표시 옵션 */}
표시 옵션
{/* 제목 폰트 */}
제목 폰트
{renderFontFields('title')}
{/* 순위별 폰트 */}
순위별 폰트
{renderFontDetails('1위', 'rank1')} {renderFontDetails('2위', 'rank2')} {renderFontDetails('3위', 'rank3')} {renderFontDetails('일반', 'row')}
{/* 버튼 */}
); }