# 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 (`10–14px`), 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. ```js 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:'UTC−5:00'}, {id:'PT',abbr:'PT',label:'US Pacific — PT',utc:'UTC−8: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 3–4 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: ```js 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, `kind` ∈ `call|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 `