FE_Dev / schema /proposal.ts
GitHub Actions
Deploy from GitHub Actions [dev] - 2025-10-31 07:28:50
68f7925
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<typeof sectionTypeSchema>;
// 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<typeof generateFvHtmlRequestSchema>;
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<typeof generateFvHtmlResponseSchema>;
// コンテンツ提案
// 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<typeof generateContentsHtmlRequestSchema>;
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<typeof generateContentsHtmlResponseSchema>;
// 変換後の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<typeof fvDataSchema>;
// 変換後の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<typeof contentSectionSchema>;
export type ContentSubItem = z.infer<typeof contentSubItemSchema>;
export type ContentField = z.infer<typeof contentFieldSchema>;
// スクリーンショット取得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<typeof screenshotRequestSchema>;
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<typeof screenshotResponseSchema>;
// 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<string, string>;
// proposalPredictionから該当する戦略・ニーズのデータを展開
prediction?: number;
// 該当するタブのThemeByMomentItemのみ
strategyXMomentItem?: ThemeByMomentItem;
// 戦略タイプの表示名
strategy: string;
// 方向性(ThemeByMomentStrategyの2つ目のキー = ニーズ)
direction?: string;
};
fv: FvData;
cn: ContentSection[];
}
export type ProposalTabsResponse = Record<string, ProposalTabData>;
// レギュレーションチェック用の生データ型定義
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;
}