feat: replace team members with cards in CMS, add date validation fields, and remove contact info section from user profile

This commit is contained in:
pradeepkumar
2026-04-15 23:39:50 +05:30
parent 6212756b54
commit a25b576ccf
6 changed files with 59 additions and 61 deletions

View File

@@ -24,7 +24,7 @@ import {
AboutFeaturesContent,
AboutFeatureItem,
AboutTeamContent,
AboutTeamMember,
AboutTeamCard,
AboutCtaContent,
FaqContent,
FaqCategoryItem,
@@ -97,7 +97,7 @@ function defaultContentFor(pageSlug: string, sectionKey: string): unknown {
case 'hero': return { badge: '', headline: '', description: '', ctaButtonText: '', bannerImageUrl: '', bannerOverlayText: '' } as AboutHeroContent;
case 'stats': return { stats: [] } as AboutStatsContent;
case 'features': return { badge: '', features: [] } as AboutFeaturesContent;
case 'team': return { title: '', subtitle: '', members: [] } as AboutTeamContent;
case 'team': return { title: '', subtitle: '', intro: '', cards: [] } as AboutTeamContent;
case 'cta': return { title: '', description: '', buttonText: '' } as AboutCtaContent;
}
}
@@ -1132,40 +1132,48 @@ export default function CmsPage() {
}
function renderAboutTeamForm() {
const data = editContent as AboutTeamContent;
const dataRaw = editContent as AboutTeamContent & { members?: unknown };
const data: AboutTeamContent = {
title: dataRaw.title ?? '',
subtitle: dataRaw.subtitle ?? '',
intro: dataRaw.intro ?? '',
cards: Array.isArray(dataRaw.cards) ? dataRaw.cards : [],
};
const update = (patch: Partial<AboutTeamContent>) => setEditContent({ ...data, ...patch });
function updateMember(index: number, patch: Partial<AboutTeamMember>) {
const members = [...data.members];
members[index] = { ...members[index], ...patch };
update({ members });
function updateCard(index: number, patch: Partial<AboutTeamCard>) {
const cards = [...data.cards];
cards[index] = { ...cards[index], ...patch };
update({ cards });
}
return (
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-[#00293d] mb-1">Section Title</label>
<input type="text" className={inputCls} value={data.title} onChange={(e) => update({ title: e.target.value })} placeholder="e.g. Meet the minds behind the platform." />
<input type="text" className={inputCls} value={data.title} onChange={(e) => update({ title: e.target.value })} placeholder="e.g. For Real Estate Professionals." />
</div>
<div>
<label className="block text-sm font-medium text-[#00293d] mb-1">Section Subtitle</label>
<textarea rows={2} className={textareaCls} value={data.subtitle} onChange={(e) => update({ subtitle: e.target.value })} placeholder="Section description" />
<textarea rows={2} className={textareaCls} value={data.subtitle} onChange={(e) => update({ subtitle: e.target.value })} placeholder="A place where your skills, not your wallet, stands out." />
</div>
<div>
<label className="block text-sm font-medium text-[#00293d] mb-1">Intro Paragraph</label>
<textarea rows={4} className={textareaCls} value={data.intro} onChange={(e) => update({ intro: e.target.value })} placeholder="RE-Quest is a unique platform..." />
</div>
<div className="flex items-center justify-between mb-2">
<label className="block text-sm font-medium text-[#00293d]">Team Members ({data.members.length})</label>
<button type="button" onClick={() => update({ members: [...data.members, { name: '', role: '', imageUrl: '' }] })} className="px-3 py-1 text-xs bg-[#f5a623] hover:bg-[#e09620] text-white rounded-lg transition-colors">+ Add Member</button>
<label className="block text-sm font-medium text-[#00293d]">Cards ({data.cards.length})</label>
<button type="button" onClick={() => update({ cards: [...data.cards, { title: '', description: '', iconPath: '' }] })} className="px-3 py-1 text-xs bg-[#f5a623] hover:bg-[#e09620] text-white rounded-lg transition-colors">+ Add Card</button>
</div>
{data.members.length === 0 && <p className="text-sm text-[#666666] italic">No team members added yet.</p>}
{data.cards.length === 0 && <p className="text-sm text-[#666666] italic">No cards added yet.</p>}
<div className="space-y-3">
{data.members.map((member, idx) => (
{data.cards.map((card, idx) => (
<div key={idx} className="p-3 border border-[#e5e7eb] rounded-lg bg-[#f9fafb] space-y-2">
<div className="flex items-center justify-between">
<span className="text-xs font-semibold text-[#666666]">Member {idx + 1}</span>
<button type="button" onClick={() => update({ members: data.members.filter((_, i) => i !== idx) })} className="text-red-500 hover:text-red-700 text-xs font-medium">Remove</button>
<span className="text-xs font-semibold text-[#666666]">Card {idx + 1}</span>
<button type="button" onClick={() => update({ cards: data.cards.filter((_, i) => i !== idx) })} className="text-red-500 hover:text-red-700 text-xs font-medium">Remove</button>
</div>
<div className="grid grid-cols-2 gap-2">
<input type="text" className={inputCls} value={member.name} onChange={(e) => updateMember(idx, { name: e.target.value })} placeholder="Name" />
<input type="text" className={inputCls} value={member.role} onChange={(e) => updateMember(idx, { role: e.target.value })} placeholder="Role" />
</div>
{renderImageUploadField(`Photo`, member.imageUrl, (url) => updateMember(idx, { imageUrl: url }))}
{renderImageUploadField('Icon', card.iconPath, (url) => updateCard(idx, { iconPath: url }))}
<input type="text" className={inputCls} value={card.title} onChange={(e) => updateCard(idx, { title: e.target.value })} placeholder="Card title" />
<textarea rows={3} className={textareaCls} value={card.description} onChange={(e) => updateCard(idx, { description: e.target.value })} placeholder="Card description" />
</div>
))}
</div>

View File

@@ -974,6 +974,28 @@ export default function SectionDetailPage() {
</div>
</>
)}
{fieldForm.fieldType === 'DATE' && (
<>
<div>
<label className="block text-xs text-[#666666] mb-1">Min Date</label>
<input
type="date"
value={fieldForm.validation?.minDate ?? ''}
onChange={(e) => updateValidation('minDate', e.target.value)}
className="w-full px-3 py-2 border border-[#e5e7eb] rounded-xl focus:outline-none focus:border-[#f5a623] text-sm text-[#00293d] bg-white placeholder:text-[#666666]"
/>
</div>
<div>
<label className="block text-xs text-[#666666] mb-1">Max Date</label>
<input
type="date"
value={fieldForm.validation?.maxDate ?? ''}
onChange={(e) => updateValidation('maxDate', e.target.value)}
className="w-full px-3 py-2 border border-[#e5e7eb] rounded-xl focus:outline-none focus:border-[#f5a623] text-sm text-[#00293d] bg-white placeholder:text-[#666666]"
/>
</div>
</>
)}
<div className="col-span-2">
<label className="block text-xs text-[#666666] mb-1">Pattern (regex)</label>
<input

View File

@@ -464,41 +464,6 @@ export default function UserDetailPage() {
</div>
</div>
{/* Contact Information */}
<div className="bg-white rounded-xl shadow-sm border border-[#e5e7eb] mb-6">
<div className="px-6 py-4 border-b border-[#e5e7eb]">
<h2 className="text-lg font-semibold text-[#00293d] font-fractul">Contact Information</h2>
</div>
<div className="p-6">
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
<div>
<p className="text-sm text-[#666666]">Phone</p>
<p className="text-sm font-medium text-[#00293d]">
{user.profile?.phone || 'Not provided'}
</p>
</div>
<div>
<p className="text-sm text-[#666666]">City</p>
<p className="text-sm font-medium text-[#00293d]">
{user.profile?.city || 'Not provided'}
</p>
</div>
<div>
<p className="text-sm text-[#666666]">State</p>
<p className="text-sm font-medium text-[#00293d]">
{user.profile?.state || 'Not provided'}
</p>
</div>
<div>
<p className="text-sm text-[#666666]">Country</p>
<p className="text-sm font-medium text-[#00293d]">
{user.profile?.country || 'Not provided'}
</p>
</div>
</div>
</div>
</div>
{/* Dynamic Profile Data (only for AGENT role) */}
{user.role === 'AGENT' && (
<div className="bg-white rounded-xl shadow-sm border border-[#e5e7eb] mb-6">

View File

@@ -98,16 +98,17 @@ export interface AboutFeaturesContent {
features: AboutFeatureItem[];
}
export interface AboutTeamMember {
name: string;
role: string;
imageUrl: string;
export interface AboutTeamCard {
title: string;
description: string;
iconPath: string;
}
export interface AboutTeamContent {
title: string;
subtitle: string;
members: AboutTeamMember[];
intro: string;
cards: AboutTeamCard[];
}
export interface AboutCtaContent {

View File

@@ -80,7 +80,7 @@ export type {
AboutStatsContent,
AboutFeatureItem,
AboutFeaturesContent,
AboutTeamMember,
AboutTeamCard,
AboutTeamContent,
AboutCtaContent,
FaqCategoryItem,

View File

@@ -47,6 +47,8 @@ export interface FieldValidation {
minLength?: number;
maxLength?: number;
pattern?: string;
minDate?: string; // ISO date (YYYY-MM-DD) for DATE fields
maxDate?: string; // ISO date (YYYY-MM-DD) for DATE fields
}
export interface FieldUiConfig {