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

This commit is contained in:
2026-04-07 23:08:04 +02:00
parent aeb1814a34
commit 2097b571f3
9 changed files with 318 additions and 156 deletions

View File

@@ -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,

View File

@@ -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}
/> />
); );

View File

@@ -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 */}

View File

@@ -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">&lt; 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"

View File

@@ -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>
${ ${

View File

@@ -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;

View File

@@ -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,

View File

@@ -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;
} }

View File

@@ -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",