forked from Goutam/lynkeduppro-crm
Redesign Support as bento hub, restore full sidebar, Western names
- Support Center overview rebuilt as a bento grid: large live-chat hero tile, support team tile, mini channel tiles and a recent-tickets strip. - Restore full workspace sidebar (Workspace/Team/Profile groups) with a premium golden active state; unbuilt modules show a Coming Soon view. - Use Western names across the UI (James Carter; Emma Wilson, Liam Foster, Sophie Turner, Noah Mitchell) incl. tickets, threads, portal demo user. - Remove the Allottee card from Profile; add a clean Account & identity card and live-editable contacts. - Relabel "Plot" -> "House number" everywhere; drop the allotment chip in favour of the property category. - Premium visual pass: brand gradients, depth, glow accents, pill tabs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -79,14 +79,14 @@ function ProfileHeaderCard() {
|
||||
<div className="profile-hero-role">{user.role} · Member since {user.memberSince}</div>
|
||||
<div className="profile-hero-chips">
|
||||
<span className="hero-chip"><Icon name="pin" size={13} /> {user.sector} · {user.pocket}</span>
|
||||
<span className="hero-chip"><Icon name="key" size={13} /> Allotment {user.allotmentNo}</span>
|
||||
<span className="hero-chip accent"><Icon name="check" size={13} /> Plot {user.plot}</span>
|
||||
<span className="hero-chip"><Icon name="owners" size={13} /> {user.category}</span>
|
||||
<span className="hero-chip accent"><Icon name="check" size={13} /> House {user.plot}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="profile-hero-stats">
|
||||
<HeroStat value={user.category} label="Category" />
|
||||
<HeroStat value={user.size} label="Plot size" />
|
||||
<HeroStat value={user.size} label="House size" />
|
||||
<HeroStat value="33%" label="KYC complete" accent="var(--orange)" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -116,33 +116,27 @@ function PersonalInfoTab() {
|
||||
|
||||
// editable contact values (live-updating after OTP confirmation)
|
||||
const [contacts, setContacts] = useState({ email: user.email.full, mobile: user.mobile.full, whatsapp: user.whatsapp.full });
|
||||
// editable account-holder fields
|
||||
const [editId, setEditId] = useState(false);
|
||||
const [holder, setHolder] = useState({ name: user.name, relationship: user.relationship });
|
||||
const [holderDraft, setHolderDraft] = useState(holder);
|
||||
|
||||
function verify() {
|
||||
if (plot.trim() === user.plot) { setVerified(true); setErr(""); push({ tone: "success", title: "Verified", desc: "Your full details are now visible and editable." }); }
|
||||
else setErr(`That doesn't match the plot number on file (hint: it's ${user.plot}).`);
|
||||
else setErr(`That doesn't match the house number on file (hint: it's ${user.plot}).`);
|
||||
}
|
||||
|
||||
function mask(kind: ContactKind, full: string) {
|
||||
return kind === "email" ? maskEmail(full) : maskPhone(full);
|
||||
}
|
||||
|
||||
const rows: { key: ContactKind | "pan" | "aadhaar"; label: string; full: string; masked: string; editable: boolean }[] = [
|
||||
{ key: "email", label: "Email", full: contacts.email, masked: mask("email", contacts.email), editable: true },
|
||||
{ key: "mobile", label: "Mobile", full: contacts.mobile, masked: mask("mobile", contacts.mobile), editable: true },
|
||||
{ key: "whatsapp", label: "WhatsApp", full: contacts.whatsapp, masked: mask("whatsapp", contacts.whatsapp), editable: true },
|
||||
{ key: "pan", label: "PAN", full: user.pan.full, masked: user.pan.masked, editable: false },
|
||||
{ key: "aadhaar", label: "Aadhaar", full: user.aadhaar.full, masked: user.aadhaar.masked, editable: false },
|
||||
const contactRows: { key: ContactKind; label: string; full: string; masked: string }[] = [
|
||||
{ key: "email", label: "Email", full: contacts.email, masked: mask("email", contacts.email) },
|
||||
{ key: "mobile", label: "Mobile", full: contacts.mobile, masked: mask("mobile", contacts.mobile) },
|
||||
{ key: "whatsapp", label: "WhatsApp", full: contacts.whatsapp, masked: mask("whatsapp", contacts.whatsapp) },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="grid-2">
|
||||
<div className="card">
|
||||
<div className="card-head">
|
||||
<h3>Personal details</h3>
|
||||
<h3>Contact details</h3>
|
||||
{verified
|
||||
? <Pill tone="green"><Icon name="eye" size={13} /> Revealed</Pill>
|
||||
: <Pill tone="orange"><Icon name="eye-off" size={13} /> Masked</Pill>}
|
||||
@@ -153,10 +147,10 @@ function PersonalInfoTab() {
|
||||
<div className="reveal-gate-ic"><Icon name="lock" size={20} /></div>
|
||||
<div className="reveal-gate-txt">
|
||||
<strong>Verify to reveal & edit</strong>
|
||||
<span>Sensitive details are masked. Enter your plot number to unmask and edit them.</span>
|
||||
<span>Sensitive details are masked. Enter your house number to unmask and edit them.</span>
|
||||
</div>
|
||||
<div className="reveal-gate-form">
|
||||
<input className="ds-input" placeholder="Plot number" value={plot} maxLength={6}
|
||||
<input className="ds-input" placeholder="House number" value={plot} maxLength={6}
|
||||
onChange={(e) => { setPlot(e.target.value.replace(/\D/g, "")); setErr(""); }}
|
||||
onKeyDown={(e) => e.key === "Enter" && verify()} />
|
||||
<Btn icon="check" onClick={verify}>Reveal</Btn>
|
||||
@@ -166,15 +160,14 @@ function PersonalInfoTab() {
|
||||
)}
|
||||
|
||||
<dl className="kv-list">
|
||||
{rows.map((f) => (
|
||||
{contactRows.map((f) => (
|
||||
<div className="kv" key={f.key}>
|
||||
<dt>{f.label}</dt>
|
||||
<dd className={verified ? "kv-edit" : "masked"}>
|
||||
{verified ? f.full : f.masked}
|
||||
{verified && f.editable && (
|
||||
<button className="kv-edit-btn" aria-label={`Edit ${f.label}`} onClick={() => setChange(f.key as ContactKind)}><Icon name="edit" size={13} /></button>
|
||||
{verified && (
|
||||
<button className="kv-edit-btn" aria-label={`Edit ${f.label}`} onClick={() => setChange(f.key)}><Icon name="edit" size={13} /></button>
|
||||
)}
|
||||
{verified && !f.editable && <span className="kv-lock"><Icon name="lock" size={12} /> via KYC</span>}
|
||||
</dd>
|
||||
</div>
|
||||
))}
|
||||
@@ -189,40 +182,26 @@ function PersonalInfoTab() {
|
||||
|
||||
<div className="card">
|
||||
<div className="card-head">
|
||||
<h3>Allottee & account holder</h3>
|
||||
{!editId
|
||||
? <Btn variant="outline" size="sm" icon="edit" onClick={() => { setHolderDraft(holder); setEditId(true); }}>Edit</Btn>
|
||||
: <div className="row gap-2"><Btn variant="ghost" size="sm" onClick={() => setEditId(false)}>Cancel</Btn><Btn size="sm" icon="check" onClick={() => { setHolder(holderDraft); setEditId(false); push({ tone: "success", title: "Details saved" }); }}>Save</Btn></div>}
|
||||
<h3>Account & identity</h3>
|
||||
{verified
|
||||
? <Pill tone="green"><Icon name="eye" size={13} /> Revealed</Pill>
|
||||
: <Pill tone="muted"><Icon name="lock" size={12} /> Protected</Pill>}
|
||||
</div>
|
||||
{user.isAllottee ? (
|
||||
<div className="callout tone-green">
|
||||
<Icon name="check-circle" size={18} />
|
||||
<div><strong>You are the allottee.</strong><span>The account holder and the allottee are the same person ({holder.relationship}).</span></div>
|
||||
<dl className="kv-list">
|
||||
<div className="kv"><dt>Full name</dt><dd>{user.name}</dd></div>
|
||||
<div className="kv"><dt>Role</dt><dd>{user.role}</dd></div>
|
||||
<div className="kv"><dt>Member since</dt><dd>{user.memberSince}</dd></div>
|
||||
<div className="kv"><dt>Account status</dt><dd><Pill tone="green"><StatusDot status="online" /> Active</Pill></dd></div>
|
||||
<div className="kv">
|
||||
<dt>PAN</dt>
|
||||
<dd className={verified ? "kv-edit" : "masked"}>{verified ? user.pan.full : user.pan.masked}{verified && <span className="kv-lock"><Icon name="lock" size={12} /> via KYC</span>}</dd>
|
||||
</div>
|
||||
) : (
|
||||
<div className="callout tone-orange">
|
||||
<Icon name="info" size={18} />
|
||||
<div><strong>Acting on behalf of the allottee.</strong><span>Account holder differs from the allottee — actions are logged against you.</span></div>
|
||||
<div className="kv">
|
||||
<dt>Aadhaar</dt>
|
||||
<dd className={verified ? "kv-edit" : "masked"}>{verified ? user.aadhaar.full : user.aadhaar.masked}{verified && <span className="kv-lock"><Icon name="lock" size={12} /> via KYC</span>}</dd>
|
||||
</div>
|
||||
)}
|
||||
{editId ? (
|
||||
<div className="edit-grid">
|
||||
<Field label="Account holder name"><input className="ds-input" value={holderDraft.name} onChange={(e) => setHolderDraft((d) => ({ ...d, name: e.target.value }))} /></Field>
|
||||
<Field label="Relationship to allottee">
|
||||
<select className="ds-select" value={holderDraft.relationship} onChange={(e) => setHolderDraft((d) => ({ ...d, relationship: e.target.value }))}>
|
||||
{["Self", "Spouse", "Son / Daughter", "Authorized Representative", "Power of Attorney"].map((r) => <option key={r} value={r}>{r}</option>)}
|
||||
</select>
|
||||
</Field>
|
||||
</div>
|
||||
) : (
|
||||
<dl className="kv-list">
|
||||
<div className="kv"><dt>Allottee name</dt><dd>{user.allotteeName}</dd></div>
|
||||
<div className="kv"><dt>Account holder</dt><dd>{holder.name}</dd></div>
|
||||
<div className="kv"><dt>Relationship</dt><dd><Pill tone="blue">{holder.relationship}</Pill></dd></div>
|
||||
<div className="kv"><dt>Allotment no.</dt><dd>{user.allotmentNo}</dd></div>
|
||||
<div className="kv"><dt>Property</dt><dd>{user.sector} · {user.pocket} · Plot {user.plot}</dd></div>
|
||||
</dl>
|
||||
)}
|
||||
</dl>
|
||||
<p className="muted-note"><Icon name="info" size={13} /> PAN & Aadhaar are managed under the KYC tab and can't be edited here.</p>
|
||||
</div>
|
||||
|
||||
<ChangeContactModal
|
||||
|
||||
Reference in New Issue
Block a user