this is how the next step looks like
This commit is contained in:
@@ -9,7 +9,8 @@ import { Step5PageCount } from './step-5-page-count';
|
|||||||
import { Step6Animations } from './step-6-animations';
|
import { Step6Animations } from './step-6-animations';
|
||||||
import { Step7Illustrations } from './step-7-illustrations';
|
import { Step7Illustrations } from './step-7-illustrations';
|
||||||
import { Step8Branding } from './step-8-branding';
|
import { Step8Branding } from './step-8-branding';
|
||||||
import { Step9Results, calculateTotalHours } from './step-9-results';
|
import { Step9AdditionalFeatures } from './step-9-additional-features';
|
||||||
|
import { Step10Results, calculateTotalHours } from './step-10-results';
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
import { AnimatePresence, motion } from 'framer-motion';
|
import { AnimatePresence, motion } from 'framer-motion';
|
||||||
|
|
||||||
@@ -31,6 +32,7 @@ export type FormData = {
|
|||||||
animatedElements: boolean | null;
|
animatedElements: boolean | null;
|
||||||
illustrations: IllustrationSelection;
|
illustrations: IllustrationSelection;
|
||||||
branding: BrandingSelection;
|
branding: BrandingSelection;
|
||||||
|
additionalFeatures: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export function CostEstimatorForm() {
|
export function CostEstimatorForm() {
|
||||||
@@ -49,6 +51,7 @@ export function CostEstimatorForm() {
|
|||||||
is3dAnimated: null,
|
is3dAnimated: null,
|
||||||
},
|
},
|
||||||
branding: null,
|
branding: null,
|
||||||
|
additionalFeatures: [],
|
||||||
});
|
});
|
||||||
const [isPending, startTransition] = useTransition();
|
const [isPending, startTransition] = useTransition();
|
||||||
|
|
||||||
@@ -83,6 +86,7 @@ export function CostEstimatorForm() {
|
|||||||
is3dAnimated: null,
|
is3dAnimated: null,
|
||||||
},
|
},
|
||||||
branding: null,
|
branding: null,
|
||||||
|
additionalFeatures: [],
|
||||||
});
|
});
|
||||||
setCurrentStep(1);
|
setCurrentStep(1);
|
||||||
}
|
}
|
||||||
@@ -164,7 +168,16 @@ export function CostEstimatorForm() {
|
|||||||
);
|
);
|
||||||
case 9:
|
case 9:
|
||||||
return (
|
return (
|
||||||
<Step9Results
|
<Step9AdditionalFeatures
|
||||||
|
onNext={handleNextStep}
|
||||||
|
onBack={handlePrevStep}
|
||||||
|
onUpdateData={handleUpdateFormData}
|
||||||
|
formData={formData}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
case 10:
|
||||||
|
return (
|
||||||
|
<Step10Results
|
||||||
formData={formData}
|
formData={formData}
|
||||||
onReset={handleReset}
|
onReset={handleReset}
|
||||||
estimatedHours={estimatedHours}
|
estimatedHours={estimatedHours}
|
||||||
|
|||||||
+23
-4
@@ -1,12 +1,12 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import type { FormData, IllustrationSelection, BrandingSelection } from './cost-estimator-form';
|
import type { FormData } from './cost-estimator-form';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
|
||||||
import { PartyPopper, RefreshCw } from 'lucide-react';
|
import { PartyPopper, RefreshCw } from 'lucide-react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
type Step9Props = {
|
type Step10Props = {
|
||||||
formData: FormData;
|
formData: FormData;
|
||||||
onReset: () => void;
|
onReset: () => void;
|
||||||
estimatedHours: number;
|
estimatedHours: number;
|
||||||
@@ -15,6 +15,21 @@ type Step9Props = {
|
|||||||
const designHoursMap = { custom: 60, mockups: 30, existing: 0 };
|
const designHoursMap = { custom: 60, mockups: 30, existing: 0 };
|
||||||
const illustrationHoursMap = { '2d_static': 10, '2d_animated': 25, '3d_static': 20, '3d_animated': 40 };
|
const illustrationHoursMap = { '2d_static': 10, '2d_animated': 25, '3d_static': 20, '3d_animated': 40 };
|
||||||
const brandingHoursMap = { 'full-cycle': 70, 'brush-up': 30, 'logo-only': 20, 'none': 0 };
|
const brandingHoursMap = { 'full-cycle': 70, 'brush-up': 30, 'logo-only': 20, 'none': 0 };
|
||||||
|
const featuresHoursMap: Record<string, number> = {
|
||||||
|
'registration': 40,
|
||||||
|
'member-profiles': 30,
|
||||||
|
'admin-panel': 80,
|
||||||
|
'crm-integration': 60,
|
||||||
|
'dashboard': 50,
|
||||||
|
'blog': 25,
|
||||||
|
'event-scheduling': 40,
|
||||||
|
'reservations': 40,
|
||||||
|
'chat-live-chat': 60,
|
||||||
|
'image-video-galleries': 30,
|
||||||
|
'location-based': 35,
|
||||||
|
'live-streaming': 70,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export const calculateTotalHours = (formData: FormData): number => {
|
export const calculateTotalHours = (formData: FormData): number => {
|
||||||
const pageVal = formData.pageCount === 10 ? 50 : (formData.pageCount + 1) * 5 - 1;
|
const pageVal = formData.pageCount === 10 ? 50 : (formData.pageCount + 1) * 5 - 1;
|
||||||
@@ -35,12 +50,16 @@ export const calculateTotalHours = (formData: FormData): number => {
|
|||||||
|
|
||||||
const brandingH = formData.branding ? (brandingHoursMap[formData.branding] ?? 0) : 0;
|
const brandingH = formData.branding ? (brandingHoursMap[formData.branding] ?? 0) : 0;
|
||||||
|
|
||||||
return pageHours + stageHours + designHours + animationHours + illustrationTotalHours + brandingH;
|
const additionalFeaturesHours = formData.additionalFeatures.reduce((total, feature) => {
|
||||||
|
return total + (featuresHoursMap[feature] || 0);
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
return pageHours + stageHours + designHours + animationHours + illustrationTotalHours + brandingH + additionalFeaturesHours;
|
||||||
};
|
};
|
||||||
|
|
||||||
const HOURLY_RATE = 75; // Example hourly rate in USD
|
const HOURLY_RATE = 75; // Example hourly rate in USD
|
||||||
|
|
||||||
export function Step9Results({ formData, onReset, estimatedHours }: Step9Props) {
|
export function Step10Results({ formData, onReset, estimatedHours }: Step10Props) {
|
||||||
|
|
||||||
const estimatedCost = estimatedHours * HOURLY_RATE;
|
const estimatedCost = estimatedHours * HOURLY_RATE;
|
||||||
|
|
||||||
@@ -56,14 +56,15 @@ export function Step2ServiceType({ onNext, onBack, onUpdateData, formData }: Ste
|
|||||||
<div className="mt-8 flex w-full items-center justify-between">
|
<div className="mt-8 flex w-full items-center justify-between">
|
||||||
<Button variant="ghost" onClick={onBack}>Back</Button>
|
<Button variant="ghost" onClick={onBack}>Back</Button>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="w-12 h-2 bg-primary rounded-full" />
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
<div className="w-12 h-2 bg-primary rounded-full" />
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
<div className="w-12 h-2 bg-muted rounded-full" />
|
<div className="w-10 h-2 bg-muted rounded-full" />
|
||||||
<div className="w-12 h-2 bg-muted rounded-full" />
|
<div className="w-10 h-2 bg-muted rounded-full" />
|
||||||
<div className="w-12 h-2 bg-muted rounded-full" />
|
<div className="w-10 h-2 bg-muted rounded-full" />
|
||||||
<div className="w-12 h-2 bg-muted rounded-full" />
|
<div className="w-10 h-2 bg-muted rounded-full" />
|
||||||
<div className="w-12 h-2 bg-muted rounded-full" />
|
<div className="w-10 h-2 bg-muted rounded-full" />
|
||||||
<div className="w-12 h-2 bg-muted rounded-full" />
|
<div className="w-10 h-2 bg-muted rounded-full" />
|
||||||
|
<div className="w-10 h-2 bg-muted rounded-full" />
|
||||||
</div>
|
</div>
|
||||||
<Button onClick={onNext} disabled={!selectedService}>
|
<Button onClick={onNext} disabled={!selectedService}>
|
||||||
Next
|
Next
|
||||||
|
|||||||
@@ -77,14 +77,15 @@ export function Step3ProjectStage({ onNext, onBack, onUpdateData, formData }: St
|
|||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<Button variant="ghost" onClick={onBack}>Back</Button>
|
<Button variant="ghost" onClick={onBack}>Back</Button>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="w-12 h-2 bg-primary rounded-full" />
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
<div className="w-12 h-2 bg-primary rounded-full" />
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
<div className="w-12 h-2 bg-primary rounded-full" />
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
<div className="w-12 h-2 bg-muted rounded-full" />
|
<div className="w-10 h-2 bg-muted rounded-full" />
|
||||||
<div className="w-12 h-2 bg-muted rounded-full" />
|
<div className="w-10 h-2 bg-muted rounded-full" />
|
||||||
<div className="w-12 h-2 bg-muted rounded-full" />
|
<div className="w-10 h-2 bg-muted rounded-full" />
|
||||||
<div className="w-12 h-2 bg-muted rounded-full" />
|
<div className="w-10 h-2 bg-muted rounded-full" />
|
||||||
<div className="w-12 h-2 bg-muted rounded-full" />
|
<div className="w-10 h-2 bg-muted rounded-full" />
|
||||||
|
<div className="w-10 h-2 bg-muted rounded-full" />
|
||||||
</div>
|
</div>
|
||||||
<Button onClick={onNext}>
|
<Button onClick={onNext}>
|
||||||
Next
|
Next
|
||||||
|
|||||||
@@ -66,16 +66,17 @@ export function Step4DesignProcess({ onNext, onBack, onUpdateData, formData }: S
|
|||||||
<span className="font-headline text-lg font-bold">{estimatedHours} hours</span>
|
<span className="font-headline text-lg font-bold">{estimatedHours} hours</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<Button variant="ghost" onClick={onBack}>Go Back</Button>
|
<Button variant="ghost" onClick={onBack}>Back</Button>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="w-12 h-2 bg-primary rounded-full" />
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
<div className="w-12 h-2 bg-primary rounded-full" />
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
<div className="w-12 h-2 bg-primary rounded-full" />
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
<div className="w-12 h-2 bg-primary rounded-full" />
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
<div className="w-12 h-2 bg-muted rounded-full" />
|
<div className="w-10 h-2 bg-muted rounded-full" />
|
||||||
<div className="w-12 h-2 bg-muted rounded-full" />
|
<div className="w-10 h-2 bg-muted rounded-full" />
|
||||||
<div className="w-12 h-2 bg-muted rounded-full" />
|
<div className="w-10 h-2 bg-muted rounded-full" />
|
||||||
<div className="w-12 h-2 bg-muted rounded-full" />
|
<div className="w-10 h-2 bg-muted rounded-full" />
|
||||||
|
<div className="w-10 h-2 bg-muted rounded-full" />
|
||||||
</div>
|
</div>
|
||||||
<Button onClick={onNext} disabled={!selectedOption}>
|
<Button onClick={onNext} disabled={!selectedOption}>
|
||||||
Next
|
Next
|
||||||
|
|||||||
@@ -84,16 +84,17 @@ export function Step5PageCount({ onNext, onBack, onUpdateData, formData }: Step5
|
|||||||
<span className="font-headline text-lg font-bold">{estimatedHours}+ hours</span>
|
<span className="font-headline text-lg font-bold">{estimatedHours}+ hours</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<Button variant="ghost" onClick={onBack}>Go Back</Button>
|
<Button variant="ghost" onClick={onBack}>Back</Button>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="w-12 h-2 bg-primary rounded-full" />
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
<div className="w-12 h-2 bg-primary rounded-full" />
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
<div className="w-12 h-2 bg-primary rounded-full" />
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
<div className="w-12 h-2 bg-primary rounded-full" />
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
<div className="w-12 h-2 bg-primary rounded-full" />
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
<div className="w-12 h-2 bg-muted rounded-full" />
|
<div className="w-10 h-2 bg-muted rounded-full" />
|
||||||
<div className="w-12 h-2 bg-muted rounded-full" />
|
<div className="w-10 h-2 bg-muted rounded-full" />
|
||||||
<div className="w-12 h-2 bg-muted rounded-full" />
|
<div className="w-10 h-2 bg-muted rounded-full" />
|
||||||
|
<div className="w-10 h-2 bg-muted rounded-full" />
|
||||||
</div>
|
</div>
|
||||||
<Button onClick={onNext}>
|
<Button onClick={onNext}>
|
||||||
Next
|
Next
|
||||||
|
|||||||
@@ -70,16 +70,17 @@ export function Step6Animations({ onNext, onBack, onUpdateData, formData }: Step
|
|||||||
<span className="font-headline text-lg font-bold">{estimatedHours}+ hours</span>
|
<span className="font-headline text-lg font-bold">{estimatedHours}+ hours</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<Button variant="ghost" onClick={onBack}>Go Back</Button>
|
<Button variant="ghost" onClick={onBack}>Back</Button>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="w-12 h-2 bg-primary rounded-full" />
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
<div className="w-12 h-2 bg-primary rounded-full" />
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
<div className="w-12 h-2 bg-primary rounded-full" />
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
<div className="w-12 h-2 bg-primary rounded-full" />
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
<div className="w-12 h-2 bg-primary rounded-full" />
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
<div className="w-12 h-2 bg-primary rounded-full" />
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
<div className="w-12 h-2 bg-muted rounded-full" />
|
<div className="w-10 h-2 bg-muted rounded-full" />
|
||||||
<div className="w-12 h-2 bg-muted rounded-full" />
|
<div className="w-10 h-2 bg-muted rounded-full" />
|
||||||
|
<div className="w-10 h-2 bg-muted rounded-full" />
|
||||||
</div>
|
</div>
|
||||||
<Button onClick={onNext} disabled={selectedOption === null}>
|
<Button onClick={onNext} disabled={selectedOption === null}>
|
||||||
Next
|
Next
|
||||||
|
|||||||
@@ -171,16 +171,17 @@ export function Step7Illustrations({ onNext, onBack, onUpdateData, formData }: S
|
|||||||
<span className="font-headline text-lg font-bold">{estimatedHours}+ hours</span>
|
<span className="font-headline text-lg font-bold">{estimatedHours}+ hours</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<Button variant="ghost" onClick={onBack}>Go Back</Button>
|
<Button variant="ghost" onClick={onBack}>Back</Button>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="w-12 h-2 bg-primary rounded-full" />
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
<div className="w-12 h-2 bg-primary rounded-full" />
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
<div className="w-12 h-2 bg-primary rounded-full" />
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
<div className="w-12 h-2 bg-primary rounded-full" />
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
<div className="w-12 h-2 bg-primary rounded-full" />
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
<div className="w-12 h-2 bg-primary rounded-full" />
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
<div className="w-12 h-2 bg-primary rounded-full" />
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
<div className="w-12 h-2 bg-muted rounded-full" />
|
<div className="w-10 h-2 bg-muted rounded-full" />
|
||||||
|
<div className="w-10 h-2 bg-muted rounded-full" />
|
||||||
</div>
|
</div>
|
||||||
<Button onClick={onNext} disabled={isNextDisabled}>
|
<Button onClick={onNext} disabled={isNextDisabled}>
|
||||||
Next
|
Next
|
||||||
|
|||||||
@@ -91,19 +91,20 @@ export function Step8Branding({ onNext, onBack, onUpdateData, formData }: Step8P
|
|||||||
<span className="font-headline text-lg font-bold">{estimatedHours}+ hours</span>
|
<span className="font-headline text-lg font-bold">{estimatedHours}+ hours</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<Button variant="ghost" onClick={onBack}>Go Back</Button>
|
<Button variant="ghost" onClick={onBack}>Back</Button>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="w-12 h-2 bg-primary rounded-full" />
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
<div className="w-12 h-2 bg-primary rounded-full" />
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
<div className="w-12 h-2 bg-primary rounded-full" />
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
<div className="w-12 h-2 bg-primary rounded-full" />
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
<div className="w-12 h-2 bg-primary rounded-full" />
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
<div className="w-12 h-2 bg-primary rounded-full" />
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
<div className="w-12 h-2 bg-primary rounded-full" />
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
<div className="w-12 h-2 bg-primary rounded-full" />
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
|
<div className="w-10 h-2 bg-muted rounded-full" />
|
||||||
</div>
|
</div>
|
||||||
<Button onClick={onNext} disabled={selected === null}>
|
<Button onClick={onNext} disabled={selected === null}>
|
||||||
See Results
|
Next
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,136 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import type { FormData } from './cost-estimator-form';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Checkbox } from '@/components/ui/checkbox';
|
||||||
|
import { Label } from '@/components/ui/label';
|
||||||
|
import { Gauge, Info } from 'lucide-react';
|
||||||
|
import React, { useState, useMemo } from 'react';
|
||||||
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
||||||
|
|
||||||
|
type Step9Props = {
|
||||||
|
onNext: () => void;
|
||||||
|
onBack: () => void;
|
||||||
|
onUpdateData: (data: Partial<FormData>) => void;
|
||||||
|
formData: FormData;
|
||||||
|
};
|
||||||
|
|
||||||
|
const features = [
|
||||||
|
{ id: 'registration', label: 'Registration & authorization', hours: 40, description: 'User accounts, login, registration, and password recovery.' },
|
||||||
|
{ id: 'member-profiles', label: 'Member profiles', hours: 30, description: 'User profiles with customizable information and settings.' },
|
||||||
|
{ id: 'admin-panel', label: 'Admin panel', hours: 80, description: 'A comprehensive backend interface to manage the platform.' },
|
||||||
|
{ id: 'crm-integration', label: 'CRM Integration', hours: 60, description: 'Integration with a Customer Relationship Management system.' },
|
||||||
|
{ id: 'dashboard', label: 'Dashboard', hours: 50, description: 'A dashboard for users to view key information and metrics.' },
|
||||||
|
{ id: 'blog', label: 'Blog', hours: 25, description: 'A blog or news section for content publication.' },
|
||||||
|
{ id: 'event-scheduling', label: 'Event scheduling', hours: 40, description: 'Functionality for scheduling and managing events.' },
|
||||||
|
{ id: 'reservations', label: 'Reservations', hours: 40, description: 'A system for making and managing reservations.' },
|
||||||
|
{ id: 'chat-live-chat', label: 'Chat/Live chat', hours: 60, description: 'Real-time chat functionality for users or support.' },
|
||||||
|
{ id: 'image-video-galleries', label: 'Image/Video galleries', hours: 30, description: 'Galleries to display images and videos.' },
|
||||||
|
{ id: 'location-based', label: 'Location-based or navigational elements', hours: 35, description: 'Features that use location data, like maps or navigation.' },
|
||||||
|
{ id: 'live-streaming', label: 'Live-streaming', hours: 70, description: 'Functionality for live video streaming.' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const designHoursMap = { custom: 60, mockups: 30, existing: 0 };
|
||||||
|
const illustrationHoursMap = { '2d_static': 10, '2d_animated': 25, '3d_static': 20, '3d_animated': 40 };
|
||||||
|
const brandingHoursMap = { 'full-cycle': 70, 'brush-up': 30, 'logo-only': 20, 'none': 0 };
|
||||||
|
|
||||||
|
const calculateHours = (formData: FormData, selectedFeatures: string[]): number => {
|
||||||
|
const pageVal = formData.pageCount === 10 ? 50 : (formData.pageCount + 1) * 5 - 1;
|
||||||
|
const pageHours = pageVal * 6;
|
||||||
|
const stageHours = Math.round((formData.projectStage / 100) * 50);
|
||||||
|
const designHours = formData.designProcess ? designHoursMap[formData.designProcess] : 0;
|
||||||
|
const animationHours = formData.animatedElements ? 30 : 0;
|
||||||
|
|
||||||
|
let illustrationTotalHours = 0;
|
||||||
|
if (formData.illustrations.has2d) {
|
||||||
|
if (formData.illustrations.is2dAnimated === 'static') illustrationTotalHours += illustrationHoursMap['2d_static'];
|
||||||
|
if (formData.illustrations.is2dAnimated === 'animated') illustrationTotalHours += illustrationHoursMap['2d_animated'];
|
||||||
|
}
|
||||||
|
if (formData.illustrations.has3d) {
|
||||||
|
if (formData.illustrations.is3dAnimated === 'static') illustrationTotalHours += illustrationHoursMap['3d_static'];
|
||||||
|
if (formData.illustrations.is3dAnimated === 'animated') illustrationTotalHours += illustrationHoursMap['3d_animated'];
|
||||||
|
}
|
||||||
|
|
||||||
|
const brandingH = formData.branding ? (brandingHoursMap[formData.branding] ?? 0) : 0;
|
||||||
|
|
||||||
|
const additionalFeaturesHours = selectedFeatures.reduce((total, featureId) => {
|
||||||
|
const feature = features.find(f => f.id === featureId);
|
||||||
|
return total + (feature ? feature.hours : 0);
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
return pageHours + stageHours + designHours + animationHours + illustrationTotalHours + brandingH + additionalFeaturesHours;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export function Step9AdditionalFeatures({ onNext, onBack, onUpdateData, formData }: Step9Props) {
|
||||||
|
const [selectedFeatures, setSelectedFeatures] = useState<string[]>(formData.additionalFeatures);
|
||||||
|
|
||||||
|
const handleSelect = (featureId: string) => {
|
||||||
|
const newSelection = selectedFeatures.includes(featureId)
|
||||||
|
? selectedFeatures.filter((id) => id !== featureId)
|
||||||
|
: [...selectedFeatures, featureId];
|
||||||
|
setSelectedFeatures(newSelection);
|
||||||
|
onUpdateData({ additionalFeatures: newSelection });
|
||||||
|
};
|
||||||
|
|
||||||
|
const estimatedHours = useMemo(() => {
|
||||||
|
return calculateHours(formData, selectedFeatures);
|
||||||
|
}, [selectedFeatures, formData]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-center">
|
||||||
|
<h2 className="font-headline text-3xl font-bold tracking-tight text-center">Would you like us to develop any additional features?</h2>
|
||||||
|
<div className="mt-8 grid w-full grid-cols-1 gap-4 md:grid-cols-2">
|
||||||
|
{features.map((feature) => (
|
||||||
|
<div
|
||||||
|
key={feature.id}
|
||||||
|
onClick={() => handleSelect(feature.id)}
|
||||||
|
className={`flex items-center space-x-3 rounded-lg border p-4 cursor-pointer transition-all ${selectedFeatures.includes(feature.id) ? 'border-primary bg-primary/5' : 'border-border'}`}
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
id={feature.id}
|
||||||
|
checked={selectedFeatures.includes(feature.id)}
|
||||||
|
onCheckedChange={() => handleSelect(feature.id)}
|
||||||
|
/>
|
||||||
|
<Label htmlFor={feature.id} className="text-base font-medium flex-1 cursor-pointer">{feature.label}</Label>
|
||||||
|
<TooltipProvider>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<button type="button" className="text-muted-foreground hover:text-foreground">
|
||||||
|
<Info className="h-4 w-4" />
|
||||||
|
</button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>{feature.description}</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
|
</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}>Back</Button>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
|
<div className="w-10 h-2 bg-primary rounded-full" />
|
||||||
|
</div>
|
||||||
|
<Button onClick={onNext}>
|
||||||
|
See Results
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user