diff --git a/docs/consent-options.md b/docs/consent-options.md new file mode 100644 index 0000000..b2915d2 --- /dev/null +++ b/docs/consent-options.md @@ -0,0 +1,38 @@ +# YEIDA Portal β€” Consent Options (for review) + +Prepared as the meeting action item. **Consent** governs *what may be collected, disclosed or processed*. **Notification** governs *how we reach you* (email / SMS / WhatsApp / call / post) β€” these are separate and live under Profile β†’ Notifications & Privacy. + +## A. Individual consent options (toggles) + +| # | Consent | Default | Locked? | What it controls | +|---|---------|---------|---------|------------------| +| 1 | **Data Processing** (statutory) | ON | πŸ”’ Yes | Administer the allotment, payments & statutory notices. Required by law β€” cannot be turned off. | +| 2 | **Profile Visibility to Authority Officers** | ON | No | Show your name & plot to the officers handling your case. | +| 3 | **Analytics & Preference Cookies** | OFF | No | Usage analytics to improve the portal. | +| 4 | **Empanelled Partner Sharing** | OFF | No | Share with empanelled service partners (contractors, banks) for services you request. | +| 5 | **Identity Disclosure for Transfers** | OFF | No | Disclose allotment identity to a prospective buyer during a plot transfer. | +| 6 | **KYC / Document Sharing** | ON | No | Share KYC docs with authority cells for verification (needed to complete KYC). | +| 7 | **Cross-border Data Transfer (NRI)** | OFF | No | Process data for an overseas account holder / allottee. | +| 8 | **Marketing Communications** | OFF | No | Newsletter / promotions / product updates (granular per type). | + +## B. Dummy consent levels (tiered variant β€” for the dropdown demo) + +A single selector that bundles the toggles above, for users who prefer one choice: + +- **Level 0 β€” Essential only**: #1 only. Everything else OFF. +- **Level 1 β€” Standard**: Essential + #2 (officer visibility) + #6 (KYC sharing). +- **Level 2 β€” Enhanced**: Standard + #3 (analytics) + product updates. +- **Level 3 β€” Full**: Enhanced + #4 (partner sharing) + #5 (transfer disclosure) + #8 (marketing). + +Selecting a level pre-sets the individual toggles; the user can still fine-tune any toggle afterwards (level becomes "Custom"). + +## C. Consent vs Notification (kept separate) + +- **Consent** β†’ the table above (governance / disclosure control). +- **Notification** β†’ delivery channels per contact (primary/secondary email & phone): Email, SMS, WhatsApp, Call, Post β€” plus preferred time window & timezone. At least one channel always stays enabled. + +## Open items for next meeting +- Confirm the final consent labels & defaults. +- Decide: tiered levels (Section B) **and** granular toggles (Section A), or granular only. +- Confirm which consents are statutory/locked vs user-controllable. +- Source of truth: pull existing consent from the authority platform vs portal-managed. diff --git a/docs/profile-support-module-prompt.md b/docs/profile-support-module-prompt.md new file mode 100644 index 0000000..6d57e62 --- /dev/null +++ b/docs/profile-support-module-prompt.md @@ -0,0 +1,353 @@ +# 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 `