o2o-clinicad-frontend/src/features/studio/hooks/useStudioWizard.ts

121 lines
3.9 KiB
TypeScript

// ─── 스튜디오 위저드 상태 관리 훅 ───────────────────────────────────────────
// DEMO/src/components/studio/StudioWizard.tsx 의 상태 로직에서 이전
// TODO: Phase 2 — Zustand store 로 전환
import { useState } from "react";
import type { StudioState, StudioChannel, ContentFormat, ContentPillarId, AssetSourceType, SoundSettings, GenerateOutputType } from "@/features/studio/types/studio";
import { getStepsForChannel } from "@/features/studio/config/studioSteps";
const DEFAULT_SOUND: SoundSettings = {
genre: 'calm',
trackId: null,
narrationEnabled: false,
narrationLanguage: 'ko',
narrationVoice: 'female',
subtitleEnabled: true,
};
const INITIAL_STATE: StudioState = {
channel: null,
format: null,
pillarId: null,
assetSources: [],
sound: DEFAULT_SOUND,
outputType: 'video',
};
export function useStudioWizard() {
const [state, setState] = useState<StudioState>(INITIAL_STATE);
const [currentStepIndex, setCurrentStepIndex] = useState(0);
const steps = getStepsForChannel(state.channel);
const currentStep = steps[currentStepIndex];
const isFirstStep = currentStepIndex === 0;
const isLastStep = currentStepIndex === steps.length - 1;
// ── 스텝별 진행 가능 여부 ─────────────────────────────────────────────────
// TODO: 각 스텝 구현 후 세부 유효성 검사 추가
function canProceed(): boolean {
switch (currentStep.key) {
case 'channel-format': return state.channel !== null && state.format !== null;
case 'strategy-source': return state.pillarId !== null && state.assetSources.length > 0;
case 'sound': return true;
case 'generate': return true;
case 'blog-editor': return true;
default: return false;
}
}
// ── 스텝 이동 ─────────────────────────────────────────────────────────────
function goNext() {
if (!isLastStep && canProceed()) setCurrentStepIndex((i) => i + 1);
}
function goPrev() {
if (!isFirstStep) setCurrentStepIndex((i) => i - 1);
}
function goToStep(index: number) {
if (index < currentStepIndex) setCurrentStepIndex(index);
}
// ── 상태 업데이트 핸들러 ──────────────────────────────────────────────────
function setChannel(channel: StudioChannel) {
// 채널 변경 시 format 초기화 + blog 채널은 outputType을 image로 고정
setState((prev) => ({
...prev,
channel,
format: null,
outputType: channel === 'naver_blog' ? 'image' : 'video',
}));
setCurrentStepIndex(0);
}
function setFormat(format: ContentFormat) {
setState((prev) => ({ ...prev, format }));
}
function setPillar(pillarId: ContentPillarId) {
setState((prev) => ({ ...prev, pillarId }));
}
function toggleAssetSource(source: AssetSourceType) {
setState((prev) => {
const exists = prev.assetSources.includes(source);
return {
...prev,
assetSources: exists
? prev.assetSources.filter((s) => s !== source)
: [...prev.assetSources, source],
};
});
}
function setSound(sound: Partial<SoundSettings>) {
setState((prev) => ({ ...prev, sound: { ...prev.sound, ...sound } }));
}
function setOutputType(outputType: GenerateOutputType) {
setState((prev) => ({ ...prev, outputType }));
}
return {
state,
steps,
currentStep,
currentStepIndex,
isFirstStep,
isLastStep,
canProceed,
goNext,
goPrev,
goToStep,
setChannel,
setFormat,
setPillar,
toggleAssetSource,
setSound,
setOutputType,
};
}