import type { ProposalCnGradioResponse } from '@/schema/gradio-proxy/proposal-cn'; import type { ProposalFvGradioResponse } from '@/schema/gradio-proxy/proposal-fv'; import { proposalFvItemSchema } from '@/schema/gradio-proxy/proposal-fv'; import type { ProposalIntentGradioResponse } from '@/schema/gradio-proxy/proposal-intent'; import type { ProposalPredictionGradioResponse } from '@/schema/gradio-proxy/proposal-prediction'; import type { ThemeByMomentItem, ThemeByMomentStrategy } from '@/schema/gradio-proxy/theme-by-moment'; import { z } from 'zod'; import { swotDataSchema } from './pox'; import { themeExtractionResultSchema } from './theme-extraction'; // セクション型の定義 export const sectionTypeSchema = z.enum([ '問題提起/共感', 'シミュレーション', '解決策/ベネフィット提示', '商品/サービスの特徴', '商品/サービスの詳細情報', 'SPオファー/限定特典', '料金/プラン', 'ご利用の流れ/ステップ', '成功事例/ユーザーボイス紹介', '活用シーン', 'FAQ/よくある質問', '競合比較', '権威訴求', 'メディア掲載情報', 'スタッフ・メンバー紹介', '店舗情報', 'News/更新情報', ]); export type SectionType = z.infer; // FV提案 export const generateFvHtmlRequestSchema = z .object({ tabName: z.string(), fvData: z .lazy(() => fvDataSchema) .optional() .nullable(), provider: z.enum(['openai', 'gemini', 'claude']).optional(), referenceUrl: z.string().optional(), screenshotUrl: z.string().optional(), dummyMode: z.boolean().optional(), swotData: z .lazy(() => swotDataSchema) .optional() .nullable(), userEmail: z.string().optional(), // ユーザーメールアドレス(エラーログ用) sourcePage: z.string().optional(), // リクエスト元のページ(APIキー選択用) forceFallbackMode: z.boolean().optional(), // 強制的にGPT-5文字起こし分析ベースフォールバックモードで動作 themeData: z.lazy(() => themeExtractionResultSchema).optional(), }) .superRefine((data, ctx) => { // ダミーモードでない場合のみfvDataを必須とする if (!data.dummyMode && !data.fvData) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'fvData is required when not in dummy mode', path: ['fvData'], }); } }); export type GenerateFvHtmlRequest = z.infer; export const generateFvHtmlResponseSchema = z.object({ html: z.string(), css: z.string().optional(), imageUrl: z.string().optional().nullable(), pipelineType: z.enum(['full', 'simple', 'text-only']).optional(), imagePrompt: z.string().optional(), }); export type GenerateFvHtmlResponse = z.infer; // コンテンツ提案 // contentSectionSchemaは後で定義されるため、遅延評価を使用 export const generateContentsHtmlRequestSchema = z.object({ tabName: z.string(), cnData: z.lazy(() => z.array(contentSectionSchema)), provider: z.enum(['openai', 'gemini', 'claude']).optional(), referenceUrl: z.string().optional(), screenshotUrl: z.string().optional(), dummyMode: z.boolean().optional(), userEmail: z.string().optional(), // ユーザーメールアドレス(エラーログ用) themeData: z.lazy(() => themeExtractionResultSchema).optional(), }); export type GenerateContentsHtmlRequest = z.infer; export const generateContentsHtmlResponseSchema = z.object({ html: z.string(), css: z.string().optional(), imageUrl: z.string().optional().nullable(), message: z.string().optional(), batchId: z.string().optional(), // 画像生成バッチID(通常モードのみ) imageCompleted: z.boolean(), // 画像生成完了フラグ }); export type GenerateContentsHtmlResponse = z.infer; // 変換後のFVタブデータ型(コンポーネントが期待する型) export const fvDataSchema = z.object({ strategy: z.string(), need: z.string(), data: z.object({ fv: proposalFvItemSchema, }), fvCreationIntent: z.string().optional(), }); export type FvData = z.infer; // 変換後のCNタブデータ型(コンポーネントが期待する型) // 動的なフィールドに対応 const contentFieldSchema = z.union([ z.object({ value: z.string() }), z.object({ 情報源: z.object({ 他社: z.string(), 自社: z.string(), }), value: z.string(), }), ]); const contentSubItemSchema = z.record(z.string(), contentFieldSchema); export const contentSectionSchema = z.object({ 大区分: sectionTypeSchema, 中区分: z.string(), 小区分json: z.array(contentSubItemSchema), 制作意図: z.string(), }); export type ContentSection = z.infer; export type ContentSubItem = z.infer; export type ContentField = z.infer; // スクリーンショット取得API export const screenshotRequestSchema = z.object({ url: z.string().url('有効なURLを入力してください'), width: z.number().optional().default(512), height: z.number().optional().default(768), }); export type ScreenshotRequest = z.infer; export const screenshotResponseSchema = z.object({ success: z.boolean(), screenshotBase64: z.string(), mimeType: z.string(), description: z.string().optional(), error: z.string().optional(), }); export type ScreenshotResponse = z.infer; // Proposal Tabs API Response Types(TypeScript型定義) export interface ProposalTabData { intent: { // proposalIntentから該当する戦略・ニーズのデータを展開 FV_intent?: string; CN_intent?: string; FV_suggest?: string; CN_suggest?: string; // fv_creation_intentを追加 fv_creation_intent?: string; // cn_creation_intentを追加 cn_creation_intent?: Record; // proposalPredictionから該当する戦略・ニーズのデータを展開 prediction?: number; // 該当するタブのThemeByMomentItemのみ strategyXMomentItem?: ThemeByMomentItem; // 戦略タイプの表示名 strategy: string; // 方向性(ThemeByMomentStrategyの2つ目のキー = ニーズ) direction?: string; }; fv: FvData; cn: ContentSection[]; } export type ProposalTabsResponse = Record; // レギュレーションチェック用の生データ型定義 export interface ProposalRawData { proposal_fv: ProposalFvGradioResponse; proposal_cn: ProposalCnGradioResponse; proposal_intent: ProposalIntentGradioResponse; proposal_prediction?: ProposalPredictionGradioResponse; // レギュレーション時に必要 strategy_x_moment?: ThemeByMomentStrategy; // 訴求テーマとstoryG表示用 } // useProposalTabsの拡張返り値型定義 export interface ExtendedProposalTabsResponse { data: ProposalTabsResponse; rawData?: ProposalRawData; }