feat(leads): Lead Creation Phase 2 — Job Details section with live fields
- UrgencyPillSelector: 3-pill selector (Standard/High/Emergency) with Framer Motion layoutId sliding background, spring physics, color transition on text - LeadJobSection: full Job Details fields — Lead Source (always), Lead Type / Work Type / Trade Type (Full Form only, each row animates in/out with spring height collapse), Urgency pill (always), Notes textarea (Full Form only) - Full Form fields use overflow-hidden + height:auto spring animation — no phantom gaps since spacing is managed via pt-4 inside each animated block - CreateLeadPage: formData state tracking all sections (scaffold for Phase 3-6 fields), updateField handler, sectionCompletion map, GSAP progress bar now driven by real field completion percentage, Job Details section wired up with live data
This commit is contained in:
@@ -0,0 +1,191 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
|
import { ChevronDown } from 'lucide-react';
|
||||||
|
import { useTheme } from '../../context/ThemeContext';
|
||||||
|
import { LEAD_FORM_OPTIONS } from '../../data/mockStore';
|
||||||
|
import UrgencyPillSelector from './UrgencyPillSelector';
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Shared field primitives (local to Job section for now)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
function FieldLabel({ children }) {
|
||||||
|
return (
|
||||||
|
<label className="block text-[11px] uppercase tracking-widest font-bold text-zinc-400 dark:text-zinc-500 mb-1.5">
|
||||||
|
{children}
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function LeadSelect({ label, value, onChange, options, placeholder }) {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
const isDark = theme === 'dark';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<FieldLabel>{label}</FieldLabel>
|
||||||
|
<div className="relative">
|
||||||
|
<select
|
||||||
|
value={value}
|
||||||
|
onChange={e => onChange(e.target.value)}
|
||||||
|
className={`w-full appearance-none rounded-xl px-4 py-3 pr-10 text-sm font-medium cursor-pointer
|
||||||
|
outline-none transition-all duration-150
|
||||||
|
${isDark
|
||||||
|
? 'bg-zinc-800/60 border border-zinc-700 text-white focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20'
|
||||||
|
: 'bg-white border border-zinc-200 text-zinc-800 focus:border-blue-400 focus:ring-2 focus:ring-blue-100'
|
||||||
|
}
|
||||||
|
${!value ? (isDark ? 'text-zinc-500' : 'text-zinc-400') : ''}
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
<option value="" disabled>{placeholder || 'Select…'}</option>
|
||||||
|
{options.map(opt => (
|
||||||
|
<option key={opt} value={opt}>{opt}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
<ChevronDown
|
||||||
|
size={14}
|
||||||
|
className="absolute right-3.5 top-1/2 -translate-y-1/2 text-zinc-400 pointer-events-none"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function LeadTextarea({ label, value, onChange, placeholder, rows = 3 }) {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
const isDark = theme === 'dark';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<FieldLabel>{label}</FieldLabel>
|
||||||
|
<textarea
|
||||||
|
value={value}
|
||||||
|
onChange={e => onChange(e.target.value)}
|
||||||
|
placeholder={placeholder}
|
||||||
|
rows={rows}
|
||||||
|
className={`w-full rounded-xl px-4 py-3 text-sm font-medium resize-none
|
||||||
|
outline-none transition-all duration-150
|
||||||
|
${isDark
|
||||||
|
? 'bg-zinc-800/60 border border-zinc-700 text-white placeholder-zinc-500 focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20'
|
||||||
|
: 'bg-white border border-zinc-200 text-zinc-800 placeholder-zinc-400 focus:border-blue-400 focus:ring-2 focus:ring-blue-100'
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shared animation config for collapsing rows
|
||||||
|
const collapseTransition = {
|
||||||
|
type: 'spring',
|
||||||
|
stiffness: 300,
|
||||||
|
damping: 32,
|
||||||
|
opacity: { duration: 0.15 },
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Job Details Section
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
export default function LeadJobSection({ formData, updateField, isQuickCapture }) {
|
||||||
|
return (
|
||||||
|
// No gap/space-y — each block controls its own top padding to avoid
|
||||||
|
// phantom gaps from collapsed motion.divs
|
||||||
|
<div>
|
||||||
|
{/* --- Lead Source — always visible --- */}
|
||||||
|
<LeadSelect
|
||||||
|
label="Lead Source"
|
||||||
|
value={formData.leadSource}
|
||||||
|
onChange={v => updateField('leadSource', v)}
|
||||||
|
options={LEAD_FORM_OPTIONS.leadSources}
|
||||||
|
placeholder="How did you find this lead?"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* --- Lead Type — Full Form only --- */}
|
||||||
|
<AnimatePresence initial={false}>
|
||||||
|
{!isQuickCapture && (
|
||||||
|
<motion.div
|
||||||
|
key="leadType"
|
||||||
|
initial={{ height: 0, opacity: 0 }}
|
||||||
|
animate={{ height: 'auto', opacity: 1 }}
|
||||||
|
exit={{ height: 0, opacity: 0 }}
|
||||||
|
transition={collapseTransition}
|
||||||
|
className="overflow-hidden"
|
||||||
|
>
|
||||||
|
<div className="pt-4">
|
||||||
|
<LeadSelect
|
||||||
|
label="Lead Type"
|
||||||
|
value={formData.leadType}
|
||||||
|
onChange={v => updateField('leadType', v)}
|
||||||
|
options={LEAD_FORM_OPTIONS.leadTypes}
|
||||||
|
placeholder="Residential, Commercial…"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
|
|
||||||
|
{/* --- Work Type + Trade Type — Full Form only --- */}
|
||||||
|
<AnimatePresence initial={false}>
|
||||||
|
{!isQuickCapture && (
|
||||||
|
<motion.div
|
||||||
|
key="workTrade"
|
||||||
|
initial={{ height: 0, opacity: 0 }}
|
||||||
|
animate={{ height: 'auto', opacity: 1 }}
|
||||||
|
exit={{ height: 0, opacity: 0 }}
|
||||||
|
transition={collapseTransition}
|
||||||
|
className="overflow-hidden"
|
||||||
|
>
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 pt-4">
|
||||||
|
<LeadSelect
|
||||||
|
label="Work Type"
|
||||||
|
value={formData.workType}
|
||||||
|
onChange={v => updateField('workType', v)}
|
||||||
|
options={LEAD_FORM_OPTIONS.workTypes}
|
||||||
|
placeholder="Roof Replacement, Repair…"
|
||||||
|
/>
|
||||||
|
<LeadSelect
|
||||||
|
label="Trade Type"
|
||||||
|
value={formData.tradeType}
|
||||||
|
onChange={v => updateField('tradeType', v)}
|
||||||
|
options={LEAD_FORM_OPTIONS.tradeTypes}
|
||||||
|
placeholder="Roofing, Gutter, Siding…"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
|
|
||||||
|
{/* --- Urgency — always visible --- */}
|
||||||
|
<div className="pt-4">
|
||||||
|
<FieldLabel>Urgency</FieldLabel>
|
||||||
|
<UrgencyPillSelector
|
||||||
|
value={formData.urgency}
|
||||||
|
onChange={v => updateField('urgency', v)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* --- Notes — Full Form only --- */}
|
||||||
|
<AnimatePresence initial={false}>
|
||||||
|
{!isQuickCapture && (
|
||||||
|
<motion.div
|
||||||
|
key="notes"
|
||||||
|
initial={{ height: 0, opacity: 0 }}
|
||||||
|
animate={{ height: 'auto', opacity: 1 }}
|
||||||
|
exit={{ height: 0, opacity: 0 }}
|
||||||
|
transition={collapseTransition}
|
||||||
|
className="overflow-hidden"
|
||||||
|
>
|
||||||
|
<div className="pt-4">
|
||||||
|
<LeadTextarea
|
||||||
|
label="Notes"
|
||||||
|
value={formData.notes}
|
||||||
|
onChange={v => updateField('notes', v)}
|
||||||
|
placeholder="First impression, visible damage, special circumstances…"
|
||||||
|
rows={3}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { motion } from 'framer-motion';
|
||||||
|
import { useTheme } from '../../context/ThemeContext';
|
||||||
|
|
||||||
|
const OPTIONS = [
|
||||||
|
{ value: 'standard', label: 'Standard', color: '#6B7280' },
|
||||||
|
{ value: 'high', label: 'High', color: '#F59E0B' },
|
||||||
|
{ value: 'emergency', label: 'Emergency', color: '#EF4444' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function UrgencyPillSelector({ value, onChange }) {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
const isDark = theme === 'dark';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`flex items-center p-1 rounded-xl ${isDark ? 'bg-zinc-800' : 'bg-zinc-100/80'}`}>
|
||||||
|
{OPTIONS.map(opt => {
|
||||||
|
const isSelected = value === opt.value;
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={opt.value}
|
||||||
|
type="button"
|
||||||
|
onClick={() => onChange(opt.value)}
|
||||||
|
className="relative flex-1 flex items-center justify-center py-2.5 rounded-lg text-xs font-bold uppercase tracking-wider transition-colors duration-150"
|
||||||
|
>
|
||||||
|
{/* Sliding background — Framer Motion layoutId handles the move */}
|
||||||
|
{isSelected && (
|
||||||
|
<motion.div
|
||||||
|
layoutId="urgency-pill-bg"
|
||||||
|
className={`absolute inset-0.5 rounded-[7px] shadow-sm ${isDark ? 'bg-zinc-700' : 'bg-white'}`}
|
||||||
|
transition={{ type: 'spring', stiffness: 420, damping: 36 }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<span
|
||||||
|
className="relative z-10"
|
||||||
|
style={{
|
||||||
|
color: isSelected
|
||||||
|
? opt.color
|
||||||
|
: isDark ? '#71717a' : '#a1a1aa',
|
||||||
|
transition: 'color 0.15s',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{opt.label}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
+104
-27
@@ -8,6 +8,7 @@ import {
|
|||||||
ChevronLeft, Zap, FileText, Save,
|
ChevronLeft, Zap, FileText, Save,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import LeadSectionWrapper from '../components/leads/LeadSectionWrapper';
|
import LeadSectionWrapper from '../components/leads/LeadSectionWrapper';
|
||||||
|
import LeadJobSection from '../components/leads/LeadJobSection';
|
||||||
import gsap from 'gsap';
|
import gsap from 'gsap';
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -73,6 +74,41 @@ const sectionVariants = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Initial form state
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
const INITIAL_FORM = {
|
||||||
|
// Job Details
|
||||||
|
leadSource: '',
|
||||||
|
leadType: '',
|
||||||
|
workType: '',
|
||||||
|
tradeType: '',
|
||||||
|
urgency: 'standard',
|
||||||
|
notes: '',
|
||||||
|
// Contact (Phase 3)
|
||||||
|
firstName: '',
|
||||||
|
lastName: '',
|
||||||
|
phones: [],
|
||||||
|
emails: [],
|
||||||
|
// Property (Phase 4)
|
||||||
|
address: '',
|
||||||
|
city: '',
|
||||||
|
state: 'TX',
|
||||||
|
zip: '',
|
||||||
|
propertyType: '',
|
||||||
|
// Insurance (Phase 5)
|
||||||
|
insuranceCompany: '',
|
||||||
|
claimNumber: '',
|
||||||
|
claimStatus: '',
|
||||||
|
adjusterName: '',
|
||||||
|
adjusterPhone: '',
|
||||||
|
policyNumber: '',
|
||||||
|
// Assignment (Phase 6)
|
||||||
|
assignedTo: '',
|
||||||
|
priority: 'medium',
|
||||||
|
followUpDate: '',
|
||||||
|
};
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Component
|
// Component
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -90,19 +126,45 @@ export default function CreateLeadPage() {
|
|||||||
insurance: false,
|
insurance: false,
|
||||||
assignment: false,
|
assignment: false,
|
||||||
});
|
});
|
||||||
|
const [formData, setFormData] = useState(INITIAL_FORM);
|
||||||
|
|
||||||
const progressBarRef = useRef(null);
|
const progressBarRef = useRef(null);
|
||||||
|
|
||||||
// Animate progress bar on mount (Phase 1: stays at 0)
|
// Derived: which fields must be filled for this mode
|
||||||
|
const requiredFields = isQuickCapture
|
||||||
|
? ['leadSource']
|
||||||
|
: ['leadSource', 'leadType', 'workType'];
|
||||||
|
|
||||||
|
const filledCount = requiredFields.filter(f => formData[f]).length;
|
||||||
|
const progress = requiredFields.length > 0
|
||||||
|
? Math.round((filledCount / requiredFields.length) * 100)
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
// GSAP animates the progress bar whenever progress changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (progressBarRef.current) {
|
if (progressBarRef.current) {
|
||||||
gsap.fromTo(
|
gsap.to(progressBarRef.current, {
|
||||||
progressBarRef.current,
|
width: `${progress}%`,
|
||||||
{ scaleX: 0, transformOrigin: 'left' },
|
duration: 0.45,
|
||||||
{ scaleX: 0, duration: 0.6, ease: 'power2.out' }
|
ease: 'power2.out',
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
}, []);
|
}, [progress]);
|
||||||
|
|
||||||
|
// Section completion map — grows as phases add fields
|
||||||
|
const sectionCompletion = {
|
||||||
|
contact: false, // Phase 3
|
||||||
|
property: false, // Phase 4
|
||||||
|
job: isQuickCapture
|
||||||
|
? !!formData.leadSource
|
||||||
|
: !!(formData.leadSource && formData.leadType && formData.workType),
|
||||||
|
insurance: false, // Phase 5
|
||||||
|
assignment: false, // Phase 6
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateField = (field, value) => {
|
||||||
|
setFormData(prev => ({ ...prev, [field]: value }));
|
||||||
|
};
|
||||||
|
|
||||||
const toggleSection = (id) => {
|
const toggleSection = (id) => {
|
||||||
setOpenSections(prev => ({ ...prev, [id]: !prev[id] }));
|
setOpenSections(prev => ({ ...prev, [id]: !prev[id] }));
|
||||||
@@ -115,21 +177,47 @@ export default function CreateLeadPage() {
|
|||||||
const modeSwitchTo = (quick) => {
|
const modeSwitchTo = (quick) => {
|
||||||
if (quick === isQuickCapture) return;
|
if (quick === isQuickCapture) return;
|
||||||
setIsQuickCapture(quick);
|
setIsQuickCapture(quick);
|
||||||
// Close insurance + assignment when switching to quick capture
|
|
||||||
if (quick) {
|
if (quick) {
|
||||||
setOpenSections(prev => ({ ...prev, insurance: false, assignment: false }));
|
setOpenSections(prev => ({ ...prev, insurance: false, assignment: false }));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Render the content for a given section (placeholder until its phase is built)
|
||||||
|
const renderSectionContent = (section) => {
|
||||||
|
if (section.id === 'job') {
|
||||||
|
return (
|
||||||
|
<LeadJobSection
|
||||||
|
formData={formData}
|
||||||
|
updateField={updateField}
|
||||||
|
isQuickCapture={isQuickCapture}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Placeholder for sections not yet built
|
||||||
|
return (
|
||||||
|
<div className={`rounded-xl border-2 border-dashed px-6 py-8 flex flex-col items-center justify-center gap-2 text-center
|
||||||
|
${isDark ? 'border-zinc-700' : 'border-zinc-200'}
|
||||||
|
`}>
|
||||||
|
<section.icon
|
||||||
|
size={28}
|
||||||
|
style={{ color: section.accentColor, opacity: 0.35 }}
|
||||||
|
/>
|
||||||
|
<p className="text-xs font-semibold uppercase tracking-widest text-zinc-400">
|
||||||
|
{section.description}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`min-h-screen transition-colors duration-300
|
<div className={`min-h-screen transition-colors duration-300
|
||||||
${isDark ? 'bg-[#09090b]' : 'bg-zinc-100/80'}
|
${isDark ? 'bg-[#09090b]' : 'bg-zinc-100/80'}
|
||||||
`}>
|
`}>
|
||||||
{/* Progress bar — thin accent line at top */}
|
{/* --- Progress bar --- */}
|
||||||
<div className={`h-[3px] w-full ${isDark ? 'bg-zinc-800' : 'bg-zinc-200'}`}>
|
<div className={`h-[3px] w-full ${isDark ? 'bg-zinc-800' : 'bg-zinc-200'}`}>
|
||||||
<div
|
<div
|
||||||
ref={progressBarRef}
|
ref={progressBarRef}
|
||||||
className="h-full bg-blue-500 transition-all duration-500 ease-out"
|
className="h-full bg-blue-500"
|
||||||
style={{ width: '0%' }}
|
style={{ width: '0%' }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -141,7 +229,6 @@ export default function CreateLeadPage() {
|
|||||||
|
|
||||||
{/* --- Page Header --- */}
|
{/* --- Page Header --- */}
|
||||||
<div className="mb-7">
|
<div className="mb-7">
|
||||||
{/* Back breadcrumb */}
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => navigate(-1)}
|
onClick={() => navigate(-1)}
|
||||||
@@ -164,7 +251,7 @@ export default function CreateLeadPage() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Quick Capture / Full Form toggle */}
|
{/* Quick / Full Form toggle */}
|
||||||
<div className={`flex items-center p-1 rounded-xl gap-0.5 shrink-0
|
<div className={`flex items-center p-1 rounded-xl gap-0.5 shrink-0
|
||||||
${isDark ? 'bg-zinc-800' : 'bg-zinc-200/80'}
|
${isDark ? 'bg-zinc-800' : 'bg-zinc-200/80'}
|
||||||
`}>
|
`}>
|
||||||
@@ -195,7 +282,7 @@ export default function CreateLeadPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Mode indicator pills */}
|
{/* Section indicator pills */}
|
||||||
<div className="flex items-center gap-2 mt-4 flex-wrap">
|
<div className="flex items-center gap-2 mt-4 flex-wrap">
|
||||||
{SECTIONS.map(s => (
|
{SECTIONS.map(s => (
|
||||||
<div
|
<div
|
||||||
@@ -243,19 +330,9 @@ export default function CreateLeadPage() {
|
|||||||
accentColor={section.accentColor}
|
accentColor={section.accentColor}
|
||||||
isOpen={openSections[section.id]}
|
isOpen={openSections[section.id]}
|
||||||
onToggle={() => toggleSection(section.id)}
|
onToggle={() => toggleSection(section.id)}
|
||||||
|
isComplete={sectionCompletion[section.id]}
|
||||||
>
|
>
|
||||||
{/* Placeholder — fields added in Phase 2+ */}
|
{renderSectionContent(section)}
|
||||||
<div className={`rounded-xl border-2 border-dashed px-6 py-8 flex flex-col items-center justify-center gap-2 text-center
|
|
||||||
${isDark ? 'border-zinc-700' : 'border-zinc-200'}
|
|
||||||
`}>
|
|
||||||
<section.icon
|
|
||||||
size={28}
|
|
||||||
style={{ color: section.accentColor, opacity: 0.4 }}
|
|
||||||
/>
|
|
||||||
<p className="text-xs font-semibold uppercase tracking-widest text-zinc-400">
|
|
||||||
{section.description}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</LeadSectionWrapper>
|
</LeadSectionWrapper>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
))}
|
))}
|
||||||
@@ -274,7 +351,7 @@ export default function CreateLeadPage() {
|
|||||||
}
|
}
|
||||||
`}>
|
`}>
|
||||||
<div className="flex items-center gap-3 max-w-3xl mx-auto">
|
<div className="flex items-center gap-3 max-w-3xl mx-auto">
|
||||||
{/* Mode toggle — compact repeat for mobile */}
|
{/* Compact mode toggle */}
|
||||||
<div className={`flex items-center p-0.5 rounded-lg gap-0.5 shrink-0
|
<div className={`flex items-center p-0.5 rounded-lg gap-0.5 shrink-0
|
||||||
${isDark ? 'bg-zinc-800' : 'bg-zinc-100'}
|
${isDark ? 'bg-zinc-800' : 'bg-zinc-100'}
|
||||||
`}>
|
`}>
|
||||||
@@ -302,7 +379,7 @@ export default function CreateLeadPage() {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Save button */}
|
{/* Save */}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="flex-1 flex items-center justify-center gap-2 py-3 rounded-xl font-bold text-sm uppercase tracking-widest text-white transition-all active:scale-[0.97]"
|
className="flex-1 flex items-center justify-center gap-2 py-3 rounded-xl font-bold text-sm uppercase tracking-widest text-white transition-all active:scale-[0.97]"
|
||||||
|
|||||||
Reference in New Issue
Block a user