185 lines
8.8 KiB
TypeScript
185 lines
8.8 KiB
TypeScript
'use client';
|
|
|
|
import * as React from 'react';
|
|
import { FormState } from '../types';
|
|
import { Zap, AlertCircle, Minus, Plus, Settings2, BarChart3 } from 'lucide-react';
|
|
import { motion, AnimatePresence } from 'framer-motion';
|
|
import { Reveal } from '../../Reveal';
|
|
|
|
interface ContentStepProps {
|
|
state: FormState;
|
|
updateState: (updates: Partial<FormState>) => void;
|
|
}
|
|
|
|
export function ContentStep({ state, updateState }: ContentStepProps) {
|
|
const toggleDontKnow = (id: string) => {
|
|
const current = state.dontKnows || [];
|
|
if (current.includes(id)) {
|
|
updateState({ dontKnows: current.filter(i => i !== id) });
|
|
} else {
|
|
updateState({ dontKnows: [...current, 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] 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-black">
|
|
<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">
|
|
Ein CMS (Content Management System) ermöglicht es Ihnen, Texte, Bilder und Blogartikel selbst zu ändern, ohne programmieren zu müssen.
|
|
Ideal, wenn Sie Ihre Website aktuell halten möchten.
|
|
</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={() => toggleDontKnow('cms')}
|
|
className={`px-6 py-3 rounded-full text-sm font-bold transition-all ${
|
|
state.dontKnows?.includes('cms') ? 'bg-slate-900 text-white' : 'bg-slate-100 text-slate-500 hover:bg-slate-200'
|
|
}`}
|
|
>
|
|
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 ${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"
|
|
/>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</Reveal>
|
|
|
|
<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-black">
|
|
<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 }}
|
|
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' : '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"
|
|
>
|
|
<div className="w-12 h-12 bg-white rounded-xl flex items-center justify-center text-amber-600 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">
|
|
<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">
|
|
<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>
|
|
</Reveal>
|
|
|
|
<Reveal width="100%" delay={0.3}>
|
|
<div className="flex flex-col gap-8 p-10 bg-white border border-slate-100 rounded-[3rem]">
|
|
<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">
|
|
Wer kümmert sich um die erste Befüllung? Wenn ich das übernehmen soll, geben Sie hier die Anzahl der Datensätze (z.B. fertige Blogartikel oder Produkte) an.
|
|
Ansonsten übergeben wir Ihnen eine leere, aber einsatzbereite Struktur.
|
|
</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"
|
|
>
|
|
<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"
|
|
>
|
|
<Plus size={28} />
|
|
</motion.button>
|
|
</div>
|
|
</div>
|
|
</Reveal>
|
|
</div>
|
|
);
|
|
}
|