sushilideaclan01's picture
remove the variations option
4a56a0b
"use client";
import React, { useEffect } from "react";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/Card";
import { Select } from "@/components/ui/Select";
import { IMAGE_MODELS, getModelCost, formatCost } from "@/lib/constants/models";
import type { Niche } from "@/types/api";
import { InfoButton } from "@/components/ui/InfoButton";
const extensiveSchema = z.object({
niche: z.enum(["home_insurance", "glp1", "auto_insurance", "others"]),
custom_niche: z.string().optional().nullable(),
target_audience: z.string().optional().nullable(),
offer: z.string().optional().nullable(),
num_images: z.number().min(1).max(3),
num_strategies: z.number().min(1).max(10),
image_model: z.string().nullable().optional(),
}).refine(
(data) => {
if (data.niche === "others") {
return data.custom_niche && data.custom_niche.trim().length > 0;
}
return true;
},
{
message: "Please enter your custom niche",
path: ["custom_niche"],
}
);
type ExtensiveFormData = z.infer<typeof extensiveSchema>;
interface ExtensiveFormProps {
onSubmit: (data: {
niche: Niche;
custom_niche?: string | null;
target_audience?: string | null;
offer?: string | null;
num_images: number;
num_strategies: number;
image_model?: string | null;
}) => Promise<void>;
isLoading: boolean;
}
export const ExtensiveForm: React.FC<ExtensiveFormProps> = ({
onSubmit,
isLoading,
}) => {
const {
register,
handleSubmit,
formState: { errors },
watch,
setValue,
} = useForm<ExtensiveFormData>({
resolver: zodResolver(extensiveSchema),
defaultValues: {
niche: "home_insurance" as const, // first option; flow works for any niche
custom_niche: "",
target_audience: "",
offer: "",
num_images: 1,
num_strategies: 5,
image_model: null,
},
});
const numImages = 1;
const numStrategies = watch("num_strategies");
const selectedNiche = watch("niche");
const selectedModel = watch("image_model");
useEffect(() => {
setValue("num_images", numImages, { shouldDirty: false, shouldValidate: false });
}, [numImages, setValue]);
const onFormSubmit = handleSubmit(({ num_images, ...rest }) =>
onSubmit({ ...rest, num_images: num_images ?? 1 })
);
return (
<Card variant="glass">
<CardHeader>
<div className="flex items-center gap-2">
<CardTitle>Extensive Generation</CardTitle>
<InfoButton
title="Extensive Generation Flow"
content="This flow works for any niche (insurance, GLP-1, auto, or custom). It uses a 4-stage process:
1. RESEARCHER: Analyzes your inputs and researches psychology triggers, angles, and concepts that work best for your niche and audience.
2. CREATIVE DIRECTOR: Uses marketing knowledge and successful ad patterns to create multiple creative strategies. Each strategy includes visual direction, text placement, CTA, and copy ideas.
3. DESIGNER: Converts each creative strategy into detailed image generation prompts optimized for affiliate marketing (authentic, low-production style).
4. COPYWRITER: Writes compelling ad copy (title, body, description) that matches each strategy's emotional tone and psychology trigger.
You can generate multiple strategies (1-10). Each strategy includes one image and a full ad package for comprehensive testing."
position="bottom"
/>
</div>
<CardDescription>
Researcher → Creative Director → Designer → Copywriter flow
</CardDescription>
</CardHeader>
<CardContent>
<form onSubmit={onFormSubmit} className="space-y-6">
<input type="hidden" value={numImages} {...register("num_images", { valueAsNumber: true })} />
<Select
label="Niche"
options={[
{ value: "home_insurance", label: "Home Insurance" },
{ value: "glp1", label: "GLP-1" },
{ value: "auto_insurance", label: "Auto Insurance" },
{ value: "others", label: "Others (Custom)" },
]}
error={errors.niche?.message}
{...register("niche")}
/>
{selectedNiche === "others" && (
<div>
<label className="block text-sm font-semibold text-gray-700 mb-2">
Custom Niche <span className="text-red-500">*</span>
</label>
<input
type="text"
className="w-full px-4 py-3 rounded-xl border-2 border-gray-300 bg-white/80 backdrop-blur-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-250"
placeholder="e.g., Pet Insurance, Solar Panels, Fitness Supplements"
{...register("custom_niche")}
/>
{errors.custom_niche && (
<p className="text-red-500 text-xs mt-1">{errors.custom_niche.message}</p>
)}
</div>
)}
<div>
<label className="block text-sm font-semibold text-gray-700 mb-2">
Target Audience <span className="text-gray-400 font-normal">(Optional)</span>
</label>
<input
type="text"
className="w-full px-4 py-3 rounded-xl border-2 border-gray-300 bg-white/80 backdrop-blur-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-250"
placeholder="e.g., US people over 50+ age"
{...register("target_audience")}
/>
{errors.target_audience && (
<p className="text-red-500 text-xs mt-1">{errors.target_audience.message}</p>
)}
</div>
<div>
<label className="block text-sm font-semibold text-gray-700 mb-2">
Offer <span className="text-gray-400 font-normal">(Optional)</span>
</label>
<input
type="text"
className="w-full px-4 py-3 rounded-xl border-2 border-gray-300 bg-white/80 backdrop-blur-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-250"
placeholder="e.g., Free quote, Save $500/year, Limited-time offer"
{...register("offer")}
/>
{errors.offer && (
<p className="text-red-500 text-xs mt-1">{errors.offer.message}</p>
)}
</div>
<Select
label="Image Model"
options={IMAGE_MODELS.map(model => ({ value: model.value, label: model.label }))}
error={errors.image_model?.message}
{...register("image_model")}
/>
<div>
<label className="block text-sm font-semibold text-gray-700 mb-2">
Number of Strategies: <span className="text-blue-600 font-bold">{numStrategies}</span>
</label>
<input
type="range"
min="1"
max="10"
step="1"
className="w-full accent-blue-500"
{...register("num_strategies", { valueAsNumber: true })}
/>
<div className="flex justify-between text-xs text-gray-500 mt-1 font-medium">
<span>1</span>
<span>10</span>
</div>
<p className="text-xs text-gray-500 mt-1">
More strategies = more variety, but longer generation time
</p>
</div>
{/* Cost Estimator */}
<div className="bg-gradient-to-r from-green-50 to-emerald-50 border border-green-200 rounded-xl p-4">
<p className="text-sm font-semibold text-gray-800">
💰 <strong>Estimated Cost:</strong> {formatCost(getModelCost(selectedModel || "", numStrategies))}
</p>
<p className="text-xs text-gray-600 mt-1">
{numStrategies} total image{numStrategies > 1 ? 's' : ''} × {IMAGE_MODELS.find(m => m.value === (selectedModel || ""))?.label.split(' - ')[0] || "Default model"}
</p>
</div>
<button
type="submit"
disabled={isLoading}
className="w-full bg-gradient-to-r from-blue-500 to-cyan-500 text-white font-bold py-4 px-6 rounded-xl hover:from-blue-600 hover:to-cyan-600 transition-all duration-300 shadow-lg hover:shadow-xl disabled:opacity-50 disabled:cursor-not-allowed"
>
{isLoading ? "Generating..." : "Generate with Extensive"}
</button>
</form>
</CardContent>
</Card>
);
};