feat: Add full CMS support for the 'Contact' page, including sections, default content factories, data fetching, and editing forms.

This commit is contained in:
pradeepkumar
2026-03-19 13:32:44 +05:30
parent b27cdaeb27
commit 4ef751509a

View File

@@ -60,6 +60,11 @@ const FAQ_SECTIONS: SectionMeta[] = [
{ pageSlug: 'faq', sectionKey: 'faqContent', label: 'FAQ Content', description: 'Categories, questions and answers' }, { pageSlug: 'faq', sectionKey: 'faqContent', label: 'FAQ Content', description: 'Categories, questions and answers' },
]; ];
const CONTACT_SECTIONS: SectionMeta[] = [
{ pageSlug: 'contact', sectionKey: 'contactDetails', label: 'Contact Details', description: 'Email, phone, office address and map' },
{ pageSlug: 'contact', sectionKey: 'cta', label: 'Call to Action', description: 'Bottom CTA section text and button' },
];
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Default content factories // Default content factories
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@@ -95,6 +100,12 @@ function defaultContentFor(pageSlug: string, sectionKey: string): unknown {
case 'faqContent': return { pageTitle: '', categories: [], faqs: [], supportTitle: '', supportDescription: '' } as FaqContent; case 'faqContent': return { pageTitle: '', categories: [], faqs: [], supportTitle: '', supportDescription: '' } as FaqContent;
} }
} }
if (pageSlug === 'contact') {
switch (sectionKey) {
case 'contactDetails': return { title: '', description: '', email: '', phone: '', phoneHours: '', officeAddress: '', officeCity: '', mapUrl: '', directionsUrl: '' };
case 'cta': return { title: '', description: '', buttonText: '', buttonLink: '' };
}
}
switch (sectionKey) { switch (sectionKey) {
case 'hero': return defaultHero(); case 'hero': return defaultHero();
case 'features': return defaultFeatures(); case 'features': return defaultFeatures();
@@ -132,10 +143,11 @@ const textareaCls = inputCls;
export default function CmsPage() { export default function CmsPage() {
const router = useRouter(); const router = useRouter();
const [activePage, setActivePage] = useState<'landing' | 'about' | 'faq'>('landing'); const [activePage, setActivePage] = useState<'landing' | 'about' | 'faq' | 'contact'>('landing');
const [records, setRecords] = useState<CmsContentRecord[]>([]); const [records, setRecords] = useState<CmsContentRecord[]>([]);
const [aboutRecords, setAboutRecords] = useState<CmsContentRecord[]>([]); const [aboutRecords, setAboutRecords] = useState<CmsContentRecord[]>([]);
const [faqRecords, setFaqRecords] = useState<CmsContentRecord[]>([]); const [faqRecords, setFaqRecords] = useState<CmsContentRecord[]>([]);
const [contactRecords, setContactRecords] = useState<CmsContentRecord[]>([]);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(''); const [error, setError] = useState('');
const [notification, setNotification] = useState<{ type: 'success' | 'error'; message: string } | null>(null); const [notification, setNotification] = useState<{ type: 'success' | 'error'; message: string } | null>(null);
@@ -161,14 +173,16 @@ export default function CmsPage() {
setIsLoading(true); setIsLoading(true);
setError(''); setError('');
try { try {
const [landingData, aboutData, faqData] = await Promise.all([ const [landingData, aboutData, faqData, contactData] = await Promise.all([
cmsService.getByPage('landing'), cmsService.getByPage('landing'),
cmsService.getByPage('about'), cmsService.getByPage('about'),
cmsService.getByPage('faq'), cmsService.getByPage('faq'),
cmsService.getByPage('contact'),
]); ]);
setRecords(landingData); setRecords(landingData);
setAboutRecords(aboutData); setAboutRecords(aboutData);
setFaqRecords(faqData); setFaqRecords(faqData);
setContactRecords(contactData);
} catch (err) { } catch (err) {
const msg = getErrorMessage(err); const msg = getErrorMessage(err);
setError(msg); setError(msg);
@@ -986,6 +1000,80 @@ export default function CmsPage() {
); );
} }
// ---------------------------------------------------------------------------
// Contact page editors
// ---------------------------------------------------------------------------
function renderContactDetailsForm() {
const data = editContent as Record<string, string>;
const update = (patch: Record<string, string>) => setEditContent({ ...data, ...patch });
return (
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-[#00293d] mb-1">Page Title</label>
<input className={inputCls} value={data.title || ''} onChange={e => update({ title: e.target.value })} placeholder="Get In Touch" />
</div>
<div>
<label className="block text-sm font-medium text-[#00293d] mb-1">Description</label>
<textarea className={textareaCls} rows={2} value={data.description || ''} onChange={e => update({ description: e.target.value })} placeholder="Page description" />
</div>
<div>
<label className="block text-sm font-medium text-[#00293d] mb-1">Support Email</label>
<input className={inputCls} value={data.email || ''} onChange={e => update({ email: e.target.value })} placeholder="support@example.com" />
</div>
<div>
<label className="block text-sm font-medium text-[#00293d] mb-1">Phone Number</label>
<input className={inputCls} value={data.phone || ''} onChange={e => update({ phone: e.target.value })} placeholder="1234567890" />
</div>
<div>
<label className="block text-sm font-medium text-[#00293d] mb-1">Phone Hours</label>
<input className={inputCls} value={data.phoneHours || ''} onChange={e => update({ phoneHours: e.target.value })} placeholder="Mon-Fri 9am-6pm" />
</div>
<div>
<label className="block text-sm font-medium text-[#00293d] mb-1">Office Address (Line 1)</label>
<input className={inputCls} value={data.officeAddress || ''} onChange={e => update({ officeAddress: e.target.value })} placeholder="123 Market Street" />
</div>
<div>
<label className="block text-sm font-medium text-[#00293d] mb-1">Office Address (Line 2)</label>
<input className={inputCls} value={data.officeCity || ''} onChange={e => update({ officeCity: e.target.value })} placeholder="New York CA 234737" />
</div>
<div>
<label className="block text-sm font-medium text-[#00293d] mb-1">Google Maps Embed URL (optional)</label>
<input className={inputCls} value={data.mapUrl || ''} onChange={e => update({ mapUrl: e.target.value })} placeholder="https://maps.google.com/..." />
</div>
<div>
<label className="block text-sm font-medium text-[#00293d] mb-1">Directions URL (optional)</label>
<input className={inputCls} value={data.directionsUrl || ''} onChange={e => update({ directionsUrl: e.target.value })} placeholder="https://maps.google.com/..." />
</div>
</div>
);
}
function renderContactCtaForm() {
const data = editContent as Record<string, string>;
const update = (patch: Record<string, string>) => setEditContent({ ...data, ...patch });
return (
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-[#00293d] mb-1">Title</label>
<input className={inputCls} value={data.title || ''} onChange={e => update({ title: e.target.value })} placeholder="Ready to find an agent?" />
</div>
<div>
<label className="block text-sm font-medium text-[#00293d] mb-1">Description</label>
<textarea className={textareaCls} rows={2} value={data.description || ''} onChange={e => update({ description: e.target.value })} placeholder="CTA description" />
</div>
<div>
<label className="block text-sm font-medium text-[#00293d] mb-1">Button Text</label>
<input className={inputCls} value={data.buttonText || ''} onChange={e => update({ buttonText: e.target.value })} placeholder="Start Your Search" />
</div>
<div>
<label className="block text-sm font-medium text-[#00293d] mb-1">Button Link</label>
<input className={inputCls} value={data.buttonLink || ''} onChange={e => update({ buttonLink: e.target.value })} placeholder="/user/profiles" />
</div>
</div>
);
}
function renderModalContent() { function renderModalContent() {
if (!editingSection || editContent == null) return null; if (!editingSection || editContent == null) return null;
const key = editingSection.sectionKey; const key = editingSection.sectionKey;
@@ -1004,6 +1092,12 @@ export default function CmsPage() {
case 'faqContent': return renderFaqForm(); case 'faqContent': return renderFaqForm();
} }
} }
if (page === 'contact') {
switch (key) {
case 'contactDetails': return renderContactDetailsForm();
case 'cta': return renderContactCtaForm();
}
}
switch (key) { switch (key) {
case 'hero': return renderHeroForm(); case 'hero': return renderHeroForm();
case 'features': return renderFeaturesForm(); case 'features': return renderFeaturesForm();
@@ -1017,8 +1111,8 @@ export default function CmsPage() {
// Render // Render
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
const currentSections = activePage === 'landing' ? LANDING_SECTIONS : activePage === 'about' ? ABOUT_SECTIONS : FAQ_SECTIONS; const currentSections = activePage === 'landing' ? LANDING_SECTIONS : activePage === 'about' ? ABOUT_SECTIONS : activePage === 'contact' ? CONTACT_SECTIONS : FAQ_SECTIONS;
const currentRecords = activePage === 'landing' ? records : activePage === 'about' ? aboutRecords : faqRecords; const currentRecords = activePage === 'landing' ? records : activePage === 'about' ? aboutRecords : activePage === 'contact' ? contactRecords : faqRecords;
return ( return (
<div> <div>
@@ -1052,6 +1146,13 @@ export default function CmsPage() {
> >
FAQ FAQ
</button> </button>
<button
type="button"
onClick={() => setActivePage('contact')}
className={`px-5 py-3 text-sm font-medium border-b-2 transition-colors ${activePage === 'contact' ? 'border-[#f5a623] text-[#f5a623]' : 'border-transparent text-[#666666] hover:text-[#00293d]'}`}
>
Contact
</button>
</nav> </nav>
</div> </div>