This commit is contained in:
@@ -2,7 +2,9 @@
|
||||
|
||||
import * as React from 'react';
|
||||
import { FormState } from '../types';
|
||||
import { Zap, AlertCircle, Minus, Plus } from 'lucide-react';
|
||||
import { Zap, AlertCircle, Minus, Plus, Settings2, BarChart3 } from 'lucide-react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { Reveal } from '../../Reveal';
|
||||
|
||||
interface ContentStepProps {
|
||||
state: FormState;
|
||||
@@ -10,74 +12,167 @@ interface ContentStepProps {
|
||||
}
|
||||
|
||||
export function ContentStep({ state, updateState }: ContentStepProps) {
|
||||
return (
|
||||
<div className="space-y-12">
|
||||
<div className="flex items-center justify-between p-10 bg-white border border-slate-100 rounded-[3rem]">
|
||||
<div className="max-w-[70%]">
|
||||
<h4 className="text-2xl font-bold text-slate-900">Inhalte selbst verwalten (CMS)</h4>
|
||||
<p className="text-lg text-slate-500 mt-2">Möchten Sie Datensätze (z.B. Blogartikel, Produkte) selbst über eine einfache Oberfläche pflegen?</p>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => updateState({ cmsSetup: !state.cmsSetup })}
|
||||
className={`w-20 h-11 rounded-full transition-colors relative focus:outline-none ${state.cmsSetup ? 'bg-slate-900' : 'bg-slate-200'}`}
|
||||
>
|
||||
<div className={`absolute top-1.5 left-1.5 w-8 h-8 bg-white rounded-full transition-transform ${state.cmsSetup ? 'translate-x-9' : ''}`} />
|
||||
</button>
|
||||
</div>
|
||||
const toggleDontKnow = (id: string) => {
|
||||
const current = state.dontKnows || [];
|
||||
if (current.includes(id)) {
|
||||
updateState({ dontKnows: current.filter(i => i !== id) });
|
||||
} else {
|
||||
updateState({ dontKnows: [...current, id] });
|
||||
}
|
||||
};
|
||||
|
||||
<div className="p-10 bg-slate-50 rounded-[3rem] border border-slate-100 space-y-6">
|
||||
<p className="text-lg font-bold text-slate-900">Wie oft ändern sich Ihre Inhalte?</p>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
{[
|
||||
{ id: 'low', label: 'Selten', desc: 'Wenige Male im Jahr.' },
|
||||
{ id: 'medium', label: 'Regelmäßig', desc: 'Monatliche Updates.' },
|
||||
{ id: 'high', label: 'Häufig', desc: 'Wöchentlich oder täglich.' },
|
||||
].map(opt => (
|
||||
<button
|
||||
key={opt.id}
|
||||
return (
|
||||
<div className="space-y-16">
|
||||
<Reveal width="100%" delay={0.1}>
|
||||
<div className="flex flex-col md:flex-row items-center justify-between p-10 bg-white border border-slate-100 rounded-[3rem] shadow-sm gap-8">
|
||||
<div className="max-w-2xl space-y-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-12 h-12 bg-slate-50 rounded-2xl flex items-center justify-center text-slate-900 shadow-inner">
|
||||
<Settings2 size={24} />
|
||||
</div>
|
||||
<h4 className="text-2xl font-bold text-slate-900">Inhalte selbst verwalten (CMS)</h4>
|
||||
</div>
|
||||
<p className="text-lg text-slate-500 leading-relaxed">Möchten Sie Datensätze (z.B. Blogartikel, Produkte) selbst über eine einfache Oberfläche pflegen?</p>
|
||||
</div>
|
||||
<div className="flex flex-col items-center md:items-end gap-6">
|
||||
<motion.button
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
type="button"
|
||||
onClick={() => updateState({ expectedAdjustments: opt.id })}
|
||||
className={`p-6 rounded-2xl border-2 text-left transition-all focus:outline-none ${
|
||||
state.expectedAdjustments === opt.id ? 'border-slate-900 bg-slate-900 text-white' : 'border-slate-200 bg-white hover:border-slate-400'
|
||||
onClick={() => toggleDontKnow('cms')}
|
||||
className={`px-6 py-3 rounded-full text-sm font-bold transition-all shadow-sm ${
|
||||
state.dontKnows?.includes('cms') ? 'bg-slate-900 text-white' : 'bg-slate-100 text-slate-500 hover:bg-slate-200'
|
||||
}`}
|
||||
>
|
||||
<p className="font-bold text-lg">{opt.label}</p>
|
||||
<p className={`text-sm mt-1 ${state.expectedAdjustments === opt.id ? 'text-slate-200' : 'text-slate-500'}`}>{opt.desc}</p>
|
||||
Ich weiß es nicht
|
||||
</motion.button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => updateState({ cmsSetup: !state.cmsSetup })}
|
||||
className={`w-24 h-12 rounded-full transition-all duration-500 relative focus:outline-none shadow-inner ${state.cmsSetup ? 'bg-slate-900' : 'bg-slate-200'}`}
|
||||
>
|
||||
<motion.div
|
||||
animate={{ x: state.cmsSetup ? 48 : 0 }}
|
||||
transition={{ type: "spring", stiffness: 500, damping: 30 }}
|
||||
className="absolute top-1.5 left-1.5 w-9 h-9 bg-white rounded-full shadow-lg"
|
||||
/>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 mt-8">
|
||||
<div className="p-8 bg-white rounded-[2rem] border border-slate-100 space-y-3">
|
||||
<div className="flex items-center gap-3 text-slate-900 font-bold text-sm uppercase tracking-wider">
|
||||
<Zap size={18} /> Vorteil CMS
|
||||
</div>
|
||||
<p className="text-sm text-slate-500 leading-relaxed">
|
||||
Volle Kontrolle über Ihre Inhalte und keine laufenden Kosten für kleine Textänderungen oder neue Blog-Beiträge.
|
||||
</p>
|
||||
</div>
|
||||
<div className="p-8 bg-white rounded-[2rem] border border-slate-100 space-y-3">
|
||||
<div className="flex items-center gap-3 text-slate-900 font-bold text-sm uppercase tracking-wider">
|
||||
<AlertCircle size={18} /> Fokus Design
|
||||
</div>
|
||||
<p className="text-sm text-slate-500 leading-relaxed">
|
||||
Ohne CMS bleibt die technische Komplexität geringer und das Design ist maximal geschützt vor ungewollten Änderungen.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Reveal>
|
||||
|
||||
<div className="flex flex-col gap-6 p-10 bg-white border border-slate-100 rounded-[3rem]">
|
||||
<div>
|
||||
<h4 className="text-2xl font-bold text-slate-900">Inhalte einpflegen</h4>
|
||||
<p className="text-lg text-slate-500 mt-2 leading-relaxed">Für wie viele Datensätze soll ich die initiale Befüllung übernehmen?</p>
|
||||
<Reveal width="100%" delay={0.2}>
|
||||
<div className="p-10 bg-slate-50 rounded-[3rem] border border-slate-100 space-y-10">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-12 h-12 bg-white rounded-2xl flex items-center justify-center text-slate-900 shadow-sm">
|
||||
<BarChart3 size={24} />
|
||||
</div>
|
||||
<p className="text-xl font-bold text-slate-900">Wie oft ändern sich Ihre Inhalte?</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
{[
|
||||
{ id: 'low', label: 'Selten', desc: 'Wenige Male im Jahr.' },
|
||||
{ id: 'medium', label: 'Regelmäßig', desc: 'Monatliche Updates.' },
|
||||
{ id: 'high', label: 'Häufig', desc: 'Wöchentlich oder täglich.' },
|
||||
].map((opt, index) => (
|
||||
<motion.button
|
||||
key={opt.id}
|
||||
whileHover={{ y: -5, boxShadow: '0 10px 15px -3px rgb(0 0 0 / 0.1)' }}
|
||||
whileTap={{ scale: 0.98 }}
|
||||
type="button"
|
||||
onClick={() => updateState({ expectedAdjustments: opt.id })}
|
||||
className={`p-6 rounded-[2rem] border-2 text-left transition-all duration-300 focus:outline-none ${
|
||||
state.expectedAdjustments === opt.id ? 'border-slate-900 bg-slate-900 text-white shadow-xl' : 'border-slate-200 bg-white hover:border-slate-400'
|
||||
}`}
|
||||
>
|
||||
<p className={`font-bold text-lg ${state.expectedAdjustments === opt.id ? 'text-white' : 'text-slate-900'}`}>{opt.label}</p>
|
||||
<p className={`text-sm mt-2 leading-relaxed ${state.expectedAdjustments === opt.id ? 'text-slate-200' : 'text-slate-500'}`}>{opt.desc}</p>
|
||||
</motion.button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<AnimatePresence>
|
||||
{state.expectedAdjustments === 'high' && !state.cmsSetup && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, height: 0, y: 20 }}
|
||||
animate={{ opacity: 1, height: 'auto', y: 0 }}
|
||||
exit={{ opacity: 0, height: 0, y: 20 }}
|
||||
className="p-8 bg-amber-50 rounded-[2.5rem] border border-amber-100 flex gap-6 items-start shadow-sm"
|
||||
>
|
||||
<div className="w-12 h-12 bg-white rounded-xl flex items-center justify-center text-amber-600 shadow-sm shrink-0">
|
||||
<AlertCircle size={24} />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<p className="text-amber-900 text-xl font-bold">Empfehlung: CMS nutzen</p>
|
||||
<p className="text-amber-800 text-base leading-relaxed max-w-3xl">
|
||||
Bei täglichen oder wöchentlichen Änderungen sparen Sie mit einem CMS langfristig viel Geld, da Sie keine externen Entwickler für Inhalts-Updates benötigen.
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mt-8">
|
||||
<div className="p-8 bg-white rounded-[2.5rem] border border-slate-100 space-y-4 shadow-sm">
|
||||
<div className="flex items-center gap-3 text-slate-900 font-bold text-sm uppercase tracking-[0.2em]">
|
||||
<Zap size={18} /> Vorteil CMS
|
||||
</div>
|
||||
<p className="text-base text-slate-500 leading-relaxed">
|
||||
Volle Kontrolle über Ihre Inhalte und keine laufenden Kosten für kleine Textänderungen oder neue Blog-Beiträge.
|
||||
</p>
|
||||
</div>
|
||||
<div className="p-8 bg-white rounded-[2.5rem] border border-slate-100 space-y-4 shadow-sm">
|
||||
<div className="flex items-center gap-3 text-slate-900 font-bold text-sm uppercase tracking-[0.2em]">
|
||||
<AlertCircle size={18} /> Fokus Design
|
||||
</div>
|
||||
<p className="text-base text-slate-500 leading-relaxed">
|
||||
Ohne CMS bleibt die technische Komplexität geringer und das Design ist maximal geschützt vor ungewollten Änderungen.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-12 mt-2">
|
||||
<button type="button" onClick={() => updateState({ newDatasets: Math.max(0, state.newDatasets - 1) })} className="w-16 h-16 rounded-full bg-slate-50 border border-slate-100 flex items-center justify-center hover:border-slate-900 transition-colors focus:outline-none"><Minus size={28} /></button>
|
||||
<span className="text-5xl font-bold w-16 text-center">{state.newDatasets}</span>
|
||||
<button type="button" onClick={() => updateState({ newDatasets: state.newDatasets + 1 })} className="w-16 h-16 rounded-full bg-slate-50 border border-slate-100 flex items-center justify-center hover:border-slate-900 transition-colors focus:outline-none"><Plus size={28} /></button>
|
||||
</Reveal>
|
||||
|
||||
<Reveal width="100%" delay={0.3}>
|
||||
<div className="flex flex-col gap-8 p-10 bg-white border border-slate-100 rounded-[3rem] shadow-sm">
|
||||
<div className="space-y-2">
|
||||
<h4 className="text-2xl font-bold text-slate-900">Inhalte einpflegen</h4>
|
||||
<p className="text-lg text-slate-500 leading-relaxed">Für wie viele Datensätze soll ich die initiale Befüllung übernehmen?</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-12 py-2">
|
||||
<motion.button
|
||||
whileHover={{ scale: 1.1 }}
|
||||
whileTap={{ scale: 0.9 }}
|
||||
type="button"
|
||||
onClick={() => updateState({ newDatasets: Math.max(0, state.newDatasets - 1) })}
|
||||
className="w-16 h-16 rounded-full bg-slate-50 border border-slate-100 flex items-center justify-center hover:border-slate-900 transition-colors focus:outline-none shadow-sm"
|
||||
>
|
||||
<Minus size={28} />
|
||||
</motion.button>
|
||||
<AnimatePresence mode="wait">
|
||||
<motion.span
|
||||
key={state.newDatasets}
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -10 }}
|
||||
className="text-7xl font-bold w-20 text-center tabular-nums"
|
||||
>
|
||||
{state.newDatasets}
|
||||
</motion.span>
|
||||
</AnimatePresence>
|
||||
<motion.button
|
||||
whileHover={{ scale: 1.1 }}
|
||||
whileTap={{ scale: 0.9 }}
|
||||
type="button"
|
||||
onClick={() => updateState({ newDatasets: state.newDatasets + 1 })}
|
||||
className="w-16 h-16 rounded-full bg-slate-50 border border-slate-100 flex items-center justify-center hover:border-slate-900 transition-colors focus:outline-none shadow-sm"
|
||||
>
|
||||
<Plus size={28} />
|
||||
</motion.button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Reveal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user