Files
Yamunaexpress/docs/profile-support-module-prompt.md
T
Goutam0612 e44e24ace2 Enhance portal profile/support modules and add rebuild prompt doc
Add profile (verify-to-reveal, KYC rules, contact-change OTP flows,
notifications/consent tabs, devices) and support (5 channels, message
center, ticket routing, live chat, help center) updates, the geo BFF
route, login auth flow, and a self-contained module rebuild prompt.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 21:02:05 +05:30

40 KiB
Raw Blame History

PROMPT — Rebuild the "Profile" + "Support Center" modules

Copy everything below this line into the other tool/website. It is a complete, framework-agnostic spec. It describes a real allottee-portal demo for a government land authority (the example is "YEIDA — Yamuna Expressway Industrial Development Authority"). Build the Profile module and the Support Center module exactly as described. You may adapt the brand name, but keep every behaviour, rule, field, screen, state and copy-tone identical.


0. Tech + look & feel

  • React (function components + hooks). Single-page feel; each module is one screen rendered inside an authenticated portal shell with a left sidebar and a content area.
  • Inline styles + a small set of utility CSS classes (list below). No component library — everything is hand-built.
  • Visual language: clean SaaS dashboard. White cards with 1px light borders, rounded corners (1014px), soft shadows, a green/navy authority palette, blue accents for interactive elements, amber for "pending/attention", red for errors, emerald-green for "success/verified".
  • Everything is a front-end demo with mocked data — no real backend. OTP verification is mocked: entering 000000 "verifies" any OTP. File uploads only capture the file name.
  • All money in INR (), dates as DD Mon YYYY, Indian number grouping.

CSS utility classes / tokens to implement (or map to your own)

Classes used throughout: row (flex row, center), col (flex column), spread (justify-between), gap-2/3/4, card, card-pad, card-hover, card-pad, btn + variants btn-primary btn-outline btn-ghost btn-emerald btn-sm btn-block, icon-btn, input, input-icon (icon-prefixed input wrapper), select, field (label + control group), label, badge + tones badge-blue badge-green badge-amber badge-navy badge-gray badge-red, chip (+.active), tbl (table), progress (bar), muted, faint, num (tabular numerals), dot (status dot), sec-eyebrow, anim-fade-up, anim-fade-in, anim-pop.

CSS variables (colors): --ink, --ink-2, --muted, --faint, --line, --line-2, --bg, --bg-2, --blue-50/100/400/500/600/700, --navy-700/800, --emerald-50/500/600/700, --amber-50/200/500/600, --red-50/500/600, --sh-xs/sm/md/lg, --font-display. Keyframe animations needed: spin, pulseDot (typing dots), growBar, fade/pop entrances.

Shared primitives you must build first

  1. Icon({name, size}) — an icon set. Names referenced: user, mail, phone, lock, shield, bell, device, edit, check, checkCircle, x, plus, search, upload, download, file, documents, building, map, mapPin, plot, grid, layers, calendar, clock, info, alert, flag, bookmark, ticket, messages, message/chat, send, paperclip, sparkles, support, funnel, arrowR, chevR, chevD, chevU, refresh, home, transfer, payments, construction, users, trend, rupee, eye, sun, moon, settings.
  2. Avatar({initials, size, src, style}) — circular; shows image if src, else initials.
  3. PageHead({title, subtitle, children}) — big H1 + muted subtitle, optional right-aligned action buttons.
  4. useToast()[toastNode, toast(msg)] — bottom-right toast stack, auto-dismiss ~3.2s.
  5. Modal({title, sub, icon, onClose, children, footer, width=520}) — centered overlay (rgba(8,26,57,.55) + blur), white rounded card, header with icon chip + title/sub + close X, scrollable body, optional footer row (right-aligned). Click backdrop to close; stop propagation on the card.
  6. Toggle({label, on, onChange}) — pill switch (emerald when on).
  7. OtpField({label, sent, value, onChange, verified, onVerify}) — 6-digit numeric input + "Verify" button (enabled at 6 digits). When verified: green border, check icon, "Verified ✓". Helper text: OTP sent to {sent} · enter 000000 to verify (demo).

1. MOCK DATA (single source of truth)

Create one data object D with these fields. Keep the exact shapes; values can be tweaked but keep them realistic.

const D = {
  user: {
    name: 'Rajeev Malhotra', firstName: 'Rajeev', lastName: 'Malhotra', initials: 'RM',
    allotment: 'YEA-654321', sector: 'Sector 18', pocket: 'Pocket B', plot: '181',
    category: 'Residential', size: '300 sq.m',
    mobile: '+91 98xxxxxx12', email: 'r.malhotra@email.com', memberSince: 'Mar 2019',
    relationship: 'Self',          // Self | Son | Daughter | Spouse | Relative | Authorized Representative
    isAllottee: true,
    allotteeFirstName: 'Rajeev', allotteeLastName: 'Malhotra',
    mailingAddress: { line1:'C-204, Orchid Residency, Sector 50', line2:'Near City Centre', city:'Noida', state:'Uttar Pradesh', pin:'201301' },
  },
  relationshipOptions: ['Self','Son','Daughter','Spouse','Relative','Authorized Representative'],

  // ----- SUPPORT -----
  // Ticket categories drive dropdown routing. attach:true => attachment required to submit.
  ticketCategories: [
    { id:'technical',   label:'Technical Support',     icon:'device',       color:'blue',    dept:'IT Helpdesk',     sla:'Replies within 4 hrs',
      subs:['Login / OTP not working','Portal error or bug','Document upload failing','Page not loading','Other technical issue'] },
    { id:'payment',     label:'Payments & Challan',    icon:'payments',     color:'emerald', dept:'Payments Desk',   sla:'Replies within 1 working day',
      subs:['Payment not reflecting','Challan / receipt not generated','Refund request','Dues / interest query','Failed online payment'] },
    { id:'kyc',         label:'KYC & Documents',       icon:'documents',    color:'navy',    dept:'Records Cell',    sla:'Replies within 2 working days', attach:true,
      subs:['KYC document rejected','Update / re-upload KYC','Lease deed correction','Certificate copy request','Name / detail correction'] },
    { id:'construction',label:'Construction Approvals',icon:'construction', color:'amber',   dept:'Construction Cell',sla:'Replies within 2 working days',
      subs:['Building plan query','Plan re-submission','Inspection / progress','Completion certificate','Map / setback / FAR query'] },
    { id:'allotment',   label:'Allotment & Transfer',  icon:'transfer',     color:'blue',    dept:'Allotment Cell',  sla:'Replies within 2 working days', attach:true,
      subs:['Allotment detail correction','Plot transfer / sale','Possession query','Mutation / succession','Address update'] },
    { id:'general',     label:'General / Other',       icon:'messages',     color:'navy',    dept:'Customer Care',   sla:'Replies within 1 working day',
      subs:['Account question','Feedback / suggestion','How do I…','Something else'] },
  ],
  tickets: [ // "My Tickets" table rows
    { id:'TKT-4821', subject:'Lease deed correction request',          category:'Records & Documents',   status:'Open',        priority:'High',   updated:'2h ago' },
    { id:'TKT-4790', subject:'Challan not reflecting after UPI payment',category:'Payments & Challan',     status:'Resolved',    priority:'Medium', updated:'3d ago' },
    { id:'TKT-4755', subject:'Building plan re-submission query',       category:'Construction Approvals', status:'In Progress', priority:'Medium', updated:'5d ago' },
  ],
  supportThreads: [ // secure Message Center inbox threads
    { id:'MSG-4821', subject:'Lease deed correction request',
      agent:{ name:'Anjali Sharma', role:'Records Officer', initials:'AS', online:true }, unread:2, time:'2h ago',
      messages:[
        {from:'me',   text:'There is a spelling error in the name on my registered lease deed. How can I get it corrected?', time:'Mon 10:12'},
        {from:'agent',text:'Hello Rajeev, thanks for reaching out. We can process a name-correction request against allotment YEA-654321. Could you share a self-attested ID proof?', time:'Mon 10:21'},
        {from:'me',   text:'Sure, I have uploaded my Aadhaar in the Documents section.', time:'Mon 10:25'},
        {from:'agent',text:'Received. Your correction request is logged as TKT-4821 and forwarded to the Records cell. Expected TAT is 7 working days.', time:'Mon 10:31'},
      ] },
    { id:'MSG-4790', subject:'Challan not reflecting after UPI payment',
      agent:{ name:'Vikram Singh', role:'Payments Desk', initials:'VS', online:true }, unread:0, time:'Yesterday',
      messages:[
        {from:'me',   text:'I paid ₹38,625 via UPI yesterday but the challan is not showing under Payments.', time:'Tue 16:40'},
        {from:'agent',text:'Apologies for the delay — UPI reconciliations can take up to 24 hours. I can see the payment now; challan CH-2026-0128-04 has been generated against your account.', time:'Tue 17:02'},
        {from:'me',   text:'Great, I can see it now. Thank you!', time:'Tue 17:05'},
      ] },
    { id:'MSG-4755', subject:'Building plan re-submission query',
      agent:{ name:'Neha Gupta', role:'Construction Cell', initials:'NG', online:false }, unread:1, time:'5d ago',
      messages:[
        {from:'me',   text:'My building plan was returned for revision. What changes are required?', time:'Thu 11:15'},
        {from:'agent',text:'The rear-side setback needs to be increased to 3m as per the updated FAR norms. Please revise and re-upload the architect-stamped plan.', time:'Thu 12:02'},
      ] },
  ],
  supportTeam: [
    { name:'Aarav',         role:'Chief AI Support', initials:'AI', online:true,  ai:true, desc:'Instant answers, available 24/7' },
    { name:'Anjali Sharma', role:'Records Officer',  initials:'AS', online:true,  desc:'Lease deed, allotment & records' },
    { name:'Vikram Singh',  role:'Payments Desk',    initials:'VS', online:true,  desc:'Challans, dues & refunds' },
    { name:'Neha Gupta',    role:'Construction Cell',initials:'NG', online:false, desc:'Building plans & approvals' },
  ],
  helpTopics: [ // Help Center categories, each with expandable Q&A articles
    { id:'payments',     icon:'payments',  color:'emerald', title:'Payments & Challan',  desc:'Dues, e-challan, receipts & refunds', articles:[ /* {q,a} … */ ] },
    { id:'kyc',          icon:'documents', color:'navy',    title:'KYC & Documents',     desc:'KYC, lease deed & certificates',     articles:[ /* … */ ] },
    { id:'construction', icon:'construction',color:'amber', title:'Construction',        desc:'Plans, approvals & progress',        articles:[ /* … */ ] },
    { id:'account',      icon:'user',      color:'blue',    title:'Account & Profile',   desc:'Login, contacts & security',         articles:[ /* … */ ] },
    { id:'property',     icon:'building',  color:'navy',    title:'Property Verification',desc:'Allotment, plot & possession checks',articles:[ /* … */ ] },
    { id:'technical',    icon:'device',    color:'blue',    title:'Technical Support',   desc:'Login, OTP, errors & the portal',    articles:[ /* … */ ] },
    { id:'documents',    icon:'documents', color:'amber',   title:'Documents & Uploads', desc:'Uploading, formats & retrieving files',articles:[ /* … */ ] },
  ],
  contactTimes: [ // for callback "preferred time"
    {id:'morning',label:'Morning',hint:'9 AM  12 PM',icon:'sun'},
    {id:'afternoon',label:'Afternoon',hint:'12 PM  4 PM',icon:'sun'},
    {id:'evening',label:'Evening',hint:'4 PM  8 PM',icon:'moon'},
    {id:'anytime',label:'Anytime',hint:'No preference',icon:'clock'},
  ],
  countryCodes: [
    {code:'+91',label:'IN',name:'India',digits:10}, {code:'+971',label:'AE',name:'UAE',digits:9},
    {code:'+1',label:'US',name:'United States',digits:10}, {code:'+44',label:'UK',name:'United Kingdom',digits:10},
    {code:'+65',label:'SG',name:'Singapore',digits:8},
  ],

  // ----- PROFILE / KYC -----
  kycDocTypes: [ // proof: 'identity' | 'address' | 'photo'; alsoAddress, expires drive rules
    {id:'aadhaar', label:'Aadhaar',          proof:'identity', alsoAddress:true,  expires:false, hint:'Identity + address proof · does not expire'},
    {id:'pan',     label:'PAN',              proof:'identity', alsoAddress:false, expires:false, hint:'Identity proof · does not expire'},
    {id:'passport',label:'Passport',         proof:'identity', alsoAddress:true,  expires:true,  hint:'Identity + address proof · has an expiry date'},
    {id:'dl',      label:'Driving Licence',  proof:'identity', alsoAddress:true,  expires:true,  hint:'Identity + address proof · has an expiry date'},
    {id:'utility', label:'Electricity Bill', proof:'address',  alsoAddress:true,  expires:true,  hint:'Address proof · recent bill (within 3 months)'},
    {id:'water',   label:'Water Bill',       proof:'address',  alsoAddress:true,  expires:true,  hint:'Address proof · recent bill (within 3 months)'},
    {id:'housetax',label:'House Tax Receipt',proof:'address',  alsoAddress:true,  expires:false, hint:'Address proof · latest receipt'},
    {id:'bankstmt',label:'Bank Statement',   proof:'address',  alsoAddress:true,  expires:true,  hint:'Address proof · recent statement showing your name & address'},
    {id:'photo',   label:'Photograph',       proof:'photo',    alsoAddress:false, expires:false, hint:'Recent passport-size photograph'},
  ],
  kyc: {
    status:'verified', verifiedOn:'12 Mar 2019', contractSigned:true, viaPOA:false,
    documents:[
      {type:'aadhaar', number:'•••• 4821', proof:'identity+address', status:'verified', expiry:null},
      {type:'pan',     number:'•••• 234F', proof:'identity',         status:'verified', expiry:null},
      {type:'passport',number:'•••• 6789', proof:'identity+address', status:'verified', expiry:'14 Aug 2028'},
    ],
  },
  contactPrefs: {
    emails:[
      {id:'e1', value:'r.malhotra@email.com',     primary:true,  verified:true,  owner:'holder'},
      {id:'e2', value:'rajeev.work@company.com',  primary:false, verified:true,  owner:'holder'},
      {id:'e3', value:'allottee.nri@email.com',   primary:false, verified:true,  owner:'allottee'},
    ],
    phones:[
      {id:'p1', value:'+91 98xxxxxx12', primary:true,  verified:true,  owner:'holder'},
      {id:'p2', value:'+91 99xxxxxx77', primary:false, verified:false, owner:'holder'},
      {id:'p3', value:'+971 50xxxxxx89',primary:false, verified:true,  owner:'allottee'},
    ],
    marketing:{ newsletter:false, promotions:false, productUpdates:true },
    preferredTime:'anytime', preferredTz:'IST',
    consent:{ dataProcessing:true, profileVisibility:true, shareWithFamily:false, kycSharing:true,
      transferDisclosure:false, authorizedReps:false, analyticsCookies:false, thirdPartySharing:false,
      crossBorder:false, platformUpdates:true, marketingConsent:false },
  },
  contactTimezones:[
    {id:'IST',abbr:'IST',label:'India — IST',utc:'UTC+5:30'}, {id:'GST',abbr:'GST',label:'UAE / Gulf — GST',utc:'UTC+4:00'},
    {id:'GMT',abbr:'GMT',label:'UK — GMT',utc:'UTC+0:00'}, {id:'ET',abbr:'ET',label:'US Eastern — ET',utc:'UTC5:00'},
    {id:'PT',abbr:'PT',label:'US Pacific — PT',utc:'UTC8:00'}, {id:'SGT',abbr:'SGT',label:'Singapore — SGT',utc:'UTC+8:00'},
  ],
  loginActivity:[
    {action:'Successful login',  device:'MacBook Pro · Chrome', ip:'49.36.x.x', location:'Greater Noida, IN', time:'Today, 09:14',     ok:true},
    {action:'Password changed',  device:'MacBook Pro · Chrome', ip:'49.36.x.x', location:'Greater Noida, IN', time:'Yesterday, 18:02', ok:true},
    {action:'New device login',  device:'iPhone 14 · YEIDA App',ip:'49.36.x.x', location:'Greater Noida, IN', time:'2 days ago',       ok:true},
    {action:'Failed login attempt',device:'Unknown · Firefox',  ip:'103.21.x.x',location:'Mumbai, IN',        time:'4 days ago',       ok:false},
    {action:'Login alert consent enabled',device:'Windows PC · Edge',ip:'157.32.x.x',location:'Delhi, IN',    time:'5 days ago',       ok:true},
  ],
};

For helpTopics[].articles, write 34 realistic {q, a} pairs per topic in the same helpful tone (e.g. Payments: "How do I generate an e-Challan?", KYC: "How do I update my KYC?" reminding ID & address must be two different docs, Account: "How do I change my email or mobile?" noting OTP goes to registered mobile, Property: "How do I verify my allotment details?" via verify-to-reveal, Technical: "I am not receiving my OTP", Documents: "Which file formats and sizes are accepted?" → JPG/PNG/PDF up to 10 MB). Each {q,a} becomes an accordion article.

A tone→color map used for category chips/cards:

const TONE = { blue:['var(--blue-50)','var(--blue-600)'], emerald:['var(--emerald-50)','var(--emerald-700)'],
               navy:['#e9eefb','var(--navy-700)'], amber:['var(--amber-50)','var(--amber-600)'] };

A persistent user-photo store + profile-override store backed by localStorage (so the uploaded avatar and edited name survive route changes): expose useUserPhoto() → [photo, setPhoto], readImageFile(file, cb) (FileReader → data URL), setUserProfile(patch), and useUser(base) which merges saved overrides over D.user.


2. SUPPORT CENTER module

A single Support({ go, toast }) screen. go(route) navigates the portal (e.g. go('assistant') opens the AI assistant). Layout top-to-bottom:

2.1 Header

PageHead title="Support Center" subtitle="Raise tickets, chat with us, or browse the help center" with a right-side "Talk to AI" outline button (calls talkAI).

2.2 Five channel cards (responsive grid, minmax(180px,1fr))

Each is a clickable card: icon chip (tone-colored), bold title, muted subtitle.

  1. AI Assistant — "Instant answers · available 24/7" — sparkles, blue → talkAI = go('assistant').
  2. Live Chat — "Choose AI or a human officer" — chat, emerald → opens a chat-choice modal.
  3. Request Callback — "We'll call you at your preferred time" — phone, navy → opens CallbackForm (kind call).
  4. Talk to Human Agent — "Connect to a support officer" — support, amber → opens LiveChatModal (human).
  5. Create Ticket — "Title, details & attachments" — ticket, emerald → opens NewTicketForm.

Chat-choice modal (title "Start a live chat", sub "Who would you like to chat with?"): two big option rows — "Chat with AI Assistant" (→ talkAI) and "Chat with a Human Agent" (→ open LiveChatModal). Each row: icon chip + title + description + chevron.

2.3 "Your support team" card

Header "Your support team." + green "● Support online" badge. 4-column grid of supportTeam cards: avatar (AI member gets a blue→navy gradient avatar + an "AI" badge) with an online green dot, name, role, description, and a full-width button — "Ask AI" (→ talkAI) for the AI member, else "Message" (→ toast "Message sent to {firstName}").

2.4 Message Center (MessageCenter)

Secure two-pane messaging, inbox + live thread:

  • Card header: gradient icon chip, "Message Center", sub "Secure messaging with YEIDA support officers", green "● Support online" badge.
  • Left inbox (300px, scroll): "Inbox" + a total-unread pill (N new). Each thread row: agent avatar (with online dot), agent name + time, subject (truncated), last-message preview (prefix You: if last from me), and a blue unread count bubble. Active thread has blue left border + blue-50 bg. Opening a thread zeroes its unread.
  • Right thread (470px tall flex column): header with agent avatar/name/role + online state + a gray badge with the thread id. Scrollable message list: my bubbles are blue, right-aligned, tail bottom-right; agent bubbles white with border + small avatar, tail bottom-left; each with a tiny timestamp. Auto-scroll to bottom on new messages.
  • Composer: paperclip (attach), text input (Enter to send), send button. On send: append my message (time "Just now"), show a 3-dot typing indicator (~1s), then append a canned agent reply: Thank you for your message. {agentFirstName} from the {role} will review this and get back to you shortly.

2.5 New Ticket form (NewTicketForm) — inline card, not a modal

Header: ticket icon, "Raise a support ticket", sub "Pick a category so we route it to the right department — no need to call", close X. Fields:

  1. Category dropdown (from ticketCategories, shows label). When chosen, show a department chip (cat.dept) next to the label, and the select icon adopts the category icon/color. Helper line lists example categories.
  2. Sub-reason dropdown (appears after a category is chosen; options = cat.subs). Helper shows {dept} · {sla} with a clock icon.
  3. Title (text) and Description (textarea).
  4. Attachments — two upload buttons: "Attach screenshot" (image/*) and "Attach document" (image/*,application/pdf). Capture file name only. If cat.attach === true, mark "· required" and show an amber "Attach a document to submit" badge until something is attached.
  5. Auto-routing note (tone-colored): "This ticket is auto-assigned to {dept} — the right officer picks it up. {sla}."
  6. Footer: a left "Talk to AI instead" ghost button (→ talkAI), plus Cancel + Submit ticket. Submit is enabled only when category + sub-reason + title + description are filled and (if attachment required) something is attached. On submit → toast: Ticket created & auto-routed to {dept} — the right officer will be assigned and you'll be notified.

2.6 My Tickets table

Card header "My Tickets" + hint "Click a ticket to check status & add an update". Table columns: Ticket id, Subject, Department (gray badge from category), Priority (red badge if "High" else gray), Status (badge tone via the status map below), Updated, chevron. Clicking a row opens TicketDetailModal.

Ticket status tones (single source): Open→amber, In Progress→blue, Waiting for User→amber, Resolved→green, Closed→gray.

TicketDetailModal: header = ticket id + subject. Body:

  • Badge row: status badge, category badge, priority badge (red if High), "Updated {updated}".
  • Status timeline — vertical stepper with fixed order Open → In Progress → Waiting for User → Resolved → Closed, each with a sub-label (Open="Logged and classified in the system", In Progress="Assigned to the handling officer", Waiting for User="Awaiting your reply or documents", Resolved="Resolution provided", Closed="Ticket closed"). Steps before current = done (emerald + check), current = blue with a "Current" badge, future = muted. Connector line emerald up to current.
  • If status is Resolved/Closed → show an emerald "This ticket is resolved…" note. Otherwise show an "Add an update / reply" textarea + Attach + Post update button (enabled when non-empty) → toast Update added to {id} and close.

2.7 Callback / Email form (CallbackForm({kind})) — a Modal, kindcall|email

  • Topic dropdown (from ticketCategories labels) — required.
  • call: country-code select (with flag emoji from ISO label; map UK→GB) + mobile number input; preferred date (date input) + preferred time (IST) — a select of contactTimes plus a "Specific time…" option that swaps to a <time> input (with an X to revert to a window). Valid when topic set and ≥8 digits.
  • email: name, mobile (optional), email (required, validated). Valid when topic + name + valid email.
  • Optional reason/notes textarea.
  • On submit → a centered thank-you confirmation state inside the modal: green check, "Thank you!", and a personalized line: "Our team will contact you shortly on {cc} {phone} / at {email}" + (if call & specific) "at around {time}" / (if call & a window) "during your preferred {window} window" + (if topic) "regarding your {topic} query". "You'll also receive a confirmation by email." Footer Done.

2.8 Live Chat with a human (LiveChatModal) — ChatGPT-style, 840px modal

  • Left conversations sidebar (252px): a "New chat" button on top, then "Previous conversations" = the supportThreads (seeded as past convos) plus the current new chat. Each item: avatar (or a "+" chip for the new chat), title, time, last-message snippet.
  • Right chat area: header with the (human) agent avatar + online dot + role + state ("Online now" / "Connecting…"); thread id badge for past convos.
  • New-chat handshake: on starting a new chat run a timed sequence — connecting (spinner "Connecting you to support…", ~1s) → waiting ("Please wait — an agent will join shortly…") → typing dots → joined: insert agent message Hi, this is {firstName} from the {role}. I've joined the chat — how can I help you today? and show "{agent} joined the chat". The input is disabled until joined.
  • Pick the human agent as the one named "Anjali" from supportThreads (fallback first). Sending a message shows typing (~1.1s) then a canned reply: "Thanks for the details — let me check that for you right away." "New chat" resets the new conversation and restarts the handshake.

2.9 Help Center (HelpCenter) — Amazon/ServiceNow-style self-service, at the bottom

  • Gradient header with support icon, "Help Center", sub "Find answers fast — most questions are solved here without calling", and a search box ("Search help — e.g. challan, KYC, transfer fee, OTP").
  • Search mode: when the query is non-empty, flatten all topics' articles, filter by (q + a) containing the query, show "N results for "query"" and render each as an accordion (Article), first one open by default. Empty → "No articles matched — try rephrasing, or raise a ticket below."
  • Browse mode: left list of topics (icon chip in topic color, title, "{n} articles"); right pane shows the active topic's articles as single-open accordions.
  • Footer bar: "Didn't find your answer?" + Ask AI (→ talkAI) and Raise a ticket (→ open NewTicketForm).
  • Article = an accordion: question row (optional gray tag badge) + chevron; expands to the answer text.

3. PROFILE module

A single Profile({ go }) screen. Owns a toast. Layout:

3.1 Profile header card

Green radial-gradient banner (height ~100px). Overlapping avatar (84px; show uploaded photo if any, else initials) with a small blue camera/edit button to upload a photo (image/* → data URL via readImageFilesetPhoto + toast "Profile photo updated"). Beside it: user name (H2) and a sub line: {isAllottee ? 'Allottee' : relationship + ' · on behalf of allottee'} · {allotment}. Right side: green "🛡 KYC Verified" badge + navy "Member since {memberSince}" badge.

3.2 Left tab rail + content

Tabs (icon + label): Personal Info (user), KYC Verification (shield), Security (lock), Notifications (bell), Privacy & Consent (shield), Devices (device). Active tab = blue-50 bg, blue text, bold. Content card re-mounts per tab (keyed) for a fade-in.

3.3 Personal Info tab (PersonalTab)

Three sub-sections plus contact-change and verify-to-reveal flows.

(a) Account Holder — editable. Edit/Cancel/Save toggle in the header.

  • First Name, Last Name (editable only in edit mode).
  • Email Address + Mobile Number shown read-only, each with a "Change with verification" ghost button → opens ChangeContactModal.
  • Intro copy: "These are the account holder's details. Your name and contact (email & mobile) are editable — the allottee may be a different person (see below). Allotment identity fields are locked to authority records after verification."
  • Save → setUserProfile({...}) (name, initials, email, relationship, isAllottee, allottee names) + toast "Personal information saved".

(b) Allottee Details — the allottee may differ from the account holder.

  • Relationship to Allottee select (relationshipOptions), editable in edit mode.
  • If relationship is Self: show "Allottee Name" = account-holder name, read-only, with note "You are the original allottee (same as account holder)."
  • If not Self: show Allottee First Name, Last Name, Allottee Email, Allottee Mobile (seed allottee email/phone from contactPrefs where owner==='allottee'), plus an info note: "Allottee ≠ account holder — registered as {relationship}. Their email & number can receive reminders (set in Notifications & Privacy)." Copy above: "The allottee may be someone other than the account holder — for example, the plot is in your spouse's name while you manage the account. The email and mobile above always belong to the account holder."

(c) Allotment Identityverify-to-reveal (the key security pattern).

  • Fields: Allotment Number, Sector, Plot Number, Pocket — all locked/read-only.
  • Until verified they are masked with bullets (•••••, letter-spaced, muted) and each shows a lock icon. Section header shows a gray "Verification required" badge.
  • Show an amber banner: "Sensitive details are hidden — Verify your identity to view your allotment number, plot & sector." with a "Verify to reveal" button → opens AllotmentVerifyModal.
  • After verify: reveal real values, header badge turns green "Verified", toast "Allotment identity verified".

AllotmentVerifyModal (expectedPlot, registeredMobile): info box "For your security, your allotment number, plot & sector stay hidden until you confirm the plot number on record. A confirmation is logged against {registeredMobile}." + a Plot Number input. On "Verify & reveal": fake ~700ms lookup (spinner); if normalized input (strip spaces/hyphens, lowercase) equals expectedPlotonVerified(), else inline error "That plot number doesn't match our records. Please try again." Helper: "Enter the plot number from your allotment (demo: 181)."

ChangeContactModal (kind ∈ email|phone, current, registeredMobile, verifyOldEmailPolicy=false): rules —

  • Email: identity is always confirmed by an OTP sent to the registered MOBILE (not the old email). Show a blue security note saying so. Steps inside a dashed box once the new email is valid: (1) "Verify identity (registered mobile)" OTP — always required; (2) "Confirm old email (policy)" OTP — only if verifyOldEmailPolicy true and not lost-access; (3) "Verify new email" OTP. A "I lost access to my old email" checkbox skips the old-email step (then show: identity is confirmed via registered mobile, change logged for review). When policy off, show "Old-email confirmation is currently not required (system-configurable by the Authority)."
  • Mobile: verify the old number then the new number; "I lost access to my old phone" checkbox skips the old-number step (only the new number is verified, change logged for review).
  • All OTPs use the OtpField (demo 000000). Save enabled only when the new value is valid and the required OTP steps are verified. On save → update the field + toast "{Email|Mobile} updated & verified".

3.4 KYC Verification tab (KycTab)

Collected "once you become a customer, after the allotment contract is signed". Intro: identity proof, address proof (incl. electricity/utility bill) and a recent photograph are ALL mandatory here (photo was optional at sign-up).

  • Completion summary card: count of verified requirements, "{n} items still pending" or "All KYC requirements complete", a big percentage + progress bar (amber if pending, emerald if complete). verifiedOn shown when fully verified.
  • If kyc.viaPOA → blue note about Power of Attorney.
  • Inline upload panel (always on screen, NOT a popup) — KycUploadPanel: pick document type from a 3-col grid of kycDocTypes (icon by proof type: photo→user, address→mapPin, else file), show the type's hint, then choose a file (image/*,application/pdf, name only) and Upload document. On upload: append a doc {type, number:fileName, proof, status:'pending', expiry: expires?'To be read from document':null} (proof becomes identity+address if the type is identity with alsoAddress), toast "{label} uploaded — pending verification".
  • Requirements checklist — three required categories: Identity Proof, Address Proof ("A different document from your identity proof — e.g. Electricity / Utility bill"), Photograph. Each shows status (Verified/green, Pending review/amber, Not uploaded/gray), a "Mandatory" badge, and an Upload/Re-upload button that scrolls to + preselects the upload panel's type. Sort pending/missing first, verified last.
  • Rule: Identity proof and Address proof must be TWO DIFFERENT documents. If the same document is relied on for both (e.g. Aadhaar for both) → red conflict banner: "Your identity and address proof are the same document. Please add a different document for address proof…". Otherwise a blue reminder "Identity proof and address proof must be two different documents." (Address requirement is only satisfied by an address-proof of a different type than the ID proof.)
  • Documents on file list: each row = type label, proof-category badge, masked number, expiry (amber "Expires {date}" if the type expires, else "No expiry"; none for photo), and a Verified/Pending badge. Verified sorted last.
  • Acceptable documents reference grid listing every kycDocTypes entry with its hint (clock icon if it expires, else green check).

3.5 Security tab (SecurityTab)

Rows:

  • Password — "Last changed 3 months ago" → "Change" opens ChangePasswordModal.
  • Two-Factor Authentication — shows current method label ("OTP via mobile" / "Authenticator app" / "OTP via email") "enabled" → "Manage" opens TwoFAModal.
  • Authenticator App — verify a QR + code to enable app-based 2FA; shows a "Verified" badge once done → "Verify to set up" / "Re-verify".
  • Login Alerts toggle — "Email me on every new-device login" → toast on change.

ChangePasswordModal: current password + new + confirm. Password policy: min 8 chars with uppercase, lowercase, a number AND a special char. Show a live checklist (At least 8, Uppercase, Lowercase, Number, Special character) turning green as satisfied; red border on weak; "Passwords do not match" when confirm differs. Update enabled only when current filled, new is strong, and confirm matches → toast "Password updated".

TwoFAModal: choose method from SMS OTP / Authenticator App / Email OTP (radio cards). Authenticator shows a faux QR. A change is only allowed when the CURRENT PASSWORD is entered AND an OTP/TP is verified (demo 000000) — otherwise Save stays blocked with the note "Change stays blocked until your current password is entered and the OTP is verified." Save → toast "2FA method saved" (or "Authenticator app verified & enabled" via the authenticator entry point).

A single card "Notification Preferences / How communication is sent to you", with:

  1. Preferred channels — high-level on/off pill switches for Email, SMS, WhatsApp, Phone Call, and an optional (dashed) Postal Mail.
  2. Preferred contact time — multi-select windows Morning (9 AM12 PM), Afternoon (12 PM4 PM), Evening (4 PM8 PM) (selected = blue filled), an Anytime option (clears windows), and a Timezone select from contactTimezones (label + UTC). A live info line states which windows + timezone abbreviation will be used. (Optionally support a specific fromto time window.)
  3. Mobile numbers table — editable rows from contactPrefs.phones: number input, a role select (Primary / Secondary / Allottee), verified/unverified badge with inline Verify (→ VerifyContactModal), and per-row channel checkboxes for SMS, WhatsApp, Call, OTP. Add/remove numbers. Copy: "WhatsApp availability is detected from numbers added at registration — we won't ask again."
  4. Email addresses — editable rows from contactPrefs.emails: email input, role select (Primary/Secondary), a "Notify" checkbox, Verify (if unverified), remove. Add email.
  5. Summary info box: "The system will only contact you via {channels} during {time} ({tz}). Urgent security alerts may be sent at any time."
  6. Pointer line: "These are notification preferences (how we reach you). Manage consent … in the Privacy & Consent tab."

Notes: changing primary uses a radio per list (only one primary); setting role to Allottee tags owner:'allottee'. Use plain render-helper functions, NOT nested components, so inputs keep focus across re-renders.

VerifyContactModal: an OtpField (demo 000000) to verify a single email/number; Done → mark verified + toast.

Intro: "Consent controls what data can be shared, stored or used — separate from your Notification preferences …which control how we reach you." A gray "Synced from authority records" badge. Data-driven consent list grouped into Essential / Disclosure / Data & cookies / Communications. Each item: title, description, and an emerald pill switch (or a locked "On" badge for required items). Items:

  • Essential: Consent to data processing & storage (locked on) — "Required to administer your allotment, payments & statutory notices."
  • Disclosure: Profile visible to handling officers; Share profile information with family members; Share information with authorised representatives (POA); KYC & document sharing; Disclose identity during a plot transfer.
  • Data & cookies: Analytics & preference cookies; Share with empanelled partners; Cross-border data transfer (NRI).
  • Communications: Allow platform updates & announcements; Receive marketing communications. When marketing is on, reveal granular toggles: Newsletter, Promotional emails, Product & service updates. Toggling any → toast "Consent preference saved". Footer note: "Withdrawing a consent stops the related processing going forward. Statutory data processing stays on while your allotment is active…"

3.8 Devices tab (DevicesTab)

  • Connected Devices list (hard-coded sample): e.g. "MacBook Pro · Chrome / Greater Noida · Active now / current", "iPhone 14 · YEIDA App / 2h ago", "Windows PC · Edge / Delhi · 5 days ago". Current device shows a green "This device" badge; others a red "Sign out" button → toast "Signed out of device".
  • Recent Activity timeline from loginActivity: each entry = a circular icon (emerald check if ok, else red alert), action, "{device} · {location}", and "IP {ip} · {time}", connected by a vertical line.

4. Behaviour rules checklist (don't miss these)

  1. OTP is mocked: 000000 verifies; the OtpField always says "enter 000000 to verify (demo)".
  2. Verify-to-reveal: allotment identity (allotment no., plot, sector, pocket) stays masked until the user confirms their plot number (demo 181).
  3. Email change is authorised by an OTP to the registered mobile, never the old email; old-email confirmation is policy-gated and skippable via "lost access".
  4. Mobile change verifies old then new number; "lost access" skips the old-number step.
  5. KYC: identity proof and address proof must be two different documents; photograph is mandatory at KYC (was optional at registration). Aadhaar can serve identity+address but cannot be used for both requirements simultaneously.
  6. Password policy: ≥8 chars + upper + lower + number + special.
  7. 2FA change requires current password and a verified OTP — otherwise blocked.
  8. Ticket routing: category → department + sub-reasons; some categories require an attachment; submit auto-routes (no "call for everything").
  9. Ticket lifecycle: Open → In Progress → Waiting for User → Resolved → Closed (timeline + badge tones above).
  10. Support channels are exactly five: AI Assistant, Live Chat (AI/human chooser), Request Callback, Talk to Human, Create Ticket.
  11. Notifications ≠ Consent: keep them in separate tabs with the cross-pointers.
  12. Allottee may differ from account holder (NRI/spouse case): per-contact owner of holder|allottee, allottee can have own email/phone, reminders can route to them.
  13. Everything writes to local React state (+ localStorage for photo/profile overrides) and surfaces a toast; no network calls.

Build it pixel-clean and production-looking, matching the SaaS-dashboard aesthetic described in §0. Use the exact copy and field names above.