diff --git a/src/app/dashboard/dashboard.css b/src/app/dashboard/dashboard.css index 263adac..7e5d3e3 100644 --- a/src/app/dashboard/dashboard.css +++ b/src/app/dashboard/dashboard.css @@ -235,6 +235,29 @@ } .dash-root .view { position: relative; z-index: 1; } +/* ---- sidebar (premium nav) ---- */ +.dash-root .dash-sidebar { width: 236px; flex: 0 0 236px; background: linear-gradient(180deg, color-mix(in srgb, var(--orange) 4%, var(--sidebar)) 0%, var(--sidebar) 30%); } +.dash-root .dash-brand { padding: 20px 18px 16px; } +.dash-root .dash-brand .bk { font-size: 15.5px; font-weight: 800; letter-spacing: -0.01em; } +.dash-root .dash-nav { padding: 4px 12px 14px; } +.dash-root .nav-section { margin-bottom: 4px; } +.dash-root .nav-section + .nav-section { border-top: 1px solid var(--border); padding-top: 4px; } +.dash-root .nav-group { font-size: 10px; font-weight: 800; letter-spacing: 0.14em; text-transform: uppercase; color: var(--faint); padding: 13px 10px 7px; } +.dash-root .nav-item { display: flex; align-items: center; gap: 11px; width: 100%; padding: 9px 11px; border-radius: 10px; border: 0; background: none; cursor: pointer; color: var(--muted); font-family: inherit; font-size: 13px; font-weight: 500; text-align: left; transition: background 0.14s, color 0.14s, transform 0.12s; } +.dash-root .nav-item svg { color: var(--faint); flex: 0 0 auto; transition: color 0.14s; } +.dash-root .nav-item-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.dash-root .nav-item:hover { background: var(--panel-2); color: var(--text); } +.dash-root .nav-item:hover svg { color: var(--text-2); } +.dash-root .nav-item.active { background: var(--grad-brand); color: #1a1206; font-weight: 700; box-shadow: var(--glow-orange); } +.dash-root .nav-item.active svg { color: #1a1206; } + +/* ---- coming-soon placeholder ---- */ +.dash-root .coming-soon { display: flex; flex-direction: column; align-items: center; text-align: center; padding: 56px 24px; gap: 8px; } +.dash-root .coming-soon-ic { width: 76px; height: 76px; border-radius: 22px; display: grid; place-items: center; color: var(--orange); background: color-mix(in srgb, var(--orange) 12%, transparent); margin-bottom: 8px; } +.dash-root .coming-soon h3 { font-size: 18px; font-weight: 800; } +.dash-root .coming-soon p { font-size: 13px; color: var(--muted); max-width: 440px; line-height: 1.55; } +.dash-root .coming-soon-actions { display: flex; gap: 10px; margin-top: 14px; flex-wrap: wrap; justify-content: center; } + /* ---- animations ---- */ @keyframes ds-fade { from { opacity: 0; } to { opacity: 1; } } @keyframes ds-pop { from { opacity: 0; transform: translateY(10px) scale(0.985); } to { opacity: 1; transform: none; } } @@ -340,7 +363,7 @@ .dash-root .link-inline { background: none; border: 0; color: var(--orange); font-size: 12px; font-weight: 600; cursor: pointer; font-family: inherit; } /* ---- Segmented / Tabs ---- */ -.dash-root .ds-segmented { display: inline-flex; gap: 3px; padding: 4px; background: var(--panel-2); border: 1px solid var(--border); border-radius: 12px; flex-wrap: wrap; } +.dash-root .ds-segmented { display: inline-flex; gap: 3px; padding: 4px; background: var(--panel-2); border: 1px solid var(--border); border-radius: 12px; flex-wrap: wrap; margin-bottom: 20px; } .dash-root .ds-segmented .seg { display: inline-flex; align-items: center; gap: 6px; height: 32px; padding: 0 14px; border: 0; border-radius: 9px; background: none; color: var(--muted); font-family: inherit; font-size: 12.5px; font-weight: 600; cursor: pointer; transition: 0.14s; } .dash-root .ds-segmented .seg:hover { color: var(--text); } .dash-root .ds-segmented .seg.active { background: var(--panel); color: var(--text); box-shadow: var(--card-hi), 0 4px 12px -7px rgba(0,0,0,0.5); } @@ -524,7 +547,73 @@ .dash-root .tl-dot.ev-resolved, .dash-root .tl-dot.ev-closed { background: color-mix(in srgb, var(--green) 18%, transparent); color: var(--green); } .dash-root .tl-body { padding-top: 2px; } .dash-root .tl-title { font-size: 13px; font-weight: 600; } .dash-root .tl-meta { font-size: 11.5px; color: var(--muted); margin-top: 1px; } .dash-root .tl-time { font-size: 11px; color: var(--faint); margin-top: 2px; } -/* ---- Support: channel cards ---- */ +/* ---- Support: Bento hub ---- */ +.dash-root .support-bento { + display: grid; gap: 16px; grid-template-columns: repeat(4, 1fr); + grid-template-areas: + "chat chat team team" + "chat chat team team" + "ticket call email help" + "recent recent recent recent"; +} +.dash-root .bento-tile { border: 1px solid var(--border); border-radius: 18px; background: var(--card-grad); box-shadow: var(--card-hi); padding: 20px; } +.dash-root .bento-tile-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 14px; } +.dash-root .bento-tile-head h3 { font-size: 15px; font-weight: 700; } + +/* hero live-chat tile */ +.dash-root .bento-chat { grid-area: chat; position: relative; overflow: hidden; display: flex; flex-direction: column; justify-content: space-between; gap: 20px; min-height: 268px; text-align: left; cursor: pointer; font-family: inherit; border: 0; color: #1a1206; background: linear-gradient(135deg, #ffb43c 0%, #fda913 45%, #fd6d13 100%); box-shadow: var(--glow-orange); transition: transform 0.18s, box-shadow 0.18s; } +.dash-root .bento-chat:hover { transform: translateY(-3px); box-shadow: 0 26px 54px -20px color-mix(in srgb, var(--orange) 85%, #000); } +.dash-root .bento-chat::after { content: ""; position: absolute; right: -70px; top: -70px; width: 260px; height: 260px; border-radius: 50%; background: radial-gradient(circle, rgba(255,255,255,0.28), transparent 68%); pointer-events: none; } +.dash-root .bento-chat::before { content: ""; position: absolute; left: -40px; bottom: -80px; width: 220px; height: 220px; border-radius: 50%; background: radial-gradient(circle, rgba(26,18,6,0.12), transparent 70%); pointer-events: none; } +.dash-root .bento-chat-top { display: flex; justify-content: space-between; align-items: center; gap: 10px; position: relative; z-index: 1; font-size: 12px; font-weight: 600; flex-wrap: wrap; } +.dash-root .bento-chat-badge { display: inline-flex; align-items: center; gap: 6px; background: rgba(26,18,6,0.14); padding: 6px 11px; border-radius: 99px; } +.dash-root .bento-chat-badge .ds-statusdot { background: #15461f; } +.dash-root .bento-chat-wait { display: inline-flex; align-items: center; gap: 5px; color: rgba(26,18,6,0.72); } +.dash-root .bento-chat-mid { position: relative; z-index: 1; } +.dash-root .bento-chat-mid h2 { font-size: 28px; font-weight: 800; letter-spacing: -0.02em; line-height: 1.12; } +.dash-root .bento-chat-mid p { font-size: 13px; margin-top: 9px; color: rgba(26,18,6,0.8); max-width: 360px; line-height: 1.5; } +.dash-root .bento-chat-foot { display: flex; align-items: center; justify-content: space-between; gap: 14px; position: relative; z-index: 1; flex-wrap: wrap; } +.dash-root .bento-avstack { display: flex; align-items: center; } +.dash-root .bento-avstack .ds-avatar { margin-left: -9px; box-shadow: 0 0 0 2px #fd8a13; border-radius: 50%; } +.dash-root .bento-avstack .ds-avatar:first-child { margin-left: 0; } +.dash-root .bento-av-more { margin-left: 11px; font-size: 12px; font-weight: 700; } +.dash-root .bento-chat-cta { display: inline-flex; align-items: center; gap: 8px; background: #1a1206; color: #fff; padding: 12px 20px; border-radius: 12px; font-size: 13.5px; font-weight: 700; transition: transform 0.14s; } +.dash-root .bento-chat:hover .bento-chat-cta { transform: translateX(3px); } + +/* team tile */ +.dash-root .bento-team { grid-area: team; display: flex; flex-direction: column; } +.dash-root .bento-team .team-list { overflow-y: auto; } + +/* mini tiles */ +.dash-root .bento-mini { position: relative; overflow: hidden; text-align: left; cursor: pointer; font-family: inherit; display: flex; flex-direction: column; gap: 5px; min-height: 150px; transition: transform 0.18s, box-shadow 0.18s, border-color 0.18s; } +.dash-root .bento-mini::before { content: ""; position: absolute; top: -45%; right: -25%; width: 150px; height: 150px; border-radius: 50%; background: radial-gradient(circle, color-mix(in srgb, var(--accent) 24%, transparent), transparent 70%); opacity: 0; transition: opacity 0.25s; pointer-events: none; } +.dash-root .bento-mini:hover { transform: translateY(-3px); border-color: color-mix(in srgb, var(--accent) 50%, transparent); box-shadow: var(--card-hi), 0 16px 36px -22px color-mix(in srgb, var(--accent) 75%, #000); } +.dash-root .bento-mini:hover::before { opacity: 1; } +.dash-root .bento-mini-ic { width: 44px; height: 44px; border-radius: 13px; display: grid; place-items: center; color: #fff; background: linear-gradient(135deg, var(--accent), color-mix(in srgb, var(--accent) 55%, #000)); box-shadow: 0 8px 18px -10px color-mix(in srgb, var(--accent) 80%, transparent); margin-bottom: 8px; } +.dash-root .bento-mini-title { font-size: 14.5px; font-weight: 700; } +.dash-root .bento-mini-desc { font-size: 12px; color: var(--muted); flex: 1; line-height: 1.45; } +.dash-root .bento-mini-meta { display: flex; align-items: center; justify-content: space-between; gap: 6px; font-size: 11.5px; color: var(--accent); font-weight: 600; margin-top: 8px; } + +/* recent tickets */ +.dash-root .bento-recent { grid-area: recent; } +.dash-root .bento-ticket-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; } +.dash-root .bento-ticket { text-align: left; cursor: pointer; font-family: inherit; padding: 14px; border-radius: 14px; border: 1px solid var(--border); background: var(--panel-2); display: flex; flex-direction: column; gap: 7px; transition: 0.14s; } +.dash-root .bento-ticket:hover { border-color: var(--border-2); background: var(--panel-3); transform: translateY(-2px); } +.dash-root .bento-ticket-top { display: flex; align-items: center; justify-content: space-between; gap: 6px; } +.dash-root .bento-ticket-subj { font-size: 13px; font-weight: 600; line-height: 1.35; } +.dash-root .bento-ticket-meta { display: flex; align-items: center; gap: 5px; font-size: 11px; color: var(--faint); } + +@media (max-width: 1180px) { + .dash-root .support-bento { grid-template-columns: repeat(2, 1fr); grid-template-areas: "chat chat" "team team" "ticket call" "email help" "recent recent"; } + .dash-root .bento-ticket-row { grid-template-columns: repeat(2, 1fr); } + .dash-root .bento-team .team-list { max-height: none; } +} +@media (max-width: 680px) { + .dash-root .support-bento { grid-template-columns: 1fr; grid-template-areas: "chat" "team" "ticket" "call" "email" "help" "recent"; } + .dash-root .bento-ticket-row { grid-template-columns: 1fr; } +} + +/* ---- Support: channel cards (legacy) ---- */ .dash-root .channel-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; } .dash-root .channel-card { text-align: left; position: relative; overflow: hidden; padding: 20px; border-radius: 18px; border: 1px solid var(--border); background: var(--card-grad); box-shadow: var(--card-hi); cursor: pointer; font-family: inherit; transition: transform 0.18s, box-shadow 0.18s, border-color 0.18s; display: flex; flex-direction: column; gap: 6px; } .dash-root .channel-card::before { content: ""; position: absolute; top: -45%; right: -20%; width: 190px; height: 190px; border-radius: 50%; background: radial-gradient(circle, color-mix(in srgb, var(--accent) 26%, transparent), transparent 70%); opacity: 0; transition: opacity 0.25s; pointer-events: none; } diff --git a/src/components/dashboard/account-data.ts b/src/components/dashboard/account-data.ts index b42bf9f..dc29147 100644 --- a/src/components/dashboard/account-data.ts +++ b/src/components/dashboard/account-data.ts @@ -4,7 +4,7 @@ // fully clickable without a backend. Ready-to-paste shapes. // // Anchored to the same demo identity used by the auth portal: -// Rajesh Kumar Sharma · Allotment YEA-654321 · Plot 181. +// James Carter · Allotment YEA-654321 · House 181. // ============================================================ /* ---------------------------------------------------------- */ @@ -38,7 +38,7 @@ export type User = { avatarGradient: string; // property / allotment allotmentNo: string; - plot: string; // verify-to-reveal key + plot: string; // house number — verify-to-reveal key sector: string; pocket: string; category: string; @@ -59,10 +59,10 @@ export type User = { export const user: User = { id: "usr_LUP_00181", - name: "Rajesh Kumar Sharma", - firstName: "Rajesh", - lastName: "Sharma", - initials: "RS", + name: "James Carter", + firstName: "James", + lastName: "Carter", + initials: "JC", role: "Property Owner", avatarGradient: "linear-gradient(135deg,#fda913,#fd6d13)", allotmentNo: "YEA-654321", @@ -73,8 +73,8 @@ export const user: User = { size: "300 sq.m", isAllottee: true, relationship: "Self", - allotteeName: "Rajesh Kumar Sharma", - email: { label: "Email", masked: maskEmail("rajesh.sharma@example.com"), full: "rajesh.sharma@example.com" }, + allotteeName: "James Carter", + email: { label: "Email", masked: maskEmail("james.carter@example.com"), full: "james.carter@example.com" }, mobile: { label: "Mobile", masked: "+91 ••••• ••012", full: "+91 98765 43012" }, whatsapp: { label: "WhatsApp", masked: "+91 ••••• ••012", full: "+91 98765 43012" }, pan: { label: "PAN", masked: "•••••••• 12F", full: "ABCPS1234F" }, @@ -170,8 +170,8 @@ export const contactPrefs: NotifCategory[] = [ export type ContactDestination = { id: string; channel: "email" | "sms" | "whatsapp"; label: string; value: string; verified: boolean; primary: boolean }; export const contactDestinations: ContactDestination[] = [ - { id: "e1", channel: "email", label: "Personal email", value: "rajesh.sharma@example.com", verified: true, primary: true }, - { id: "e2", channel: "email", label: "Work email", value: "r.sharma@acme.co.in", verified: false, primary: false }, + { id: "e1", channel: "email", label: "Personal email", value: "james.carter@example.com", verified: true, primary: true }, + { id: "e2", channel: "email", label: "Work email", value: "j.carter@acme.co", verified: false, primary: false }, { id: "s1", channel: "sms", label: "Registered mobile", value: "+91 98765 43012", verified: true, primary: true }, { id: "w1", channel: "whatsapp", label: "WhatsApp", value: "+91 98765 43012", verified: true, primary: true }, ]; @@ -290,10 +290,10 @@ export type SupportAgent = { }; export const supportTeam: SupportAgent[] = [ - { id: "a1", name: "Priya Nair", initials: "PN", role: "Senior Support Lead", team: "Accounts & KYC", gradient: "linear-gradient(135deg,#fda913,#fd6d13)", status: "online", rating: 4.9 }, - { id: "a2", name: "Arjun Mehta", initials: "AM", role: "Support Specialist", team: "Billing", gradient: "linear-gradient(135deg,#285ef0,#09b9c6)", status: "online", rating: 4.8 }, - { id: "a3", name: "Sara Khan", initials: "SK", role: "Technical Support", team: "Portal & Login", gradient: "linear-gradient(135deg,#9036e9,#285ef0)", status: "away", rating: 4.7 }, - { id: "a4", name: "Vikram Rao", initials: "VR", role: "Field Coordinator", team: "Inspections", gradient: "linear-gradient(135deg,#14bc83,#09b9c6)", status: "offline", rating: 4.9 }, + { id: "a1", name: "Emma Wilson", initials: "EW", role: "Senior Support Lead", team: "Accounts & KYC", gradient: "linear-gradient(135deg,#fda913,#fd6d13)", status: "online", rating: 4.9 }, + { id: "a2", name: "Liam Foster", initials: "LF", role: "Support Specialist", team: "Billing", gradient: "linear-gradient(135deg,#285ef0,#09b9c6)", status: "online", rating: 4.8 }, + { id: "a3", name: "Sophie Turner", initials: "ST", role: "Technical Support", team: "Portal & Login", gradient: "linear-gradient(135deg,#9036e9,#285ef0)", status: "away", rating: 4.7 }, + { id: "a4", name: "Noah Mitchell", initials: "NM", role: "Field Coordinator", team: "Inspections", gradient: "linear-gradient(135deg,#14bc83,#09b9c6)", status: "offline", rating: 4.9 }, ]; // Category → department routing + attachment rules for the New Ticket form. @@ -345,8 +345,8 @@ export const tickets: Ticket[] = [ timeline: [ { status: "created", label: "Ticket created", at: "24 Jun, 10:12 AM", by: "You" }, { status: "open", label: "Routed to Accounts & KYC", at: "24 Jun, 10:13 AM", by: "System" }, - { status: "in-progress", label: "Priya Nair picked up the ticket", at: "24 Jun, 11:40 AM", by: "Priya Nair" }, - { status: "awaiting-customer", label: "Requested a clearer scan", at: "25 Jun, 09:05 AM", by: "Priya Nair" }, + { status: "in-progress", label: "Emma Wilson picked up the ticket", at: "24 Jun, 11:40 AM", by: "Emma Wilson" }, + { status: "awaiting-customer", label: "Requested a clearer scan", at: "25 Jun, 09:05 AM", by: "Emma Wilson" }, { status: "in-progress", label: "You re-uploaded the document", at: "28 Jun, 04:22 PM", by: "You" }, ], }, @@ -362,8 +362,8 @@ export const tickets: Ticket[] = [ agentId: "a3", timeline: [ { status: "created", label: "Ticket created", at: "18 Jun, 08:40 PM", by: "You" }, - { status: "in-progress", label: "Sara Khan investigating SMS gateway", at: "18 Jun, 09:02 PM", by: "Sara Khan" }, - { status: "resolved", label: "OTP delivery restored", at: "19 Jun, 10:15 AM", by: "Sara Khan" }, + { status: "in-progress", label: "Sophie Turner investigating SMS gateway", at: "18 Jun, 09:02 PM", by: "Sophie Turner" }, + { status: "resolved", label: "OTP delivery restored", at: "19 Jun, 10:15 AM", by: "Sophie Turner" }, ], }, { @@ -378,8 +378,8 @@ export const tickets: Ticket[] = [ agentId: "a2", timeline: [ { status: "created", label: "Ticket created", at: "12 Jun, 02:10 PM", by: "You" }, - { status: "in-progress", label: "Arjun Mehta reviewing billing run", at: "12 Jun, 03:30 PM", by: "Arjun Mehta" }, - { status: "awaiting-customer", label: "Need bank reference for the second debit", at: "26 Jun, 11:00 AM", by: "Arjun Mehta" }, + { status: "in-progress", label: "Liam Foster reviewing billing run", at: "12 Jun, 03:30 PM", by: "Liam Foster" }, + { status: "awaiting-customer", label: "Need bank reference for the second debit", at: "26 Jun, 11:00 AM", by: "Liam Foster" }, ], }, { @@ -394,8 +394,8 @@ export const tickets: Ticket[] = [ agentId: "a4", timeline: [ { status: "created", label: "Ticket created", at: "02 Jun, 06:20 PM", by: "You" }, - { status: "in-progress", label: "Vikram Rao coordinating slot", at: "03 Jun, 10:00 AM", by: "Vikram Rao" }, - { status: "resolved", label: "Visit moved to 09 Jun, 11 AM", at: "04 Jun, 04:45 PM", by: "Vikram Rao" }, + { status: "in-progress", label: "Noah Mitchell coordinating slot", at: "03 Jun, 10:00 AM", by: "Noah Mitchell" }, + { status: "resolved", label: "Visit moved to 09 Jun, 11 AM", at: "04 Jun, 04:45 PM", by: "Noah Mitchell" }, { status: "closed", label: "Inspection completed", at: "06 Jun, 01:30 PM", by: "System" }, ], }, @@ -426,8 +426,8 @@ export const supportThreads: SupportThread[] = [ typing: true, updatedAt: "2 min ago", messages: [ - { id: "m1", from: "agent", text: "Hi Rajesh, this is Priya from Accounts & KYC. I'm looking into ticket LUP-48217.", at: "11:40 AM" }, - { id: "m2", from: "me", text: "Thanks Priya. The earlier scan was blurry so I uploaded a sharper one.", at: "11:42 AM" }, + { id: "m1", from: "agent", text: "Hi James, this is Emma from Accounts & KYC. I'm looking into ticket LUP-48217.", at: "11:40 AM" }, + { id: "m2", from: "me", text: "Thanks Emma. The earlier scan was blurry so I uploaded a sharper one.", at: "11:42 AM" }, { id: "m3", from: "agent", text: "Thanks, I can see the new scan now. Reviewing it with the verification team.", at: "11:43 AM" }, ], }, @@ -478,7 +478,7 @@ export const helpTopics: HelpTopic[] = [ accent: "var(--orange)", count: 3, articles: [ - { q: "How do I verify my property details?", a: "Open Profile → Personal Info and click ‘Verify to reveal’. Enter your plot number (e.g. 181) to unmask and confirm your records." }, + { q: "How do I verify my property details?", a: "Open Profile → Personal Info and click ‘Verify to reveal’. Enter your house number (e.g. 181) to unmask and confirm your records." }, { q: "What does ‘allottee’ mean if I'm a representative?", a: "If you manage the property on someone's behalf, the account holder and the allottee differ. Your relationship is shown on the Personal Info tab." }, { q: "How do I update my profile photo?", a: "Use the camera button on your profile header card. JPG or PNG up to 5 MB." }, ], @@ -530,7 +530,7 @@ export const helpTopics: HelpTopic[] = [ export type Rule = { n: number; title: string; detail: string; tag: string; tagColor: string }; export const rules: Rule[] = [ - { n: 1, title: "Verify-to-reveal", detail: "Masked personal details (email, mobile, PAN, Aadhaar) stay hidden until the account holder confirms their plot number. For this account the plot is 181.", tag: "Profile", tagColor: "var(--orange)" }, + { n: 1, title: "Verify-to-reveal", detail: "Masked personal details (email, mobile, PAN, Aadhaar) stay hidden until the account holder confirms their house number. For this account the house number is 181.", tag: "Profile", tagColor: "var(--orange)" }, { n: 2, title: "OTP goes to the registered mobile", detail: "Any sensitive change — email, mobile or password — sends a one-time code to the registered mobile number, never to the new/unverified destination.", tag: "Security", tagColor: "var(--blue)" }, { n: 3, title: "Allottee ≠ account holder", detail: "When the relationship is not ‘Self’, the account holder is a representative and the allottee's name is shown separately. Actions are logged against the account holder.", tag: "Profile", tagColor: "var(--orange)" }, { n: 4, title: "KYC needs two different documents", detail: "Identity proof and address proof must be two different document types. A single document (e.g. Aadhaar) cannot satisfy both slots at once.", tag: "KYC", tagColor: "var(--purple)" }, diff --git a/src/components/dashboard/dashboard.tsx b/src/components/dashboard/dashboard.tsx index 6aeb789..1d84c8e 100644 --- a/src/components/dashboard/dashboard.tsx +++ b/src/components/dashboard/dashboard.tsx @@ -1,28 +1,23 @@ "use client"; // ============================================================ -// Dashboard shell — keeps the sidebar + header and swaps the -// inner content between the Profile, Support and Rules views. +// Dashboard shell — premium dark theme. Keeps the sidebar + +// header and swaps the inner content. Profile / Support / Rules +// render real views; other workspace modules show a placeholder. // ============================================================ import { useEffect, useState } from "react"; -import { Sidebar } from "./sidebar"; +import { Sidebar, NAV_ITEMS } from "./sidebar"; import { Topbar } from "./topbar"; -import { ToastProvider } from "./ui"; +import { ToastProvider, PageHead, Btn, Icon } from "./ui"; import { Profile } from "./profile"; import { Support } from "./support"; import { Rules } from "./rules"; import "../../app/dashboard/dashboard.css"; -const HEADINGS: Record = { - profile: { title: "Profile", subtitle: "Your account, identity and preferences" }, - support: { title: "Support Center", subtitle: "Get help, track requests and find answers" }, - rules: { title: "Rules Checklist", subtitle: "The 13 rules behind Profile & Support" }, -}; - export function Dashboard() { const [theme, setTheme] = useState<"dark" | "light">("dark"); - const [active, setActive] = useState("profile"); + const [active, setActive] = useState("dashboard"); useEffect(() => { // Sync the persisted theme from localStorage (an external system) on mount. @@ -33,21 +28,44 @@ export function Dashboard() { setTheme((t) => { const n = t === "dark" ? "light" : "dark"; try { localStorage.setItem("lup_dash_theme", n); } catch {} return n; }); } - const head = HEADINGS[active] ?? HEADINGS.profile; + const item = NAV_ITEMS.find((i) => i.key === active); + const title = item?.label ?? "Dashboard"; + const subtitle = item?.subtitle ?? "Workspace module"; return (
- +
- {active === "profile" && } - {active === "support" && } - {active === "rules" && } + {active === "profile" ? + : active === "support" ? + : active === "rules" ? + : }
); } + +// Placeholder for workspace modules that don't have a view yet — keeps the +// full navigation coherent and points users to the live sections. +function ComingSoon({ title, icon, onGo }: { title: string; icon: string; onGo: (k: string) => void }) { + return ( +
+ +
+ +

{title} is coming soon

+

This area is reserved for the {title} module. In the meantime, the account experience is fully built out.

+
+ onGo("profile")}>Open Profile + onGo("support")}>Support Center + onGo("rules")}>Rules +
+
+
+ ); +} diff --git a/src/components/dashboard/profile.tsx b/src/components/dashboard/profile.tsx index 7f9fa8d..ea0f19c 100644 --- a/src/components/dashboard/profile.tsx +++ b/src/components/dashboard/profile.tsx @@ -79,14 +79,14 @@ function ProfileHeaderCard() {
{user.role} · Member since {user.memberSince}
{user.sector} · {user.pocket} - Allotment {user.allotmentNo} - Plot {user.plot} + {user.category} + House {user.plot}
- +
@@ -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 (
-

Personal details

+

Contact details

{verified ? Revealed : Masked} @@ -153,10 +147,10 @@ function PersonalInfoTab() {
Verify to reveal & edit - Sensitive details are masked. Enter your plot number to unmask and edit them. + Sensitive details are masked. Enter your house number to unmask and edit them.
- { setPlot(e.target.value.replace(/\D/g, "")); setErr(""); }} onKeyDown={(e) => e.key === "Enter" && verify()} /> Reveal @@ -166,15 +160,14 @@ function PersonalInfoTab() { )}
- {rows.map((f) => ( + {contactRows.map((f) => (
{f.label}
{verified ? f.full : f.masked} - {verified && f.editable && ( - + {verified && ( + )} - {verified && !f.editable && via KYC}
))} @@ -189,40 +182,26 @@ function PersonalInfoTab() {
-

Allottee & account holder

- {!editId - ? { setHolderDraft(holder); setEditId(true); }}>Edit - :
setEditId(false)}>Cancel { setHolder(holderDraft); setEditId(false); push({ tone: "success", title: "Details saved" }); }}>Save
} +

Account & identity

+ {verified + ? Revealed + : Protected}
- {user.isAllottee ? ( -
- -
You are the allottee.The account holder and the allottee are the same person ({holder.relationship}).
+
+
Full name
{user.name}
+
Role
{user.role}
+
Member since
{user.memberSince}
+
Account status
Active
+
+
PAN
+
{verified ? user.pan.full : user.pan.masked}{verified && via KYC}
- ) : ( -
- -
Acting on behalf of the allottee.Account holder differs from the allottee — actions are logged against you.
+
+
Aadhaar
+
{verified ? user.aadhaar.full : user.aadhaar.masked}{verified && via KYC}
- )} - {editId ? ( -
- setHolderDraft((d) => ({ ...d, name: e.target.value }))} /> - - - -
- ) : ( -
-
Allottee name
{user.allotteeName}
-
Account holder
{holder.name}
-
Relationship
{holder.relationship}
-
Allotment no.
{user.allotmentNo}
-
Property
{user.sector} · {user.pocket} · Plot {user.plot}
-
- )} +
+

PAN & Aadhaar are managed under the KYC tab and can't be edited here.

g.items); + export function Sidebar({ active, onSelect }: { active: string; onSelect: (k: string) => void }) { return (