import { Routes } from '../navigation/Routes' export const CREATION_STAGE_KEYS = ['songwriter', 'beatmaker', 'director'] const getSectionLyrics = (section) => { if (typeof section === 'string') { return section.trim() } if (section && typeof section === 'object') { const value = typeof section.lyrics === 'string' ? section.lyrics.trim() : '' return value } return '' } const getLyricsCount = (project) => { if (!project) return 0 const { lyrics } = project if (Array.isArray(lyrics)) { return lyrics.reduce((count, section) => (getSectionLyrics(section) ? count + 1 : count), 0) } if (lyrics && typeof lyrics === 'object') { return Object.values(lyrics).reduce( (count, value) => (getSectionLyrics(value) ? count + 1 : count), 0 ) } return getSectionLyrics(lyrics) ? 1 : 0 } const getStageMetadata = (project) => { const lyricsCount = getLyricsCount(project) const hasLyrics = !!(project?.hasLyrics || lyricsCount > 0) const hasSongUrl = !!project?.songUrl const cover = project?.cover || {} const hasCoverUrl = !!project?.coverUrl const hasPlaybackAsset = !!project?.playbackUrl const hasPlayback = hasPlaybackAsset || hasSongUrl const musicUrls = Array.isArray(project?.musicUrls) ? project.musicUrls.filter(Boolean) : [] const musicStatus = project?.musicStatus || null const coverStatus = project?.coverStatus || null const playbackStatus = project?.playbackStatus || null const isPlaybackGenerating = project?.playbackGenerating === true || project?.isPlaybackGenerating === true || playbackStatus === 'GENERATING' const isPlaybackDraftReady = playbackStatus === 'DRAFT_READY' return { lyricsCount, hasLyrics, hasSongUrl, hasCover: hasCoverUrl, hasPlayback, hasPlaybackAsset, hasMusicDraft: musicUrls.length > 0, musicStatus, coverStatus, playbackStatus, isPlaybackDraftReady, isMusicGenerating: musicStatus === 'GENERATING', isCoverGenerating: coverStatus === 'GENERATING', isPlaybackGenerating, } } const getStageLockState = (key, metadata) => { switch (key) { case 'songwriter': return metadata.hasSongUrl case 'beatmaker': if (metadata.hasCover) { return true } return metadata.lyricsCount <= 0 case 'director': return !metadata.hasCover default: return true } } const getStageDescription = (key, metadata) => { const { hasLyrics, hasSongUrl, hasCover, hasPlaybackAsset, hasMusicDraft, isMusicGenerating, isPlaybackGenerating, isPlaybackDraftReady, } = metadata switch (key) { case 'songwriter': if (!hasLyrics) { return 'Commencer à créer des paroles pour votre chanson' } if (!hasSongUrl) { return 'Continuer la création de paroles' } return 'Modifier les paroles créées' case 'beatmaker': if (metadata.hasCover) { return 'Impossible de modifier la cover ou la production musicale' } if (!hasLyrics) { return 'Écrivez vos paroles pour débloquer la musique' } if (isMusicGenerating) { return 'Votre morceau est en cours de création' } if (hasSongUrl) { if (!hasCover) { return 'Créer ou modifier la cover de votre morceau' } return 'Modifier la cover ou la production musicale' } if (hasMusicDraft) { return 'Modifier la production musicale' } return 'Commencer la création de votre morceau' case 'director': if (!hasCover) { return 'Validez votre pochette pour débloquer le playback' } if (isPlaybackGenerating) { return 'Votre playback est en cours de préparation' } if (isPlaybackDraftReady) { return 'Votre playback IA est prêt à être validé' } if (hasPlaybackAsset) { return 'Télécharger ou publier votre playback' } return 'Commencer la création de votre playback' default: return '' } } const getStageLockedDescription = (key, metadata) => { switch (key) { case 'songwriter': return metadata.hasSongUrl ? 'Impossible de modifier les paroles' : undefined case 'beatmaker': if (metadata.hasCover) { return 'Impossible de modifier la cover ou la production musicale' } return metadata.lyricsCount > 0 ? undefined : 'Créez vos paroles pour débloquer le studio' case 'director': return undefined default: return undefined } } const getStageCompletionState = (key, metadata) => { switch (key) { case 'songwriter': return metadata.hasLyrics case 'beatmaker': return metadata.hasSongUrl || metadata.musicStatus === 'GENERATED' case 'director': return metadata.hasPlaybackAsset default: return false } } export const getCreationStageStates = (project) => { const metadata = getStageMetadata(project) return CREATION_STAGE_KEYS.map((key, index) => { const isLocked = getStageLockState(key, metadata) const description = getStageDescription(key, metadata) const lockedDescription = getStageLockedDescription(key, metadata) return { key, index, isLocked, isCompleted: getStageCompletionState(key, metadata), description: isLocked && lockedDescription ? lockedDescription : description, lockedDescription, } }) } export const getStageAction = (key, project) => { switch (key) { case 'songwriter': return { route: getLyricsCount(project) > 0 ? Routes.Lyrics : Routes.CreateLyricsWithAi, } case 'beatmaker': { if (!project) { return { route: Routes.ComposeSong } } const musicStatus = project?.musicStatus || null const songUrl = project?.songUrl || null const coverStatus = project?.coverStatus || null const cover = project?.cover || {} const hasCoverResult = !!cover?.result const hasFinalCover = !!project?.coverUrl if (musicStatus === 'GENERATING') { return { route: Routes.GeneratingSong } } if (!songUrl) { if (musicStatus === 'GENERATED') { return { route: Routes.SongReady } } return { route: Routes.ComposeSong } } if (coverStatus === 'GENERATING') { return { route: Routes.PouchReady } } if (!hasCoverResult) { return { route: Routes.ChooseCoverType } } return { route: hasFinalCover ? Routes.ChooseCoverType : Routes.PouchReady } } case 'director': if (project?.playbackUrl) { return { route: Routes.PlaybackDownload, params: { action: 'playback', project, }, } } return { route: Routes.Playback, params: project ? { project } : undefined, } default: return {} } } export const findFirstUnlockedStageIndex = (project) => { const stages = getCreationStageStates(project) if (!stages.length) { return 0 } const actionableIndex = stages.findIndex((stage) => !stage.isCompleted && !stage.isLocked) if (actionableIndex !== -1) { return actionableIndex } const firstIncomplete = stages.findIndex((stage) => !stage.isCompleted) if (firstIncomplete !== -1) { return firstIncomplete } const firstUnlocked = stages.findIndex((stage) => !stage.isLocked) if (firstUnlocked !== -1) { return firstUnlocked } return stages.length - 1 }