diff --git a/src/components/ContactForm.tsx b/src/components/ContactForm.tsx
index a2bd637..93893e4 100644
--- a/src/components/ContactForm.tsx
+++ b/src/components/ContactForm.tsx
@@ -24,6 +24,7 @@ import { ContentStep } from './ContactForm/steps/ContentStep';
import { LanguageStep } from './ContactForm/steps/LanguageStep';
import { TimelineStep } from './ContactForm/steps/TimelineStep';
import { ContactStep } from './ContactForm/steps/ContactStep';
+import { WebAppStep } from './ContactForm/steps/WebAppStep';
import {
ConceptTarget,
@@ -84,7 +85,11 @@ export function ContactForm() {
languagesCount: state.languagesCount,
deadline: state.deadline,
designVibe: state.designVibe,
- colorScheme: state.colorScheme
+ colorScheme: state.colorScheme,
+ targetAudience: state.targetAudience,
+ userRoles: state.userRoles,
+ dataSensitivity: state.dataSensitivity,
+ platformType: state.platformType
};
const stateString = btoa(unescape(encodeURIComponent(JSON.stringify(configData))));
@@ -182,11 +187,22 @@ export function ContactForm() {
{ id: 'language', title: 'Sprachen', description: 'Globale Reichweite planen.', illustration: },
{ id: 'timeline', title: 'Zeitplan', description: 'Wann soll das Projekt live gehen?', illustration: },
{ id: 'contact', title: 'Abschluss', description: 'Erzählen Sie mir mehr über Ihr Vorhaben.', illustration: },
+ { id: 'webapp', title: 'Web App Details', description: 'Spezifische Anforderungen für Ihre Anwendung.', illustration: },
];
const activeSteps = useMemo(() => {
- if (state.projectType === 'website') return steps;
- return [steps[0], steps[9], steps[10]];
+ if (state.projectType === 'website') {
+ return steps.filter(s => s.id !== 'webapp');
+ }
+ // Web App flow
+ return [
+ steps.find(s => s.id === 'type')!,
+ steps.find(s => s.id === 'webapp')!,
+ { ...steps.find(s => s.id === 'functions')!, title: 'Funktionen', description: 'Kern-Features Ihrer Anwendung.' },
+ { ...steps.find(s => s.id === 'api')!, title: 'Integrationen', description: 'Anbindung an bestehende Systeme.' },
+ steps.find(s => s.id === 'timeline')!,
+ steps.find(s => s.id === 'contact')!,
+ ];
}, [state.projectType]);
useEffect(() => {
@@ -218,6 +234,8 @@ export function ContactForm() {
return ;
case 'contact':
return ;
+ case 'webapp':
+ return ;
default: return null;
}
};
@@ -264,7 +282,7 @@ export function ContactForm() {
{activeSteps[stepIndex].illustration}
-
Schritt {stepIndex + 1} von {activeSteps.length}
+
Schritt {stepIndex + 1} von {activeSteps.length}
{activeSteps[stepIndex].title}
{activeSteps[stepIndex].description}
@@ -291,7 +309,7 @@ export function ContactForm() {
initial={{ opacity: 0, y: 5, x: '-50%' }}
animate={{ opacity: 1, y: 0, x: '-50%' }}
exit={{ opacity: 0, y: 5, x: '-50%' }}
- className="absolute bottom-full left-1/2 mb-1 px-3 py-1.5 bg-slate-900 text-white text-[10px] font-bold uppercase tracking-wider rounded-lg whitespace-nowrap pointer-events-none z-50 shadow-xl"
+ className="absolute bottom-full left-1/2 mb-1 px-4 py-2 bg-slate-900 text-white text-sm font-bold uppercase tracking-wider rounded-lg whitespace-nowrap pointer-events-none z-50 shadow-xl"
>
{step.title}
@@ -303,16 +321,16 @@ export function ContactForm() {
-
+
{stepIndex > 0 ? (
-
- Zurück
+
+ Zurück
) :
}
{stepIndex < activeSteps.length - 1 && (
-
- Weiter
+
+ Weiter
)}
@@ -321,8 +339,8 @@ export function ContactForm() {
diff --git a/src/components/ContactForm/components/Checkbox.tsx b/src/components/ContactForm/components/Checkbox.tsx
index 3ab56c0..1ceea8d 100644
--- a/src/components/ContactForm/components/Checkbox.tsx
+++ b/src/components/ContactForm/components/Checkbox.tsx
@@ -15,7 +15,7 @@ export function Checkbox({ label, desc, checked, onChange }: CheckboxProps) {
@@ -23,8 +23,8 @@ export function Checkbox({ label, desc, checked, onChange }: CheckboxProps) {
{checked && }
-
{label}
-
{desc}
+
{label}
+
{desc}
);
diff --git a/src/components/ContactForm/components/PriceCalculation.tsx b/src/components/ContactForm/components/PriceCalculation.tsx
index 7c8884f..598aee2 100644
--- a/src/components/ContactForm/components/PriceCalculation.tsx
+++ b/src/components/ContactForm/components/PriceCalculation.tsx
@@ -90,7 +90,7 @@ export function PriceCalculation({
-
Apps und Individual-Software werden nach tatsächlichem Aufwand abgerechnet.
+
Web Apps und Individual-Software werden nach tatsächlichem Aufwand abgerechnet.
{PRICING.APP_HOURLY} € / Std.
diff --git a/src/components/ContactForm/components/RepeatableList.tsx b/src/components/ContactForm/components/RepeatableList.tsx
index d85aabd..986c221 100644
--- a/src/components/ContactForm/components/RepeatableList.tsx
+++ b/src/components/ContactForm/components/RepeatableList.tsx
@@ -20,8 +20,8 @@ export function RepeatableList({
}: RepeatableListProps) {
const [input, setInput] = useState('');
return (
-
-
+
+
-
+
{items.map((item, i) => (
- {item}
- onRemove(i)} className="text-slate-400 hover:text-slate-900 transition-colors focus:outline-none overflow-hidden relative rounded-full">
-
+ {item}
+ onRemove(i)} className="text-slate-400 hover:text-slate-900 transition-colors focus:outline-none">
+
))}
diff --git a/src/components/ContactForm/constants.tsx b/src/components/ContactForm/constants.tsx
index 0df242f..19aa4a2 100644
--- a/src/components/ContactForm/constants.tsx
+++ b/src/components/ContactForm/constants.tsx
@@ -45,6 +45,10 @@ export const initialState: FormState = {
expectedAdjustments: 'low',
languagesCount: 1,
deadline: 'flexible',
+ targetAudience: 'internal',
+ userRoles: [],
+ dataSensitivity: 'standard',
+ platformType: 'web-only',
};
export const PAGE_SAMPLES = [
diff --git a/src/components/ContactForm/steps/ApiStep.tsx b/src/components/ContactForm/steps/ApiStep.tsx
index c6eaec8..3dce25d 100644
--- a/src/components/ContactForm/steps/ApiStep.tsx
+++ b/src/components/ContactForm/steps/ApiStep.tsx
@@ -2,10 +2,8 @@
import * as React from 'react';
import { FormState } from '../types';
-import { API_OPTIONS } from '../constants';
import { Checkbox } from '../components/Checkbox';
import { RepeatableList } from '../components/RepeatableList';
-import { Info } from 'lucide-react';
interface ApiStepProps {
state: FormState;
@@ -14,33 +12,56 @@ interface ApiStepProps {
}
export function ApiStep({ state, updateState, toggleItem }: ApiStepProps) {
+ const isWebApp = state.projectType === 'web-app';
+
return (
-
-
-
-
-
Wichtig zu wissen
-
Ich biete diese Drittsysteme nicht selbst an, sondern entwickle die Schnittstelle (API) , damit Ihre Website nahtlos mit ihnen kommunizieren kann.
+
+
+
+ {isWebApp ? 'Integrationen & Datenquellen' : 'Schnittstellen (API)'}
+
+
+ {isWebApp
+ ? 'Mit welchen Systemen soll die Web App kommunizieren?'
+ : 'Datenaustausch mit Drittsystemen zur Automatisierung.'}
+
+
+ updateState({ apiSystems: toggleItem(state.apiSystems, 'crm_erp') })}
+ />
+ updateState({ apiSystems: toggleItem(state.apiSystems, 'payment') })}
+ />
+ updateState({ apiSystems: toggleItem(state.apiSystems, 'marketing') })}
+ />
+ updateState({ apiSystems: toggleItem(state.apiSystems, 'ecommerce') })}
+ />
-
- {API_OPTIONS.map(opt => (
- updateState({ apiSystems: toggleItem(state.apiSystems, opt.id) })}
- />
- ))}
-
-
-
Weitere Systeme?
-
+ Weitere Systeme oder eigene APIs?
+ updateState({ otherTech: [...state.otherTech, v] })}
- onRemove={(i) => updateState({ otherTech: state.otherTech.filter((_, idx) => idx !== i) })}
- placeholder="z.B. Personio, DATEV, Salesforce..."
+ onRemove={(i) => updateTech(i)}
+ placeholder="z.B. Microsoft Graph, Google Maps, Custom REST API..."
/>
);
+
+ function updateTech(index: number) {
+ updateState({ otherTech: state.otherTech.filter((_, idx) => idx !== index) });
+ }
}
diff --git a/src/components/ContactForm/steps/AssetsStep.tsx b/src/components/ContactForm/steps/AssetsStep.tsx
index f1f72e0..f49bff2 100644
--- a/src/components/ContactForm/steps/AssetsStep.tsx
+++ b/src/components/ContactForm/steps/AssetsStep.tsx
@@ -14,8 +14,8 @@ interface AssetsStepProps {
export function AssetsStep({ state, updateState, toggleItem }: AssetsStepProps) {
return (
-
-
+
+
{ASSET_OPTIONS.map(opt => (
))}
-
-
Weitere Materialien?
-
+ Weitere vorhandene Unterlagen?
+ updateState({ otherAssets: [...state.otherAssets, v] })}
onRemove={(i) => updateState({ otherAssets: state.otherAssets.filter((_, idx) => idx !== i) })}
- placeholder="z.B. Stock-Fotos, Video-Footage, Präsentationen..."
+ placeholder="z.B. Lastenheft, Wireframes, Bilddatenbank..."
/>
diff --git a/src/components/ContactForm/steps/BaseStep.tsx b/src/components/ContactForm/steps/BaseStep.tsx
index 232b7b5..40340fe 100644
--- a/src/components/ContactForm/steps/BaseStep.tsx
+++ b/src/components/ContactForm/steps/BaseStep.tsx
@@ -2,10 +2,8 @@
import * as React from 'react';
import { FormState } from '../types';
-import { PAGE_SAMPLES } from '../constants';
import { Checkbox } from '../components/Checkbox';
import { RepeatableList } from '../components/RepeatableList';
-import { Info, FileText, Upload, X } from 'lucide-react';
interface BaseStepProps {
state: FormState;
@@ -15,66 +13,31 @@ interface BaseStepProps {
export function BaseStep({ state, updateState, toggleItem }: BaseStepProps) {
return (
-
-
- {PAGE_SAMPLES.map(p => (
+
+
+ {[
+ { id: 'Home', label: 'Startseite', desc: 'Der erste Eindruck Ihrer Marke.' },
+ { id: 'About', label: 'Über uns', desc: 'Ihre Geschichte und Ihr Team.' },
+ { id: 'Services', label: 'Leistungen', desc: 'Übersicht Ihres Angebots.' },
+ { id: 'Contact', label: 'Kontakt', desc: 'Anlaufstelle für Ihre Kunden.' },
+ { id: 'Landing', label: 'Landingpage', desc: 'Optimiert für Marketing-Kampagnen.' },
+ { id: 'Legal', label: 'Rechtliches', desc: 'Impressum & Datenschutz.' },
+ ].map(opt => (
updateState({ selectedPages: toggleItem(state.selectedPages, p.id) })}
+ key={opt.id} label={opt.label} desc={opt.desc}
+ checked={state.selectedPages.includes(opt.id)}
+ onChange={() => updateState({ selectedPages: toggleItem(state.selectedPages, opt.id) })}
/>
))}
-
-
-
Weitere Seiten?
-
updateState({ otherPages: [...state.otherPages, v] })}
- onRemove={(i) => updateState({ otherPages: state.otherPages.filter((_, idx) => idx !== i) })}
- placeholder="z.B. Team-Detail, FAQ..."
- />
-
-
-
Sitemap hochladen (optional)
-
{ e.preventDefault(); e.stopPropagation(); }}
- onDrop={(e) => {
- e.preventDefault();
- e.stopPropagation();
- const file = e.dataTransfer.files?.[0];
- if (file) updateState({ sitemapFile: file });
- }}
- onClick={() => document.getElementById('sitemap-upload')?.click()}
- >
-
{
- const file = e.target.files?.[0];
- if (file) updateState({ sitemapFile: file });
- }} />
- {state.sitemapFile ? (
-
-
- {state.sitemapFile.name}
- { e.stopPropagation(); updateState({ sitemapFile: null }); }} className="p-1 hover:bg-slate-200 rounded-full transition-colors focus:outline-none overflow-hidden relative rounded-full">
-
- ) : (
- <>
-
-
Sitemap hierher ziehen oder klicken
- >
- )}
-
-
-
-
-
-
-
Was zählt als Seite?
-
Eine Seite ist ein eigenständiges Layout. Wenn Sie 10 Leistungen haben, die alle das gleiche Layout nutzen, zählt das als 1 Seite plus ein "System-Modul" für die Verwaltung der Leistungen.
-
+
+
Weitere individuelle Seiten?
+
updateState({ otherPages: [...state.otherPages, v] })}
+ onRemove={(i) => updateState({ otherPages: state.otherPages.filter((_, idx) => idx !== i) })}
+ placeholder="z.B. Karriere, FAQ, Team-Detail..."
+ />
);
diff --git a/src/components/ContactForm/steps/ContactStep.tsx b/src/components/ContactForm/steps/ContactStep.tsx
index 7b517ae..8e5a0fb 100644
--- a/src/components/ContactForm/steps/ContactStep.tsx
+++ b/src/components/ContactForm/steps/ContactStep.tsx
@@ -11,18 +11,44 @@ interface ContactStepProps {
export function ContactStep({ state, updateState }: ContactStepProps) {
return (
-