190 lines
7.7 KiB
TypeScript
190 lines
7.7 KiB
TypeScript
|
|
"use client";
|
||
|
|
|
||
|
|
import type { FormData, IllustrationSelection } from './cost-estimator-form';
|
||
|
|
import { Button } from '@/components/ui/button';
|
||
|
|
import { Checkbox } from '@/components/ui/checkbox';
|
||
|
|
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
|
||
|
|
import { Label } from '@/components/ui/label';
|
||
|
|
import { Gauge } from 'lucide-react';
|
||
|
|
import React, { useState, useMemo, useEffect } from 'react';
|
||
|
|
|
||
|
|
type Step7Props = {
|
||
|
|
onNext: () => void;
|
||
|
|
onBack: () => void;
|
||
|
|
onUpdateData: (data: Partial<FormData>) => void;
|
||
|
|
formData: FormData;
|
||
|
|
};
|
||
|
|
|
||
|
|
const designHoursMap = {
|
||
|
|
custom: 60,
|
||
|
|
mockups: 30,
|
||
|
|
existing: 0,
|
||
|
|
};
|
||
|
|
|
||
|
|
const illustrationHours = {
|
||
|
|
'2d_static': 40,
|
||
|
|
'2d_animated': 80,
|
||
|
|
'3d_static': 100,
|
||
|
|
'3d_animated': 200,
|
||
|
|
}
|
||
|
|
|
||
|
|
const calculateHours = (
|
||
|
|
pageCount: number,
|
||
|
|
projectStage: number,
|
||
|
|
designProcess: 'custom' | 'mockups' | 'existing' | null,
|
||
|
|
animatedElements: boolean | null,
|
||
|
|
illustrations: IllustrationSelection
|
||
|
|
): number => {
|
||
|
|
const pageVal = pageCount === 10 ? 50 : (pageCount + 1) * 5 - 1;
|
||
|
|
const pageHours = pageVal * 6;
|
||
|
|
const stageHours = Math.round((projectStage / 100) * 50);
|
||
|
|
const designHours = designProcess ? designHoursMap[designProcess] : 0;
|
||
|
|
const animationHours = animatedElements ? 30 : 0;
|
||
|
|
|
||
|
|
let illustrationTotalHours = 0;
|
||
|
|
if (illustrations.has2d) {
|
||
|
|
if (illustrations.is2dAnimated === 'static') illustrationTotalHours += illustrationHours['2d_static'];
|
||
|
|
if (illustrations.is2dAnimated === 'animated') illustrationTotalHours += illustrationHours['2d_animated'];
|
||
|
|
}
|
||
|
|
if (illustrations.has3d) {
|
||
|
|
if (illustrations.is3dAnimated === 'static') illustrationTotalHours += illustrationHours['3d_static'];
|
||
|
|
if (illustrations.is3dAnimated === 'animated') illustrationTotalHours += illustrationHours['3d_animated'];
|
||
|
|
}
|
||
|
|
|
||
|
|
return pageHours + stageHours + designHours + animationHours + illustrationTotalHours;
|
||
|
|
};
|
||
|
|
|
||
|
|
export function Step7Illustrations({ onNext, onBack, onUpdateData, formData }: Step7Props) {
|
||
|
|
const [selections, setSelections] = useState<IllustrationSelection>(formData.illustrations);
|
||
|
|
const [noIllustrations, setNoIllustrations] = useState(false);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
const no2d = !selections.has2d || selections.is2dAnimated === null;
|
||
|
|
const no3d = !selections.has3d || selections.is3dAnimated === null;
|
||
|
|
setNoIllustrations(no2d && no3d);
|
||
|
|
}, [selections]);
|
||
|
|
|
||
|
|
const handleUpdate = (newSelections: Partial<IllustrationSelection>) => {
|
||
|
|
const updated = { ...selections, ...newSelections };
|
||
|
|
setSelections(updated);
|
||
|
|
onUpdateData({ illustrations: updated });
|
||
|
|
};
|
||
|
|
|
||
|
|
const handleNoIllustrationsChange = (checked: boolean) => {
|
||
|
|
setNoIllustrations(checked);
|
||
|
|
if(checked) {
|
||
|
|
const resetSelections = {
|
||
|
|
has2d: false,
|
||
|
|
is2dAnimated: null,
|
||
|
|
has3d: false,
|
||
|
|
is3dAnimated: null,
|
||
|
|
};
|
||
|
|
setSelections(resetSelections);
|
||
|
|
onUpdateData({ illustrations: resetSelections });
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
const handle2dCheck = (checked: boolean) => {
|
||
|
|
handleUpdate({ has2d: checked, is2dAnimated: checked ? 'static' : null });
|
||
|
|
if (!checked && !selections.has3d) setNoIllustrations(true);
|
||
|
|
};
|
||
|
|
|
||
|
|
const handle3dCheck = (checked: boolean) => {
|
||
|
|
handleUpdate({ has3d: checked, is3dAnimated: checked ? 'static' : null });
|
||
|
|
if (!checked && !selections.has2d) setNoIllustrations(true);
|
||
|
|
};
|
||
|
|
|
||
|
|
const estimatedHours = useMemo(() => {
|
||
|
|
return calculateHours(
|
||
|
|
formData.pageCount,
|
||
|
|
formData.projectStage,
|
||
|
|
formData.designProcess,
|
||
|
|
formData.animatedElements,
|
||
|
|
selections
|
||
|
|
);
|
||
|
|
}, [selections, formData]);
|
||
|
|
|
||
|
|
const isNextDisabled = (selections.has2d && !selections.is2dAnimated) || (selections.has3d && !selections.is3dAnimated)
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="flex flex-col items-start">
|
||
|
|
<h2 className="font-headline text-3xl font-bold tracking-tight text-center w-full">Would you like us to create illustrations tailored to your project?</h2>
|
||
|
|
<div className="mt-8 w-full space-y-4">
|
||
|
|
|
||
|
|
<div className="space-y-4 rounded-lg border p-4">
|
||
|
|
<div className="flex items-center space-x-2">
|
||
|
|
<Checkbox id="2d-illustrations" checked={selections.has2d} onCheckedChange={handle2dCheck} />
|
||
|
|
<Label htmlFor="2d-illustrations" className="text-lg">Yes, I need eye-catching 2D illustrations</Label>
|
||
|
|
</div>
|
||
|
|
{selections.has2d && (
|
||
|
|
<RadioGroup
|
||
|
|
value={selections.is2dAnimated || undefined}
|
||
|
|
onValueChange={(value: 'static' | 'animated') => handleUpdate({ is2dAnimated: value })}
|
||
|
|
className="ml-6 space-y-2"
|
||
|
|
>
|
||
|
|
<div className="flex items-center space-x-2">
|
||
|
|
<RadioGroupItem value="static" id="2d-static" />
|
||
|
|
<Label htmlFor="2d-static">Make them static</Label>
|
||
|
|
</div>
|
||
|
|
<div className="flex items-center space-x-2">
|
||
|
|
<RadioGroupItem value="animated" id="2d-animated" />
|
||
|
|
<Label htmlFor="2d-animated">Make them animated</Label>
|
||
|
|
</div>
|
||
|
|
</RadioGroup>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="space-y-4 rounded-lg border p-4">
|
||
|
|
<div className="flex items-center space-x-2">
|
||
|
|
<Checkbox id="3d-illustrations" checked={selections.has3d} onCheckedChange={handle3dCheck} />
|
||
|
|
<Label htmlFor="3d-illustrations" className="text-lg">Yes, let's add some gorgeous 3D illustrations</Label>
|
||
|
|
</div>
|
||
|
|
{selections.has3d && (
|
||
|
|
<RadioGroup
|
||
|
|
value={selections.is3dAnimated || undefined}
|
||
|
|
onValueChange={(value: 'static' | 'animated') => handleUpdate({ is3dAnimated: value })}
|
||
|
|
className="ml-6 space-y-2"
|
||
|
|
>
|
||
|
|
<div className="flex items-center space-x-2">
|
||
|
|
<RadioGroupItem value="static" id="3d-static" />
|
||
|
|
<Label htmlFor="3d-static">Make them static</Label>
|
||
|
|
</div>
|
||
|
|
<div className="flex items-center space-x-2">
|
||
|
|
<RadioGroupItem value="animated" id="3d-animated" />
|
||
|
|
<Label htmlFor="3d-animated">Make them animated</Label>
|
||
|
|
</div>
|
||
|
|
</RadioGroup>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="flex items-center space-x-2 p-4">
|
||
|
|
<Checkbox id="no-illustrations" checked={noIllustrations} onCheckedChange={handleNoIllustrationsChange} />
|
||
|
|
<Label htmlFor="no-illustrations" className="text-lg">No, I don't need illustrations</Label>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
<div className="mt-16 flex w-full items-center justify-between">
|
||
|
|
<div className="flex items-center gap-2">
|
||
|
|
<Gauge className="h-6 w-6 text-primary" />
|
||
|
|
<span className="font-headline text-lg font-bold">{estimatedHours}+ hours</span>
|
||
|
|
</div>
|
||
|
|
<div className="flex items-center gap-4">
|
||
|
|
<Button variant="ghost" onClick={onBack}>Go Back</Button>
|
||
|
|
<div className="flex items-center gap-2">
|
||
|
|
<div className="w-12 h-2 bg-primary rounded-full" />
|
||
|
|
<div className="w-12 h-2 bg-primary rounded-full" />
|
||
|
|
<div className="w-12 h-2 bg-primary rounded-full" />
|
||
|
|
<div className="w-12 h-2 bg-primary rounded-full" />
|
||
|
|
<div className="w-12 h-2 bg-primary rounded-full" />
|
||
|
|
<div className="w-12 h-2 bg-primary rounded-full" />
|
||
|
|
<div className="w-12 h-2 bg-primary rounded-full" />
|
||
|
|
</div>
|
||
|
|
<Button onClick={onNext} disabled={isNextDisabled}>
|
||
|
|
Next
|
||
|
|
</Button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|