This commit is contained in:
@@ -6,7 +6,7 @@ import { ContactForm } from '../../src/components/ContactForm';
|
||||
|
||||
export default function ContactPage() {
|
||||
return (
|
||||
<div className="flex flex-col gap-12 py-12 md:py-24 overflow-hidden">
|
||||
<div className="flex flex-col gap-12 py-12 md:py-24">
|
||||
<PageHeader
|
||||
title={<>Projekt <br /><span className="text-slate-200">konfigurieren.</span></>}
|
||||
description="Nutzen Sie den Konfigurator für eine erste Einschätzung oder schreiben Sie mir direkt eine Email."
|
||||
@@ -15,9 +15,7 @@ export default function ContactPage() {
|
||||
/>
|
||||
|
||||
<Section number="01" title="Konfigurator" containerVariant="wide" className="!py-12">
|
||||
<Reveal delay={0.2}>
|
||||
<ContactForm />
|
||||
</Reveal>
|
||||
<ContactForm />
|
||||
</Section>
|
||||
|
||||
<Section number="02" title="Direkt" className="!py-12">
|
||||
|
||||
36
src/components/ContactForm/components/Input.tsx
Normal file
36
src/components/ContactForm/components/Input.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
import { LucideIcon } from 'lucide-react';
|
||||
|
||||
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement | HTMLTextAreaElement> {
|
||||
label?: string;
|
||||
icon?: LucideIcon;
|
||||
isTextArea?: boolean;
|
||||
rows?: number;
|
||||
}
|
||||
|
||||
export function Input({ label, icon: Icon, isTextArea, className = '', ...props }: InputProps) {
|
||||
const InputComponent = isTextArea ? 'textarea' : 'input';
|
||||
|
||||
return (
|
||||
<div className="space-y-4 w-full">
|
||||
{label && (
|
||||
<label className="block text-sm font-bold uppercase tracking-widest text-slate-400 ml-2">
|
||||
{label}
|
||||
</label>
|
||||
)}
|
||||
<div className="relative group">
|
||||
{Icon && (
|
||||
<div className="absolute left-6 top-[1.8rem] -translate-y-1/2 text-black transition-colors">
|
||||
<Icon size={24} />
|
||||
</div>
|
||||
)}
|
||||
<InputComponent
|
||||
{...(props as any)}
|
||||
className={`w-full p-8 ${Icon ? 'pl-16' : 'px-10'} bg-white border border-slate-100 rounded-[2.5rem] focus:outline-none focus:border-slate-900 transition-all duration-500 text-xl focus:shadow-2xl ${isTextArea ? 'resize-none' : ''} ${className}`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -39,39 +39,56 @@ export function PriceCalculation({
|
||||
const totalFunctions = state.functions.length + state.otherFunctions.length + (state.otherFunctionsCount || 0);
|
||||
const totalApis = state.apiSystems.length + state.otherTech.length + (state.otherTechCount || 0);
|
||||
|
||||
const [pdfLoading, setPdfLoading] = React.useState(false);
|
||||
const languagesCount = state.languagesList.length || 1;
|
||||
|
||||
return (
|
||||
<div className="lg:col-span-4 lg:sticky lg:top-24">
|
||||
<div className="p-10 bg-slate-50 border border-slate-100 rounded-[3rem] space-y-10">
|
||||
<div className="space-y-3"><div className="flex items-center gap-3"><ConceptPrice className="w-8 h-8" /><h3 className="text-2xl font-bold text-slate-900">Kalkulation</h3></div><p className="text-sm text-slate-500 leading-relaxed">Unverbindliche Schätzung.</p></div>
|
||||
<div className="lg:col-span-4 lg:sticky lg:top-32 self-start z-30">
|
||||
<div className="bg-slate-50 border border-slate-100 rounded-[3rem] p-6 space-y-6">
|
||||
<div className="space-y-6">
|
||||
{state.projectType === 'website' ? (
|
||||
<>
|
||||
<div className="flex justify-between items-center py-4 border-b border-slate-200"><span className="text-slate-600 font-medium">Basis Website</span><span className="font-bold text-lg text-slate-900">{PRICING.BASE_WEBSITE.toLocaleString()} €</span></div>
|
||||
<div className="space-y-4 max-h-[400px] overflow-y-auto pr-2 hide-scrollbar">
|
||||
<div className="space-y-4 overflow-y-auto pr-2 hide-scrollbar max-h-[120px]">
|
||||
{totalPages > 0 && (<div className="flex justify-between items-center text-sm"><span className="text-slate-500">{totalPages}x Seite</span><span className="font-medium text-slate-900">{(totalPages * PRICING.PAGE).toLocaleString()} €</span></div>)}
|
||||
{totalFeatures > 0 && (<div className="flex justify-between items-center text-sm"><span className="text-slate-500">{totalFeatures}x System-Modul</span><span className="font-medium text-slate-900">{(totalFeatures * PRICING.FEATURE).toLocaleString()} €</span></div>)}
|
||||
{totalFunctions > 0 && (<div className="flex justify-between items-center text-sm"><span className="text-slate-500">{totalFunctions}x Logik-Funktion</span><span className="font-medium text-slate-900">{(totalFunctions * PRICING.FUNCTION).toLocaleString()} €</span></div>)}
|
||||
{state.visualStaging > 0 && (<div className="flex justify-between items-center text-sm"><span className="text-slate-500">{state.visualStaging}x Visuelle Inszenierung</span><span className="font-medium text-slate-900">{(state.visualStaging * PRICING.VISUAL_STAGING).toLocaleString()} €</span></div>)}
|
||||
{state.complexInteractions > 0 && (<div className="flex justify-between items-center text-sm"><span className="text-slate-500">{state.complexInteractions}x Komplexe Interaktion</span><span className="font-medium text-slate-900">{(state.complexInteractions * PRICING.COMPLEX_INTERACTION).toLocaleString()} €</span></div>)}
|
||||
{totalApis > 0 && (<div className="flex justify-between items-center text-sm"><span className="text-slate-500">{totalApis}x API Sync</span><span className="font-medium text-slate-900">{(totalApis * PRICING.API_INTEGRATION).toLocaleString()} €</span></div>)}
|
||||
{state.cmsSetup && (<div className="flex justify-between items-center text-sm"><span className="text-slate-500">CMS Setup & Anbindung</span><span className="font-medium text-slate-900">{(PRICING.CMS_SETUP + totalFeatures * PRICING.CMS_CONNECTION_PER_FEATURE).toLocaleString()} €</span></div>)}
|
||||
{state.newDatasets > 0 && (<div className="flex justify-between items-center text-sm"><span className="text-slate-500">{state.newDatasets}x Inhalte einpflegen</span><span className="font-medium text-slate-900">{(state.newDatasets * PRICING.NEW_DATASET).toLocaleString()} €</span></div>)}
|
||||
{state.languagesCount > 1 && (<div className="flex justify-between items-center text-sm text-slate-900 font-bold pt-2 border-t border-slate-100"><span className="text-slate-500">Mehrsprachigkeit ({state.languagesCount}x)</span><span>+{(totalPrice - (totalPrice / (1 + (state.languagesCount - 1) * 0.2))).toLocaleString()} €</span></div>)}
|
||||
{languagesCount > 1 && (<div className="flex justify-between items-center text-sm text-slate-900 font-bold pt-2 border-t border-slate-100"><span className="text-slate-500">Mehrsprachigkeit ({languagesCount}x)</span><span>+{(totalPrice - (totalPrice / (1 + (languagesCount - 1) * 0.2))).toLocaleString()} €</span></div>)}
|
||||
</div>
|
||||
<div className="pt-4 space-y-2">
|
||||
<div className="flex justify-between items-end">
|
||||
<span className="text-lg font-bold text-slate-900">Gesamt</span>
|
||||
<div className="text-right">
|
||||
<div className="text-2xl font-bold tracking-tighter text-slate-900">
|
||||
<AnimatedNumber value={totalPrice} /> €
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="pt-4 border-t border-slate-200 space-y-4">
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-slate-600 font-medium text-sm">Betrieb & Hosting</span>
|
||||
<span className="text-base font-bold text-slate-900">{monthlyPrice.toLocaleString()} € / Monat</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="pt-8 space-y-2"><div className="flex justify-between items-end"><span className="text-2xl font-bold text-slate-900">Gesamt</span><div className="text-right"><div className="text-4xl font-bold tracking-tighter text-slate-900"><AnimatedNumber value={totalPrice} /> €</div><p className="text-[10px] text-slate-400 mt-1 uppercase tracking-widest font-bold">Einmalig / Netto</p></div></div></div>
|
||||
<div className="pt-8 border-t border-slate-200 space-y-4"><div className="flex justify-between items-center"><span className="text-slate-600 font-medium">Betrieb & Hosting</span><span className="font-bold text-lg text-slate-900">{monthlyPrice.toLocaleString()} € / Monat</span></div><div className="p-6 bg-white rounded-[2rem] text-xs text-slate-500 flex gap-4 leading-relaxed border border-slate-100"><Info size={18} className="shrink-0 text-slate-300" /><p>Inklusive Hosting, Sicherheitsupdates, Backups und Analytics-Reports.</p></div></div>
|
||||
|
||||
<div className="pt-6 space-y-4">
|
||||
<div className="pt-4 space-y-4">
|
||||
{isClient && (
|
||||
<PDFDownloadLink
|
||||
document={<EstimationPDF state={state} totalPrice={totalPrice} monthlyPrice={monthlyPrice} totalPagesCount={totalPagesCount} pricing={PRICING} qrCodeData={qrCodeData} />}
|
||||
fileName={`kalkulation-${state.name.toLowerCase().replace(/\s+/g, '-') || 'projekt'}.pdf`}
|
||||
className="w-full flex items-center justify-center gap-3 px-8 py-4 rounded-full border border-slate-200 text-slate-900 font-bold text-sm uppercase tracking-widest hover:bg-white hover:border-slate-900 transition-all focus:outline-none overflow-hidden relative rounded-full"
|
||||
className="w-full flex items-center justify-center gap-3 px-8 py-4 rounded-full border border-slate-200 text-slate-900 font-bold text-sm uppercase tracking-widest hover:bg-white hover:border-slate-900 transition-all focus:outline-none overflow-hidden relative"
|
||||
onClick={() => {
|
||||
setPdfLoading(true);
|
||||
setTimeout(() => setPdfLoading(false), 2000);
|
||||
}}
|
||||
>
|
||||
{({ loading }) => (
|
||||
<div className="flex items-center gap-3">
|
||||
<Download size={18} />
|
||||
<span>{loading ? 'PDF wird erstellt...' : 'Als PDF speichern'}</span>
|
||||
<span>{loading || pdfLoading ? 'PDF wird erstellt...' : 'Als PDF speichern'}</span>
|
||||
</div>
|
||||
)}
|
||||
</PDFDownloadLink>
|
||||
@@ -81,33 +98,33 @@ export function PriceCalculation({
|
||||
<button
|
||||
type="button"
|
||||
onClick={onShare}
|
||||
className="w-full flex items-center justify-center gap-3 px-8 py-4 rounded-full bg-slate-900 text-white font-bold text-sm uppercase tracking-widest hover:bg-slate-800 transition-all shadow-xl shadow-slate-200 focus:outline-none"
|
||||
className="w-full flex items-center justify-center gap-3 rounded-full bg-slate-900 text-white font-bold text-sm uppercase tracking-widest hover:bg-slate-800 transition-all focus:outline-none px-6 py-3"
|
||||
>
|
||||
<Share2 size={18} />
|
||||
<span>Konfiguration teilen</span>
|
||||
<span>Teilen</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="space-y-6">
|
||||
<div className="py-12 text-center space-y-6">
|
||||
<div className="w-20 h-20 bg-white rounded-full flex items-center justify-center mx-auto shadow-sm">
|
||||
<ConceptAutomation className="w-12 h-12" />
|
||||
<div className="py-6 text-center space-y-4">
|
||||
<div className="w-16 h-16 bg-white rounded-full flex items-center justify-center mx-auto border border-slate-100">
|
||||
<ConceptAutomation className="w-10 h-10 text-black" />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<p className="text-slate-600 text-sm leading-relaxed">Web Apps und Individual-Software werden nach tatsächlichem Aufwand abgerechnet.</p>
|
||||
<p className="text-3xl font-bold text-slate-900">{PRICING.APP_HOURLY} € <span className="text-lg text-slate-400 font-normal">/ Std.</span></p>
|
||||
<div className="space-y-1">
|
||||
<p className="text-slate-600 text-xs leading-relaxed">Web Apps werden nach Aufwand abgerechnet.</p>
|
||||
<p className="text-2xl font-bold text-slate-900">{PRICING.APP_HOURLY} € <span className="text-base text-slate-400 font-normal">/ Std.</span></p>
|
||||
</div>
|
||||
</div>
|
||||
{onShare && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onShare}
|
||||
className="w-full flex items-center justify-center gap-3 px-8 py-4 rounded-full bg-slate-900 text-white font-bold text-sm uppercase tracking-widest hover:bg-slate-800 transition-all shadow-xl shadow-slate-200 focus:outline-none"
|
||||
className="w-full flex items-center justify-center gap-3 px-6 py-3 rounded-full bg-slate-900 text-white font-bold text-sm uppercase tracking-widest hover:bg-slate-800 transition-all focus:outline-none"
|
||||
>
|
||||
<Share2 size={18} />
|
||||
<span>Konfiguration teilen</span>
|
||||
<span>Teilen</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -4,8 +4,8 @@ import * as React from 'react';
|
||||
import { FormState } from '../types';
|
||||
import { Checkbox } from '../components/Checkbox';
|
||||
import { RepeatableList } from '../components/RepeatableList';
|
||||
import { Minus, Plus, Share2, ListPlus } from 'lucide-react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { Share2, ListPlus } from 'lucide-react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Reveal } from '../../Reveal';
|
||||
|
||||
interface ApiStepProps {
|
||||
@@ -32,7 +32,7 @@ export function ApiStep({ state, updateState, toggleItem }: ApiStepProps) {
|
||||
<div className="space-y-8">
|
||||
<div className="flex justify-between items-center">
|
||||
<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-sm">
|
||||
<div className="w-12 h-12 bg-slate-50 rounded-2xl flex items-center justify-center text-black">
|
||||
<Share2 size={24} />
|
||||
</div>
|
||||
<h4 className="text-2xl font-bold text-slate-900">
|
||||
@@ -44,7 +44,7 @@ export function ApiStep({ state, updateState, toggleItem }: ApiStepProps) {
|
||||
whileTap={{ scale: 0.95 }}
|
||||
type="button"
|
||||
onClick={() => toggleDontKnow('api')}
|
||||
className={`px-6 py-3 rounded-full text-sm font-bold transition-all shadow-sm ${
|
||||
className={`px-6 py-3 rounded-full text-sm font-bold transition-all ${
|
||||
state.dontKnows?.includes('api') ? 'bg-slate-900 text-white' : 'bg-slate-100 text-slate-500 hover:bg-slate-200'
|
||||
}`}
|
||||
>
|
||||
@@ -86,7 +86,7 @@ export function ApiStep({ state, updateState, toggleItem }: ApiStepProps) {
|
||||
<div className="space-y-12">
|
||||
<div className="space-y-8">
|
||||
<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-sm">
|
||||
<div className="w-12 h-12 bg-slate-50 rounded-2xl flex items-center justify-center text-black">
|
||||
<ListPlus size={24} />
|
||||
</div>
|
||||
<h4 className="text-2xl font-bold text-slate-900">Weitere Systeme oder eigene APIs?</h4>
|
||||
@@ -98,51 +98,6 @@ export function ApiStep({ state, updateState, toggleItem }: ApiStepProps) {
|
||||
placeholder="z.B. Microsoft Graph, Google Search Console, Custom REST API..."
|
||||
/>
|
||||
</div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 0.4 }}
|
||||
className="p-10 bg-white border border-slate-100 rounded-[3rem] space-y-6 shadow-sm hover:shadow-xl transition-all duration-500"
|
||||
>
|
||||
<div className="flex flex-col md:flex-row justify-between items-center gap-8">
|
||||
<div>
|
||||
<h4 className="text-xl font-bold text-slate-900">Anzahl weiterer Schnittstellen</h4>
|
||||
<p className="text-base text-slate-500 mt-1">Falls Sie weitere Integrationen planen, diese aber noch nicht benennen können.</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-8">
|
||||
<motion.button
|
||||
whileHover={{ scale: 1.1 }}
|
||||
whileTap={{ scale: 0.9 }}
|
||||
type="button"
|
||||
onClick={() => updateState({ otherTechCount: Math.max(0, state.otherTechCount - 1) })}
|
||||
className="w-14 h-14 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={24} />
|
||||
</motion.button>
|
||||
<AnimatePresence mode="wait">
|
||||
<motion.span
|
||||
key={state.otherTechCount}
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -10 }}
|
||||
className="text-5xl font-bold w-12 text-center"
|
||||
>
|
||||
{state.otherTechCount}
|
||||
</motion.span>
|
||||
</AnimatePresence>
|
||||
<motion.button
|
||||
whileHover={{ scale: 1.1 }}
|
||||
whileTap={{ scale: 0.9 }}
|
||||
type="button"
|
||||
onClick={() => updateState({ otherTechCount: state.otherTechCount + 1 })}
|
||||
className="w-14 h-14 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={24} />
|
||||
</motion.button>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</Reveal>
|
||||
</div>
|
||||
|
||||
@@ -31,7 +31,7 @@ export function AssetsStep({ state, updateState, toggleItem }: AssetsStepProps)
|
||||
<div className="space-y-8">
|
||||
<div className="flex justify-between items-center">
|
||||
<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-sm">
|
||||
<div className="w-12 h-12 bg-slate-50 rounded-2xl flex items-center justify-center text-black">
|
||||
<Briefcase size={24} />
|
||||
</div>
|
||||
<h4 className="text-2xl font-bold text-slate-900">Vorhandene Assets</h4>
|
||||
@@ -41,7 +41,7 @@ export function AssetsStep({ state, updateState, toggleItem }: AssetsStepProps)
|
||||
whileTap={{ scale: 0.95 }}
|
||||
type="button"
|
||||
onClick={() => toggleDontKnow('assets')}
|
||||
className={`px-6 py-3 rounded-full text-sm font-bold transition-all shadow-sm ${
|
||||
className={`px-6 py-3 rounded-full text-sm font-bold transition-all ${
|
||||
state.dontKnows?.includes('assets') ? 'bg-slate-900 text-white' : 'bg-slate-100 text-slate-500 hover:bg-slate-200'
|
||||
}`}
|
||||
>
|
||||
@@ -71,7 +71,7 @@ export function AssetsStep({ state, updateState, toggleItem }: AssetsStepProps)
|
||||
<div className="space-y-12">
|
||||
<div className="space-y-8">
|
||||
<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-sm">
|
||||
<div className="w-12 h-12 bg-slate-50 rounded-2xl flex items-center justify-center text-black">
|
||||
<ListPlus size={24} />
|
||||
</div>
|
||||
<h4 className="text-2xl font-bold text-slate-900">Weitere vorhandene Unterlagen?</h4>
|
||||
@@ -88,7 +88,7 @@ export function AssetsStep({ state, updateState, toggleItem }: AssetsStepProps)
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 0.4 }}
|
||||
className="p-10 bg-white border border-slate-100 rounded-[3rem] space-y-6 shadow-sm hover:shadow-xl transition-all duration-500"
|
||||
className="p-10 bg-white border border-slate-100 rounded-[3rem] space-y-6 hover:shadow-xl transition-all duration-500"
|
||||
>
|
||||
<div className="flex flex-col md:flex-row justify-between items-center gap-8">
|
||||
<div>
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Checkbox } from '../components/Checkbox';
|
||||
import { RepeatableList } from '../components/RepeatableList';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { Minus, Plus, FileText, ListPlus } from 'lucide-react';
|
||||
import { Input } from '../components/Input';
|
||||
|
||||
interface BaseStepProps {
|
||||
state: FormState;
|
||||
@@ -30,20 +31,18 @@ export function BaseStep({ state, updateState, toggleItem }: BaseStepProps) {
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
className="space-y-8"
|
||||
>
|
||||
<h4 className="text-2xl font-bold text-slate-900">Thema der Website</h4>
|
||||
<input
|
||||
type="text"
|
||||
<Input
|
||||
label="Thema der Website"
|
||||
placeholder="z.B. Portfolio für Architektur, Onlineshop für Bio-Tee..."
|
||||
value={state.websiteTopic}
|
||||
onChange={(e) => updateState({ websiteTopic: e.target.value })}
|
||||
className="w-full p-10 bg-white border border-slate-100 rounded-[3rem] focus:outline-none focus:border-slate-900 transition-all duration-500 text-xl shadow-sm focus:shadow-2xl"
|
||||
/>
|
||||
</motion.div>
|
||||
|
||||
<div className="space-y-8">
|
||||
<div className="flex justify-between items-center">
|
||||
<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-sm">
|
||||
<div className="w-12 h-12 bg-slate-50 rounded-2xl flex items-center justify-center text-black">
|
||||
<FileText size={24} />
|
||||
</div>
|
||||
<h4 className="text-2xl font-bold text-slate-900">Die Seiten</h4>
|
||||
@@ -53,7 +52,7 @@ export function BaseStep({ state, updateState, toggleItem }: BaseStepProps) {
|
||||
whileTap={{ scale: 0.95 }}
|
||||
type="button"
|
||||
onClick={() => toggleDontKnow('pages')}
|
||||
className={`px-6 py-3 rounded-full text-sm font-bold transition-all shadow-sm ${
|
||||
className={`px-6 py-3 rounded-full text-sm font-bold transition-all ${
|
||||
state.dontKnows?.includes('pages') ? 'bg-slate-900 text-white' : 'bg-slate-100 text-slate-500 hover:bg-slate-200'
|
||||
}`}
|
||||
>
|
||||
@@ -88,7 +87,7 @@ export function BaseStep({ state, updateState, toggleItem }: BaseStepProps) {
|
||||
<div className="space-y-12">
|
||||
<div className="space-y-8">
|
||||
<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-sm">
|
||||
<div className="w-12 h-12 bg-slate-50 rounded-2xl flex items-center justify-center text-black">
|
||||
<ListPlus size={24} />
|
||||
</div>
|
||||
<h4 className="text-2xl font-bold text-slate-900">Weitere individuelle Seiten?</h4>
|
||||
@@ -105,7 +104,7 @@ export function BaseStep({ state, updateState, toggleItem }: BaseStepProps) {
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 0.4 }}
|
||||
className="p-10 bg-white border border-slate-100 rounded-[3rem] space-y-6 shadow-sm hover:shadow-xl transition-all duration-500"
|
||||
className="p-10 bg-white border border-slate-100 rounded-[3rem] space-y-6 hover:shadow-xl transition-all duration-500"
|
||||
>
|
||||
<div className="flex flex-col md:flex-row justify-between items-center gap-8">
|
||||
<div>
|
||||
|
||||
@@ -6,6 +6,7 @@ import { EMPLOYEE_OPTIONS } from '../constants';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Building2, Users } from 'lucide-react';
|
||||
import { Reveal } from '../../Reveal';
|
||||
import { Input } from '../components/Input';
|
||||
|
||||
interface CompanyStepProps {
|
||||
state: FormState;
|
||||
@@ -18,28 +19,24 @@ export function CompanyStep({ state, updateState }: CompanyStepProps) {
|
||||
<Reveal width="100%" delay={0.1}>
|
||||
<div className="space-y-8">
|
||||
<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-sm">
|
||||
<div className="w-12 h-12 bg-slate-50 rounded-2xl flex items-center justify-center text-black">
|
||||
<Building2 size={24} />
|
||||
</div>
|
||||
<h4 className="text-2xl font-bold text-slate-900">Unternehmen</h4>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<label className="block text-sm font-bold uppercase tracking-widest text-slate-400 ml-2">Name des Unternehmens</label>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="z.B. Muster GmbH"
|
||||
value={state.companyName}
|
||||
onChange={(e) => updateState({ companyName: e.target.value })}
|
||||
className="w-full p-8 bg-white border border-slate-100 rounded-[3rem] focus:outline-none focus:border-slate-900 transition-all duration-500 text-xl shadow-sm focus:shadow-2xl"
|
||||
/>
|
||||
</div>
|
||||
<Input
|
||||
label="Name des Unternehmens"
|
||||
placeholder="z.B. Muster GmbH"
|
||||
value={state.companyName}
|
||||
onChange={(e) => updateState({ companyName: e.target.value })}
|
||||
/>
|
||||
</div>
|
||||
</Reveal>
|
||||
|
||||
<Reveal width="100%" delay={0.2}>
|
||||
<div className="space-y-8">
|
||||
<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-sm">
|
||||
<div className="w-12 h-12 bg-slate-50 rounded-2xl flex items-center justify-center text-black">
|
||||
<Users size={24} />
|
||||
</div>
|
||||
<h4 className="text-2xl font-bold text-slate-900">Mitarbeiteranzahl</h4>
|
||||
@@ -48,12 +45,12 @@ export function CompanyStep({ state, updateState }: CompanyStepProps) {
|
||||
{EMPLOYEE_OPTIONS.map((option, index) => (
|
||||
<motion.button
|
||||
key={option.id}
|
||||
whileHover={{ y: -5, boxShadow: '0 10px 15px -3px rgb(0 0 0 / 0.1)' }}
|
||||
whileHover={{ y: -5 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
type="button"
|
||||
onClick={() => updateState({ employeeCount: option.id })}
|
||||
className={`p-6 rounded-[2rem] border-2 transition-all duration-300 font-bold text-lg ${
|
||||
state.employeeCount === option.id ? 'border-slate-900 bg-slate-900 text-white shadow-lg' : 'border-slate-100 bg-white hover:border-slate-300 text-slate-600'
|
||||
state.employeeCount === option.id ? 'border-slate-900 bg-slate-900 text-white' : 'border-slate-100 bg-white hover:border-slate-300 text-slate-600'
|
||||
}`}
|
||||
>
|
||||
{option.label}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { FormState } from '../types';
|
||||
import { FileText, Upload, X, User, Mail, Briefcase, MessageSquare } from 'lucide-react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { Reveal } from '../../Reveal';
|
||||
import { Input } from '../components/Input';
|
||||
|
||||
interface ContactStepProps {
|
||||
state: FormState;
|
||||
@@ -16,78 +17,49 @@ export function ContactStep({ state, updateState }: ContactStepProps) {
|
||||
<div className="space-y-12">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
<Reveal width="100%" delay={0.1}>
|
||||
<div className="space-y-4">
|
||||
<label className="block text-sm font-bold uppercase tracking-widest text-slate-400 ml-2">Ihr Name</label>
|
||||
<div className="relative group">
|
||||
<div className="absolute left-6 top-1/2 -translate-y-1/2 text-slate-300 group-focus-within:text-slate-900 transition-colors">
|
||||
<User size={24} />
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Max Mustermann"
|
||||
required
|
||||
value={state.name}
|
||||
onChange={(e) => updateState({ name: e.target.value })}
|
||||
className="w-full p-8 pl-16 bg-white border border-slate-100 rounded-[2.5rem] focus:outline-none focus:border-slate-900 transition-all duration-500 text-xl shadow-sm focus:shadow-2xl"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Input
|
||||
label="Ihr Name"
|
||||
icon={User}
|
||||
placeholder="Max Mustermann"
|
||||
required
|
||||
value={state.name}
|
||||
onChange={(e) => updateState({ name: e.target.value })}
|
||||
/>
|
||||
</Reveal>
|
||||
|
||||
<Reveal width="100%" delay={0.1}>
|
||||
<div className="space-y-4">
|
||||
<label className="block text-sm font-bold uppercase tracking-widest text-slate-400 ml-2">Ihre Email</label>
|
||||
<div className="relative group">
|
||||
<div className="absolute left-6 top-1/2 -translate-y-1/2 text-slate-300 group-focus-within:text-slate-900 transition-colors">
|
||||
<Mail size={24} />
|
||||
</div>
|
||||
<input
|
||||
type="email"
|
||||
placeholder="max@beispiel.de"
|
||||
required
|
||||
value={state.email}
|
||||
onChange={(e) => updateState({ email: e.target.value })}
|
||||
className="w-full p-8 pl-16 bg-white border border-slate-100 rounded-[2.5rem] focus:outline-none focus:border-slate-900 transition-all duration-500 text-xl shadow-sm focus:shadow-2xl"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Input
|
||||
label="Ihre Email"
|
||||
icon={Mail}
|
||||
type="email"
|
||||
placeholder="max@beispiel.de"
|
||||
required
|
||||
value={state.email}
|
||||
onChange={(e) => updateState({ email: e.target.value })}
|
||||
/>
|
||||
</Reveal>
|
||||
</div>
|
||||
|
||||
<Reveal width="100%" delay={0.2}>
|
||||
<div className="space-y-4">
|
||||
<label className="block text-sm font-bold uppercase tracking-widest text-slate-400 ml-2">Ihre Rolle</label>
|
||||
<div className="relative group">
|
||||
<div className="absolute left-6 top-1/2 -translate-y-1/2 text-slate-300 group-focus-within:text-slate-900 transition-colors">
|
||||
<Briefcase size={24} />
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="z.B. CEO, Marketing Manager..."
|
||||
value={state.role}
|
||||
onChange={(e) => updateState({ role: e.target.value })}
|
||||
className="w-full p-8 pl-16 bg-white border border-slate-100 rounded-[2.5rem] focus:outline-none focus:border-slate-900 transition-all duration-500 text-xl shadow-sm focus:shadow-2xl"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Input
|
||||
label="Ihre Rolle"
|
||||
icon={Briefcase}
|
||||
placeholder="z.B. CEO, Marketing Manager..."
|
||||
value={state.role}
|
||||
onChange={(e) => updateState({ role: e.target.value })}
|
||||
/>
|
||||
</Reveal>
|
||||
|
||||
<Reveal width="100%" delay={0.3}>
|
||||
<div className="space-y-4">
|
||||
<label className="block text-sm font-bold uppercase tracking-widest text-slate-400 ml-2">Nachricht</label>
|
||||
<div className="relative group">
|
||||
<div className="absolute left-6 top-10 text-slate-300 group-focus-within:text-slate-900 transition-colors">
|
||||
<MessageSquare size={24} />
|
||||
</div>
|
||||
<textarea
|
||||
placeholder="Erzählen Sie mir kurz von Ihrem Projekt..."
|
||||
value={state.message}
|
||||
onChange={(e) => updateState({ message: e.target.value })}
|
||||
rows={5}
|
||||
className="w-full p-8 pl-16 bg-white border border-slate-100 rounded-[2.5rem] focus:outline-none focus:border-slate-900 transition-all duration-500 resize-none text-xl shadow-sm focus:shadow-2xl"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Input
|
||||
label="Nachricht"
|
||||
icon={MessageSquare}
|
||||
isTextArea
|
||||
rows={5}
|
||||
placeholder="Erzählen Sie mir kurz von Ihrem Projekt..."
|
||||
value={state.message}
|
||||
onChange={(e) => updateState({ message: e.target.value })}
|
||||
/>
|
||||
</Reveal>
|
||||
|
||||
<Reveal width="100%" delay={0.4}>
|
||||
@@ -95,7 +67,7 @@ export function ContactStep({ state, updateState }: ContactStepProps) {
|
||||
<p className="text-lg font-bold text-slate-900 ml-2">Dateien hochladen (optional)</p>
|
||||
<div
|
||||
className={`relative group border-2 border-dashed rounded-[3rem] p-12 transition-all duration-500 flex flex-col items-center justify-center gap-6 cursor-pointer min-h-[250px] ${
|
||||
state.contactFiles.length > 0 ? 'border-slate-900 bg-slate-50 shadow-inner' : 'border-slate-200 hover:border-slate-400 bg-white hover:shadow-xl'
|
||||
state.contactFiles.length > 0 ? 'border-slate-900 bg-slate-50' : 'border-slate-200 hover:border-slate-400 bg-white hover:shadow-xl'
|
||||
}`}
|
||||
onDragOver={(e) => { e.preventDefault(); e.stopPropagation(); }}
|
||||
onDrop={(e) => {
|
||||
@@ -126,7 +98,7 @@ export function ContactStep({ state, updateState }: ContactStepProps) {
|
||||
initial={{ opacity: 0, x: -10 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ delay: idx * 0.05 }}
|
||||
className="flex items-center justify-between p-5 bg-white border border-slate-100 rounded-2xl shadow-sm group/file"
|
||||
className="flex items-center justify-between p-5 bg-white border border-slate-100 rounded-2xl group/file"
|
||||
>
|
||||
<div className="flex items-center gap-4 text-slate-900">
|
||||
<div className="w-10 h-10 bg-slate-50 rounded-xl flex items-center justify-center text-slate-400 group-hover/file:text-slate-900 transition-colors">
|
||||
|
||||
@@ -24,10 +24,10 @@ export function ContentStep({ state, updateState }: ContentStepProps) {
|
||||
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="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-slate-900 shadow-inner">
|
||||
<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>
|
||||
@@ -40,7 +40,7 @@ export function ContentStep({ state, updateState }: ContentStepProps) {
|
||||
whileTap={{ scale: 0.95 }}
|
||||
type="button"
|
||||
onClick={() => toggleDontKnow('cms')}
|
||||
className={`px-6 py-3 rounded-full text-sm font-bold transition-all shadow-sm ${
|
||||
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'
|
||||
}`}
|
||||
>
|
||||
@@ -49,12 +49,12 @@ export function ContentStep({ state, updateState }: ContentStepProps) {
|
||||
<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'}`}
|
||||
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 shadow-lg"
|
||||
className="absolute top-1.5 left-1.5 w-9 h-9 bg-white rounded-full"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
@@ -64,7 +64,7 @@ export function ContentStep({ state, updateState }: ContentStepProps) {
|
||||
<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">
|
||||
<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>
|
||||
@@ -78,12 +78,12 @@ export function ContentStep({ state, updateState }: ContentStepProps) {
|
||||
].map((opt, index) => (
|
||||
<motion.button
|
||||
key={opt.id}
|
||||
whileHover={{ y: -5, boxShadow: '0 10px 15px -3px rgb(0 0 0 / 0.1)' }}
|
||||
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 shadow-xl' : 'border-slate-200 bg-white hover:border-slate-400'
|
||||
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>
|
||||
@@ -98,9 +98,9 @@ export function ContentStep({ state, updateState }: ContentStepProps) {
|
||||
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"
|
||||
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 shadow-sm shrink-0">
|
||||
<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">
|
||||
@@ -114,7 +114,7 @@ export function ContentStep({ state, updateState }: ContentStepProps) {
|
||||
</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="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>
|
||||
@@ -122,7 +122,7 @@ export function ContentStep({ state, updateState }: ContentStepProps) {
|
||||
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="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>
|
||||
@@ -135,7 +135,7 @@ export function ContentStep({ state, updateState }: ContentStepProps) {
|
||||
</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="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">Für wie viele Datensätze soll ich die initiale Befüllung übernehmen?</p>
|
||||
@@ -146,7 +146,7 @@ export function ContentStep({ state, updateState }: ContentStepProps) {
|
||||
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"
|
||||
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>
|
||||
@@ -166,7 +166,7 @@ export function ContentStep({ state, updateState }: ContentStepProps) {
|
||||
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"
|
||||
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>
|
||||
|
||||
@@ -6,6 +6,7 @@ import { DESIGN_VIBES } from '../constants';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { Plus, X, Palette, Pipette, RefreshCw } from 'lucide-react';
|
||||
import { Reveal } from '../../Reveal';
|
||||
import { Input } from '../components/Input';
|
||||
|
||||
interface DesignStepProps {
|
||||
state: FormState;
|
||||
@@ -82,7 +83,7 @@ export function DesignStep({ state, updateState }: DesignStepProps) {
|
||||
whileTap={{ scale: 0.95 }}
|
||||
type="button"
|
||||
onClick={() => toggleDontKnow('design_vibe')}
|
||||
className={`px-6 py-3 rounded-full text-sm font-bold transition-all shadow-sm ${
|
||||
className={`px-6 py-3 rounded-full text-sm font-bold transition-all ${
|
||||
state.dontKnows?.includes('design_vibe') ? 'bg-slate-900 text-white' : 'bg-slate-100 text-slate-500 hover:bg-slate-200'
|
||||
}`}
|
||||
>
|
||||
@@ -93,14 +94,14 @@ export function DesignStep({ state, updateState }: DesignStepProps) {
|
||||
{DESIGN_VIBES.map((vibe, index) => (
|
||||
<motion.button
|
||||
key={vibe.id}
|
||||
whileHover={{ y: -5, boxShadow: '0 20px 25px -5px rgb(0 0 0 / 0.1)' }}
|
||||
whileHover={{ y: -5 }}
|
||||
type="button"
|
||||
onClick={() => updateState({ designVibe: vibe.id })}
|
||||
className={`p-8 rounded-[2.5rem] border-2 text-left transition-all duration-500 focus:outline-none overflow-hidden relative group ${
|
||||
state.designVibe === vibe.id ? 'border-slate-900 bg-slate-900 text-white' : 'border-slate-100 bg-white hover:border-slate-300'
|
||||
}`}
|
||||
>
|
||||
<div className={`w-16 h-10 mb-6 transition-transform duration-500 group-hover:scale-110 ${state.designVibe === vibe.id ? 'text-white' : 'text-slate-900'}`}>{vibe.illustration}</div>
|
||||
<div className={`w-16 h-10 mb-6 transition-transform duration-500 group-hover:scale-110 ${state.designVibe === vibe.id ? 'text-white' : 'text-black'}`}>{vibe.illustration}</div>
|
||||
<h4 className={`text-2xl font-bold mb-3 ${state.designVibe === vibe.id ? 'text-white' : 'text-slate-900'}`}>{vibe.label}</h4>
|
||||
<p className={`text-lg leading-relaxed ${state.designVibe === vibe.id ? 'text-slate-200' : 'text-slate-500'}`}>{vibe.desc}</p>
|
||||
{state.designVibe === vibe.id && (
|
||||
@@ -118,45 +119,31 @@ export function DesignStep({ state, updateState }: DesignStepProps) {
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="space-y-1">
|
||||
<h4 className="text-2xl font-bold text-slate-900">Farbschema</h4>
|
||||
<p className="text-slate-500">Generieren Sie eine harmonische Palette oder definieren Sie eigene Farben.</p>
|
||||
<p className="text-slate-500">Definieren Sie Ihre Markenfarben oder lassen Sie sich inspirieren.</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<motion.button
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
type="button"
|
||||
onClick={generateHarmonicPalette}
|
||||
className="flex items-center gap-2 px-6 py-3 rounded-full text-sm font-bold bg-white border border-slate-200 text-slate-900 hover:border-slate-900 transition-all"
|
||||
>
|
||||
<RefreshCw size={16} />
|
||||
Zufall
|
||||
</motion.button>
|
||||
<motion.button
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
type="button"
|
||||
onClick={() => toggleDontKnow('color_scheme')}
|
||||
className={`px-6 py-3 rounded-full text-sm font-bold transition-all ${
|
||||
state.dontKnows?.includes('color_scheme') ? 'bg-slate-900 text-white' : 'bg-slate-100 text-slate-500 hover:bg-slate-200'
|
||||
}`}
|
||||
>
|
||||
Ich weiß es nicht
|
||||
</motion.button>
|
||||
</div>
|
||||
<motion.button
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
type="button"
|
||||
onClick={() => toggleDontKnow('color_scheme')}
|
||||
className={`px-6 py-3 rounded-full text-sm font-bold transition-all shadow-sm ${
|
||||
state.dontKnows?.includes('color_scheme') ? 'bg-slate-900 text-white' : 'bg-slate-100 text-slate-500 hover:bg-slate-200'
|
||||
}`}
|
||||
>
|
||||
Ich weiß es nicht
|
||||
</motion.button>
|
||||
</div>
|
||||
|
||||
{/* Generator */}
|
||||
<div className="space-y-6">
|
||||
<motion.button
|
||||
whileHover={{ scale: 1.02 }}
|
||||
whileTap={{ scale: 0.98 }}
|
||||
type="button"
|
||||
onClick={generateHarmonicPalette}
|
||||
className="w-full p-8 rounded-[3rem] border-2 border-slate-100 bg-white hover:border-slate-900 transition-all duration-500 flex items-center justify-between group shadow-sm hover:shadow-xl"
|
||||
>
|
||||
<div className="flex items-center gap-6">
|
||||
<div className="w-14 h-14 bg-slate-50 rounded-2xl flex items-center justify-center text-slate-900 group-hover:rotate-180 transition-transform duration-700">
|
||||
<RefreshCw size={28} />
|
||||
</div>
|
||||
<div className="text-left">
|
||||
<p className="text-xl font-bold text-slate-900">Harmonischer Zufall</p>
|
||||
<p className="text-slate-500">Erzeugt eine mathematisch abgestimmte Palette.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex h-12 w-48 rounded-xl overflow-hidden shadow-inner border border-slate-100">
|
||||
{state.colorScheme.map((color, j) => (
|
||||
<div key={j} className="flex-1" style={{ backgroundColor: color }} />
|
||||
))}
|
||||
</div>
|
||||
</motion.button>
|
||||
</div>
|
||||
|
||||
{/* Custom Picker */}
|
||||
@@ -176,14 +163,14 @@ export function DesignStep({ state, updateState }: DesignStepProps) {
|
||||
exit={{ opacity: 0, scale: 0.8 }}
|
||||
className="relative group"
|
||||
>
|
||||
<div className="relative">
|
||||
<div className="relative w-24 h-24 rounded-3xl overflow-hidden border-2 border-white group-hover:scale-105 transition-transform duration-300">
|
||||
<input
|
||||
type="color"
|
||||
value={color}
|
||||
onChange={(e) => updateColor(i, e.target.value)}
|
||||
className="w-24 h-24 rounded-3xl cursor-pointer border-4 border-white shadow-lg overflow-hidden transition-transform duration-300 group-hover:scale-105"
|
||||
className="absolute inset-[-100%] w-[300%] h-[300%] cursor-pointer outline-none border-none appearance-none bg-transparent"
|
||||
/>
|
||||
<div className="absolute inset-0 rounded-3xl pointer-events-none border border-black/5" />
|
||||
<div className="absolute inset-0 pointer-events-none border border-black/5 rounded-3xl" />
|
||||
</div>
|
||||
<div className="mt-2 text-center font-mono text-[10px] text-slate-400 uppercase">{color}</div>
|
||||
{state.colorScheme.length > 1 && (
|
||||
@@ -192,7 +179,7 @@ export function DesignStep({ state, updateState }: DesignStepProps) {
|
||||
whileTap={{ scale: 0.9 }}
|
||||
type="button"
|
||||
onClick={() => removeColor(i)}
|
||||
className="absolute -top-3 -right-3 w-8 h-8 bg-white text-red-500 rounded-full flex items-center justify-center shadow-xl border border-slate-100 opacity-0 group-hover:opacity-100 transition-all duration-300 z-10"
|
||||
className="absolute -top-3 -right-3 w-8 h-8 bg-white text-red-500 rounded-full flex items-center justify-center border border-slate-100 opacity-0 group-hover:opacity-100 transition-all duration-300 z-10"
|
||||
>
|
||||
<X size={16} strokeWidth={3} />
|
||||
</motion.button>
|
||||
@@ -221,16 +208,14 @@ export function DesignStep({ state, updateState }: DesignStepProps) {
|
||||
|
||||
{/* Wishes */}
|
||||
<Reveal width="100%" delay={0.3}>
|
||||
<div className="space-y-6">
|
||||
<h4 className="text-2xl font-bold text-slate-900">Individuelle Wünsche</h4>
|
||||
<textarea
|
||||
placeholder="Haben Sie bereits konkrete Vorstellungen oder Referenzen?"
|
||||
value={state.designWishes}
|
||||
onChange={(e) => updateState({ designWishes: e.target.value })}
|
||||
rows={4}
|
||||
className="w-full p-8 bg-white border border-slate-100 rounded-[3rem] focus:outline-none focus:border-slate-900 transition-all duration-500 resize-none text-xl shadow-sm focus:shadow-2xl"
|
||||
/>
|
||||
</div>
|
||||
<Input
|
||||
label="Individuelle Wünsche"
|
||||
isTextArea
|
||||
rows={4}
|
||||
placeholder="Haben Sie bereits konkrete Vorstellungen oder Referenzen?"
|
||||
value={state.designWishes}
|
||||
onChange={(e) => updateState({ designWishes: e.target.value })}
|
||||
/>
|
||||
</Reveal>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -29,7 +29,7 @@ export function FeaturesStep({ state, updateState, toggleItem }: FeaturesStepPro
|
||||
<div className="space-y-8">
|
||||
<div className="flex justify-between items-center">
|
||||
<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-sm">
|
||||
<div className="w-12 h-12 bg-slate-50 rounded-2xl flex items-center justify-center text-black">
|
||||
<LayoutGrid size={24} />
|
||||
</div>
|
||||
<h4 className="text-2xl font-bold text-slate-900">System-Module</h4>
|
||||
@@ -39,7 +39,7 @@ export function FeaturesStep({ state, updateState, toggleItem }: FeaturesStepPro
|
||||
whileTap={{ scale: 0.95 }}
|
||||
type="button"
|
||||
onClick={() => toggleDontKnow('features')}
|
||||
className={`px-6 py-3 rounded-full text-sm font-bold transition-all shadow-sm ${
|
||||
className={`px-6 py-3 rounded-full text-sm font-bold transition-all ${
|
||||
state.dontKnows?.includes('features') ? 'bg-slate-900 text-white' : 'bg-slate-100 text-slate-500 hover:bg-slate-200'
|
||||
}`}
|
||||
>
|
||||
@@ -67,7 +67,7 @@ export function FeaturesStep({ state, updateState, toggleItem }: FeaturesStepPro
|
||||
<div className="space-y-12">
|
||||
<div className="space-y-8">
|
||||
<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-sm">
|
||||
<div className="w-12 h-12 bg-slate-50 rounded-2xl flex items-center justify-center text-black">
|
||||
<ListPlus size={24} />
|
||||
</div>
|
||||
<h4 className="text-2xl font-bold text-slate-900">Weitere inhaltliche Module?</h4>
|
||||
@@ -84,7 +84,7 @@ export function FeaturesStep({ state, updateState, toggleItem }: FeaturesStepPro
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 0.4 }}
|
||||
className="p-10 bg-white border border-slate-100 rounded-[3rem] space-y-6 shadow-sm hover:shadow-xl transition-all duration-500"
|
||||
className="p-10 bg-white border border-slate-100 rounded-[3rem] space-y-6 hover:shadow-xl transition-all duration-500"
|
||||
>
|
||||
<div className="flex flex-col md:flex-row justify-between items-center gap-8">
|
||||
<div>
|
||||
|
||||
@@ -4,8 +4,8 @@ import * as React from 'react';
|
||||
import { FormState } from '../types';
|
||||
import { Checkbox } from '../components/Checkbox';
|
||||
import { RepeatableList } from '../components/RepeatableList';
|
||||
import { Minus, Plus, Cpu, ListPlus } from 'lucide-react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { Minus, Plus, Cpu, ListPlus } from 'lucide-react';
|
||||
import { Reveal } from '../../Reveal';
|
||||
|
||||
interface FunctionsStepProps {
|
||||
@@ -32,7 +32,7 @@ export function FunctionsStep({ state, updateState, toggleItem }: FunctionsStepP
|
||||
<div className="space-y-8">
|
||||
<div className="flex justify-between items-center">
|
||||
<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-sm">
|
||||
<div className="w-12 h-12 bg-slate-50 rounded-2xl flex items-center justify-center text-black">
|
||||
<Cpu size={24} />
|
||||
</div>
|
||||
<h4 className="text-2xl font-bold text-slate-900">
|
||||
@@ -44,7 +44,7 @@ export function FunctionsStep({ state, updateState, toggleItem }: FunctionsStepP
|
||||
whileTap={{ scale: 0.95 }}
|
||||
type="button"
|
||||
onClick={() => toggleDontKnow('functions')}
|
||||
className={`px-6 py-3 rounded-full text-sm font-bold transition-all shadow-sm ${
|
||||
className={`px-6 py-3 rounded-full text-sm font-bold transition-all ${
|
||||
state.dontKnows?.includes('functions') ? 'bg-slate-900 text-white' : 'bg-slate-100 text-slate-500 hover:bg-slate-200'
|
||||
}`}
|
||||
>
|
||||
@@ -107,7 +107,7 @@ export function FunctionsStep({ state, updateState, toggleItem }: FunctionsStepP
|
||||
<div className="space-y-12">
|
||||
<div className="space-y-8">
|
||||
<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-sm">
|
||||
<div className="w-12 h-12 bg-slate-50 rounded-2xl flex items-center justify-center text-black">
|
||||
<ListPlus size={24} />
|
||||
</div>
|
||||
<h4 className="text-2xl font-bold text-slate-900">Weitere spezifische Wünsche?</h4>
|
||||
@@ -124,7 +124,7 @@ export function FunctionsStep({ state, updateState, toggleItem }: FunctionsStepP
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 0.4 }}
|
||||
className="p-10 bg-white border border-slate-100 rounded-[3rem] space-y-6 shadow-sm hover:shadow-xl transition-all duration-500"
|
||||
className="p-10 bg-white border border-slate-100 rounded-[3rem] space-y-6 hover:shadow-xl transition-all duration-500"
|
||||
>
|
||||
<div className="flex flex-col md:flex-row justify-between items-center gap-8">
|
||||
<div>
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
|
||||
import * as React from 'react';
|
||||
import { FormState } from '../types';
|
||||
import { Globe, Info, ListPlus } from 'lucide-react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { RepeatableList } from '../components/RepeatableList';
|
||||
import { Globe, Info, Plus, X } from 'lucide-react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { Reveal } from '../../Reveal';
|
||||
|
||||
interface LanguageStepProps {
|
||||
@@ -12,9 +11,26 @@ interface LanguageStepProps {
|
||||
updateState: (updates: Partial<FormState>) => void;
|
||||
}
|
||||
|
||||
const COMMON_LANGUAGES = [
|
||||
{ id: 'de', label: 'Deutsch' },
|
||||
{ id: 'en', label: 'Englisch' },
|
||||
{ id: 'fr', label: 'Französisch' },
|
||||
{ id: 'es', label: 'Spanisch' },
|
||||
{ id: 'it', label: 'Italienisch' },
|
||||
];
|
||||
|
||||
export function LanguageStep({ state, updateState }: LanguageStepProps) {
|
||||
const basePriceExplanation = "Jede zusätzliche Sprache erhöht den Gesamtaufwand für Design, Entwicklung und Qualitätssicherung um ca. 20%. Dies deckt die technische Implementierung der Übersetzungsschicht sowie die Anpassung von Layouts für unterschiedliche Textlängen ab.";
|
||||
|
||||
const toggleLanguage = (lang: string) => {
|
||||
const current = state.languagesList || [];
|
||||
if (current.includes(lang)) {
|
||||
updateState({ languagesList: current.filter(l => l !== lang) });
|
||||
} else {
|
||||
updateState({ languagesList: [...current, lang] });
|
||||
}
|
||||
};
|
||||
|
||||
const toggleDontKnow = (id: string) => {
|
||||
const current = state.dontKnows || [];
|
||||
if (current.includes(id)) {
|
||||
@@ -32,7 +48,7 @@ export function LanguageStep({ state, updateState }: LanguageStepProps) {
|
||||
<div className="space-y-8">
|
||||
<div className="flex justify-between items-center">
|
||||
<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-sm">
|
||||
<div className="w-12 h-12 bg-slate-50 rounded-2xl flex items-center justify-center text-black">
|
||||
<Globe size={24} />
|
||||
</div>
|
||||
<h4 className="text-2xl font-bold text-slate-900">Sprachen</h4>
|
||||
@@ -42,7 +58,7 @@ export function LanguageStep({ state, updateState }: LanguageStepProps) {
|
||||
whileTap={{ scale: 0.95 }}
|
||||
type="button"
|
||||
onClick={() => toggleDontKnow('languages')}
|
||||
className={`px-6 py-3 rounded-full text-sm font-bold transition-all shadow-sm ${
|
||||
className={`px-6 py-3 rounded-full text-sm font-bold transition-all ${
|
||||
state.dontKnows?.includes('languages') ? 'bg-slate-900 text-white' : 'bg-slate-100 text-slate-500 hover:bg-slate-200'
|
||||
}`}
|
||||
>
|
||||
@@ -50,27 +66,78 @@ export function LanguageStep({ state, updateState }: LanguageStepProps) {
|
||||
</motion.button>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-10 h-10 bg-slate-50 rounded-xl flex items-center justify-center text-slate-900 shadow-sm">
|
||||
<ListPlus size={20} />
|
||||
</div>
|
||||
<p className="text-lg font-bold text-slate-900">Welche Sprachen planen Sie?</p>
|
||||
<div className="space-y-8">
|
||||
<p className="text-lg text-slate-500 leading-relaxed ml-2">
|
||||
Welche Sprachen soll Ihre Website unterstützen?
|
||||
</p>
|
||||
|
||||
<div className="flex flex-wrap gap-4">
|
||||
{COMMON_LANGUAGES.map((lang) => (
|
||||
<motion.button
|
||||
key={lang.id}
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
type="button"
|
||||
onClick={() => toggleLanguage(lang.label)}
|
||||
className={`px-8 py-4 rounded-2xl font-bold transition-all border-2 ${
|
||||
state.languagesList.includes(lang.label)
|
||||
? 'bg-slate-900 border-slate-900 text-white'
|
||||
: 'bg-white border-slate-100 text-slate-600 hover:border-slate-300'
|
||||
}`}
|
||||
>
|
||||
{lang.label}
|
||||
</motion.button>
|
||||
))}
|
||||
</div>
|
||||
<div className="p-2">
|
||||
<RepeatableList
|
||||
items={state.languagesList}
|
||||
onAdd={(v) => updateState({ languagesList: [...state.languagesList, v] })}
|
||||
onRemove={(i) => updateState({ languagesList: state.languagesList.filter((_, idx) => idx !== i) })}
|
||||
placeholder="z.B. Englisch, Französisch, Spanisch..."
|
||||
/>
|
||||
|
||||
<div className="pt-4">
|
||||
<div className="flex gap-3">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Weitere Sprache hinzufügen..."
|
||||
className="flex-1 p-6 bg-white border border-slate-100 rounded-2xl focus:outline-none focus:border-slate-900 transition-colors text-lg"
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
const val = e.currentTarget.value.trim();
|
||||
if (val && !state.languagesList.includes(val)) {
|
||||
updateState({ languagesList: [...state.languagesList, val] });
|
||||
e.currentTarget.value = '';
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-3">
|
||||
<AnimatePresence>
|
||||
{state.languagesList.filter(l => !COMMON_LANGUAGES.find(cl => cl.label === l)).map((lang, i) => (
|
||||
<motion.div
|
||||
key={lang}
|
||||
initial={{ opacity: 0, scale: 0.8 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
exit={{ opacity: 0, scale: 0.8 }}
|
||||
className="flex items-center gap-3 px-6 py-3 bg-slate-100 rounded-full text-base font-bold text-slate-700"
|
||||
>
|
||||
<span>{lang}</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => updateState({ languagesList: state.languagesList.filter(l => l !== lang) })}
|
||||
className="text-slate-400 hover:text-slate-900 transition-colors"
|
||||
>
|
||||
<X size={18} />
|
||||
</button>
|
||||
</motion.div>
|
||||
))}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Reveal>
|
||||
|
||||
<Reveal width="100%" delay={0.3}>
|
||||
<div className="p-10 bg-slate-900 text-white rounded-[3rem] space-y-8 shadow-2xl relative overflow-hidden">
|
||||
<div className="p-10 bg-slate-900 text-white rounded-[3rem] space-y-8 relative overflow-hidden">
|
||||
<div className="absolute top-0 right-0 w-64 h-64 bg-white/5 rounded-full -mr-32 -mt-32 blur-3xl" />
|
||||
<div className="flex items-center gap-4 text-slate-400 relative z-10">
|
||||
<Info size={24} />
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
import * as React from 'react';
|
||||
import { FormState } from '../types';
|
||||
import { Checkbox } from '../components/Checkbox';
|
||||
import { RepeatableList } from '../components/RepeatableList';
|
||||
import { Minus, Plus, Link2, Globe, Share2 } from 'lucide-react';
|
||||
import { Link2, Globe, Share2 } from 'lucide-react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { Reveal } from '../../Reveal';
|
||||
import { Input } from '../components/Input';
|
||||
|
||||
interface PresenceStepProps {
|
||||
state: FormState;
|
||||
@@ -38,60 +38,45 @@ export function PresenceStep({ state, updateState, toggleItem }: PresenceStepPro
|
||||
<Reveal width="100%" delay={0.1}>
|
||||
<div className="space-y-8">
|
||||
<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-sm">
|
||||
<div className="w-12 h-12 bg-slate-50 rounded-2xl flex items-center justify-center text-black">
|
||||
<Globe size={24} />
|
||||
</div>
|
||||
<h4 className="text-2xl font-bold text-slate-900">Bestehende Website</h4>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<label className="block text-sm font-bold uppercase tracking-widest text-slate-400 ml-2">URL (falls vorhanden)</label>
|
||||
<div className="relative group">
|
||||
<div className="absolute left-6 top-1/2 -translate-y-1/2 text-slate-300 group-focus-within:text-slate-900 transition-colors">
|
||||
<Link2 size={24} />
|
||||
</div>
|
||||
<input
|
||||
type="url"
|
||||
placeholder="https://www.beispiel.de"
|
||||
value={state.existingWebsite}
|
||||
onChange={(e) => updateState({ existingWebsite: e.target.value })}
|
||||
className="w-full p-8 pl-16 bg-white border border-slate-100 rounded-[3rem] focus:outline-none focus:border-slate-900 transition-all duration-500 text-xl shadow-sm focus:shadow-2xl"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Input
|
||||
label="URL (falls vorhanden)"
|
||||
type="url"
|
||||
icon={Link2}
|
||||
placeholder="https://www.beispiel.de"
|
||||
value={state.existingWebsite}
|
||||
onChange={(e) => updateState({ existingWebsite: e.target.value })}
|
||||
/>
|
||||
</div>
|
||||
</Reveal>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
<Reveal width="100%" delay={0.2}>
|
||||
<div className="space-y-4">
|
||||
<label className="block text-sm font-bold uppercase tracking-widest text-slate-400 ml-2">Bestehende Domain</label>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="z.B. beispiel.de"
|
||||
value={state.existingDomain}
|
||||
onChange={(e) => updateState({ existingDomain: e.target.value })}
|
||||
className="w-full p-6 bg-white border border-slate-100 rounded-[2rem] focus:outline-none focus:border-slate-900 transition-all duration-500 text-lg shadow-sm focus:shadow-xl"
|
||||
/>
|
||||
</div>
|
||||
<Input
|
||||
label="Bestehende Domain"
|
||||
placeholder="z.B. beispiel.de"
|
||||
value={state.existingDomain}
|
||||
onChange={(e) => updateState({ existingDomain: e.target.value })}
|
||||
/>
|
||||
</Reveal>
|
||||
<Reveal width="100%" delay={0.2}>
|
||||
<div className="space-y-4">
|
||||
<label className="block text-sm font-bold uppercase tracking-widest text-slate-400 ml-2">Wunsch-Domain</label>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="z.B. neue-marke.de"
|
||||
value={state.wishedDomain}
|
||||
onChange={(e) => updateState({ wishedDomain: e.target.value })}
|
||||
className="w-full p-6 bg-white border border-slate-100 rounded-[2rem] focus:outline-none focus:border-slate-900 transition-all duration-500 text-lg shadow-sm focus:shadow-xl"
|
||||
/>
|
||||
</div>
|
||||
<Input
|
||||
label="Wunsch-Domain"
|
||||
placeholder="z.B. neue-marke.de"
|
||||
value={state.wishedDomain}
|
||||
onChange={(e) => updateState({ wishedDomain: e.target.value })}
|
||||
/>
|
||||
</Reveal>
|
||||
</div>
|
||||
|
||||
<Reveal width="100%" delay={0.3}>
|
||||
<div className="space-y-8">
|
||||
<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-sm">
|
||||
<div className="w-12 h-12 bg-slate-50 rounded-2xl flex items-center justify-center text-black">
|
||||
<Share2 size={24} />
|
||||
</div>
|
||||
<h4 className="text-2xl font-bold text-slate-900">Social Media Accounts</h4>
|
||||
@@ -104,7 +89,7 @@ export function PresenceStep({ state, updateState, toggleItem }: PresenceStepPro
|
||||
<motion.div
|
||||
key={option.id}
|
||||
className={`p-6 rounded-[2.5rem] border-2 transition-all duration-500 ${
|
||||
isSelected ? 'border-slate-900 bg-slate-50 shadow-md' : 'border-slate-100 bg-white hover:border-slate-300'
|
||||
isSelected ? 'border-slate-900 bg-slate-50' : 'border-slate-100 bg-white hover:border-slate-300'
|
||||
}`}
|
||||
>
|
||||
<div className="flex flex-col gap-4">
|
||||
@@ -125,7 +110,7 @@ export function PresenceStep({ state, updateState, toggleItem }: PresenceStepPro
|
||||
className="overflow-hidden"
|
||||
>
|
||||
<div className="relative group/input pt-2">
|
||||
<div className="absolute left-5 top-[calc(50%+4px)] -translate-y-1/2 text-slate-300 group-focus-within/input:text-slate-900 transition-colors">
|
||||
<div className="absolute left-5 top-[calc(50%+4px)] -translate-y-1/2 text-black transition-colors">
|
||||
<Link2 size={18} />
|
||||
</div>
|
||||
<input
|
||||
@@ -133,7 +118,7 @@ export function PresenceStep({ state, updateState, toggleItem }: PresenceStepPro
|
||||
placeholder={`URL zu Ihrem ${option.label} Profil`}
|
||||
value={state.socialMediaUrls[option.id] || ''}
|
||||
onChange={(e) => updateUrl(option.id, e.target.value)}
|
||||
className="w-full p-4 pl-14 bg-white border border-slate-200 rounded-2xl focus:outline-none focus:border-slate-900 transition-all duration-300 text-base shadow-inner"
|
||||
className="w-full p-4 pl-14 bg-white border border-slate-200 rounded-2xl focus:outline-none focus:border-slate-900 transition-all duration-300 text-base"
|
||||
/>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
@@ -20,7 +20,7 @@ export function TypeStep({ state, updateState }: TypeStepProps) {
|
||||
].map((type, index) => (
|
||||
<Reveal key={type.id} width="100%" delay={index * 0.1}>
|
||||
<motion.button
|
||||
whileHover={{ y: -8, boxShadow: '0 25px 50px -12px rgb(0 0 0 / 0.15)' }}
|
||||
whileHover={{ y: -8 }}
|
||||
whileTap={{ scale: 0.98 }}
|
||||
type="button"
|
||||
onClick={() => updateState({ projectType: type.id as ProjectType })}
|
||||
|
||||
@@ -23,7 +23,7 @@ export function WebAppStep({ state, updateState }: WebAppStepProps) {
|
||||
{/* Target Audience */}
|
||||
<div className="space-y-6">
|
||||
<h4 className="text-2xl font-bold text-slate-900 flex items-center gap-3">
|
||||
<Users size={24} /> Zielgruppe
|
||||
<Users size={24} className="text-black" /> Zielgruppe
|
||||
</h4>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{[
|
||||
@@ -68,7 +68,7 @@ export function WebAppStep({ state, updateState }: WebAppStepProps) {
|
||||
{/* Platform Type */}
|
||||
<div className="space-y-6">
|
||||
<h4 className="text-2xl font-bold text-slate-900 flex items-center gap-3">
|
||||
<Monitor size={24} /> Plattform-Fokus
|
||||
<Monitor size={24} className="text-black" /> Plattform-Fokus
|
||||
</h4>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
{[
|
||||
@@ -84,7 +84,9 @@ export function WebAppStep({ state, updateState }: WebAppStepProps) {
|
||||
state.platformType === opt.id ? 'border-slate-900 bg-slate-900 text-white' : 'border-slate-100 bg-white hover:border-slate-200'
|
||||
}`}
|
||||
>
|
||||
{opt.icon}
|
||||
<div className={state.platformType === opt.id ? 'text-white' : 'text-black'}>
|
||||
{opt.icon}
|
||||
</div>
|
||||
<span className="font-bold text-lg">{opt.label}</span>
|
||||
</button>
|
||||
))}
|
||||
@@ -94,7 +96,7 @@ export function WebAppStep({ state, updateState }: WebAppStepProps) {
|
||||
{/* Data Sensitivity */}
|
||||
<div className="space-y-6">
|
||||
<h4 className="text-2xl font-bold text-slate-900 flex items-center gap-3">
|
||||
<Shield size={24} /> Datensicherheit
|
||||
<Shield size={24} className="text-black" /> Datensicherheit
|
||||
</h4>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{[
|
||||
@@ -119,7 +121,7 @@ export function WebAppStep({ state, updateState }: WebAppStepProps) {
|
||||
{/* Authentication */}
|
||||
<div className="p-10 bg-slate-50 rounded-[3rem] border border-slate-100 space-y-6">
|
||||
<h4 className="text-2xl font-bold text-slate-900 flex items-center gap-3">
|
||||
<Lock size={24} /> Authentifizierung
|
||||
<Lock size={24} className="text-black" /> Authentifizierung
|
||||
</h4>
|
||||
<p className="text-lg text-slate-500">Wie sollen sich Nutzer anmelden?</p>
|
||||
<div className="flex flex-wrap gap-4">
|
||||
|
||||
Reference in New Issue
Block a user