feat(web): streamline contact form and integrate identity fields
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 1m9s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🏗️ Build (push) Failing after 30m21s
Build & Deploy / 🧪 QA (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 4s
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 1m9s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🏗️ Build (push) Failing after 30m21s
Build & Deploy / 🧪 QA (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 4s
This commit is contained in:
@@ -11,8 +11,11 @@ import configPromise from "@payload-config";
|
|||||||
export async function sendContactInquiry(data: {
|
export async function sendContactInquiry(data: {
|
||||||
name: string;
|
name: string;
|
||||||
email: string;
|
email: string;
|
||||||
|
phone?: string;
|
||||||
|
role?: string;
|
||||||
companyName: string;
|
companyName: string;
|
||||||
projectType: string;
|
projectType: string;
|
||||||
|
deadline?: string;
|
||||||
message: string;
|
message: string;
|
||||||
isFreeText: boolean;
|
isFreeText: boolean;
|
||||||
config?: any;
|
config?: any;
|
||||||
@@ -25,8 +28,11 @@ export async function sendContactInquiry(data: {
|
|||||||
data: {
|
data: {
|
||||||
name: data.name,
|
name: data.name,
|
||||||
email: data.email,
|
email: data.email,
|
||||||
|
phone: data.phone,
|
||||||
|
role: data.role,
|
||||||
companyName: data.companyName,
|
companyName: data.companyName,
|
||||||
projectType: data.projectType,
|
projectType: data.projectType,
|
||||||
|
deadline: data.deadline,
|
||||||
message: data.message,
|
message: data.message,
|
||||||
isFreeText: data.isFreeText,
|
isFreeText: data.isFreeText,
|
||||||
config: data.config || null,
|
config: data.config || null,
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export function ContactForm({
|
|||||||
initialStepIndex = 0,
|
initialStepIndex = 0,
|
||||||
initialState: injectedState,
|
initialState: injectedState,
|
||||||
}: ContactFormProps) {
|
}: ContactFormProps) {
|
||||||
const [flow, setFlow] = useState<FlowState>("discovery");
|
const [flow, setFlow] = useState<FlowState>("direct-message");
|
||||||
const [stepIndex, setStepIndex] = useState(initialStepIndex);
|
const [stepIndex, setStepIndex] = useState(initialStepIndex);
|
||||||
const [state, setState] = useState<FormState>({
|
const [state, setState] = useState<FormState>({
|
||||||
...initialState,
|
...initialState,
|
||||||
@@ -125,8 +125,11 @@ export function ContactForm({
|
|||||||
const result = await sendContactInquiry({
|
const result = await sendContactInquiry({
|
||||||
name: state.name,
|
name: state.name,
|
||||||
email: state.email,
|
email: state.email,
|
||||||
|
phone: state.phone,
|
||||||
|
role: state.role,
|
||||||
companyName: state.companyName,
|
companyName: state.companyName,
|
||||||
projectType: state.projectType,
|
projectType: state.projectType,
|
||||||
|
deadline: state.deadline,
|
||||||
message: state.message,
|
message: state.message,
|
||||||
isFreeText: flow === "direct-message",
|
isFreeText: flow === "direct-message",
|
||||||
config: flow === "configurator" ? state : undefined,
|
config: flow === "configurator" ? state : undefined,
|
||||||
@@ -190,7 +193,7 @@ export function ContactForm({
|
|||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsSubmitted(false);
|
setIsSubmitted(false);
|
||||||
setFlow("discovery");
|
setFlow("direct-message"); // Reset back to dm
|
||||||
setStepIndex(0);
|
setStepIndex(0);
|
||||||
setState(initialState);
|
setState(initialState);
|
||||||
}}
|
}}
|
||||||
@@ -204,7 +207,7 @@ export function ContactForm({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gateway Flow
|
// Gateway Flow (Disabled but kept logically if needed, can just not render)
|
||||||
if (flow === "discovery") {
|
if (flow === "discovery") {
|
||||||
return (
|
return (
|
||||||
<ContactGateway
|
<ContactGateway
|
||||||
@@ -225,13 +228,23 @@ export function ContactForm({
|
|||||||
return (
|
return (
|
||||||
<DirectMessageFlow
|
<DirectMessageFlow
|
||||||
name={state.name}
|
name={state.name}
|
||||||
|
setName={(v) => updateState({ name: v })}
|
||||||
email={state.email}
|
email={state.email}
|
||||||
setEmail={(v) => updateState({ email: v })}
|
setEmail={(v) => updateState({ email: v })}
|
||||||
|
phone={state.phone}
|
||||||
|
setPhone={(v) => updateState({ phone: v })}
|
||||||
|
role={state.role}
|
||||||
|
setRole={(v) => updateState({ role: v })}
|
||||||
company={state.companyName}
|
company={state.companyName}
|
||||||
|
setCompany={(v) => updateState({ companyName: v })}
|
||||||
|
projectType={state.projectType}
|
||||||
|
setProjectType={(v) => updateState({ projectType: v })}
|
||||||
|
deadline={state.deadline}
|
||||||
|
setDeadline={(v) => updateState({ deadline: v })}
|
||||||
message={state.message}
|
message={state.message}
|
||||||
setMessage={(v) => updateState({ message: v })}
|
setMessage={(v) => updateState({ message: v })}
|
||||||
onBack={() => setFlow("discovery")}
|
onBack={() => setFlow("discovery")} // Can keep, but won't be seen if we hide button
|
||||||
onSubmit={handleSubmit}
|
onSubmit={() => handleSubmit()}
|
||||||
isSubmitting={isSubmitting}
|
isSubmitting={isSubmitting}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -88,41 +88,40 @@ export const ContactGateway = ({
|
|||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 w-full">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 w-full">
|
||||||
{/* Configurator Path */}
|
{/* Configurator Path */}
|
||||||
<Reveal width="100%" delay={0.3} direction="up">
|
<Reveal width="100%" delay={0.3} direction="up">
|
||||||
<button
|
<div className="relative group p-[1px] rounded-3xl overflow-hidden transition-all duration-500">
|
||||||
onClick={onChooseConfigurator}
|
{/* Disabled Overlay Background */}
|
||||||
disabled={!name}
|
<div className="absolute inset-0 bg-gradient-to-br from-slate-200 to-slate-100 dark:from-slate-800 dark:to-slate-900 opacity-50" />
|
||||||
className={cn(
|
|
||||||
"group relative flex flex-col items-start p-8 rounded-3xl border text-left transition-all duration-500 overflow-hidden",
|
|
||||||
name
|
|
||||||
? "bg-slate-900 border-slate-800 text-white shadow-2xl hover:-translate-y-2"
|
|
||||||
: "bg-slate-50 border-slate-100 text-slate-400 cursor-not-allowed opacity-60",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<div className="absolute top-0 right-0 p-8 opacity-10 group-hover:opacity-20 transition-opacity">
|
|
||||||
<Settings2 size={120} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Settings2 size={24} className="mb-6 text-green-400" />
|
<button
|
||||||
<h3 className="text-2xl font-bold mb-2 tracking-tight">
|
disabled
|
||||||
System-Konfigurator
|
className={cn(
|
||||||
</h3>
|
"w-full h-full relative flex flex-col items-start p-8 rounded-[23px] border text-left bg-slate-50 border-slate-100 text-slate-400 cursor-not-allowed",
|
||||||
<p className="text-sm text-slate-400 font-medium mb-8 max-w-[280px]">
|
)}
|
||||||
Konfigurieren Sie Ihr Projekt modular für eine präzise
|
>
|
||||||
Aufwandsschätzung.
|
<div className="absolute top-0 right-0 p-8 opacity-5">
|
||||||
</p>
|
<Settings2 size={120} />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="mt-auto flex items-center gap-2 text-[10px] font-mono uppercase tracking-widest font-bold">
|
<div className="flex items-center gap-3 mb-6">
|
||||||
<span>Sitzung starten</span>
|
<Settings2 size={24} className="text-slate-300" />
|
||||||
<ArrowRight
|
<span className="px-2 py-0.5 rounded-full bg-slate-200 text-[8px] font-bold uppercase tracking-wider text-slate-500">
|
||||||
size={14}
|
Wartungsmodus
|
||||||
className="group-hover:translate-x-1 transition-transform"
|
</span>
|
||||||
/>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
{!name && (
|
<h3 className="text-2xl font-bold mb-2 tracking-tight opacity-50">
|
||||||
<div className="absolute inset-0 bg-slate-50/60 backdrop-blur-[6px] z-20" />
|
System-Konfigurator
|
||||||
)}
|
</h3>
|
||||||
</button>
|
<p className="text-sm text-slate-400 font-medium mb-8 max-w-[280px] opacity-70">
|
||||||
|
Dieser Modus wird aktuell optimiert und steht in Kürze wieder
|
||||||
|
zur Verfügung.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="mt-auto flex items-center gap-2 text-[10px] font-mono uppercase tracking-widest font-bold opacity-30">
|
||||||
|
<span>Konfigurator offline</span>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</Reveal>
|
</Reveal>
|
||||||
|
|
||||||
{/* Direct Mail Path */}
|
{/* Direct Mail Path */}
|
||||||
|
|||||||
@@ -3,13 +3,33 @@
|
|||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { cn } from "../../utils/cn";
|
import { cn } from "../../utils/cn";
|
||||||
import { Reveal } from "../Reveal";
|
import { Reveal } from "../Reveal";
|
||||||
import { Mail, MessageSquare, ArrowLeft, Send } from "lucide-react";
|
import { ProjectType } from "./types";
|
||||||
|
import {
|
||||||
|
Mail,
|
||||||
|
MessageSquare,
|
||||||
|
ArrowLeft,
|
||||||
|
Send,
|
||||||
|
Phone,
|
||||||
|
User as UserIcon,
|
||||||
|
Calendar,
|
||||||
|
Layers,
|
||||||
|
} from "lucide-react";
|
||||||
|
|
||||||
interface DirectMessageFlowProps {
|
interface DirectMessageFlowProps {
|
||||||
name: string;
|
name: string;
|
||||||
|
setName: (val: string) => void;
|
||||||
email: string;
|
email: string;
|
||||||
setEmail: (val: string) => void;
|
setEmail: (val: string) => void;
|
||||||
|
phone: string;
|
||||||
|
setPhone: (val: string) => void;
|
||||||
|
role: string;
|
||||||
|
setRole: (val: string) => void;
|
||||||
company: string;
|
company: string;
|
||||||
|
setCompany: (val: string) => void;
|
||||||
|
projectType: ProjectType;
|
||||||
|
setProjectType: (val: ProjectType) => void;
|
||||||
|
deadline: string;
|
||||||
|
setDeadline: (val: string) => void;
|
||||||
message: string;
|
message: string;
|
||||||
setMessage: (val: string) => void;
|
setMessage: (val: string) => void;
|
||||||
onBack: () => void;
|
onBack: () => void;
|
||||||
@@ -19,9 +39,19 @@ interface DirectMessageFlowProps {
|
|||||||
|
|
||||||
export const DirectMessageFlow = ({
|
export const DirectMessageFlow = ({
|
||||||
name,
|
name,
|
||||||
|
setName,
|
||||||
email,
|
email,
|
||||||
setEmail,
|
setEmail,
|
||||||
|
phone,
|
||||||
|
setPhone,
|
||||||
|
role,
|
||||||
|
setRole,
|
||||||
company,
|
company,
|
||||||
|
setCompany,
|
||||||
|
projectType,
|
||||||
|
setProjectType,
|
||||||
|
deadline,
|
||||||
|
setDeadline,
|
||||||
message,
|
message,
|
||||||
setMessage,
|
setMessage,
|
||||||
onBack,
|
onBack,
|
||||||
@@ -30,72 +60,167 @@ export const DirectMessageFlow = ({
|
|||||||
}: DirectMessageFlowProps) => {
|
}: DirectMessageFlowProps) => {
|
||||||
return (
|
return (
|
||||||
<div className="w-full max-w-3xl mx-auto px-4 py-12">
|
<div className="w-full max-w-3xl mx-auto px-4 py-12">
|
||||||
<Reveal width="100%" delay={0.1}>
|
|
||||||
<button
|
|
||||||
onClick={onBack}
|
|
||||||
className="flex items-center gap-2 text-[10px] font-mono font-bold uppercase tracking-widest text-slate-400 hover:text-slate-900 transition-colors mb-12"
|
|
||||||
>
|
|
||||||
<ArrowLeft size={14} /> Zurück zur Auswahl
|
|
||||||
</button>
|
|
||||||
</Reveal>
|
|
||||||
|
|
||||||
<div className="space-y-12">
|
<div className="space-y-12">
|
||||||
<Reveal width="100%" delay={0.2}>
|
<Reveal width="100%" delay={0.2}>
|
||||||
<div className="space-y-2">
|
<div className="space-y-4">
|
||||||
<span className="text-[10px] font-mono text-green-600 uppercase tracking-[0.3em] font-bold">
|
<span className="text-[10px] font-mono text-green-600 uppercase tracking-[0.3em] font-bold">
|
||||||
DIREKTNACHRICHT // MODUS_AKTIVIERT
|
DIREKTANFRAGE // SCHRITT_01
|
||||||
</span>
|
</span>
|
||||||
<h2 className="text-3xl font-bold tracking-tight text-slate-900">
|
<h2 className="text-3xl md:text-5xl font-bold tracking-tight text-slate-900 line-clamp-2">
|
||||||
Wie kann ich helfen, {name.split(" ")[0]}?
|
Lassen Sie uns sprechen.
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-slate-500 font-medium">
|
<p className="text-slate-500 font-medium text-lg">
|
||||||
Sende mir eine Nachricht zu {company || "deinem Projekt"} und ich
|
Senden Sie mir eine Nachricht und ich melde mich zeitnah zurück.
|
||||||
melde mich in Kürze.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</Reveal>
|
</Reveal>
|
||||||
|
|
||||||
<div className="space-y-8">
|
<div className="space-y-12">
|
||||||
{/* Email Input */}
|
{/* Section: Mission Focus */}
|
||||||
<Reveal width="100%" delay={0.3} direction="up">
|
<Reveal width="100%" delay={0.3} direction="up">
|
||||||
<div className="space-y-4">
|
<div className="space-y-6">
|
||||||
<label className="flex items-center gap-2 text-[10px] font-mono font-bold uppercase tracking-widest text-slate-400">
|
<label className="flex items-center gap-2 text-[10px] font-mono font-bold uppercase tracking-widest text-slate-400">
|
||||||
<Mail size={12} /> Rückantwort an
|
<Layers size={12} /> Mission // Projekt-Typ
|
||||||
</label>
|
</label>
|
||||||
<input
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
|
||||||
type="email"
|
{(["website", "web-app", "ecommerce"] as ProjectType[]).map(
|
||||||
value={email}
|
(type) => {
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
const labels = {
|
||||||
placeholder="ihre@email.de"
|
website: "Website",
|
||||||
className="w-full bg-slate-50 border border-slate-100 rounded-2xl px-6 py-4 text-lg font-medium focus:outline-none focus:ring-2 focus:ring-slate-900/5 focus:border-slate-900 transition-all"
|
"web-app": "Web Application",
|
||||||
/>
|
ecommerce: "E-Commerce",
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={type}
|
||||||
|
onClick={() => setProjectType(type)}
|
||||||
|
className={cn(
|
||||||
|
"px-6 py-4 rounded-xl border font-bold text-sm transition-all duration-200 text-left",
|
||||||
|
projectType === type
|
||||||
|
? "bg-slate-900 text-white border-slate-900 shadow-lg"
|
||||||
|
: "bg-white border-slate-100 text-slate-500 hover:border-slate-300",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{labels[type]}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Reveal>
|
</Reveal>
|
||||||
|
|
||||||
{/* Message Input */}
|
{/* Section: Identity Details */}
|
||||||
<Reveal width="100%" delay={0.4} direction="up">
|
<Reveal width="100%" delay={0.4} direction="up">
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
|
<div className="space-y-4">
|
||||||
|
<label className="flex items-center gap-2 text-[10px] font-mono font-bold uppercase tracking-widest text-slate-400">
|
||||||
|
<UserIcon size={12} /> Ihr Name
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={name}
|
||||||
|
onChange={(e) => setName(e.target.value)}
|
||||||
|
placeholder="Max Mustermann"
|
||||||
|
className="w-full bg-slate-50 border border-slate-100 rounded-xl px-4 py-3 text-base font-medium focus:outline-none focus:ring-2 focus:ring-slate-900/5 focus:border-slate-900 transition-all"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<label className="flex items-center gap-2 text-[10px] font-mono font-bold uppercase tracking-widest text-slate-400">
|
||||||
|
<Layers size={12} /> Unternehmen (Optional)
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={company}
|
||||||
|
onChange={(e) => setCompany(e.target.value)}
|
||||||
|
placeholder="Beispiel GmbH"
|
||||||
|
className="w-full bg-slate-50 border border-slate-100 rounded-xl px-4 py-3 text-base font-medium focus:outline-none focus:ring-2 focus:ring-slate-900/5 focus:border-slate-900 transition-all"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<label className="flex items-center gap-2 text-[10px] font-mono font-bold uppercase tracking-widest text-slate-400">
|
||||||
|
<Mail size={12} /> E-Mail Adresse
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
value={email}
|
||||||
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
|
placeholder="name@firma.de"
|
||||||
|
className="w-full bg-slate-50 border border-slate-100 rounded-xl px-4 py-3 text-base font-medium focus:outline-none focus:ring-2 focus:ring-slate-900/5 focus:border-slate-900 transition-all"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<label className="flex items-center gap-2 text-[10px] font-mono font-bold uppercase tracking-widest text-slate-400">
|
||||||
|
<Phone size={12} /> Rückruf-Nummer (Optional)
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="tel"
|
||||||
|
value={phone}
|
||||||
|
onChange={(e) => setPhone(e.target.value)}
|
||||||
|
placeholder="+49 123 456789"
|
||||||
|
className="w-full bg-slate-50 border border-slate-100 rounded-xl px-4 py-3 text-base font-medium focus:outline-none focus:ring-2 focus:ring-slate-900/5 focus:border-slate-900 transition-all"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Reveal>
|
||||||
|
|
||||||
|
{/* Section: Additional Context */}
|
||||||
|
<Reveal width="100%" delay={0.5} direction="up">
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
|
<div className="space-y-4">
|
||||||
|
<label className="flex items-center gap-2 text-[10px] font-mono font-bold uppercase tracking-widest text-slate-400">
|
||||||
|
<UserIcon size={12} /> Ihre Position
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={role}
|
||||||
|
onChange={(e) => setRole(e.target.value)}
|
||||||
|
placeholder="z.B. Gründer, Marketing Lead..."
|
||||||
|
className="w-full bg-slate-50 border border-slate-100 rounded-xl px-4 py-3 text-base font-medium focus:outline-none focus:ring-2 focus:ring-slate-900/5 focus:border-slate-900 transition-all"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<label className="flex items-center gap-2 text-[10px] font-mono font-bold uppercase tracking-widest text-slate-400">
|
||||||
|
<Calendar size={12} /> Zeitfenster
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
value={deadline}
|
||||||
|
onChange={(e) => setDeadline(e.target.value)}
|
||||||
|
className="w-full bg-slate-50 border border-slate-100 rounded-xl px-4 py-3 text-base font-medium focus:outline-none focus:ring-2 focus:ring-slate-900/5 focus:border-slate-900 transition-all appearance-none cursor-pointer"
|
||||||
|
>
|
||||||
|
<option value="asap">ASAP (Sofort)</option>
|
||||||
|
<option value="1month">< 1 Monat (Priorität)</option>
|
||||||
|
<option value="3months">1-3 Monate (Standard)</option>
|
||||||
|
<option value="flexible">Flexibel</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Reveal>
|
||||||
|
|
||||||
|
{/* Section: Payload */}
|
||||||
|
<Reveal width="100%" delay={0.6} direction="up">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<label className="flex items-center gap-2 text-[10px] font-mono font-bold uppercase tracking-widest text-slate-400">
|
<label className="flex items-center gap-2 text-[10px] font-mono font-bold uppercase tracking-widest text-slate-400">
|
||||||
<MessageSquare size={12} /> Ihre Nachricht
|
<MessageSquare size={12} /> Nachricht // Briefing
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
value={message}
|
value={message}
|
||||||
onChange={(e) => setMessage(e.target.value)}
|
onChange={(e) => setMessage(e.target.value)}
|
||||||
placeholder="Beschreiben Sie kurz Ihr Anliegen..."
|
placeholder="Beschreiben Sie kurz Ihr Anliegen oder hinterlassen Sie einen Link zum Briefing..."
|
||||||
rows={6}
|
rows={5}
|
||||||
className="w-full bg-slate-50 border border-slate-100 rounded-2xl px-6 py-4 text-lg font-medium focus:outline-none focus:ring-2 focus:ring-slate-900/5 focus:border-slate-900 transition-all resize-none"
|
className="w-full bg-slate-50 border border-slate-100 rounded-2xl px-6 py-4 text-base font-medium focus:outline-none focus:ring-2 focus:ring-slate-900/5 focus:border-slate-900 transition-all resize-none"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Reveal>
|
</Reveal>
|
||||||
|
|
||||||
{/* Submit Button */}
|
{/* Submit Button */}
|
||||||
<Reveal width="100%" delay={0.5} direction="up">
|
<Reveal width="100%" delay={0.7} direction="up">
|
||||||
<button
|
<button
|
||||||
onClick={onSubmit}
|
onClick={onSubmit}
|
||||||
disabled={isSubmitting || !email || !message}
|
disabled={isSubmitting || !email || !message || !name}
|
||||||
className={cn(
|
className={cn(
|
||||||
"group relative w-full py-5 rounded-2xl font-bold text-lg transition-all duration-300 flex items-center justify-center gap-3 overflow-hidden",
|
"group relative w-full py-5 rounded-2xl font-bold text-lg transition-all duration-300 flex items-center justify-center gap-3 overflow-hidden",
|
||||||
isSubmitting || !email || !message
|
isSubmitting || !email || !message || !name
|
||||||
? "bg-slate-100 text-slate-400 cursor-not-allowed"
|
? "bg-slate-100 text-slate-400 cursor-not-allowed"
|
||||||
: "bg-slate-900 text-white shadow-xl hover:shadow-2xl hover:-translate-y-1 active:scale-[0.98]",
|
: "bg-slate-900 text-white shadow-xl hover:shadow-2xl hover:-translate-y-1 active:scale-[0.98]",
|
||||||
)}
|
)}
|
||||||
@@ -109,7 +234,7 @@ export const DirectMessageFlow = ({
|
|||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<span>Nachricht absenden</span>
|
<span>Anfrage senden</span>
|
||||||
<Send
|
<Send
|
||||||
size={20}
|
size={20}
|
||||||
className="group-hover:translate-x-1 group-hover:-translate-y-1 transition-transform"
|
className="group-hover:translate-x-1 group-hover:-translate-y-1 transition-transform"
|
||||||
|
|||||||
@@ -21,15 +21,19 @@ export const getInquiryEmailHtml = (data: any) => `
|
|||||||
<div class="title">NEUE_ANFRAGE_INPUT</div>
|
<div class="title">NEUE_ANFRAGE_INPUT</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="section">
|
|
||||||
<div class="label">ABSENDER</div>
|
<div class="label">ABSENDER</div>
|
||||||
<div class="value">${data.name} (${data.email})</div>
|
<div class="value">${data.name} (${data.email})</div>
|
||||||
|
|
||||||
|
${data.phone ? `<div class="label">TELEFON</div><div class="value">${data.phone}</div>` : ""}
|
||||||
|
${data.role ? `<div class="label">POSITION</div><div class="value">${data.role}</div>` : ""}
|
||||||
|
|
||||||
<div class="label">UNTERNEHMEN</div>
|
<div class="label">UNTERNEHMEN</div>
|
||||||
<div class="value">${data.companyName || "N/A"}</div>
|
<div class="value">${data.companyName || "N/A"}</div>
|
||||||
|
|
||||||
<div class="label">PROJEKT_TYP</div>
|
<div class="label">PROJEKT_TYP</div>
|
||||||
<div class="value">${data.projectType}</div>
|
<div class="value">${data.projectType}</div>
|
||||||
|
|
||||||
|
${data.deadline ? `<div class="label">ZEITRAUM</div><div class="value">${data.deadline}</div>` : ""}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
${
|
${
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ export interface FormState {
|
|||||||
storageExpansion: number;
|
storageExpansion: number;
|
||||||
name: string;
|
name: string;
|
||||||
email: string;
|
email: string;
|
||||||
|
phone: string;
|
||||||
role: string;
|
role: string;
|
||||||
message: string;
|
message: string;
|
||||||
sitemapFile: File | null;
|
sitemapFile: File | null;
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ export const initialState: FormState = {
|
|||||||
storageExpansion: 0,
|
storageExpansion: 0,
|
||||||
name: "",
|
name: "",
|
||||||
email: "",
|
email: "",
|
||||||
|
phone: "",
|
||||||
role: "",
|
role: "",
|
||||||
message: "",
|
message: "",
|
||||||
sitemapFile: null,
|
sitemapFile: null,
|
||||||
|
|||||||
@@ -1,89 +1,90 @@
|
|||||||
export type ProjectType = 'website' | 'web-app';
|
export type ProjectType = "website" | "web-app";
|
||||||
|
|
||||||
export interface FormState {
|
export interface FormState {
|
||||||
projectType: ProjectType;
|
projectType: ProjectType;
|
||||||
// Company
|
// Company
|
||||||
companyName: string;
|
companyName: string;
|
||||||
employeeCount: string;
|
employeeCount: string;
|
||||||
// Existing Presence
|
// Existing Presence
|
||||||
existingWebsite: string;
|
existingWebsite: string;
|
||||||
socialMedia: string[];
|
socialMedia: string[];
|
||||||
socialMediaUrls: Record<string, string>;
|
socialMediaUrls: Record<string, string>;
|
||||||
existingDomain: string;
|
existingDomain: string;
|
||||||
wishedDomain: string;
|
wishedDomain: string;
|
||||||
// Project
|
// Project
|
||||||
websiteTopic: string;
|
websiteTopic: string;
|
||||||
selectedPages: string[];
|
selectedPages: string[];
|
||||||
otherPages: string[];
|
otherPages: string[];
|
||||||
otherPagesCount: number;
|
otherPagesCount: number;
|
||||||
features: string[];
|
features: string[];
|
||||||
otherFeatures: string[];
|
otherFeatures: string[];
|
||||||
otherFeaturesCount: number;
|
otherFeaturesCount: number;
|
||||||
functions: string[];
|
functions: string[];
|
||||||
otherFunctions: string[];
|
otherFunctions: string[];
|
||||||
otherFunctionsCount: number;
|
otherFunctionsCount: number;
|
||||||
apiSystems: string[];
|
apiSystems: string[];
|
||||||
otherTech: string[];
|
otherTech: string[];
|
||||||
otherTechCount: number;
|
otherTechCount: number;
|
||||||
assets: string[];
|
assets: string[];
|
||||||
otherAssets: string[];
|
otherAssets: string[];
|
||||||
otherAssetsCount: number;
|
otherAssetsCount: number;
|
||||||
newDatasets: number;
|
newDatasets: number;
|
||||||
cmsSetup: boolean;
|
cmsSetup: boolean;
|
||||||
storageExpansion: number;
|
storageExpansion: number;
|
||||||
name: string;
|
name: string;
|
||||||
email: string;
|
email: string;
|
||||||
role: string;
|
phone: string;
|
||||||
message: string;
|
role: string;
|
||||||
sitemapFile: any; // Using any for File/null to be CLI-compatible
|
message: string;
|
||||||
contactFiles: any[]; // Using any[] for File[]
|
sitemapFile: any; // Using any for File/null to be CLI-compatible
|
||||||
// Design
|
contactFiles: any[]; // Using any[] for File[]
|
||||||
designVibe: string;
|
// Design
|
||||||
colorScheme: string[];
|
designVibe: string;
|
||||||
references: string[];
|
colorScheme: string[];
|
||||||
designWishes: string;
|
references: string[];
|
||||||
// Maintenance
|
designWishes: string;
|
||||||
expectedAdjustments: string;
|
// Maintenance
|
||||||
languagesList: string[];
|
expectedAdjustments: string;
|
||||||
// Timeline
|
languagesList: string[];
|
||||||
deadline: string;
|
// Timeline
|
||||||
// Web App specific
|
deadline: string;
|
||||||
targetAudience: string;
|
// Web App specific
|
||||||
userRoles: string[];
|
targetAudience: string;
|
||||||
dataSensitivity: string;
|
userRoles: string[];
|
||||||
platformType: string;
|
dataSensitivity: string;
|
||||||
// Meta
|
platformType: string;
|
||||||
dontKnows: string[];
|
// Meta
|
||||||
visualStaging: string;
|
dontKnows: string[];
|
||||||
complexInteractions: string;
|
visualStaging: string;
|
||||||
gridDontKnows?: Record<string, string>;
|
complexInteractions: string;
|
||||||
briefingSummary?: string;
|
gridDontKnows?: Record<string, string>;
|
||||||
companyAddress?: string;
|
briefingSummary?: string;
|
||||||
companyPhone?: string;
|
companyAddress?: string;
|
||||||
personName?: string;
|
companyPhone?: string;
|
||||||
taxId?: string;
|
personName?: string;
|
||||||
designVision?: string;
|
taxId?: string;
|
||||||
positionDescriptions?: Record<string, string>;
|
designVision?: string;
|
||||||
sitemap?: {
|
positionDescriptions?: Record<string, string>;
|
||||||
category: string;
|
sitemap?: {
|
||||||
pages: { title: string; desc: string }[];
|
category: string;
|
||||||
}[];
|
pages: { title: string; desc: string }[];
|
||||||
|
}[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Position {
|
export interface Position {
|
||||||
pos: number;
|
pos: number;
|
||||||
title: string;
|
title: string;
|
||||||
desc: string;
|
desc: string;
|
||||||
qty: number;
|
qty: number;
|
||||||
price: number;
|
price: number;
|
||||||
isRecurring?: boolean;
|
isRecurring?: boolean;
|
||||||
}
|
}
|
||||||
export interface Totals {
|
export interface Totals {
|
||||||
totalPrice: number;
|
totalPrice: number;
|
||||||
monthlyPrice: number;
|
monthlyPrice: number;
|
||||||
totalPagesCount: number;
|
totalPagesCount: number;
|
||||||
totalFeatures: number;
|
totalFeatures: number;
|
||||||
totalFunctions: number;
|
totalFunctions: number;
|
||||||
totalApis: number;
|
totalApis: number;
|
||||||
languagesCount: number;
|
languagesCount: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,10 +62,22 @@ export const Inquiries: CollectionConfig = {
|
|||||||
name: "companyName",
|
name: "companyName",
|
||||||
type: "text",
|
type: "text",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "phone",
|
||||||
|
type: "text",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "role",
|
||||||
|
type: "text",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "projectType",
|
name: "projectType",
|
||||||
type: "text",
|
type: "text",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "deadline",
|
||||||
|
type: "text",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "message",
|
name: "message",
|
||||||
type: "textarea",
|
type: "textarea",
|
||||||
|
|||||||
Reference in New Issue
Block a user