File size: 7,212 Bytes
68f7925
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
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;
}