`; }; const downloadLetter = () => { const html = generateLetterHTML(aiState.content); const blob = new Blob([html], { type: 'text/html' }); const url = URL.createObjectURL(blob); const win = window.open(url, '_blank'); if (!win) { toast('Pop-up blocked — allow pop-ups and try again', 'error'); } }; const sendLetter = async (c) => { if (!c.email) { toast('No client email on this case — add one first', 'error'); return; } setAiSending(true); try { const data = await api('/ai/send-letter', { method: 'POST', body: JSON.stringify({ caseId: c.id, letterText: aiState.content, clientEmail: c.email, clientName: c.clientName || ((c.sur||'')+(c.rest||'')) }) }); if (data.error) throw new Error(data.error); toast('Letter sent to ' + c.email + ' ✓', 'info'); } catch(e) { toast('Send failed: ' + (e.message || 'unknown error'), 'error'); } setAiSending(false); }; useEffect(() => { if (showAuth) return; // Fetch BoE base rate via lawes-proxy (already cached in KV, no auth needed) (async () => { try { const r = await fetch('https://lawes-proxy.nathan-042.workers.dev/rates'); const data = await r.json(); if (data && data.baseRate) { setBoeRate(parseFloat(data.baseRate)); setBoeDate(data.updatedMonth || ''); } } catch(e) { /* silently fail */ } })(); }, [showAuth]); useEffect(() => { const handler = e => { if (e.key === 'Escape') { if (showCompose) { setShowCompose(false); return; } if (showNewCase) { setShowNewCase(false); return; } if (cmdCentre) { setCmdCentre(false); return; } if (sel) { setSel(null); return; } } }; window.addEventListener('keydown', handler); return () => window.removeEventListener('keydown', handler); }, [showCompose, showNewCase, cmdCentre, sel]); useEffect(() => { if (showAuth) return; (async () => { try { const data = await api('/cases'); if (data.cases && data.cases.length > 0) { // Patch any cases missing new schema fields — never wipe real data const patched = data.cases .filter(c => c.stage !== '_deleted' && !c._deleted) .map(c => { const compDefaults = {tcsSigned:false,tcsSignedDate:null,tcsSignatoryName:null,tcsSignatoryIP:null,suitabilityRecorded:false,reasonWhy:"",consumerDutyNotes:"",declarations:[]}; const protDefaults = {life:{inPlace:false,provider:null,sumAssured:null,premium:null},ci:{inPlace:false,provider:null,sumAssured:null,premium:null},ip:{inPlace:false,provider:null,sumAssured:null,premium:null},bc:{inPlace:false,provider:null,sumAssured:null,premium:null}}; return { phone: "", email: "", todos: [], protection: protDefaults, docs: initDocs(c.type || 'Residential', 0), compliance: compDefaults, income: {basicSalary:null,bonus:null,bonusGuaranteed:false,dividends:null,rentalIncome:null,otherIncome:null,yearsTradingSelfEmployed:null}, commitments: {loans:0,creditCards:0,carHP:0,otherMortgages:0,totalMonthly:0}, ap2income: {basicSalary:null,bonus:null,bonusGuaranteed:false,dividends:null,rentalIncome:null,otherIncome:null,yearsTradingSelfEmployed:null}, ap2commitments: {loans:0,creditCards:0,carHP:0,otherMortgages:0,totalMonthly:0}, ap2currentAddr: "", ap2nationality: "", solicitor: {firm:"",contact:"",phone:"",email:"",status:"Not yet instructed"}, valuation: {status:"Not required",surveyor:"",ordered:"",received:"",value:""}, bdm: {name:"TBC",phone:"",email:""}, stageHistory: [], activityLog: [], ...c, // Deep-merge nested objects so KV partial data never wipes defaults compliance: { ...compDefaults, ...(c.compliance || {}) }, protection: { ...protDefaults, ...(c.protection || {}) }, lifecycle: { ...(c.lifecycle || {}) }, }; }); setCases(patched); } else { setCases([]); } } catch (e) { setApiError(e.message); setCases(INIT_CASES); } finally { setLoading(false); } })(); }, [showAuth]); const handleAuth = async () => { if (ppInput.length < 8) { setPpError('Passphrase too short'); return; } try { const test = await fetch('https://pipeline.lawesfinancial.com/cases', { headers: { 'Authorization': 'Bearer ' + ppInput } }); if (test.status === 401) { setPpError('Incorrect passphrase'); return; } window.sessionStorage.setItem('lwpp', ppInput); setShowAuth(false); } catch (e) { setPpError('Connection error'); } }; const selected = cases.find(c => c.id === sel); const typeOpts = ["All", ...Object.keys(TYPES)]; const updateCase = async (id, upd) => { setCases(prev => prev.map(c => c.id === id ? { ...c, ...upd, updatedAt: new Date().toISOString() } : c)); try { await api('/cases/' + id, { method: 'PUT', body: JSON.stringify(upd) }); } catch (e) { console.warn('Save:', e.message); } }; const toggleDoc = (id, i) => setCases(prev => prev.map(c => { if (c.id !== id) return c; const docs = c.docs.map((d, j) => j === i ? { ...d, received: !d.received, receivedDate: d.received ? null : new Date().toISOString() } : d); return { ...c, docs }; })); const uploadToSharePoint = async (file, docType, caseData, folderOverride) => { setUploading(true); setUploadMsg(null); try { const reader = new FileReader(); const base64 = await new Promise((res, rej) => { reader.onload = e => res(e.target.result.split(',')[1]); reader.onerror = rej; reader.readAsDataURL(file); }); const pp = window.sessionStorage.getItem('lwpp') || ''; const resp = await fetch('https://pipeline.lawesfinancial.com/files/upload', { method: 'POST', headers: { 'Authorization': 'Bearer ' + pp, 'Content-Type': 'application/json' }, body: JSON.stringify({ clientName: caseData.clientName, caseType: caseData.type, documentType: docType, fileName: file.name, fileBase64: base64, folderOverride: folderOverride || undefined }) }); const result = await resp.json(); if (result.success) { setUploadMsg({ ok: true, text: '✓ Uploaded to ' + result.targetFolder }); } else { setUploadMsg({ ok: false, text: result.error || 'Upload failed' }); } } catch (e) { setUploadMsg({ ok: false, text: 'Error: ' + e.message }); } setUploading(false); }; const moveCard = async (id, dir, targetStage = null) => { const c = cases.find(x => x.id === id); if (!c) return; let newStage; if (targetStage) { newStage = targetStage; } else { const idx = STAGES.indexOf(c.stage) + dir; if (idx < 0 || idx >= STAGES.length) return; newStage = STAGES[idx]; } if (newStage === c.stage) return; const now = new Date().toISOString(); const history = [...(c.stageHistory || [])].map(s => !s.exitedAt ? { ...s, exitedAt: now } : s ); history.push({ stage: newStage, enteredAt: now, exitedAt: null }); await updateCase(id, { stage: newStage, stageHistory: history }); }; const deleteCase = async (id) => { setCases(prev => prev.filter(c => c.id !== id)); setSel(null); setDeleteConfirm(false); toast('Case removed from pipeline', 'ok'); try { // Try DELETE first, fall back to marking deleted in KV via PUT const res = await fetch('https://pipeline.lawesfinancial.com/cases/' + id, { method: 'DELETE', headers: { 'Authorization': 'Bearer ' + (window.sessionStorage.getItem('lwpp')||''), 'Content-Type': 'application/json' } }); if (!res.ok) { // Worker may not support DELETE — mark as deleted via PUT await api('/cases/' + id, { method: 'PUT', body: JSON.stringify({ _deleted: true, stage: '_deleted' }) }); } } catch(e) { console.warn('Delete:', e.message); } }; const toggleTodo = (caseId, todoId) => { const c = cases.find(x => x.id === caseId); if (!c) return; const todos = (c.todos || []).map(t => t.id === todoId ? { ...t, done: !t.done } : t); updateCase(caseId, { todos }); }; const addTodo = (caseId, text) => { if (!text.trim()) return; const c = cases.find(x => x.id === caseId); if (!c) return; const todos = [...(c.todos || []), { id: Date.now(), text: text.trim(), done: false }]; updateCase(caseId, { todos }); setNewTodo(""); }; const urgencyInfo = (c) => { const oe = daysUntil(c.offerExpiry); const tc = daysUntil(c.targetCompletion); const ae = daysUntil(c.aipExpiry); if (oe !== null && oe >= 0 && oe <= 7) return { cls: 'urg-r', msg: `Offer expires in ${oe}d` }; if (tc !== null && tc >= 0 && tc <= 7) return { cls: 'urg-r', msg: `Completion in ${tc}d` }; if (oe !== null && oe >= 0 && oe <= 21) return { cls: 'urg-a', msg: `Offer expires in ${oe}d` }; if (tc !== null && tc >= 0 && tc <= 21) return { cls: 'urg-a', msg: `Completion in ${tc}d` }; if (ae !== null && ae >= 0 && ae <= 14) return { cls: 'urg-a', msg: `AIP expires in ${ae}d` }; return null; }; const handleNcLoan = v => { const loan = parseFloat(v.replace(/[£,]/g,'')); const ltv = parseFloat(ncData.ltv); if (!isNaN(loan) && !isNaN(ltv) && ltv > 0 && ltv < 100) { const dep = Math.round(loan * (100 - ltv) / ltv); setNcData(p => ({...p, loan:v, deposit:dep.toLocaleString()})); } else { setNcData(p => ({...p, loan:v})); } }; const handleNcLtv = v => { const ltv = parseFloat(v); const loan = parseFloat(ncData.loan.replace(/[£,]/g,'')); if (!isNaN(loan) && !isNaN(ltv) && ltv > 0 && ltv < 100) { const dep = Math.round(loan * (100 - ltv) / ltv); setNcData(p => ({...p, ltv:v, deposit:dep.toLocaleString()})); } else { setNcData(p => ({...p, ltv:v})); } }; const handleNcDeposit = v => { const dep = parseFloat(v.replace(/[£,]/g,'')); const loan = parseFloat(ncData.loan.replace(/[£,]/g,'')); if (!isNaN(loan) && !isNaN(dep) && dep >= 0) { const ltv = Math.round(loan / (loan + dep) * 1000) / 10; setNcData(p => ({...p, deposit:v, ltv:ltv + '%'})); } else { setNcData(p => ({...p, deposit:v})); } }; const createCase = async () => { if (!ncData.sur || !ncData.first || !ncData.loan) { setNcStatus({ type:'err', msg:'Name and loan amount are required.' }); return; } setNcSaving(true); setNcStatus(null); const now = new Date().toISOString(); const tempId = 'temp-' + Date.now(); const clientName = ncData.sur.trim() + ', ' + ncData.first.trim(); const loanNum = parseFloat(ncData.loan.replace(/[£,\s]/g, '')) || null; const nc = { id: tempId, firmId:"lawes-financial", userId:"nathan-lawes", clientId:"client-" + tempId, acreRef:"", createdAt:now, updatedAt:now, createdBy:"nathan-lawes", sur: ncData.sur.trim(), rest: ', ' + ncData.first.trim(), isJoint: ncData.isJoint, applicant2: ncData.isJoint && ncData.ap2sur ? { sur:ncData.ap2sur.trim(), rest:', '+ncData.ap2first.trim(), dob:"" } : null, dob:"", currentAddr:"", employer:"", incomeType:"Employed", nationality:"", residencyStatus:"", creditStatus:"Clean", adverseDetail:"", leadSource:"Direct", introducerId:null, type:ncData.type, stage:newCaseStage, stageHistory:[{ stage:newCaseStage, enteredAt:now, exitedAt:null }], propAddr:ncData.propAddr, propertyType:"", loan:ncData.loan.replace(/[£,]/g,''), ltv:ncData.ltv, term:"", product:"", repaymentType:"Capital repayment", deposit:ncData.deposit, depositType:"Savings", lender:"TBC", rateStartDate:null, rateExpiry:null, aipRef:"", aipExpiry:"", appRef:"", hasAip:false, bdm:{name:"TBC",phone:"",email:""}, offerExpiry:"", targetExchange:"", targetCompletion:"", completedAt:null, fee:"0", procFeePercent:0.5, brokerFee:0, brokerFeeCharged:false, commissionReceived:false, commissionReceivedDate:null, solicitor:{firm:"",contact:"",phone:"",email:"",status:"Not yet instructed"}, valuation:{status:"Not required",surveyor:"",ordered:"",received:"",value:""}, protection:{life:pe(),ci:pe(),ip:pe(),bc:pe()}, protAction:"Not yet raised — raise before application", docs: initDocs(ncData.type, 0), compliance: emptyComp(), activityLog:[{ ts:now, type:"stage", text:"Case opened — " + ncData.type + " enquiry" }], income:{basicSalary:null,bonus:null,bonusGuaranteed:false,dividends:null,rentalIncome:null,otherIncome:null,yearsTradingSelfEmployed:null}, commitments:{loans:0,creditCards:0,carHP:0,otherMortgages:0,totalMonthly:0}, ap2income:{basicSalary:null,bonus:null,bonusGuaranteed:false,dividends:null,rentalIncome:null,otherIncome:null,yearsTradingSelfEmployed:null}, ap2commitments:{loans:0,creditCards:0,carHP:0,otherMortgages:0,totalMonthly:0}, keyDate:"", action:"Send ToB, fact find link and welcome email", quickNote:"", phone:ncData.phone, email:ncData.email, company:ncData.company||"", lenderRef:"", paymentRoute:"", completionDate:"", todos:[] }; setCases(prev => [nc, ...prev]); setSel(tempId); setPTab("case"); setShowNewCase(false); setNcData({ sur:"", first:"", type:"First-Time Buyer", phone:"", email:"", loan:"", ltv:"", propAddr:"", deposit:"", company:"", isJoint:false, ap2sur:"", ap2first:"" }); try { const { id: _discard, ...ncRest } = nc; const workerPayload = { ...ncRest, clientName, loanAmount: loanNum, type: ncData.type, stage: newCaseStage }; const result = await api('/cases', { method:'POST', body:JSON.stringify(workerPayload) }); if (result && result.id) { setCases(prev => prev.map(c => c.id === tempId ? { ...c, id: result.id } : c)); setSel(result.id); toast('Case created · SharePoint folder ready', 'ok'); } } catch(e) { console.warn('Create case:', e.message); toast('Case saved locally — worker sync failed', 'warn'); } setNcSaving(false); }; const openCompose = (c) => { const subject = 'Re: Your mortgage — ' + cName(c); const body = 'Dear ' + cFirst(c) + ',\n\n'; setComposeData({ to: c.email || '', subject, body }); setComposeStatus(null); setShowCompose(true); }; // ── LIFECYCLE EMAIL HELPERS ────────────────────────────────────────────── const getCompletedAt = (c) => { const completedStage = (c.stageHistory || []).find(s => s.stage === 'Completed'); return completedStage ? new Date(completedStage.enteredAt) : (c.completedAt ? new Date(c.completedAt) : null); }; const lifecycleDaysSince = (c) => { const d = getCompletedAt(c); if (!d) return null; return Math.floor((Date.now() - d.getTime()) / 86400000); }; const lifecycleStatus = (c, key) => (c.lifecycle || {})[key]; const sendLifecycleEmail = async (c, key, subject, body) => { setComposeData({ to: c.email || '', subject, body }); setComposeStatus(null); setShowCompose(true); // Mark as sent const lifecycle = { ...(c.lifecycle || {}), [key]: new Date().toISOString() }; updateCase(c.id, { lifecycle }); const log = [...(c.activityLog || []), { ts: new Date().toISOString(), type: 'email', text: `Lifecycle email sent: ${subject}` }]; updateCase(c.id, { activityLog: log }); }; const lcDay0 = (c) => sendLifecycleEmail(c, 'day0', `Congratulations — your mortgage has completed!`, `Dear ${cFirst(c)},\n\nI'm delighted to let you know that your mortgage has now completed. This is a huge milestone and I wanted to take a moment to personally congratulate you.\n\nIt has been a pleasure working with you on this, and I hope the move goes smoothly. If there is anything you need in the days ahead, please don't hesitate to reach out.\n\nWarm regards,\nNathan`); const lcDay2 = (c) => sendLifecycleEmail(c, 'day2', `A small favour — Lawes Financial`, `Dear ${cFirst(c)},\n\nI hope you are settling in well and everything is going smoothly.\n\nIf you are happy with the service I provided, I would be truly grateful if you could take two minutes to leave a Google review. It makes an enormous difference to a small, independent business like mine.\n\nhttps://g.page/r/lawesfinancial/review\n\nThank you so much — it really does mean a lot.\n\nWarm regards,\nNathan`); const lcDay7 = (c) => sendLifecycleEmail(c, 'day7', `One more thing — Lawes Financial`, `Dear ${cFirst(c)},\n\nI hope you are getting settled and enjoying your new home.\n\nI just wanted to mention — if you know anyone who might benefit from independent mortgage or protection advice, I would be delighted to help them. A personal introduction from a happy client is the greatest compliment I can receive.\n\nThey can book a call directly at lawesfinancial.com, or simply pass on my details — nathan@lawesfinancial.com or 020 7788 7872.\n\nThank you again for choosing Lawes Financial.\n\nWarm regards,\nNathan`); const lcDay30 = (c) => sendLifecycleEmail(c, 'day30', `Your protection — a quick check-in`, `Dear ${cFirst(c)},\n\nNow that you have settled in, I wanted to reach out about something important that can sometimes get overlooked in the excitement of moving — your protection.\n\nYour mortgage is now in place, but it is worth making sure that you and your family are properly covered in the event of illness, injury, or worse. A short conversation is often all it takes to either put your mind at rest or identify something worth addressing.\n\nProtection advice costs you nothing — I am paid by the insurer only if you choose to proceed. Would you be available for a quick call this week?\n\nWarm regards,\nNathan`); const sendToB = async (c) => { if (!c.email) { toast('No email address on this case', 'warn'); return; } try { const result = await api('/sign/send', { method:'POST', body: JSON.stringify({ caseId: c.id, clientEmail: c.email, clientName: cName(c) }) }); if (result.success) { toast(`ToB sent to ${c.email}`, 'ok'); updateCase(c.id, { tobSentAt: new Date().toISOString(), tobSentTo: c.email }); } else { toast(result.error || 'Failed to send ToB', 'warn'); } } catch(e) { toast('Failed to send ToB — check connection', 'warn'); } }; const openProtectionEmail = (c) => { const subject = 'Protecting What Matters — ' + cFirst(c) + ', a quick note from Lawes Financial'; const body = `Dear ${cFirst(c)},\n\nI hope you're well. I wanted to take a moment to raise something important that often gets overlooked in the excitement of the mortgage process — your protection.\n\nYour home and income are your most valuable assets. While we've made great progress on your ${c.type.toLowerCase()}, I'd love to have a quick conversation to make sure you and your family are properly covered in the event of illness, injury, or worse.\n\nThis doesn't have to be complicated or expensive — even a short call can make a real difference.\n\nWould you be available for a 15-minute call this week? Simply reply to this email or call me on 020 7788 7872.\n\nKind regards,\nNathan`; setComposeData({ to: c.email || '', subject, body }); setComposeStatus(null); setShowCompose(true); }; const cloneCase = (type) => { const c = selected; if (!c) return; const now = new Date().toISOString(); const tempId = 'temp-' + Date.now(); const base = { id: tempId, stage: 'Enquiry', stageHistory: [{ stage: 'Enquiry', enteredAt: now, exitedAt: null }], createdAt: now, updatedAt: now, aipRef: '', aipExpiry: '', appRef: '', lenderRef: '', acreRef: '', hasAip: false, offerExpiry: '', targetExchange: '', targetCompletion: '', completionDate: '', completedAt: null, rateExpiry: '', rateStartDate: null, commissionReceived: false, commissionReceivedDate: null, compliance: emptyComp(), todos: [], quickNote: '', keyDate: '', action: 'Send ToB, fact find link and welcome email', fee: '0', procFeePercent: 0.5, brokerFee: 0, brokerFeeCharged: false, }; let cloned, workerPayload, label; if (type === 'pt') { // Product Transfer — lightest, same lender, same property label = 'Product Transfer'; cloned = { ...c, ...base, type: 'Remortgage', product: '', rate: '', rateExpiry: '', rateStartDate: null, aipRef: '', appRef: '', lenderRef: '', acreRef: '', docs: initDocs('Remortgage', 0), activityLog: [{ ts: now, type: 'note', text: `Cloned from ${cName(c)} — Product Transfer (same lender: ${c.lender})` }], isPT: true, }; workerPayload = { clientName: cName(c), type: 'Remortgage', stage: 'Enquiry', loanAmount: c.loanAmount, propertyValue: c.propertyValue, phone: c.phone, email: c.email, ltv: c.ltv, term: c.term, repaymentType: c.repaymentType, propertyType: c.propertyType, propAddr: c.propAddr, lender: c.lender, incomeType: c.incomeType, dob: c.dob, currentAddr: c.currentAddr, nationality: c.nationality, employer: c.employer, leadSource: 'Existing client — product transfer', notes: `Product transfer — cloned from previous case. Same lender: ${c.lender}.`, }; } else if (type === 'remortgage') { // Remortgage — same property, new lender label = 'Remortgage'; cloned = { ...c, ...base, type: 'Remortgage', lender: 'TBC', product: '', bdm: { name:'TBC', phone:'', email:'' }, solicitor: { firm:'', contact:'', phone:'', email:'', status:'Not yet instructed' }, docs: initDocs('Remortgage', 0), activityLog: [{ ts: now, type: 'note', text: `Cloned from ${cName(c)} — Remortgage (new lender)` }], isPT: false, }; workerPayload = { clientName: cName(c), type: 'Remortgage', stage: 'Enquiry', loanAmount: c.loanAmount, propertyValue: c.propertyValue, phone: c.phone, email: c.email, ltv: c.ltv, term: c.term, repaymentType: c.repaymentType, propertyType: c.propertyType, propAddr: c.propAddr, incomeType: c.incomeType, dob: c.dob, currentAddr: c.currentAddr, nationality: c.nationality, employer: c.employer, leadSource: 'Existing client — remortgage', notes: `Remortgage — cloned from previous case. Previous lender: ${c.lender}.`, }; } else { // New Purchase — client details only label = 'New Purchase'; cloned = { ...c, ...base, type: c.type === 'Remortgage' ? 'Residential' : c.type, loanAmount: null, loan: '', propertyValue: null, deposit: '', depositType: 'Savings', ltv: '', term: '', product: '', repaymentType: 'Capital repayment', propAddr: '', propertyType: '', lender: 'TBC', bdm: { name:'TBC', phone:'', email:'' }, solicitor: { firm:'', contact:'', phone:'', email:'', status:'Not yet instructed' }, valuation: { status:'Not required', surveyor:'', ordered:'', received:'', value:'' }, aipRef: '', appRef: '', lenderRef: '', acreRef: '', docs: initDocs(c.type === 'Remortgage' ? 'Residential' : c.type, 0), activityLog: [{ ts: now, type: 'note', text: `Cloned from ${cName(c)} — New Purchase` }], isPT: false, }; workerPayload = { clientName: cName(c), type: cloned.type, stage: 'Enquiry', phone: c.phone, email: c.email, incomeType: c.incomeType, dob: c.dob, currentAddr: c.currentAddr, nationality: c.nationality, employer: c.employer, leadSource: 'Existing client — new purchase', notes: `New purchase — cloned from existing client. Previous case: ${c.type}.`, }; } setShowClone(false); setCases(prev => [cloned, ...prev]); setSel(tempId); setPTab('case'); setShowAllActivity(false); toast(`${label} case created — ${cName(c)}`, 'ok'); api('/cases', { method:'POST', body: JSON.stringify(workerPayload) }) .then(result => { if (result?.id) { setCases(prev => prev.map(x => x.id === tempId ? { ...x, id: result.id } : x)); setSel(result.id); } }).catch(() => toast('Cloned locally — worker sync failed', 'warn')); }; const sendEmail = async () => { if (!composeData.to || !composeData.subject || !composeData.body) { setComposeStatus({ type:'err', msg:'Please complete all fields.' }); return; } setComposeSending(true); setComposeStatus(null); try { await api('/mail/send', { method:'POST', body:JSON.stringify({ to: composeData.to, subject: composeData.subject, body: composeData.body }) }); setComposeStatus({ type:'ok', msg:'Email sent successfully.' }); toast('Email sent', 'ok'); if (selected) { const log = [...(selected.activityLog||[]), { ts:new Date().toISOString(), type:'email', text:'Email sent: ' + composeData.subject }]; updateCase(selected.id, { activityLog: log }); } setTimeout(() => { setShowCompose(false); setComposeStatus(null); }, 1500); } catch(e) { setComposeStatus({ type:'err', msg:'Failed to send. Check Worker connection.' }); toast('Email failed: ' + (e.message||'Worker error'), 'err'); } setComposeSending(false); }; const filteredCases = useMemo(() => cases.filter(c => { const matchType = typeF === "All" || c.type === typeF; const matchHlth = hlthF === "All" || calcHealth(c) === hlthF; const q = search.toLowerCase(); const matchSearch = !q || cName(c).toLowerCase().includes(q) || (c.appRef || '').toLowerCase().includes(q) || (c.aipRef || '').toLowerCase().includes(q) || (c.lender || '').toLowerCase().includes(q) || (c.phone || '').toLowerCase().includes(q) || (c.email || '').toLowerCase().includes(q) || (c.propAddr || '').toLowerCase().includes(q) || (c.type || '').toLowerCase().includes(q) || (c.acreRef || '').toLowerCase().includes(q) || (c.bdm?.phone || '').toLowerCase().includes(q); return matchType && matchHlth && matchSearch; }), [cases, typeF, hlthF, search]); // ── INLINE EDIT HANDLERS ── const startEdit = (id, field, value) => setEditState({ id, field, value: value || '' }); const cancelEdit = () => setEditState(null); const saveEdit = (id, field) => { if (!editState) return; const val = editState.value; const keys = field.split('.'); if (keys.length === 1) { updateCase(id, { [field]: val }); } else { const c = cases.find(x => x.id === id); if (!c) { setEditState(null); return; } const topKey = keys[0]; const nested = JSON.parse(JSON.stringify(c[topKey] || {})); let obj = nested; for (let i = 1; i < keys.length - 1; i++) { if(!obj[keys[i]])obj[keys[i]]={}; obj = obj[keys[i]]; } // applicant2.rest needs comma prefix for display const leafKey = keys[keys.length-1]; const saveVal = (topKey === 'applicant2' && leafKey === 'rest' && val && !val.startsWith(',')) ? ', ' + val : val; obj[leafKey] = saveVal; updateCase(id, { [topKey]: nested }); } setEditState(null); }; const editCtx = { editState, setEditState, startEdit, cancelEdit, saveEdit }; const EF = (props) => React.createElement(EditField, { editCtx, ...props }); // ── NOTE DEBOUNCE ── const updateNote = (id, val) => { setCases(prev => prev.map(c => c.id === id ? {...c, quickNote: val} : c)); clearTimeout(noteTimer.current); noteTimer.current = setTimeout(() => { try { api('/cases/' + id, { method:'PUT', body:JSON.stringify({quickNote:val}) }); } catch(e) {} }, 1200); }; // ── SHAREPOINT ── const openSharePoint = async (c) => { const clientName = c.clientName || (c.sur + ', ' + c.rest.replace(/^,\s*/, '')); toast('Opening SharePoint folder...', 'info', 2000); try { const data = await api('/files/list?client=' + encodeURIComponent(clientName)); const url = data?.webUrl || data?.value?.[0]?.webUrl || data?.folder?.webUrl; if (url) { window.open(url, '_blank'); return; } // Try SharePoint search URL as fallback window.open('https://lawesfinancialltd.sharepoint.com/', '_blank'); } catch(e) { toast('SharePoint: ' + (e.message || 'could not open folder'), 'err'); window.open('https://lawesfinancialltd.sharepoint.com/', '_blank'); } }; // ── LOG DECLINE ── const logDecline = (caseId) => { if (!declineData.lender) return; const c = cases.find(x => x.id === caseId); if (!c) return; const text = `Declined by ${declineData.lender}${declineData.reason ? ': ' + declineData.reason : ''}`; const log = [...(c.activityLog||[]), { ts:new Date().toISOString(), type:'lender', text }]; updateCase(caseId, { activityLog: log }); setShowDecline(false); setDeclineData({ lender:'', reason:'' }); }; // ── PROTECTION TOGGLE ── const toggleProtection = (caseId, key) => { const c = cases.find(x => x.id === caseId); if (!c) return; const prot = JSON.parse(JSON.stringify(c.protection)); prot[key].inPlace = !prot[key].inPlace; if (!prot[key].inPlace) { prot[key].provider = null; prot[key].sumAssured = null; prot[key].premium = null; } updateCase(caseId, { protection: prot }); }; // ── ACTIVITY LOG HANDLERS ── const logCall = (caseId, note) => { const c = cases.find(x => x.id === caseId); if (!c) return; const text = note ? 'Call: ' + note : 'Call logged'; const log = [...(c.activityLog||[]), { ts:new Date().toISOString(), type:'call', text }]; updateCase(caseId, { activityLog: log }); setShowCallNote(false); setCallNoteText(''); }; const logProtFollowUp = (caseId) => { const c = cases.find(x => x.id === caseId); if (!c) return; const log = [...(c.activityLog||[]), { ts:new Date().toISOString(), type:'note', text:'Protection follow-up actioned' }]; updateCase(caseId, { activityLog: log }); }; const markCommissionReceived = (caseId, undo = false) => { const now = new Date().toISOString(); const c = cases.find(x => x.id === caseId); if (!c) return; if (undo) { const log = [...(c.activityLog||[]), { ts:now, type:'note', text:'Commission received status reversed' }]; updateCase(caseId, { commissionReceived:false, commissionReceivedDate:null, activityLog:log }); toast('Commission status reversed', 'warn'); } else { const log = [...(c.activityLog||[]), { ts:now, type:'note', text:'Commission received — £' + c.fee }]; updateCase(caseId, { commissionReceived:true, commissionReceivedDate:now, activityLog:log }); toast('Commission marked as received', 'ok'); } }; // ── AFFORDABILITY ── const affordability = (c) => { const salary = c.income?.basicSalary || 0; const bonus = c.income?.bonusGuaranteed ? (c.income?.bonus || 0) : 0; const rental = c.income?.rentalIncome || 0; const divs = c.income?.dividends || 0; const totalIncome = salary + bonus + rental + divs; const loan = parseInt((c.loan||'0').replace(/,/g,'')); if (!totalIncome || !loan) return null; const maxLoan = totalIncome * 4.5; const ratio = loan / maxLoan; if (ratio <= 0.85) return { cls:'afford-ok', label:'Comfortable', detail:`£${Math.round(totalIncome).toLocaleString()} income → max ~£${Math.round(maxLoan).toLocaleString()}` }; if (ratio <= 1.0) return { cls:'afford-tight', label:'Tight — stress test', detail:`Loan is ${Math.round(ratio*100)}% of 4.5× income max` }; return { cls:'afford-risk', label:'Exceeds 4.5× — specialist lender', detail:`Loan is ${Math.round(ratio*100)}% of 4.5× income max` }; }; // ── PIPELINE VELOCITY ── const velocity = useMemo(() => { const completed = cases.filter(c => c.stage === 'Completed' && c.stageHistory?.length > 1); if (!completed.length) return null; const durations = completed.map(c => { const first = c.stageHistory[0]?.enteredAt; const last = c.stageHistory[c.stageHistory.length-1]?.enteredAt; return first && last ? daysSince(first) : null; }).filter(Boolean); const avg = Math.round(durations.reduce((a,b)=>a+b,0)/durations.length); const fastest = Math.min(...durations); const slowest = Math.max(...durations); return { avg, fastest, slowest, count: completed.length }; }, [cases]); const byStage = s => filteredCases.filter(c => c.stage === s); const activeRev = cases.filter(c => c.stage !== "Completed"); const totalExp = activeRev.reduce((a, c) => a + parseInt((c.fee || '0').replace(/,/g, '') || 0), 0); const totalRecv = cases.filter(c => c.commissionReceived).reduce((a, c) => a + parseInt((c.fee || '0').replace(/,/g, '') || 0), 0); const rateReviews = cases.filter(c => c.rateExpiry).map(c => ({ ...c, months: monthsUntil(c.rateExpiry) })).filter(c => c.months !== null && c.months <= 72).sort((a, b) => a.months - b.months); const hlthLabel = h => h === 'r' ? 'Critical' : h === 'a' ? 'Attention' : 'On track'; if (showAuth) return React.createElement("div", { style: { background: "#22202A", minHeight: "100vh", display: "flex", alignItems: "center", justifyContent: "center", fontFamily: "'DM Sans',sans-serif", padding: "1rem" } }, React.createElement("style", { dangerouslySetInnerHTML: { __html: `@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;0,600;1,400&family=DM+Sans:wght@300;400;500;600&display=swap');` } }), React.createElement("div", { style: { background: "#2D2A38", border: "1px solid rgba(190,160,128,.15)", borderRadius: 14, padding: "2.75rem 2.5rem", width: "100%", maxWidth: 420, textAlign: "center", boxShadow: "0 24px 60px rgba(0,0,0,.5)" } }, React.createElement("div", { style: { width: 44, height: 44, background: "rgba(140,53,24,.2)", border: "1px solid rgba(140,53,24,.35)", borderRadius: "50%", display: "flex", alignItems: "center", justifyContent: "center", margin: "0 auto 1.25rem" } }, React.createElement("div", { style: { width: 14, height: 14, borderRadius: "50%", background: "rgba(232,121,90,.7)" } })), React.createElement("div", { style: { fontFamily: "'Cormorant Garamond',serif", fontSize: "1.75rem", color: "#FDFAF6", marginBottom: ".3rem", fontWeight: 600, letterSpacing: ".04em" } }, "Lawes Pipeline"), React.createElement("div", { style: { fontSize: ".72rem", color: "rgba(190,160,128,.42)", textTransform: "uppercase", letterSpacing: ".16em", marginBottom: "2.25rem" } }, forgotMode ? "Passphrase Recovery" : "Secure Access"), forgotMode ? React.createElement(React.Fragment, null, React.createElement("div", { style: { background: "rgba(196,120,72,.1)", border: "1px solid rgba(196,120,72,.2)", borderRadius: 8, padding: "1.1rem 1.25rem", marginBottom: "1.75rem", textAlign: "left" } }, React.createElement("div", { style: { fontSize: ".78rem", color: "#C47848", fontWeight: 600, marginBottom: ".6rem", letterSpacing: ".05em", textTransform: "uppercase" } }, "How to reset your passphrase"), React.createElement("div", { style: { fontSize: ".82rem", color: "rgba(253,250,246,.72)", lineHeight: 1.65, marginBottom: ".85rem" } }, "As a single-user system, passphrases are stored as encrypted secrets in Cloudflare — they can't be reset by email."), React.createElement("div", { style: { fontSize: ".82rem", color: "rgba(253,250,246,.72)", lineHeight: 1.65 } }, React.createElement("span", { style: { color: "rgba(190,160,128,.55)", fontWeight: 600 } }, "To reset: "), "Cloudflare dashboard → Workers & Pages → lawes-pipeline-worker → Settings → Variables & Secrets → PASSPHRASE → Edit" ) ), React.createElement("button", { onClick: () => { setForgotMode(false); setPpError(""); }, style: { width: "100%", background: "rgba(190,160,128,.1)", border: "1px solid rgba(190,160,128,.2)", borderRadius: 8, padding: ".8rem", color: "rgba(253,250,246,.75)", fontSize: ".85rem", fontWeight: 500, cursor: "pointer", fontFamily: "'DM Sans',sans-serif", transition: "all .2s" } }, "← Back to login") ) : React.createElement(React.Fragment, null, React.createElement("div", { style: { position: "relative", marginBottom: ".85rem" } }, React.createElement("input", { type: showPass ? "text" : "password", placeholder: "Enter passphrase", value: ppInput, onChange: e => { setPpInput(e.target.value); setPpError(""); }, onKeyDown: e => e.key === "Enter" && handleAuth(), autoFocus: true, style: { width: "100%", background: "rgba(190,160,128,.07)", border: `1px solid ${ppError ? "rgba(232,121,90,.45)" : "rgba(190,160,128,.2)"}`, borderRadius: 8, padding: ".85rem 3rem .85rem 1.1rem", fontSize: "1rem", color: "#FDFAF6", fontFamily: "'DM Sans',sans-serif", outline: "none", boxSizing: "border-box", transition: "border-color .2s", letterSpacing: showPass ? "normal" : ".2em" } }), React.createElement("button", { onClick: () => setShowPass(p => !p), style: { position: "absolute", right: ".9rem", top: "50%", transform: "translateY(-50%)", background: "none", border: "none", cursor: "pointer", color: "rgba(190,160,128,.4)", fontSize: ".75rem", fontFamily: "'DM Sans',sans-serif", padding: "2px 4px", letterSpacing: ".05em", textTransform: "uppercase" } }, showPass ? "Hide" : "Show") ), ppError && React.createElement("div", { style: { fontSize: ".78rem", color: "#E8795A", marginBottom: ".85rem", fontWeight: 500 } }, ppError), React.createElement("button", { onClick: handleAuth, style: { width: "100%", background: "#8C3518", border: "none", borderRadius: 8, padding: ".85rem", color: "#FDFAF6", fontSize: ".88rem", fontWeight: 600, letterSpacing: ".1em", textTransform: "uppercase", cursor: "pointer", fontFamily: "'DM Sans',sans-serif", marginBottom: ".85rem", transition: "background .2s" } }, "Access Pipeline"), React.createElement("button", { onClick: () => { setForgotMode(true); setPpError(""); }, style: { background: "none", border: "none", cursor: "pointer", color: "rgba(190,160,128,.38)", fontSize: ".75rem", fontFamily: "'DM Sans',sans-serif", letterSpacing: ".04em", textDecoration: "underline", textUnderlineOffset: "3px" } }, "Forgot passphrase?") ), React.createElement("div", { style: { fontSize: ".65rem", color: "rgba(190,160,128,.22)", marginTop: "2rem", borderTop: "1px solid rgba(190,160,128,.08)", paddingTop: "1rem", letterSpacing: ".06em" } }, "Lawes Financial Limited · FCA No. 1046161") ) ); if (loading) return React.createElement("div", { style: { background: "#22202A", minHeight: "100vh", display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", fontFamily: "'DM Sans',sans-serif", gap: ".75rem" } }, React.createElement("div", { style: { width: 32, height: 32, border: "2px solid rgba(190,160,128,.12)", borderTopColor: "rgba(190,160,128,.5)", borderRadius: "50%", animation: "spin .8s linear infinite" } }), React.createElement("style", null, "@keyframes spin{to{transform:rotate(360deg)}}"), React.createElement("div", { style: { color: "rgba(190,160,128,.45)", letterSpacing: ".15em", textTransform: "uppercase", fontSize: ".78rem" } }, "Loading cases", apiError ? " — " + apiError : "") ); return /*#__PURE__*/React.createElement("div", { className: "root" + (lightMode ? " light-mode" : "") }, /*#__PURE__*/React.createElement("style", { dangerouslySetInnerHTML: { __html: CSS } }), /*#__PURE__*/React.createElement("div", { className: "topbar" }, /*#__PURE__*/React.createElement("div", { className: "brand" }, "Lawes Financial ", /*#__PURE__*/React.createElement("em", null, "Pipeline")), /*#__PURE__*/React.createElement("div", { className: "search-wrap" }, /*#__PURE__*/React.createElement("input", { className: "search-input", placeholder: "Search client, lender ref, app ref, phone, email...", value: search, onChange: e => setSearch(e.target.value) })), /*#__PURE__*/React.createElement("div", { className: "metrics" }, /*#__PURE__*/React.createElement("div", { className: "met" }, /*#__PURE__*/React.createElement("div", { className: "met-v" }, cases.filter(c => c.stage !== "Completed").length), /*#__PURE__*/React.createElement("div", { className: "met-l" }, "Active")), /*#__PURE__*/React.createElement("div", { className: "met" }, /*#__PURE__*/React.createElement("div", { className: "met-v c-copper" }, "\xA3", totalExp.toLocaleString()), /*#__PURE__*/React.createElement("div", { className: "met-l" }, "Expected")), /*#__PURE__*/React.createElement("div", { className: "met" }, /*#__PURE__*/React.createElement("div", { className: "met-v c-rust" }, cases.filter(c => calcHealth(c) === 'r').length), /*#__PURE__*/React.createElement("div", { className: "met-l" }, "Critical")), /*#__PURE__*/React.createElement("div", { className: "met" }, /*#__PURE__*/React.createElement("div", { className: "met-v c-green" }, "\xA3", totalRecv.toLocaleString()), /*#__PURE__*/React.createElement("div", { className: "met-l" }, "Received")), React.createElement("div", { className: "met", style:{ cursor:"pointer", borderLeft:"1px solid rgba(190,160,128,.1)", paddingLeft:"1.5rem" }, onClick: () => { setView("reviews"); setSel(null); } }, React.createElement("div", { className:"met-v", style:{ color: rateReviews.filter(r=>r.months<=12).length > 0 ? "var(--copper)" : "rgba(190,160,128,.3)" } }, rateReviews.filter(r=>r.months<=12).length ), React.createElement("div", { className:"met-l" }, "Reviews Due") ), React.createElement("button", { className: "mode-toggle", onClick: () => { const next = !lightMode; setLightMode(next); localStorage.setItem('lf-lightmode', next ? '1' : '0'); } }, lightMode ? "🌙 Dark" : "☀️ Light") )), /*#__PURE__*/React.createElement("div", { className: "tabs" }, [["pipeline", "Pipeline"], ["revenue", "Revenue"], ["reviews", "Rate Reviews"], ["protection", "Protection"]].map(([v, l]) => /*#__PURE__*/React.createElement("button", { key: v, className: "tab" + (view === v ? " on" : ""), onClick: () => { setView(v); setSel(null); } }, l)), React.createElement("button", { className: "tab", style: { marginLeft: "auto", color: "rgba(196,120,72,.7)", borderBottomColor: "transparent" }, onClick: () => setCmdCentre(true) }, "⊞ Overview")), view === "pipeline" && /*#__PURE__*/React.createElement("div", { style: { display: "flex", flex: 1, overflow: "hidden", flexDirection: "column" } }, (() => { const aipAlerts = cases.filter(c => { const ae = daysUntil(c.aipExpiry); return ae !== null && ae >= 0 && ae <= 14; }); const rateAlerts = rateReviews.filter(r => r.months <= 3); const lcAlerts = cases.filter(c => { if (c.stage !== 'Completed') return false; const stageEntry = (c.stageHistory || []).find(s => s.stage === 'Completed'); if (!stageEntry) return false; const days = Math.floor((Date.now() - new Date(stageEntry.enteredAt)) / 86400000); const lc = c.lifecycle || {}; return ( (!lc.day0 && days >= 0) || (!lc.day2 && days >= 2) || (!lc.day7 && days >= 7) || (!lc.day30 && days >= 30) ); }); if (!aipAlerts.length && !rateAlerts.length && !lcAlerts.length) return null; return React.createElement("div", { style:{ display:"flex", flexWrap:"wrap", gap:".5rem", padding:".5rem 1rem", borderBottom:"1px solid rgba(190,160,128,.08)", background:"rgba(140,53,24,.06)" } }, aipAlerts.map(c => { const ae = daysUntil(c.aipExpiry); return React.createElement("div", { key:c.id, style:{ display:"flex", alignItems:"center", gap:".5rem", background: ae<=7?"rgba(232,121,90,.12)":"rgba(196,120,72,.1)", border:`1px solid ${ae<=7?"rgba(232,121,90,.3)":"rgba(196,120,72,.25)"}`, borderRadius:"6px", padding:".3rem .75rem", cursor:"pointer" }, onClick:()=>{ setSel(c.id); setPTab("case"); } }, React.createElement("span", { style:{ width:7, height:7, borderRadius:"50%", background: ae<=7?"#E8795A":"var(--copper)", flexShrink:0 } }), React.createElement("span", { style:{ fontSize:".72rem", color:"rgba(253,250,246,.8)" } }, React.createElement("strong", null, cSur(c).trim()), ` — AIP expires in ${ae}d` ) ); }), rateAlerts.map(r => React.createElement("div", { key:r.id, style:{ display:"flex", alignItems:"center", gap:".5rem", background:"rgba(74,122,158,.1)", border:"1px solid rgba(74,122,158,.25)", borderRadius:"6px", padding:".3rem .75rem", cursor:"pointer" }, onClick:()=>{ setSel(r.id); setView("reviews"); } }, React.createElement("span", { style:{ width:7, height:7, borderRadius:"50%", background:"#78AACC", flexShrink:0 } }), React.createElement("span", { style:{ fontSize:".72rem", color:"rgba(253,250,246,.8)" } }, React.createElement("strong", null, cSur(r).trim()), ` — rate expires in ${r.months}mo` ) )), lcAlerts.map(c => React.createElement("div", { key:c.id, style:{ display:"flex", alignItems:"center", gap:".5rem", background:"rgba(90,158,114,.08)", border:"1px solid rgba(90,158,114,.2)", borderRadius:"6px", padding:".3rem .75rem", cursor:"pointer" }, onClick:()=>{ setSel(c.id); setPTab("case"); } }, React.createElement("span", { style:{ width:7, height:7, borderRadius:"50%", background:"#4ECB71", flexShrink:0 } }), React.createElement("span", { style:{ fontSize:".72rem", color:"rgba(253,250,246,.8)" } }, React.createElement("strong", null, cSur(c).trim()), ` — lifecycle email due` ) )) ); })(), /*#__PURE__*/React.createElement("div", { className: "filters" }, /*#__PURE__*/React.createElement("span", { className: "fl" }, "Type:"), typeOpts.map(t => { const tc = TYPES[t]; return /*#__PURE__*/React.createElement("button", { key: t, className: "fb" + (typeF === t ? " on" : ""), style: typeF === t && tc ? { background: tc.bg, color: tc.text, borderColor: tc.bar + "60" } : typeF === t ? { background: "rgba(190,160,128,.12)", color: "var(--warm)" } : tc ? { borderColor: tc.bar + "55", color: tc.text, opacity: .65 } : {}, onClick: () => setTypeF(t) }, t); }), /*#__PURE__*/React.createElement("div", { className: "fsep" }), /*#__PURE__*/React.createElement("span", { className: "fl" }, "Health:"), [["All", "All"], ["r", "Critical"], ["a", "Attention"], ["g", "On track"]].map(([v, l]) => /*#__PURE__*/React.createElement("button", { key: v, className: "fb" + (hlthF === v ? " on" : ""), style: hlthF === v ? { background: v === "r" ? "rgba(140,40,24,.25)" : v === "a" ? "rgba(196,120,72,.2)" : v === "g" ? "rgba(61,140,80,.2)" : "rgba(190,160,128,.12)", color: v === "r" ? "#E8795A" : v === "a" ? "var(--copper)" : v === "g" ? "#5A9E72" : "var(--warm)", borderColor: "transparent" } : {}, onClick: () => setHlthF(v) }, l))), /*#__PURE__*/React.createElement("div", { style: { display: "flex", flex: 1, overflow: "hidden" } }, /*#__PURE__*/React.createElement("div", { className: "board" }, STAGES.map(stage => { const cards = byStage(stage); return /*#__PURE__*/React.createElement("div", { className: "col", key: stage }, /*#__PURE__*/React.createElement("div", { className: "col-head" }, /*#__PURE__*/React.createElement("span", { className: "col-name" }, stage), /*#__PURE__*/React.createElement("span", { className: "col-count" }, cards.length)), cards.length === 0 && /*#__PURE__*/React.createElement("div", { className: "empty-col" }, "No cases", /*#__PURE__*/React.createElement("br", null), "in ", stage), cards.map(c => { const t = TYPES[c.type] || TYPES["Remortgage"]; const d = getDays(c); const h = calcHealth(c); const ps = protSummary(c.protection); const docsTotal = c.docs?.length || 0; const docsRecv = c.docs?.filter(x => x.received).length || 0; return /*#__PURE__*/React.createElement("div", { key: c.id, className: "card" + (sel === c.id ? " sel" : ""), style: sel === c.id ? { borderColor: t.bar } : {}, onClick: () => { setSel(sel === c.id ? null : c.id); setPTab("case"); setShowAllActivity(false); } }, React.createElement("div", { className: "card-bar", style: { background: t.bar } }), React.createElement("div", { className: "hlth hlth-" + h }), React.createElement("div", { className: "card-name" }, cSur(c), React.createElement("span", null, cRest(c))), c.isJoint && c.applicant2 && React.createElement("div", { style:{ fontSize:".72rem", color:"rgba(190,160,128,.45)", padding:"0 .75rem .1rem", fontWeight:400 } }, "& ", c.applicant2.sur, c.applicant2.rest ), React.createElement("div", { className: "bwrap" }, React.createElement("span", { className: "badge", style: { background: t.bg, color: t.text } }, c.type), c.hasAip && React.createElement("span", { className: "aip-badge" }, "AIP"), c.isPT && React.createElement("span", { className: "aip-badge", style:{ background:"rgba(74,122,158,.18)", color:"#78AACC" } }, "PT"), c.isJoint && React.createElement("span", { className: "aip-badge", style: { background: "rgba(138,122,58,.15)", color: "#C0AA60" } }, "Joint"), c.commissionReceived ? React.createElement("span", { className: "comm-pill comm-in" }, "✓ Paid") : React.createElement("span", { className: "comm-pill comm-out" }, "£" + c.fee) ), (c.phone || c.email) && React.createElement("div", { className: "card-contact", onClick: e => e.stopPropagation() }, c.phone && React.createElement("a", { href: "tel:" + c.phone, className: "cc-btn" }, "📞 ", c.phone), c.phone && React.createElement("a", { href: "https://wa.me/" + c.phone.replace(/\D/g,'').replace(/^0/,'44'), target:"_blank", className: "wa-btn" }, "WhatsApp"), c.email && React.createElement("a", { href: "https://outlook.office.com/mail/deeplink/compose?to=" + encodeURIComponent(c.email), target:"_blank", className: "cc-btn" }, "✉ Email") ), React.createElement("div", { className: "divl", style: { margin: ".4rem .5rem" } }), React.createElement("div", { className: "row" }, React.createElement("span", { className: "rl" }, "Loan"), React.createElement("span", { className: "rv" }, "£", c.loan)), React.createElement("div", { className: "row" }, React.createElement("span", { className: "rl" }, "LTV"), React.createElement("span", { className: "rv" }, c.ltv)), React.createElement("div", { className: "row" }, React.createElement("span", { className: "rl" }, "Lender"), React.createElement("span", { className: "rv" }, c.lender)), React.createElement("div", { className: "divl" }), (() => { const u = urgencyInfo(c); return u ? React.createElement("div", { className: "card-urgency " + u.cls }, React.createElement("span", { className: "urg-dot" }), u.msg) : null; })(), (() => { if (!c.rateExpiry) return null; const mo = monthsUntil(c.rateExpiry); if (mo === null || mo > 72) return null; const color = mo <= 6 ? "#E8795A" : mo <= 12 ? "var(--copper)" : "rgba(190,160,128,.35)"; return React.createElement("div", { className:"ca", style:{ color, fontSize:".67rem", cursor:"pointer" }, onClick: e => { e.stopPropagation(); setView("reviews"); setSel(null); } }, `⟳ Rate review in ${mo}mo — ${fmtDate(c.rateExpiry)}` ); })(), c.keyDate && React.createElement("div", { className: "ca ca-warn" }, c.keyDate), React.createElement("div", { className: "ca " + (d > 7 ? "ca-warn" : "ca-muted") }, c.action), c.quickNote && React.createElement("div", { className: "note-prev" }, c.quickNote.slice(0, 55), c.quickNote.length > 55 ? "..." : ""), React.createElement("div", { className: "foot" }, React.createElement("div", { style: { display: "flex", alignItems: "center", gap: ".5rem" } }, React.createElement("span", { className: "days" + (d > 7 ? " late" : "") }, d, "d"), (c.todos || []).filter(t => !t.done).length > 0 && React.createElement("span", { className: "todo-badge" }, "◦ ", (c.todos || []).filter(t => !t.done).length, " tasks" ) ), React.createElement("div", { style: { display: "flex", alignItems: "center", gap: ".45rem" } }, docsTotal > 0 && React.createElement("div", { className: "doc-mini" }, c.docs.slice(0, 5).map((dd, i) => React.createElement("div", { key: i, className: "doc-tick", style: { background: dd.received ? "#5A9E72" : "rgba(190,160,128,.18)" } })) ), docsTotal > 0 && React.createElement("span", { style:{ fontSize:".6rem", color: docsRecv === docsTotal ? "#5A9E72" : "rgba(190,160,128,.38)" } }, docsRecv, "/", docsTotal), React.createElement("span", { className: "dot dot-" + ps }) ) ), React.createElement("div", { className: "mv-btns" }, React.createElement("button", { className: "mv-btn", onClick: e => { e.stopPropagation(); moveCard(c.id, -1); } }, "‹ Back"), React.createElement("button", { className: "mv-btn", onClick: e => { e.stopPropagation(); moveCard(c.id, 1); } }, "Next ›") ) ); }), /*#__PURE__*/React.createElement("button", { className: "add-btn", onClick: () => { setNewCaseStage(stage); setShowNewCase(true); } }, "+ New Case")); })), selected && (() => { const t = TYPES[selected.type] || TYPES["Remortgage"]; const ps = protSummary(selected.protection); const d = getDays(selected); const h = calcHealth(selected); const docsRecv = selected.docs?.filter(x => x.received).length || 0; return /*#__PURE__*/React.createElement("div", { className: "panel" }, /*#__PURE__*/React.createElement("div", { className: "ph", style: { borderLeft: `3px solid ${t.bar}` } }, /*#__PURE__*/React.createElement("div", { className: "bwrap", style: { paddingLeft: 0, marginBottom: ".3rem" } }, /*#__PURE__*/React.createElement("span", { className: "badge", style: { background: t.bg, color: t.text } }, selected.type), /*#__PURE__*/React.createElement("span", { className: "badge", style: { background: "rgba(190,160,128,.1)", color: "rgba(190,160,128,.55)" } }, selected.stage), selected.hasAip && /*#__PURE__*/React.createElement("span", { className: "aip-badge" }, "AIP Active"), selected.isJoint && /*#__PURE__*/React.createElement("span", { className: "aip-badge", style: { background: "rgba(138,122,58,.15)", color: "#C0AA60" } }, "Joint")), /*#__PURE__*/React.createElement("div", { className: "pn" }, React.createElement(EF, { id:selected.id, field:"sur", value:selected.sur||cSur(selected) }), React.createElement(EF, { id:selected.id, field:"rest", value:selected.rest||cRest(selected) }) ), selected.isJoint && selected.applicant2 && React.createElement("div", { style:{ padding:"0 1.4rem .1rem", display:"flex", alignItems:"center", gap:".4rem" } }, React.createElement("span", { style:{ fontSize:".65rem", color:"rgba(190,160,128,.35)" } }, "&"), React.createElement("span", { style:{ fontSize:".88rem", fontWeight:500, color:"rgba(253,250,246,.7)", fontFamily:"'Cormorant Garamond',Georgia,serif" } }, selected.applicant2.sur, selected.applicant2.rest ) ), React.createElement("div", { style:{ padding:"0 1.4rem .25rem", display:"flex", alignItems:"center", gap:".5rem", marginTop:".3rem", flexWrap:"wrap" } }, React.createElement("span", { style:{fontSize:".65rem",color:"rgba(190,160,128,.35)",textTransform:"uppercase",letterSpacing:".08em"} }, "Stage:"), React.createElement("select", { value: selected.stage, onChange: e => moveCard(selected.id, null, e.target.value), style:{ background:"rgba(190,160,128,.08)", border:"1px solid rgba(190,160,128,.2)", color:"rgba(190,160,128,.85)", fontSize:".72rem", padding:".2rem .5rem", borderRadius:"3px", cursor:"pointer" } }, STAGES.map(s => React.createElement("option", { key:s, value:s, style:{background:"#2D2A38"} }, s))) ), React.createElement("div", { style:{ padding:"0 1.4rem .15rem", display:"flex", alignItems:"center", gap:".4rem", flexWrap:"wrap", marginTop:".2rem" } }, React.createElement("span", { style:{fontSize:".65rem",color:"rgba(190,160,128,.35)"} }, "Co:"), React.createElement(EF, { id:selected.id, field:"company", value:selected.company||"" }) ), React.createElement("div", { style:{ padding:"0 1.4rem .15rem", display:"flex", alignItems:"center", gap:".4rem", flexWrap:"wrap", marginTop:".15rem" } }, React.createElement("span", { style:{fontSize:".65rem",color:"rgba(190,160,128,.35)"} }, "☎"), React.createElement(EF, { id:selected.id, field:"phone", value:selected.phone||"" }), selected.phone && React.createElement("a", { href:"https://wa.me/"+selected.phone.replace(/\D/g,"").replace(/^0/,"44"), target:"_blank", className:"wa-btn", style:{fontSize:".68rem"} }, "WA"), React.createElement("span", { style:{color:"rgba(190,160,128,.18)"} }, "|"), React.createElement("span", { style:{fontSize:".65rem",color:"rgba(190,160,128,.35)"} }, "✉"), React.createElement(EF, { id:selected.id, field:"email", value:selected.email||"" }) ), /*#__PURE__*/React.createElement("div", { style: { display:"flex", alignItems:"center", gap:".6rem", marginTop:".4rem", flexWrap:"wrap", padding:"0 1.4rem" } }, React.createElement("div", { className:"acre-edit" }, React.createElement("span", { style:{ fontSize:".65rem", color:"rgba(190,160,128,.35)", textTransform:"uppercase", letterSpacing:".08em" } }, "ACRE:"), editState?.id === selected.id && editState?.field === 'acreRef' ? React.createElement("span", { className:"edit-row" }, React.createElement("input", { className:"edit-input", autoFocus:true, value:editState.value, style:{fontSize:".78rem"}, onChange:e=>setEditState(p=>({...p,value:e.target.value})), onKeyDown:e=>{ if(e.key==='Enter')saveEdit(selected.id,'acreRef'); if(e.key==='Escape')cancelEdit(); } }), React.createElement("button",{className:"edit-save",onClick:()=>saveEdit(selected.id,'acreRef')},"✓"), React.createElement("button",{className:"edit-cancel",onClick:cancelEdit},"✕") ) : React.createElement("span", { className:"acre-val", onClick:()=>startEdit(selected.id,'acreRef',selected.acreRef) }, selected.acreRef || "add ref"), selected.acreRef && React.createElement(CopyBtn, { text:selected.acreRef }) ), React.createElement("span", { style:{color:"rgba(190,160,128,.2)"} }, "·"), React.createElement("span", { style:{ fontSize:".72rem", color:"rgba(190,160,128,.35)" } }, d, "d in stage") ), React.createElement("div", { style:{ display:"flex", alignItems:"center", gap:".5rem", marginTop:".5rem", flexWrap:"wrap" } }, selected.phone && React.createElement("a", { href:"tel:"+selected.phone, className:"cc-btn", style:{fontSize:".72rem"} }, "📞 ", selected.phone), selected.phone && React.createElement("a", { href:"https://wa.me/"+selected.phone.replace(/\D/g,'').replace(/^0/,'44'), target:"_blank", className:"wa-btn", style:{fontSize:".72rem"} }, "WhatsApp"), selected.email && React.createElement("button", { className:"email-btn", onClick:()=>openCompose(selected) }, "✉ Email"), React.createElement("div", { className:"call-pop-wrap" }, React.createElement("button", { className:"log-call-btn", onClick:()=>setShowCallNote(p=>!p) }, "📋 Log Call"), showCallNote && React.createElement("div", { className:"call-popover" }, React.createElement("div", { style:{fontSize:".68rem",color:"rgba(190,160,128,.45)",marginBottom:".5rem",textTransform:"uppercase",letterSpacing:".08em"} }, "What was discussed?"), React.createElement("input", { className:"todo-input", placeholder:"e.g. Chased BDM re: offer timeline", autoFocus:true, value:callNoteText, onChange:e=>setCallNoteText(e.target.value), onKeyDown:e=>e.key==='Enter'&&logCall(selected.id,callNoteText), style:{marginBottom:".5rem"} }), React.createElement("div", { style:{display:"flex",gap:".5rem"} }, React.createElement("button", { className:"todo-submit", style:{flex:1}, onClick:()=>logCall(selected.id,callNoteText) }, "Log"), React.createElement("button", { className:"edit-cancel", onClick:()=>setShowCallNote(false) }, "Cancel") ) ) ) ), /*#__PURE__*/React.createElement("button", { className: "clone-btn", onClick: () => setShowClone(true), title: "Clone this case" }, "Clone"), /*#__PURE__*/React.createElement("button", { className: "px", onClick: () => setSel(null) }, "×")), /*#__PURE__*/React.createElement("div", { className: "ptabs" }, [["case", "Case"], ["security", "Security"], ["docs", "Docs"], ["property", "Property"], ["protection", "Protection"], ["compliance", "Compliance"], ["activity", "Activity"], ["todos", "To-Do"], ["ai", "AI"]].map(([v, l]) => /*#__PURE__*/React.createElement("button", { key: v, className: "ptab" + (pTab === v ? " on" : ""), onClick: () => setPTab(v) }, l))), pTab === "case" && React.createElement(React.Fragment, null, // AFFORDABILITY (() => { const af = affordability(selected); return af ? React.createElement("div", { className:"afford-bar " + af.cls, style:{margin:".75rem 1.4rem 0"} }, React.createElement("div", null, React.createElement("div", { className:"afford-label" }, af.label), React.createElement("div", { className:"afford-detail" }, af.detail) ) ) : null; })(), // APPLICANT 2 NAME — joint cases, editable selected.isJoint && React.createElement(Section, { title:"Applicant 2 — Name & Contact" }, React.createElement("div", { className:"pg2" }, React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Surname"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"applicant2.sur", value:selected.applicant2?.sur||"" })) ), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "First Name"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"applicant2.rest", value:selected.applicant2?.rest?.replace(/^,\s*/,'')||"" })) ), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Mobile"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"ap2phone", value:selected.ap2phone||"" })) ), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Email"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"ap2email", value:selected.ap2email||"" })) ), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Date of Birth"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"applicant2.dob", value:selected.applicant2?.dob||"" })) ), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "NI Number"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"ap2niNumber", value:selected.ap2niNumber||"" })) ) ) ), // INCOME & COMMITMENTS — editable React.createElement(Section, { title: selected.isJoint ? "Applicant 1 — Income & Commitments" : "Income & Commitments — tap to edit" }, React.createElement("div", { className:"pg2" }, React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Basic Salary £/yr"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"income.basicSalary", value:selected.income?.basicSalary?String(selected.income.basicSalary):"" }))), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Bonus £"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"income.bonus", value:selected.income?.bonus?String(selected.income.bonus):"" }))), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Dividends £"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"income.dividends", value:selected.income?.dividends?String(selected.income.dividends):"" }))), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Rental Income £/yr"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"income.rentalIncome", value:selected.income?.rentalIncome?String(selected.income.rentalIncome):"" }))), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Other Income £"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"income.otherIncome", value:selected.income?.otherIncome?String(selected.income.otherIncome):"" }))), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Self-Emp Years"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"income.yearsTradingSelfEmployed", value:selected.income?.yearsTradingSelfEmployed?String(selected.income.yearsTradingSelfEmployed):"" }))), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Loans £/m"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"commitments.loans", value:selected.commitments?.loans?String(selected.commitments.loans):"" }))), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Credit Cards £/m"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"commitments.creditCards", value:selected.commitments?.creditCards?String(selected.commitments.creditCards):"" }))), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Car / HP £/m"), React.createElement("div", { className:"pfv c-rust" }, React.createElement(EF, { id:selected.id, field:"commitments.carHP", value:selected.commitments?.carHP?String(selected.commitments.carHP):"" }))), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Other Mortgages £/m"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"commitments.otherMortgages", value:selected.commitments?.otherMortgages?String(selected.commitments.otherMortgages):"" }))), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Total Monthly £"), React.createElement("div", { className:"pfv", style:{color:(selected.commitments?.totalMonthly||0)>0?'#E8795A':'rgba(253,250,246,.72)'} }, React.createElement(EF, { id:selected.id, field:"commitments.totalMonthly", value:selected.commitments?.totalMonthly?String(selected.commitments.totalMonthly):"" }))) ) ), // APPLICANT 2 INCOME & COMMITMENTS — joint cases only selected.isJoint && React.createElement(Section, { title:"Applicant 2 — Income & Commitments" }, React.createElement("div", { className:"pg2" }, React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Basic Salary £/yr"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"ap2income.basicSalary", value:selected.ap2income?.basicSalary?String(selected.ap2income.basicSalary):"" }))), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Bonus £"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"ap2income.bonus", value:selected.ap2income?.bonus?String(selected.ap2income.bonus):"" }))), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Dividends £"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"ap2income.dividends", value:selected.ap2income?.dividends?String(selected.ap2income.dividends):"" }))), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Rental Income £/yr"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"ap2income.rentalIncome", value:selected.ap2income?.rentalIncome?String(selected.ap2income.rentalIncome):"" }))), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Other Income £"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"ap2income.otherIncome", value:selected.ap2income?.otherIncome?String(selected.ap2income.otherIncome):"" }))), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Self-Emp Years"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"ap2income.yearsTradingSelfEmployed", value:selected.ap2income?.yearsTradingSelfEmployed?String(selected.ap2income.yearsTradingSelfEmployed):"" }))), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Loans £/m"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"ap2commitments.loans", value:selected.ap2commitments?.loans?String(selected.ap2commitments.loans):"" }))), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Credit Cards £/m"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"ap2commitments.creditCards", value:selected.ap2commitments?.creditCards?String(selected.ap2commitments.creditCards):"" }))), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Car / HP £/m"), React.createElement("div", { className:"pfv c-rust" }, React.createElement(EF, { id:selected.id, field:"ap2commitments.carHP", value:selected.ap2commitments?.carHP?String(selected.ap2commitments.carHP):"" }))), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Other Mortgages £/m"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"ap2commitments.otherMortgages", value:selected.ap2commitments?.otherMortgages?String(selected.ap2commitments.otherMortgages):"" }))), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Total Monthly £"), React.createElement("div", { className:"pfv", style:{color:(selected.ap2commitments?.totalMonthly||0)>0?'#E8795A':'rgba(253,250,246,.72)'} }, React.createElement(EF, { id:selected.id, field:"ap2commitments.totalMonthly", value:selected.ap2commitments?.totalMonthly?String(selected.ap2commitments.totalMonthly):"" }))) ) ), // CASE DETAILS — EDITABLE React.createElement(Section, { title:"Case Details — tap any value to edit" }, React.createElement("div", { className:"pg2" }, React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Loan"), React.createElement("div", { className:"pfv c-copper" }, React.createElement(EF, { id:selected.id, field:"loan", value:selected.loan }))), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "LTV"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"ltv", value:selected.ltv }))), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Lender"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"lender", value:selected.lender }))), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Product"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"product", value:selected.product }))), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Term"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"term", value:selected.term }))), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Repayment"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"repaymentType", value:selected.repaymentType }))), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Income Type"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"incomeType", value:selected.incomeType }))), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Lead Source"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"leadSource", value:selected.leadSource }))), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Deposit £"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"deposit", value:selected.deposit||"" }))), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Deposit Type"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"depositType", value:selected.depositType||"" }))) ) ), // KEY DATES — EDITABLE React.createElement(Section, { title:"Key Dates — tap to edit" }, React.createElement("div", { className:"pg2" }, React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Offer Expiry"), React.createElement("div", { className:"pfv", style:{color:selected.offerExpiry?"#E8795A":"rgba(190,160,128,.3)"} }, React.createElement(EF, { id:selected.id, field:"offerExpiry", value:selected.offerExpiry, type:"date" }))), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Target Exchange"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"targetExchange", value:selected.targetExchange, type:"date" }))), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Target Completion"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"targetCompletion", value:selected.targetCompletion, type:"date" }))), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Rate Expiry"), React.createElement("div", { className:"pfv", style:{color:selected.rateExpiry?"var(--copper)":"rgba(190,160,128,.3)"} }, React.createElement(EF, { id:selected.id, field:"rateExpiry", value:selected.rateExpiry, type:"date" }))), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "AIP Expiry"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"aipExpiry", value:selected.aipExpiry, type:"date" }))), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Key Date Note"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"keyDate", value:selected.keyDate }))) ) ), // REVENUE — expanded React.createElement(Section, { title:"Revenue" }, React.createElement("div", { className:"pg2", style:{marginBottom:".75rem"} }, React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Proc Fee %"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"procFeePercent", value:String(selected.procFeePercent) })) ), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Fee / Expected £"), React.createElement("div", { className:"pfv c-copper" }, React.createElement(EF, { id:selected.id, field:"fee", value:selected.fee })) ), selected.brokerFeeCharged && React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Broker Fee £"), React.createElement("div", { className:"pfv c-copper" }, "£", selected.brokerFee.toLocaleString()) ), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Lender Reference No."), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"lenderRef", value:selected.lenderRef||"" })) ), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Payment Route"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"paymentRoute", value:selected.paymentRoute||"" })) ), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Date of Completion"), React.createElement("div", { className:"pfv" }, React.createElement(EF, { id:selected.id, field:"completionDate", value:selected.completionDate||"", type:"date" })) ), React.createElement("div", null, React.createElement("div", { className:"pfl" }, "Payment Status"), React.createElement("div", { className:"pfv" }, selected.commissionReceived ? React.createElement("div", { style:{display:"flex",alignItems:"center",gap:".5rem"} }, React.createElement("span", { className:"comm-recv-done" }, "✓ Received", selected.commissionReceivedDate ? " · " + fmtDate(selected.commissionReceivedDate) : ""), React.createElement("button", { onClick:()=>markCommissionReceived(selected.id, true), style:{ background:"none", border:"none", cursor:"pointer", fontSize:".65rem", color:"rgba(190,160,128,.35)", textDecoration:"underline", padding:0, fontFamily:"'DM Sans',sans-serif" } }, "undo") ) : React.createElement("button", { className:"comm-recv-btn", onClick:()=>markCommissionReceived(selected.id) }, "Mark as Received") ) ) ) ), // NEXT ACTION — EDITABLE React.createElement(Section, { title:"Next Action" }, React.createElement("div", { className:"pfv", style:{marginBottom:".5rem"} }, React.createElement(EF, { id:selected.id, field:"action", value:selected.action })), selected.keyDate && React.createElement("div", { style:{fontSize:".72rem", color:"#E8795A", fontWeight:500} }, selected.keyDate) ), // CLIENT LIFECYCLE PANEL — only show for Completed cases selected.stage === 'Completed' && (() => { const daysSince = lifecycleDaysSince(selected); const lc = selected.lifecycle || {}; const steps = [ { key:'day0', label:'Congratulations email', due:0, fn:lcDay0 }, { key:'day2', label:'Google review request', due:2, fn:lcDay2 }, { key:'day7', label:'Referral ask', due:7, fn:lcDay7 }, { key:'day30',label:'Protection check-in', due:30, fn:lcDay30, skip: (selected.protection?.life?.inPlace && selected.protection?.ci?.inPlace) }, ]; return React.createElement("div", { style:{ margin:"0 0 .75rem" } }, React.createElement("div", { style:{ fontSize:".65rem", fontWeight:600, letterSpacing:".1em", textTransform:"uppercase", color:"var(--copper)", padding:".6rem 1.4rem .4rem" } }, "Client Lifecycle"), React.createElement("div", { style:{ background:"rgba(253,250,246,.02)", border:"1px solid rgba(190,160,128,.1)", borderRadius:"6px", margin:"0 1.4rem", overflow:"hidden" } }, steps.map((step, i) => { const sent = lc[step.key]; const dueIn = daysSince !== null ? step.due - daysSince : null; const isDue = dueIn !== null && dueIn <= 0 && !sent; const isUpcoming = dueIn !== null && dueIn > 0 && !sent; const isSkipped = step.skip; return React.createElement("div", { key: step.key, style:{ display:"flex", alignItems:"center", gap:".75rem", padding:".6rem .9rem", borderBottom: i < steps.length-1 ? "1px solid rgba(190,160,128,.07)" : "none", background: isDue ? "rgba(140,53,24,.06)" : "transparent" } }, // Status dot React.createElement("div", { style:{ width:8, height:8, borderRadius:"50%", flexShrink:0, background: sent ? "#4ECB71" : isSkipped ? "rgba(190,160,128,.2)" : isDue ? "#E03030" : "rgba(190,160,128,.25)" } }), // Label + due date React.createElement("div", { style:{ flex:1, minWidth:0 } }, React.createElement("div", { style:{ fontSize:".75rem", color: sent ? "rgba(253,250,246,.5)" : isSkipped ? "rgba(190,160,128,.3)" : "rgba(253,250,246,.85)", textDecoration: isSkipped ? "line-through" : "none" } }, step.label), React.createElement("div", { style:{ fontSize:".62rem", color: sent ? "#4ECB71" : isDue ? "#E8795A" : "rgba(190,160,128,.38)", marginTop:"1px" } }, sent ? `Sent ${fmtDate(sent)}` : isSkipped ? "Skipped — protection in place" : daysSince === null ? `Day ${step.due} after completion` : isDue ? (dueIn === 0 ? "Due today" : `Overdue by ${Math.abs(dueIn)}d`) : `Due in ${dueIn}d` ) ), // Action button !sent && !isSkipped && (isDue || isUpcoming) && React.createElement("button", { onClick: () => step.fn(selected), style:{ flexShrink:0, padding:".3rem .7rem", fontSize:".65rem", fontWeight:600, letterSpacing:".05em", textTransform:"uppercase", background: isDue ? "rgba(140,53,24,.25)" : "none", border:`1px solid ${isDue ? "rgba(140,53,24,.5)" : "rgba(190,160,128,.2)"}`, borderRadius:"4px", color: isDue ? "#E8795A" : "rgba(190,160,128,.5)", cursor:"pointer", fontFamily:"'DM Sans',sans-serif" } }, isDue ? "Send Now" : "Send Early") ); }) ) ); })(), // QUICK NOTE with save confirmation React.createElement("div", { className:"ps" }, React.createElement("div", { style:{display:"flex", alignItems:"center", justifyContent:"space-between", marginBottom:".5rem"} }, React.createElement("div", { className:"pst", style:{margin:0} }, "Quick Note"), noteSaved && React.createElement("span", { className:"save-dot" }, "✓ Saved") ), React.createElement("textarea", { className:"note-area", rows:3, placeholder:"Add a note...", value:selected.quickNote, onChange:e=>updateNote(selected.id, e.target.value), onBlur:()=>{ setNoteSaved(true); setTimeout(()=>setNoteSaved(false), 2500); } }) ), React.createElement("button", { className:"pbtn pri", onClick:()=>openCompose(selected) }, "✉ Draft Client Email"), React.createElement("button", { className:"pbtn sec", onClick:()=>openSharePoint(selected) }, "📁 Open SharePoint Folder"), React.createElement("button", { className:"pbtn sec", style:{color:"#E8795A",borderColor:"rgba(232,121,90,.25)"}, onClick:()=>setShowDecline(true) }, "Log Lender Decline"), React.createElement("button", { className:"pbtn sec" }, "Run Lender Match") ), pTab === "security" && React.createElement(React.Fragment, null, React.createElement("div", { style: { padding: ".75rem 1.4rem .25rem" } }, React.createElement("div", { style: { fontSize: ".72rem", color: "rgba(190,160,128,.4)", lineHeight: 1.55 } }, "Quick reference for lender calls and security checks — all key fields in one place.") ), React.createElement("div", { className: "sec-box", style: { background: "rgba(74,122,158,.08)", border: "1px solid rgba(74,122,158,.2)", marginTop: ".75rem" } }, React.createElement("div", { className: "sec-title", style: { color: "#78AACC" } }, "Security — Property Details"), React.createElement("div", { style: { marginBottom: ".55rem" } }, React.createElement("div", { className: "sec-label" }, "Security Address"), React.createElement("div", { className: "sec-val sec-hl", style: { color: "var(--warm)" } }, selected.propAddr || "—") ), React.createElement("div", { style: { display: "flex", gap: "1.5rem", marginBottom: ".55rem" } }, React.createElement("div", null, React.createElement("div", { className: "sec-label" }, "Property Type"), React.createElement("div", { className: "sec-val" }, selected.propertyType || "—") ), React.createElement("div", null, React.createElement("div", { className: "sec-label" }, "Loan Amount"), React.createElement("div", { className: "sec-val sec-hl", style: { color: "var(--copper)" } }, "£", selected.loan) ), React.createElement("div", null, React.createElement("div", { className: "sec-label" }, "LTV"), React.createElement("div", { className: "sec-val" }, selected.ltv) ) ), selected.valuation.value && React.createElement("div", { style: { display: "flex", gap: "1.5rem", marginBottom: ".55rem" } }, React.createElement("div", null, React.createElement("div", { className: "sec-label" }, "Valuation / Security Value"), React.createElement("div", { className: "sec-val sec-hl", style: { fontSize: ".95rem", color: "#5A9E72" } }, "£", selected.valuation.value) ), React.createElement("div", null, React.createElement("div", { className: "sec-label" }, "Valuation Status"), React.createElement("div", { className: "sec-val" }, selected.valuation.status) ), selected.valuation.surveyor && React.createElement("div", null, React.createElement("div", { className: "sec-label" }, "Surveyor"), React.createElement("div", { className: "sec-val" }, selected.valuation.surveyor) ) ) ), React.createElement("div", { className: "ref-box" }, React.createElement("div", { className: "ref-title" }, "Reference Numbers"), React.createElement("div", { className: "ref-row" }, React.createElement("div", { className: "ref-label" }, "AIP / DIP Reference"), React.createElement("div", { className:"ref-copy-row" }, React.createElement("div", { className: selected.aipRef ? "ref-val" : "ref-val ref-na" }, React.createElement(EF, {id:selected.id, field:"aipRef", value:selected.aipRef})), selected.aipRef && React.createElement(CopyBtn, { text:selected.aipRef }) ) ), selected.aipRef && selected.aipExpiry && React.createElement("div", { className: "ref-row" }, React.createElement("div", { className: "ref-label" }, "AIP Expiry"), React.createElement("div", { className: "ref-val", style: { color: "#E8A87A" } }, selected.aipExpiry) ), React.createElement("div", { className: "ref-row", style: { marginBottom: 0 } }, React.createElement("div", { className: "ref-label" }, "Full Application Reference"), React.createElement("div", { className:"ref-copy-row" }, React.createElement("div", { className: selected.appRef ? "ref-val" : "ref-val ref-na" }, React.createElement(EF, {id:selected.id, field:"appRef", value:selected.appRef})), selected.appRef && React.createElement(CopyBtn, { text:selected.appRef }) ) ) ), React.createElement("div", { className: "sec-box" }, React.createElement("div", { className: "sec-title" }, selected.isJoint ? "Applicant 1 Details" : "Applicant Details"), React.createElement("div", { style: { marginBottom: ".5rem" } }, React.createElement("div", { className: "sec-label" }, "Full Name"), React.createElement("div", { className: "sec-val sec-hl" }, cName(selected)) ), React.createElement("div", { style: { marginBottom: ".5rem" } }, React.createElement("div", { className: "sec-label" }, "Date of Birth"), React.createElement("div", { className: "sec-val sec-hl" }, selected.dob) ), React.createElement("div", { style: { marginBottom: ".5rem" } }, React.createElement("div", { className: "sec-label" }, "Current Address"), React.createElement("div", { className: "sec-val", style: { color: "var(--warm)", fontWeight: 500 } }, selected.currentAddr) ), React.createElement("div", { style: { marginBottom: ".5rem" } }, React.createElement("div", { className: "sec-label" }, "Nationality / Residency"), React.createElement("div", { className: "sec-val" }, selected.nationality, " · ", selected.residencyStatus) ), selected.creditStatus !== "Clean" && React.createElement("div", { style: { marginBottom: ".5rem" } }, React.createElement("div", { className: "sec-label" }, "Credit Status"), React.createElement("div", { className: "sec-val", style: { color: "#E8A87A" } }, selected.creditStatus, " — ", selected.adverseDetail) ), (selected.phone || selected.email) && React.createElement("div", { style: { marginTop: ".65rem", display: "flex", gap: ".6rem" } }, selected.phone && React.createElement("a", { href: "tel:" + selected.phone, className: "cc-btn", style: { fontSize: ".75rem" } }, "📞 ", selected.phone), selected.email && React.createElement("a", { href: "https://outlook.office.com/mail/deeplink/compose?to=" + encodeURIComponent(selected.email), target:"_blank", className: "cc-btn", style: { fontSize: ".75rem" } }, "✉ ", selected.email) ) ), selected.isJoint && React.createElement("div", { className: "sec-box", style:{ marginTop:0 } }, React.createElement("div", { className: "sec-title" }, "Applicant 2 Details"), React.createElement("div", { style: { marginBottom: ".5rem" } }, React.createElement("div", { className: "sec-label" }, "Full Name"), React.createElement("div", { className: "sec-val sec-hl" }, (selected.applicant2?.sur || "—"), (selected.applicant2?.rest || "") ) ), React.createElement("div", { style: { marginBottom: ".5rem" } }, React.createElement("div", { className: "sec-label" }, "Date of Birth"), React.createElement("div", { className: "sec-val sec-hl" }, React.createElement(EF, { id:selected.id, field:"applicant2.dob", value:selected.applicant2?.dob||"" }) ) ), React.createElement("div", { style: { marginBottom: ".5rem" } }, React.createElement("div", { className: "sec-label" }, "Current Address"), React.createElement("div", { className: "sec-val", style:{ color:"var(--warm)", fontWeight:500 } }, React.createElement(EF, { id:selected.id, field:"ap2currentAddr", value:selected.ap2currentAddr||"" }) ) ), React.createElement("div", { style: { marginBottom: ".5rem" } }, React.createElement("div", { className: "sec-label" }, "Nationality / Residency"), React.createElement("div", { className: "sec-val" }, React.createElement(EF, { id:selected.id, field:"ap2nationality", value:selected.ap2nationality||"" }) ) ), (selected.ap2phone || selected.ap2email) && React.createElement("div", { style: { marginTop: ".65rem", display: "flex", gap: ".6rem", flexWrap:"wrap" } }, selected.ap2phone && React.createElement("a", { href: "tel:" + selected.ap2phone, className: "cc-btn", style: { fontSize: ".75rem" } }, "📞 ", selected.ap2phone), selected.ap2phone && React.createElement("a", { href:"https://wa.me/"+selected.ap2phone.replace(/\D/g,"").replace(/^0/,"44"), target:"_blank", className:"wa-btn", style:{fontSize:".75rem"} }, "WA"), selected.ap2email && React.createElement("a", { href: "https://outlook.office.com/mail/deeplink/compose?to=" + encodeURIComponent(selected.ap2email), target:"_blank", className: "cc-btn", style: { fontSize: ".75rem" } }, "✉ ", selected.ap2email) ) ), React.createElement(Section, { title: "BDM — " + (selected.lender||"TBC") }, React.createElement("div", { className: "pg2" }, React.createElement("div", { style: { gridColumn: "1/-1" } }, React.createElement("div", { className: "pfl" }, "BDM Name"), React.createElement("div", { className: "pfv" }, React.createElement(EF, { id:selected.id, field:"bdm.name", value:selected.bdm?.name||"" })) ), React.createElement("div", null, React.createElement("div", { className: "pfl" }, "Direct Line"), React.createElement("div", { className: "pfv" }, React.createElement(EF, { id:selected.id, field:"bdm.phone", value:selected.bdm?.phone||"" })) ), React.createElement("div", null, React.createElement("div", { className: "pfl" }, "Email"), React.createElement("div", { className: "pfv" }, React.createElement(EF, { id:selected.id, field:"bdm.email", value:selected.bdm?.email||"" })) ) ) ), /*#__PURE__*/React.createElement("div", { className: "ps" }, /*#__PURE__*/React.createElement("div", { className: "pst" }, "Mortgage Summary"), /*#__PURE__*/React.createElement("div", { className: "pg2" }, /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", { className: "pfl" }, "Loan"), /*#__PURE__*/React.createElement("div", { className: "pfv c-copper" }, "\xA3", selected.loan)), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", { className: "pfl" }, "LTV"), /*#__PURE__*/React.createElement("div", { className: "pfv" }, selected.ltv)), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", { className: "pfl" }, "Term"), /*#__PURE__*/React.createElement("div", { className: "pfv" }, selected.term)), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", { className: "pfl" }, "Product"), /*#__PURE__*/React.createElement("div", { className: "pfv" }, selected.product)), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", { className: "pfl" }, "Deposit"), /*#__PURE__*/React.createElement("div", { className: "pfv" }, "\xA3", selected.deposit)), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", { className: "pfl" }, "Deposit Type"), /*#__PURE__*/React.createElement("div", { className: "pfv" }, selected.depositType))))), pTab === "docs" && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", { className: "ps" }, /*#__PURE__*/React.createElement("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: ".52rem" } }, /*#__PURE__*/React.createElement("div", { className: "pst", style: { marginBottom: 0 } }, "Document Checklist"), /*#__PURE__*/React.createElement("span", { style: { fontSize: ".6rem", color: docsRecv === selected.docs?.length ? "#5A9E72" : "var(--copper)", fontWeight: 600 } }, docsRecv, "/", selected.docs?.length || 0, " received")), /*#__PURE__*/React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: ".08rem" } }, (selected.docs || []).map((doc, i) => /*#__PURE__*/React.createElement("div", { key: i, className: "doc-row", onClick: () => toggleDoc(selected.id, i) }, /*#__PURE__*/React.createElement("div", { className: "doc-chk" + (doc.received ? " done" : "") }, doc.received && /*#__PURE__*/React.createElement("span", { style: { color: "#fff", fontSize: ".58rem", fontWeight: 700 } }, "\u2713")), /*#__PURE__*/React.createElement("div", { style: { flex: 1 } }, /*#__PURE__*/React.createElement("div", { className: "doc-lbl" + (doc.received ? " done" : "") }, doc.name), doc.received && doc.receivedDate && /*#__PURE__*/React.createElement("div", { style: { fontSize: ".55rem", color: "rgba(190,160,128,.35)" } }, fmtDate(doc.receivedDate))))))), /*#__PURE__*/React.createElement("button", { className: "pbtn sec", onClick: () => { const missing = (selected.docs||[]).filter(d=>!d.received).map(d=>' • '+d.name).join('\n'); setComposeData({ to:selected.email||"", subject:"Outstanding documents — "+cName(selected), body:"Dear "+cFirst(selected)+",\n\nCould you please send the following documents at your earliest convenience:\n\n"+missing+"\n\nKind regards,\nNathan" }); setComposeStatus(null); setShowCompose(true); } }, "Chase Outstanding Documents"), React.createElement("button", { className: "pbtn sec", onClick: () => openSharePoint(selected) }, "Open SharePoint Folder"), React.createElement("div", { style:{ marginTop:"1rem", background:"rgba(253,250,246,.03)", border:"1px solid rgba(190,160,128,.1)", borderRadius:"8px", padding:".9rem 1rem" } }, React.createElement("div", { style:{ fontSize:".68rem", letterSpacing:".09em", textTransform:"uppercase", color:"var(--copper)", marginBottom:".65rem", fontWeight:500 } }, "Upload to SharePoint"), React.createElement("div", { style:{ display:"flex", flexDirection:"column", gap:".5rem" } }, React.createElement("div", { style:{ fontSize:".65rem", color:"rgba(190,160,128,.45)", marginBottom:"-.1rem" } }, "1 — Document type"), React.createElement("select", { value: uploadDocType, onChange: e => { const t = e.target.value; setUploadDocType(t); const map = { payslip:'Application Pack', bank_statement:'Application Pack', p60:'Application Pack', sa302:'Application Pack', accounts:'Application Pack', proof_of_deposit:'Application Pack', proof_of_address:'Application Pack', illustration:'Application Pack', aip:'Lender Documents', decision:'Lender Documents', lender_letter:'Lender Documents', offer:'Offer Documents', valuation:'Valuation Reports', note:'Notes & Communications', communication:'Notes & Communications', case_note:'Notes & Communications', passport:'ID Docs', driving_licence:'ID Docs', id:'ID Docs', policy:'Protection Case', protection:'Protection Case', other:'' }; setUploadFolder(map[t] || ''); }, style:{ background:"rgba(253,250,246,.06)", border:"1px solid rgba(190,160,128,.2)", borderRadius:"6px", color:"rgba(253,250,246,.85)", padding:".45rem .6rem", fontSize:".78rem", fontFamily:"inherit", outline:"none" } }, React.createElement("optgroup", { label:"Application Pack" }, React.createElement("option", { value:"payslip" }, "Payslip"), React.createElement("option", { value:"bank_statement" }, "Bank Statement"), React.createElement("option", { value:"p60" }, "P60"), React.createElement("option", { value:"sa302" }, "SA302"), React.createElement("option", { value:"accounts" }, "Accounts"), React.createElement("option", { value:"proof_of_deposit" }, "Proof of Deposit"), React.createElement("option", { value:"proof_of_address" }, "Proof of Address"), React.createElement("option", { value:"illustration" }, "Mortgage Illustration") ), React.createElement("optgroup", { label:"Lender Documents" }, React.createElement("option", { value:"aip" }, "AIP"), React.createElement("option", { value:"decision" }, "Decision"), React.createElement("option", { value:"lender_letter" }, "Lender Letter") ), React.createElement("optgroup", { label:"Offer Documents" }, React.createElement("option", { value:"offer" }, "Offer Letter") ), React.createElement("optgroup", { label:"Valuation Reports" }, React.createElement("option", { value:"valuation" }, "Valuation Report") ), React.createElement("optgroup", { label:"Notes & Communications" }, React.createElement("option", { value:"note" }, "Note"), React.createElement("option", { value:"communication" }, "Communication"), React.createElement("option", { value:"case_note" }, "Case Note") ), React.createElement("optgroup", { label:"ID Docs" }, React.createElement("option", { value:"passport" }, "Passport"), React.createElement("option", { value:"driving_licence" }, "Driving Licence"), React.createElement("option", { value:"id" }, "Other ID") ), React.createElement("optgroup", { label:"Protection Case" }, React.createElement("option", { value:"policy" }, "Protection Policy"), React.createElement("option", { value:"protection" }, "Protection Document") ), React.createElement("optgroup", { label:"Other" }, React.createElement("option", { value:"other" }, "Other — I'll choose folder") ) ), React.createElement("div", { style:{ fontSize:".65rem", color:"rgba(190,160,128,.45)", marginTop:".1rem" } }, "2 — Destination folder", uploadDocType !== 'other' && React.createElement("span", { style:{ color:"rgba(190,160,128,.25)", marginLeft:".3rem" } }, "(auto-selected, override if needed)") ), React.createElement("select", { value: uploadFolder, onChange: e => setUploadFolder(e.target.value), style:{ background: uploadDocType === 'other' ? "rgba(140,53,24,.12)" : "rgba(253,250,246,.04)", border: uploadDocType === 'other' ? "1px solid rgba(140,53,24,.4)" : "1px solid rgba(190,160,128,.15)", borderRadius:"6px", color: uploadFolder ? "rgba(253,250,246,.85)" : "rgba(253,250,246,.35)", padding:".45rem .6rem", fontSize:".78rem", fontFamily:"inherit", outline:"none" } }, React.createElement("option", { value:"" }, "— select folder —"), React.createElement("option", { value:"Application Pack" }, "Application Pack"), React.createElement("option", { value:"Lender Documents" }, "Lender Documents"), React.createElement("option", { value:"Offer Documents" }, "Offer Documents"), React.createElement("option", { value:"Valuation Reports" }, "Valuation Reports"), React.createElement("option", { value:"Notes & Communications" }, "Notes & Communications"), React.createElement("option", { value:"ID Docs" }, "ID Docs"), React.createElement("option", { value:"Protection Case" }, "Protection Case") ), React.createElement("label", { style:{ display:"flex", alignItems:"center", justifyContent:"center", gap:".4rem", background: uploadFolder ? "rgba(140,53,24,.15)" : "rgba(253,250,246,.04)", border: uploadFolder ? "1px dashed rgba(140,53,24,.5)" : "1px dashed rgba(190,160,128,.2)", borderRadius:"6px", padding:".55rem .8rem", cursor: uploadFolder ? "pointer" : "not-allowed", fontSize:".78rem", color: uploadFolder ? "rgba(253,250,246,.75)" : "rgba(253,250,246,.25)", transition:"all .2s" }, onMouseOver: e => { if(uploadFolder) e.currentTarget.style.borderColor="rgba(140,53,24,.8)"; }, onMouseOut: e => { if(uploadFolder) e.currentTarget.style.borderColor="rgba(140,53,24,.5)"; } }, React.createElement("input", { type:"file", disabled: !uploadFolder || uploading, style:{ display:"none" }, onChange: e => { const f = e.target.files[0]; if (f && uploadFolder) uploadToSharePoint(f, uploadDocType === 'other' ? 'note' : uploadDocType, selected, uploadFolder); e.target.value=''; } }), uploading ? "Uploading…" : uploadFolder ? "📎 Select file to upload" : "Select a folder first" ), uploadMsg && React.createElement("div", { style:{ fontSize:".75rem", color: uploadMsg.ok ? "#5A9E72" : "#E07050", padding:".3rem .2rem" } }, uploadMsg.text) ) )), pTab === "property" && React.createElement(React.Fragment, null, React.createElement(Section, { title:"Property — tap values to edit" }, React.createElement("div", { style:{marginBottom:".5rem"} }, React.createElement("div",{className:"pfl"},"Property Address"), React.createElement("div",{className:"pfv"}, React.createElement(EF,{id:selected.id,field:"propAddr",value:selected.propAddr}))), React.createElement("div", { style:{marginBottom:".5rem"} }, React.createElement("div",{className:"pfl"},"Property Type"), React.createElement("div",{className:"pfv"}, React.createElement(EF,{id:selected.id,field:"propertyType",value:selected.propertyType}))), React.createElement("div", null, React.createElement("div",{className:"pfl"},"Current Address"), React.createElement("div",{className:"pfv"}, React.createElement(EF,{id:selected.id,field:"currentAddr",value:selected.currentAddr}))) ), React.createElement(Section, { title:"Valuation" }, React.createElement("div", { className:"pg2" }, React.createElement("div", null, React.createElement("div",{className:"pfl"},"Status"), React.createElement("div",{className:"pfv"}, React.createElement(EF,{id:selected.id,field:"valuation.status",value:selected.valuation.status}))), React.createElement("div", null, React.createElement("div",{className:"pfl"},"Surveyor"), React.createElement("div",{className:"pfv"}, React.createElement(EF,{id:selected.id,field:"valuation.surveyor",value:selected.valuation.surveyor}))), React.createElement("div", null, React.createElement("div",{className:"pfl"},"Ordered"), React.createElement("div",{className:"pfv"}, React.createElement(EF,{id:selected.id,field:"valuation.ordered",value:selected.valuation.ordered}))), React.createElement("div", null, React.createElement("div",{className:"pfl"},"Report Received"), React.createElement("div",{className:"pfv"}, React.createElement(EF,{id:selected.id,field:"valuation.received",value:selected.valuation.received}))), React.createElement("div", null, React.createElement("div",{className:"pfl"},"Assessed Value £"), React.createElement("div",{className:"pfv c-copper"}, React.createElement(EF,{id:selected.id,field:"valuation.value",value:selected.valuation.value}))) ) ), React.createElement(Section, { title:"Solicitor / Conveyancer" }, React.createElement("div", { className:"pg2" }, React.createElement("div", null, React.createElement("div",{className:"pfl"},"Status"), React.createElement("div",{className:"pfv"}, React.createElement(EF,{id:selected.id,field:"solicitor.status",value:selected.solicitor.status}))), React.createElement("div", null, React.createElement("div",{className:"pfl"},"Firm"), React.createElement("div",{className:"pfv"}, React.createElement(EF,{id:selected.id,field:"solicitor.firm",value:selected.solicitor.firm}))), React.createElement("div", null, React.createElement("div",{className:"pfl"},"Contact"), React.createElement("div",{className:"pfv"}, React.createElement(EF,{id:selected.id,field:"solicitor.contact",value:selected.solicitor.contact}))), React.createElement("div", null, React.createElement("div",{className:"pfl"},"Phone"), React.createElement("div",{className:"pfv ref-copy-row"}, React.createElement(EF,{id:selected.id,field:"solicitor.phone",value:selected.solicitor.phone}), selected.solicitor.phone && React.createElement("a",{href:"tel:"+selected.solicitor.phone,style:{color:"var(--copper)",fontSize:".72rem",textDecoration:"none"}},"📞") ) ), React.createElement("div", null, React.createElement("div",{className:"pfl"},"Email"), React.createElement("div",{className:"pfv"}, React.createElement(EF,{id:selected.id,field:"solicitor.email",value:selected.solicitor.email}))) ) ), React.createElement(Section, { title:"Applicant Details" }, React.createElement("div", { className:"pg2" }, React.createElement("div", null, React.createElement("div",{className:"pfl"},"Date of Birth"), React.createElement("div",{className:"pfv"}, React.createElement(EF,{id:selected.id,field:"dob",value:selected.dob}))), React.createElement("div", null, React.createElement("div",{className:"pfl"},"Employer"), React.createElement("div",{className:"pfv"}, React.createElement(EF,{id:selected.id,field:"employer",value:selected.employer}))), React.createElement("div", null, React.createElement("div",{className:"pfl"},"Nationality"), React.createElement("div",{className:"pfv"}, React.createElement(EF,{id:selected.id,field:"nationality",value:selected.nationality}))), React.createElement("div", null, React.createElement("div",{className:"pfl"},"Residency"), React.createElement("div",{className:"pfv"}, React.createElement(EF,{id:selected.id,field:"residencyStatus",value:selected.residencyStatus}))), React.createElement("div", null, React.createElement("div",{className:"pfl"},"Credit Status"), React.createElement("div",{className:"pfv"}, React.createElement(EF,{id:selected.id,field:"creditStatus",value:selected.creditStatus}))), React.createElement("div", null, React.createElement("div",{className:"pfl"},"Adverse Detail"), React.createElement("div",{className:"pfv"}, React.createElement(EF,{id:selected.id,field:"adverseDetail",value:selected.adverseDetail}))) ) ) ), pTab === "protection" && React.createElement(React.Fragment, null, React.createElement(Section, { title:"Protection Status" }, React.createElement("div", { style:{display:"flex",alignItems:"center",gap:".6rem",marginBottom:".65rem"} }, React.createElement("span", { className:"dot dot-"+ps, style:{width:10,height:10} }), React.createElement("span", { style:{fontSize:".8rem",color:"rgba(253,250,246,.72)"} }, ps==='y'?"All protection in place":ps==='p'?"Partially protected":"Not yet discussed"), ps!=='y' && React.createElement("button", { className:"prot-act-btn", onClick:()=>logProtFollowUp(selected.id) }, "Log Follow-up") ), selected.protAction && React.createElement("div", { style:{fontSize:".75rem",color:"#E8A87A",marginBottom:".5rem"} }, React.createElement(EF,{id:selected.id,field:"protAction",value:selected.protAction})) ), React.createElement(Section, { title:"Cover Detail — click dot to toggle in place" }, [["Life Insurance","life"],["Critical Illness","ci"],["Income Protection","ip"],["Buildings & Contents","bc"]].map(([label, key]) => { const cv = selected.protection[key]; return React.createElement("div", { key, className:"prot-row" }, React.createElement("div", { className:"prot-top" }, React.createElement("span", { style:{fontSize:".8rem",color:"rgba(253,250,246,.78)",fontWeight:500} }, label), React.createElement("div", { style:{display:"flex",alignItems:"center",gap:".5rem"} }, React.createElement("span", { className:"dot "+(cv.inPlace?"dot-y":"dot-n"), style:{width:9,height:9,cursor:"pointer"}, onClick:()=>toggleProtection(selected.id,key), title:"Toggle in place" }), React.createElement("span", { style:{fontSize:".65rem",color:"rgba(190,160,128,.4)"} }, cv.inPlace?"In place":"Not in place") ) ), cv.inPlace && React.createElement("div", { className:"prot-edit-row" }, React.createElement("div", null, React.createElement("div",{className:"pfl"},"Provider"), React.createElement("div",{className:"pfv"}, React.createElement(EF,{id:selected.id,field:"protection."+key+".provider",value:cv.provider||""}))), React.createElement("div", null, React.createElement("div",{className:"pfl"},"Premium £/m"), React.createElement("div",{className:"pfv"}, React.createElement(EF,{id:selected.id,field:"protection."+key+".premium",value:cv.premium?String(cv.premium):""}))), React.createElement("div", null, React.createElement("div",{className:"pfl"},"Sum Assured £"), React.createElement("div",{className:"pfv"}, React.createElement(EF,{id:selected.id,field:"protection."+key+".sumAssured",value:cv.sumAssured?String(cv.sumAssured):""}))) ) ); }) ), React.createElement("button", { className:"pbtn pri", onClick:()=>openCompose(selected) }, "Draft Protection Email"), React.createElement("button", { className:"pbtn sec", onClick:()=>logProtFollowUp(selected.id) }, "Log Protection Discussion") ), pTab === "compliance" && React.createElement(React.Fragment, null, React.createElement(Section, { title:"Terms of Business" }, selected.compliance.tcsSigned ? React.createElement("div", { style:{ display:"flex", flexDirection:"column", gap:".5rem" } }, React.createElement("div", { style:{ display:"flex", alignItems:"center", gap:".6rem", background:"rgba(90,158,114,.08)", border:"1px solid rgba(90,158,114,.25)", borderRadius:"6px", padding:".65rem .9rem" } }, React.createElement("span", { style:{ fontSize:"1rem" } }, "✓"), React.createElement("div", null, React.createElement("div", { style:{ fontSize:".78rem", fontWeight:600, color:"#5A9E72" } }, "Terms of Business signed"), React.createElement("div", { style:{ fontSize:".68rem", color:"rgba(190,160,128,.55)", marginTop:"2px" } }, `Signed by ${selected.compliance.tcsSignatoryName || 'client'} · ${fmtDate(selected.compliance.tcsSignedDate)}` ) ) ), selected.compliance.tcsSignatoryIP && React.createElement("div", { style:{ fontSize:".62rem", color:"rgba(190,160,128,.3)", paddingLeft:".2rem" } }, `IP: ${selected.compliance.tcsSignatoryIP}`), React.createElement("button", { className:"pbtn sec", style:{ fontSize:".68rem", marginTop:".25rem" }, onClick:() => { const confirmed = window.confirm("Send a new ToB signing link? The client will receive a fresh email."); if (confirmed) sendToB(selected); }}, "Resend ToB") ) : React.createElement("div", { style:{ display:"flex", flexDirection:"column", gap:".75rem" } }, React.createElement("div", { style:{ display:"flex", alignItems:"center", gap:".6rem", background:"rgba(140,53,24,.07)", border:"1px solid rgba(140,53,24,.2)", borderRadius:"6px", padding:".65rem .9rem" } }, React.createElement("span", { style:{ fontSize:".75rem", color:"#E8795A" } }, "●"), React.createElement("div", null, React.createElement("div", { style:{ fontSize:".78rem", fontWeight:600, color:"rgba(253,250,246,.85)" } }, "Not yet signed"), selected.tobSentAt ? React.createElement("div", { style:{ fontSize:".68rem", color:"rgba(190,160,128,.5)", marginTop:"2px" } }, `Link sent ${fmtDate(selected.tobSentAt)} to ${selected.tobSentTo || 'client'}`) : React.createElement("div", { style:{ fontSize:".68rem", color:"rgba(190,160,128,.4)", marginTop:"2px" } }, "No link sent yet") ) ), selected.email ? React.createElement("button", { className:"pbtn", style:{ background:"var(--rust)" }, onClick:() => sendToB(selected) }, "Send ToB for Signing") : React.createElement("div", { style:{ fontSize:".75rem", color:"rgba(190,160,128,.4)", fontStyle:"italic" } }, "Add client email address to send ToB") ) ), React.createElement(Section, { title:"Compliance Status" }, React.createElement("div", { className:"comp-row" }, React.createElement("span",{className:"comp-label"},"Suitability Recorded"), React.createElement("span",{className:selected.compliance.suitabilityRecorded?"comp-yes":"comp-no"}, selected.compliance.suitabilityRecorded?"Yes":"No") ), React.createElement("div", { className:"comp-row" }, React.createElement("span",{className:"comp-label"},"Consumer Duty Assessment"), React.createElement("span",{className:selected.compliance.declarations.length>0?"comp-yes":"comp-no"}, selected.compliance.declarations.length>0?"Complete":"Not yet completed") ) ), React.createElement(Section, { title:"Reason Why — tap to edit" }, React.createElement("textarea", { className:"note-area", rows:4, placeholder:"Document your reason why — lender selection rationale...", value:selected.compliance.reasonWhy||"", onChange:e=>updateCase(selected.id,{compliance:{...selected.compliance,reasonWhy:e.target.value}}) }) ), React.createElement(Section, { title:"Consumer Duty Notes — tap to edit" }, React.createElement("textarea", { className:"note-area", rows:4, placeholder:"Consumer Duty assessment notes...", value:selected.compliance.consumerDutyNotes||"", onChange:e=>updateCase(selected.id,{compliance:{...selected.compliance,consumerDutyNotes:e.target.value}}) }) ), selected.compliance.declarations.length>0 && React.createElement(Section, { title:"Declarations" }, selected.compliance.declarations.map((d,i)=>React.createElement("div",{key:i,style:{display:"flex",alignItems:"center",gap:".5rem",padding:".28rem 0"}}, React.createElement("span",{style:{color:"#5A9E72",fontSize:".68rem"}},"✓"), React.createElement("span",{style:{fontSize:".75rem",color:"rgba(253,250,246,.72)"}},d) )) ), React.createElement("button", { className:"pbtn sec" }, "Generate Compliance Export"), React.createElement("button", { className:"pbtn sec", onClick:()=>{ const decl = prompt("Add declaration:"); if(decl) updateCase(selected.id,{compliance:{...selected.compliance,declarations:[...selected.compliance.declarations,decl]}}); }}, "Add Declaration") ), pTab === "activity" && React.createElement(React.Fragment, null, React.createElement("div", { style:{ padding:".75rem 1.4rem .25rem", display:"flex", justifyContent:"space-between", alignItems:"center" } }, React.createElement("div", { style:{ fontSize:".72rem", color:"rgba(190,160,128,.5)" } }, "Full case timeline — all touchpoints logged"), selected.activityLog?.length > 5 && React.createElement("button", { onClick: () => setShowAllActivity(p => !p), style:{ background:"none", border:"none", cursor:"pointer", fontSize:".68rem", color:"var(--copper)", letterSpacing:".06em", textTransform:"uppercase", padding:"0" } }, showAllActivity ? "Show less ▲" : `Show all ${selected.activityLog.length} ▼`) ), React.createElement("div", { className:"tl" }, (() => { const reversed = [...selected.activityLog].reverse(); const visible = showAllActivity ? reversed : reversed.slice(0, 5); return visible.map((entry, i, arr) => { const nextEntry = arr[i+1]; const daysBetween = nextEntry ? Math.max(0, Math.floor((new Date(entry.ts) - new Date(nextEntry.ts)) / 86400000)) : null; return React.createElement(React.Fragment, { key:i }, React.createElement("div", { className:"tl-item" }, React.createElement("div", { className:"tl-left" }, React.createElement("div", { className:"tl-dot", style:{ background:ACT_COL[entry.type]||"var(--stone)" } }), i < arr.length-1 && React.createElement("div", { className:"tl-line" }) ), React.createElement("div", { className:"tl-content" }, React.createElement("div", { className:"tl-time" }, fmtTime(entry.ts)), React.createElement("div", { style:{ fontSize:".6rem", fontWeight:600, letterSpacing:".07em", textTransform:"uppercase", color:ACT_COL[entry.type], marginBottom:"2px" } }, entry.type), React.createElement("div", { className:"tl-text" }, entry.text) ) ), daysBetween !== null && daysBetween > 0 && React.createElement("div", { className:"tl-duration" }, "— ", daysBetween, daysBetween===1?" day":" days" ," —") ); }); })() ), !showAllActivity && selected.activityLog?.length > 5 && React.createElement("div", { style:{ textAlign:"center", padding:".5rem 1.4rem .25rem" } }, React.createElement("button", { onClick: () => setShowAllActivity(true), style:{ background:"none", border:"1px solid rgba(190,160,128,.15)", borderRadius:"6px", cursor:"pointer", fontSize:".72rem", color:"rgba(190,160,128,.55)", padding:".35rem 1rem", width:"100%" } }, `+ ${selected.activityLog.length - 5} older entries`) ), React.createElement("div", { style:{ padding:".75rem 1.4rem", display:"flex", gap:".6rem" } }, React.createElement("input", { className:"todo-input", placeholder:"Add activity note...", id:"actNoteInput" }), React.createElement("button", { className:"todo-submit", onClick:()=>{ const inp = document.getElementById('actNoteInput'); if(inp&&inp.value.trim()){ const log=[...(selected.activityLog||[]),{ts:new Date().toISOString(),type:'note',text:inp.value.trim()}]; updateCase(selected.id,{activityLog:log}); inp.value=''; } }}, "Add") ) ), pTab === "todos" && React.createElement(React.Fragment, null, React.createElement("div", { className: "ps" }, React.createElement("div", { className: "pst" }, "Action Items"), (selected.todos || []).length === 0 && React.createElement("div", { style: { fontSize: ".78rem", color: "rgba(190,160,128,.3)", padding: ".25rem .5rem" } }, "No tasks yet — add one below."), (selected.todos || []).map(todo => React.createElement("div", { key: todo.id, className: "todo-item", onClick: () => toggleTodo(selected.id, todo.id) }, React.createElement("div", { className: "todo-chk" + (todo.done ? " done" : "") }, todo.done && React.createElement("span", { style: { color: "#fff", fontSize: ".65rem", fontWeight: 700 } }, "✓") ), React.createElement("span", { className: "todo-txt" + (todo.done ? " done" : "") }, todo.text) ) ), React.createElement("div", { className: "todo-add" }, React.createElement("input", { className: "todo-input", placeholder: "Add a task...", value: newTodo, onChange: e => setNewTodo(e.target.value), onKeyDown: e => e.key === "Enter" && addTodo(selected.id, newTodo) }), React.createElement("button", { className: "todo-submit", onClick: () => addTodo(selected.id, newTodo) }, "Add") ) ) ), pTab === "ai" && React.createElement(React.Fragment, null, React.createElement("div", { style:{ padding:".75rem 1.4rem .5rem" } }, React.createElement("div", { style:{ fontSize:".72rem", color:"rgba(190,160,128,.45)", lineHeight:1.6, marginBottom:"1.1rem" } }, "AI-generated documents based on this case. Always review before sending." ), React.createElement("div", { style:{ display:"flex", gap:".6rem", marginBottom:"1.1rem" } }, React.createElement("button", { className:"pbtn pri", style:{ flex:1, opacity: aiState.loading ? .5 : 1 }, disabled: aiState.loading, onClick: () => generateAI(selected.id, 'suitability_letter') }, aiState.loading && aiState.type === 'suitability_letter' ? "Generating…" : "✍ Suitability Letter"), React.createElement("button", { className:"pbtn sec", style:{ flex:1, opacity: aiState.loading ? .5 : 1 }, disabled: aiState.loading, onClick: () => generateAI(selected.id, 'packaging_email') }, aiState.loading && aiState.type === 'packaging_email' ? "Generating…" : "✉ Packaging Email") ), aiState.error && React.createElement("div", { style:{ background:"rgba(224,48,48,.1)", border:"1px solid rgba(224,48,48,.25)", borderRadius:"6px", padding:".65rem .9rem", fontSize:".75rem", color:"#E8795A", marginBottom:"1rem", lineHeight:1.5 } }, "⚠ ", aiState.error), aiState.content && React.createElement(React.Fragment, null, React.createElement("div", { style:{ display:"flex", justifyContent:"space-between", alignItems:"center", marginBottom:".5rem" } }, React.createElement("div", { style:{ fontSize:".65rem", fontWeight:600, letterSpacing:".1em", textTransform:"uppercase", color:"var(--copper)" } }, aiState.type === 'suitability_letter' ? "Suitability Letter" : "Packaging Email" ), React.createElement("button", { onClick: () => { navigator.clipboard.writeText(aiState.content); toast('Copied to clipboard ✓', 'info'); }, style:{ background:"none", border:"1px solid rgba(190,160,128,.2)", borderRadius:"4px", padding:".25rem .65rem", fontSize:".65rem", color:"rgba(190,160,128,.7)", cursor:"pointer", fontFamily:"'DM Sans',sans-serif", letterSpacing:".05em" } }, "Copy") ), React.createElement("div", { style:{ background:"rgba(253,250,246,.03)", border:"1px solid rgba(190,160,128,.1)", borderRadius:"6px", padding:"1rem 1.1rem", fontSize:".77rem", color:"rgba(253,250,246,.78)", lineHeight:1.78, whiteSpace:"pre-wrap", maxHeight:"44vh", overflowY:"auto", fontFamily:"'DM Sans',sans-serif", marginBottom:".85rem" } }, aiState.content), aiState.type === 'suitability_letter' && React.createElement("div", { style:{ display:"flex", gap:".6rem" } }, React.createElement("button", { className:"pbtn pri", style:{ flex:1 }, onClick: downloadLetter }, "⬇ Download PDF"), React.createElement("button", { className:"pbtn sec", style:{ flex:1, opacity: aiSending ? .5 : 1 }, disabled: aiSending, onClick: () => sendLetter(selected) }, aiSending ? "Sending…" : `✉ Send to ${cFirst(selected) || 'Client'}`) ) ) ) ), React.createElement("div", { className: "del-zone" }, React.createElement("div", { className: "del-title" }, "Danger Zone"), deleteConfirm ? React.createElement(React.Fragment, null, React.createElement("div", { className: "del-body" }, "This will permanently remove ", cName(selected), " from the pipeline. This cannot be undone."), React.createElement("div", { className: "del-btns" }, React.createElement("button", { className: "del-confirm", onClick: () => deleteCase(selected.id) }, "Yes, delete case"), React.createElement("button", { className: "del-cancel", onClick: () => setDeleteConfirm(false) }, "Cancel") ) ) : React.createElement("button", { className: "del-cancel", style: { width: "100%", textAlign: "center", padding: ".55rem" }, onClick: () => setDeleteConfirm(true) }, "Delete this case") ) ); })())), view === "revenue" && React.createElement("div", { className:"page" }, React.createElement("div", { className:"pg-title" }, "Revenue"), React.createElement("div", { className:"pg-sub" }, "Derived from case data · All figures auto-calculated"), velocity && React.createElement("div", { className:"velocity-card" }, React.createElement("div", { className:"velocity-title" }, "Pipeline Velocity — completed cases"), React.createElement("div", { className:"velocity-grid" }, React.createElement("div", { className:"vel-item" }, React.createElement("div", { className:"vel-v" }, velocity.avg), React.createElement("div", { className:"vel-l" }, "Avg days")), React.createElement("div", { className:"vel-item" }, React.createElement("div", { className:"vel-v c-green" }, velocity.fastest), React.createElement("div", { className:"vel-l" }, "Fastest")), React.createElement("div", { className:"vel-item" }, React.createElement("div", { className:"vel-v c-rust" }, velocity.slowest), React.createElement("div", { className:"vel-l" }, "Slowest")), React.createElement("div", { className:"vel-item" }, React.createElement("div", { className:"vel-v" }, velocity.count), React.createElement("div", { className:"vel-l" }, "Completed")) ) ), /*#__PURE__*/React.createElement("div", { className: "sum-grid" }, /*#__PURE__*/React.createElement("div", { className: "sum-card" }, /*#__PURE__*/React.createElement("div", { className: "sum-l" }, "Expected (active)"), /*#__PURE__*/React.createElement("div", { className: "sum-v c-copper" }, "\xA3", totalExp.toLocaleString())), /*#__PURE__*/React.createElement("div", { className: "sum-card" }, /*#__PURE__*/React.createElement("div", { className: "sum-l" }, "Received YTD"), /*#__PURE__*/React.createElement("div", { className: "sum-v c-green" }, "\xA3", totalRecv.toLocaleString())), /*#__PURE__*/React.createElement("div", { className: "sum-card" }, /*#__PURE__*/React.createElement("div", { className: "sum-l" }, "Outstanding"), /*#__PURE__*/React.createElement("div", { className: "sum-v c-rust" }, "\xA3", (totalExp - totalRecv).toLocaleString())), /*#__PURE__*/React.createElement("div", { className: "sum-card" }, /*#__PURE__*/React.createElement("div", { className: "sum-l" }, "Cases active"), /*#__PURE__*/React.createElement("div", { className: "sum-v" }, cases.filter(c => c.stage !== "Completed").length))), /*#__PURE__*/React.createElement("table", null, /*#__PURE__*/React.createElement("thead", null, /*#__PURE__*/React.createElement("tr", null, /*#__PURE__*/React.createElement("th", null, "Client"), /*#__PURE__*/React.createElement("th", null, "Type"), /*#__PURE__*/React.createElement("th", null, "Stage"), /*#__PURE__*/React.createElement("th", null, "Lender"), React.createElement("th", null, "Lender Ref"), /*#__PURE__*/React.createElement("th", null, "Loan"), /*#__PURE__*/React.createElement("th", null, "Proc Fee"), /*#__PURE__*/React.createElement("th", null, "Broker Fee"), React.createElement("th", null, "Completion"), React.createElement("th", null, "Payment Route"), /*#__PURE__*/React.createElement("th", null, "Status"))), /*#__PURE__*/React.createElement("tbody", null, cases.map((c, i) => { const tc = TYPES[c.type] || TYPES["Remortgage"]; const st = c.commissionReceived ? "received" : c.stage === "Completed" ? "overdue" : "pending"; return /*#__PURE__*/React.createElement("tr", { key: i }, /*#__PURE__*/React.createElement("td", { style: { fontFamily: "'Cormorant Garamond',serif", fontSize: ".83rem", color: "rgba(253,250,246,.9)" } }, cSur(c), cRest(c)), /*#__PURE__*/React.createElement("td", null, /*#__PURE__*/React.createElement("span", { className: "badge", style: { background: tc.bg, color: tc.text } }, c.type)), /*#__PURE__*/React.createElement("td", { style: { color: "rgba(190,160,128,.6)" } }, c.stage), /*#__PURE__*/React.createElement("td", null, c.lender), React.createElement("td", { style:{color:"rgba(190,160,128,.5)",fontSize:".72rem"} }, c.lenderRef||"—"), /*#__PURE__*/React.createElement("td", { style: { color: "var(--copper)" } }, "\xA3", c.loan), /*#__PURE__*/React.createElement("td", { style: { fontWeight: 500, color: "rgba(253,250,246,.92)" } }, "\xA3", c.fee), /*#__PURE__*/React.createElement("td", { style: { color: c.brokerFeeCharged ? "var(--copper)" : "rgba(190,160,128,.3)" } }, c.brokerFeeCharged ? `£${c.brokerFee.toLocaleString()}` : "—"), React.createElement("td", { style:{fontSize:".72rem",color:"rgba(190,160,128,.6)"} }, c.completionDate ? fmtDate(c.completionDate) : "—"), React.createElement("td", { style:{fontSize:".72rem",color:"rgba(190,160,128,.5)"} }, c.paymentRoute||"—"), React.createElement("td", null, c.commissionReceived ? React.createElement("div", { style:{display:"flex",alignItems:"center",gap:".5rem"} }, React.createElement("span", { className:"pill pill-r" }, "received"), React.createElement("button", { onClick:()=>markCommissionReceived(c.id, true), style:{ background:"none", border:"none", cursor:"pointer", fontSize:".65rem", color:"rgba(190,160,128,.35)", textDecoration:"underline", padding:0, fontFamily:"'DM Sans',sans-serif" } }, "undo") ) : React.createElement("div", { style:{display:"flex",alignItems:"center",gap:".5rem"} }, React.createElement("span", { className:"pill pill-"+(st==="overdue"?"o":"p") }, st), React.createElement("button", { className:"rev-recv-btn", onClick:()=>markCommissionReceived(c.id) }, "Mark ✓") ) )); }), React.createElement("tr", { className:"rev-total-row" }, React.createElement("td", { colSpan:5 }, "Total"), React.createElement("td", { style:{color:"var(--copper)"} }, "—"), React.createElement("td", null, "£", cases.reduce((a,c)=>a+parseInt((c.fee||'0').replace(/,/g,'')),0).toLocaleString()), React.createElement("td", null, "£", cases.filter(c=>c.brokerFeeCharged).reduce((a,c)=>a+c.brokerFee,0).toLocaleString()), React.createElement("td", null, "—"), React.createElement("td", null, "—"), React.createElement("td", null, React.createElement("span",{style:{color:"#5A9E72",fontSize:".75rem",fontWeight:600}},"£"+totalRecv.toLocaleString()+" received"), " · ", React.createElement("span",{style:{color:"#E8795A",fontSize:".75rem"}},"£"+(totalExp-totalRecv).toLocaleString()+" pending")) ) ))), view === "reviews" && React.createElement("div", { className:"page" }, React.createElement("div", { className:"pg-title" }, "Rate Reviews"), React.createElement("div", { className:"pg-sub" }, "Upcoming fixed rate expiries — derived from case data"), boeRate !== null && React.createElement("div", { className:"boe-banner" }, React.createElement("div", null, React.createElement("div", { className:"boe-rate" }, boeRate.toFixed(2), "%"), React.createElement("div", { className:"boe-label" }, "BoE Base Rate · " + boeDate) ), React.createElement("div", { className:"boe-context" }, React.createElement("div", { className:"boe-ctx-line" }, React.createElement("strong", null, "Current base rate: " + boeRate.toFixed(2) + "%."), " Standard residential products price 0.5–2% above base. Bridging and commercial significantly higher."), React.createElement("div", { className:"boe-ctx-line" }, "Clients expiring within 6 months should be contacted now — fixing early protects against upward movement. All cases within 5 years shown.") ) ), rateReviews.length === 0 ? React.createElement("div", { style:{ color:"rgba(190,160,128,.35)", fontSize:".8rem", marginTop:"1rem" } }, "No rate expiries tracked in the next 6 years. Set rateExpiry on cases to populate this view.") : React.createElement(React.Fragment, null, React.createElement("div", { className:"sum-grid", style:{ gridTemplateColumns:"repeat(4,1fr)", marginBottom:"1rem" } }, React.createElement("div", { className:"sum-card" }, React.createElement("div", { className:"sum-l" }, "Expiring < 3 months"), React.createElement("div", { className:"sum-v c-rust" }, rateReviews.filter(r=>r.months<=3).length) ), React.createElement("div", { className:"sum-card" }, React.createElement("div", { className:"sum-l" }, "Expiring 3–6 months"), React.createElement("div", { className:"sum-v c-copper" }, rateReviews.filter(r=>r.months>3&&r.months<=6).length) ), React.createElement("div", { className:"sum-card" }, React.createElement("div", { className:"sum-l" }, "Expiring 6–12 months"), React.createElement("div", { className:"sum-v" }, rateReviews.filter(r=>r.months>6&&r.months<=12).length) ), React.createElement("div", { className:"sum-card" }, React.createElement("div", { className:"sum-l" }, "Expiring 12–72 months"), React.createElement("div", { className:"sum-v", style:{color:"rgba(190,160,128,.5)"} }, rateReviews.filter(r=>r.months>12).length) ) ), React.createElement("div", { className:"rlist" }, rateReviews.map((r, i) => { const tc = TYPES[r.type] || TYPES["Remortgage"]; const contactBy = (() => { const d = new Date(r.rateExpiry); d.setMonth(d.getMonth()-6); return d.toLocaleDateString('en-GB',{month:'short',year:'numeric'}); })(); const urgent = r.months <= 6; return React.createElement("div", { className:"ri", key:i }, React.createElement("div", { className:"rmn" }, React.createElement("div", { className:"rmv", style:{ color: r.months<=3?"#E8795A":r.months<=6?"var(--copper)":"var(--warm)" } }, r.months), React.createElement("div", { className:"rml" }, "months") ), React.createElement("div", { className:"rdiv" }), React.createElement("div", { className:"rinfo" }, React.createElement("div", { className:"rname" }, cSur(r), cRest(r)), React.createElement("div", { className:"rdet" }, React.createElement("span", { className:"badge", style:{ background:tc.bg, color:tc.text, marginRight:".35rem" } }, r.type), r.product, " · ", r.lender, " · expires ", fmtDate(r.rateExpiry) ), boeRate !== null && React.createElement("div", { style:{ fontSize:".72rem", color:"rgba(190,160,128,.42)", marginTop:".25rem" } }, "Base rate: ", React.createElement("strong", { style:{ color:"#78AACC" } }, boeRate.toFixed(2), "%"), " · Contact by: ", React.createElement("strong", { style:{ color: r.months<=6?"#E8795A":"var(--copper)" } }, contactBy) ) ), React.createElement("div", { style:{ display:"flex", flexDirection:"column", gap:".4rem", flexShrink:0 } }, urgent && React.createElement("button", { className:"rbtn", style:{ background:"rgba(140,53,24,.2)", borderColor:"rgba(140,53,24,.4)", color:"#E8795A" }, onClick: () => { const subject = `Your mortgage rate is expiring \u2014 let\u2019s find you a better deal`; const body = `Dear ${cFirst(r)},\n\nI hope you\u2019re well. I wanted to reach out because your current fixed rate with ${r.lender} (${r.product}) is due to expire in ${r.months} month${r.months===1?'':'s'}, in ${fmtDate(r.rateExpiry)}.\n\nThis is the ideal time to start looking at your options \u2014 acting early gives us access to the best rates and means you won\u2019t fall onto your lender\u2019s standard variable rate, which is typically much higher.\n\nI\u2019d love to run through the current market with you. Are you available for a quick call this week?\n\nKind regards,\nNathan`; setComposeData({ to: r.email||'', subject, body }); setComposeStatus(null); setShowCompose(true); setSel(r.id); } }, "Contact Now"), urgent ? React.createElement("button", { className:"rbtn", onClick: () => { setSel(r.id); setShowClone(true); setView("pipeline"); } }, "Start Remortgage") : React.createElement("button", { className:"rbtn", style:{ opacity:.85 }, onClick: () => { const contactDate = new Date(r.rateExpiry); contactDate.setMonth(contactDate.getMonth()-4); const contactDateStr = contactDate.toLocaleDateString('en-GB',{day:'numeric',month:'short',year:'numeric'}); // Format dates for Outlook — YYYY-MM-DDTHH:mm:ss const pad = n => String(n).padStart(2,'0'); const y = contactDate.getFullYear(); const mo = pad(contactDate.getMonth()+1); const d = pad(contactDate.getDate()); const startDt = `${y}-${mo}-${d}T09:00:00`; const endDt = `${y}-${mo}-${d}T09:30:00`; // Log note on case const existing = cases.find(x => x.id === r.id); if (existing) { const log = [...(existing.activityLog||[]), { ts: new Date().toISOString(), type:'note', text:`Rate review scheduled — contact by ${contactDateStr} (rate expires ${fmtDate(r.rateExpiry)})` }]; updateCase(r.id, { activityLog: log }); } // Open Outlook calendar with correct date and reminder const title = encodeURIComponent(`Rate Review — ${cName(r)}`); const body = encodeURIComponent(`Contact ${cName(r)} to discuss remortgage options.\n\nCurrent rate: ${r.product||'—'} with ${r.lender||'—'}\nRate expires: ${fmtDate(r.rateExpiry)}\nContact by: ${contactDateStr}`); window.open(`https://outlook.office.com/calendar/action/compose?subject=${title}&body=${body}&startdt=${encodeURIComponent(startDt)}&enddt=${encodeURIComponent(endDt)}&reminderMinutesBeforeStart=1440`, '_blank'); toast(`Scheduled — contact ${cSur(r).trim()} by ${contactDateStr}`, 'ok'); } }, "Schedule") ) ); }) ) ) ), view === "protection" && /*#__PURE__*/React.createElement("div", { className: "page" }, /*#__PURE__*/React.createElement("div", { className: "pg-title" }, "Protection"), /*#__PURE__*/React.createElement("div", { className: "pg-sub" }, "All active clients \u2014 protection status"), /*#__PURE__*/React.createElement("div", { className: "sum-grid", style: { gridTemplateColumns: "repeat(3,1fr)" } }, /*#__PURE__*/React.createElement("div", { className: "sum-card" }, /*#__PURE__*/React.createElement("div", { className: "sum-l" }, "Fully covered"), /*#__PURE__*/React.createElement("div", { className: "sum-v c-green" }, cases.filter(c => protSummary(c.protection) === "y").length)), /*#__PURE__*/React.createElement("div", { className: "sum-card" }, /*#__PURE__*/React.createElement("div", { className: "sum-l" }, "Discussed \u2014 not converted"), /*#__PURE__*/React.createElement("div", { className: "sum-v c-copper" }, cases.filter(c => protSummary(c.protection) === "p").length)), /*#__PURE__*/React.createElement("div", { className: "sum-card" }, /*#__PURE__*/React.createElement("div", { className: "sum-l" }, "Not yet raised"), /*#__PURE__*/React.createElement("div", { className: "sum-v c-rust" }, cases.filter(c => protSummary(c.protection) === "n").length))), /*#__PURE__*/React.createElement("div", { style: { marginBottom: ".6rem" } }, /*#__PURE__*/React.createElement("div", { style: { fontSize: ".57rem", color: "rgba(190,160,128,.38)", textTransform: "uppercase", letterSpacing: ".1em", marginBottom: ".5rem", fontWeight: 600 } }, "Needs Attention"), cases.filter(c => protSummary(c.protection) !== "y").sort((a, b) => protSummary(a.protection) === "n" ? -1 : 1).map(c => { const ps = protSummary(c.protection); return /*#__PURE__*/React.createElement("div", { className: "prot-card", key: c.id }, /*#__PURE__*/React.createElement("div", { style: { width: 10, height: 10, borderRadius: "50%", background: ps === "n" ? "#E8795A" : "var(--copper)", flexShrink: 0, marginTop: ".2rem" } }), /*#__PURE__*/React.createElement("div", { className: "prot-info" }, /*#__PURE__*/React.createElement("div", { className: "prot-name" }, cSur(c), cRest(c)), /*#__PURE__*/React.createElement("div", { className: "prot-detail" }, c.type, " · ", c.stage), /*#__PURE__*/React.createElement("div", { className: "prot-types" }, [["Life", c.protection.life], ["CI", c.protection.ci], ["IP", c.protection.ip], ["B&C", c.protection.bc]].map(([l, v]) => /*#__PURE__*/React.createElement("span", { key: l, className: "pt-pill " + (v.inPlace ? "pt-yes" : "pt-no") }, l, ": ", v.inPlace ? "Yes" : "No")) ), /*#__PURE__*/React.createElement("div", { style: { fontSize: ".6rem", color: "#E8A87A", marginTop: ".3rem" } }, c.protAction)), React.createElement("div", { style:{ display:"flex", flexDirection:"column", gap:".4rem", flexShrink:0 } }, React.createElement("button", { className: "prot-act-btn", onClick: () => { openProtectionEmail(c); setSel(c.id); } }, "Draft Email"), React.createElement("button", { className: "prot-act-btn", onClick: () => logProtFollowUp(c.id) }, "Follow up") )); })), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", { style: { fontSize: ".57rem", color: "rgba(190,160,128,.38)", textTransform: "uppercase", letterSpacing: ".1em", margin: ".6rem 0 .45rem", fontWeight: 600 } }, "Fully Protected"), cases.filter(c => protSummary(c.protection) === "y").map(c => /*#__PURE__*/React.createElement("div", { className: "prot-card", key: c.id, style: { opacity: .55 } }, /*#__PURE__*/React.createElement("div", { style: { width: 10, height: 10, borderRadius: "50%", background: "#5A9E72", flexShrink: 0, marginTop: ".2rem" } }), /*#__PURE__*/React.createElement("div", { className: "prot-info" }, /*#__PURE__*/React.createElement("div", { className: "prot-name" }, cSur(c), cRest(c)), /*#__PURE__*/React.createElement("div", { className: "prot-detail" }, c.type, " \xB7 ", c.stage), /*#__PURE__*/React.createElement("div", { className: "prot-types" }, [["Life", c.protection.life], ["CI", c.protection.ci], ["IP", c.protection.ip], ["B&C", c.protection.bc]].map(([l, v]) => /*#__PURE__*/React.createElement("span", { key: l, className: "pt-pill " + (v.inPlace ? "pt-yes" : "pt-na") }, l, ": ", v.inPlace ? "Yes" : "—")))))))), showClone && selected && React.createElement("div", { className:"modal-overlay", onClick:e=>e.target===e.currentTarget&&setShowClone(false) }, React.createElement("div", { className:"modal", style:{ maxWidth:"420px" } }, React.createElement("div", { className:"modal-head" }, React.createElement("div", null, React.createElement("div", { className:"modal-title" }, "Clone Case"), React.createElement("div", { className:"modal-sub" }, cName(selected), " — choose the type of new case") ), React.createElement("button", { className:"modal-close", onClick:()=>setShowClone(false) }, "×") ), React.createElement("div", { style:{ padding:"1rem 1.5rem 1.5rem", display:"flex", flexDirection:"column", gap:".75rem" } }, React.createElement("button", { onClick: () => cloneCase('pt'), style:{ background:"rgba(190,160,128,.06)", border:"1px solid rgba(190,160,128,.18)", borderRadius:"8px", padding:"1rem 1.25rem", cursor:"pointer", textAlign:"left", transition:"border-color .15s" }, onMouseOver: e=>e.currentTarget.style.borderColor="rgba(190,160,128,.4)", onMouseOut: e=>e.currentTarget.style.borderColor="rgba(190,160,128,.18)" }, React.createElement("div", { style:{ fontSize:".8rem", fontWeight:600, color:"var(--warm)", marginBottom:".25rem", fontFamily:"'DM Sans',sans-serif" } }, "Product Transfer"), React.createElement("div", { style:{ fontSize:".72rem", color:"rgba(190,160,128,.55)", lineHeight:1.5 } }, "Same lender, same property. Carries over all client, property, income and lender details. Clears product, rate, refs and compliance." ) ), React.createElement("button", { onClick: () => cloneCase('remortgage'), style:{ background:"rgba(190,160,128,.06)", border:"1px solid rgba(190,160,128,.18)", borderRadius:"8px", padding:"1rem 1.25rem", cursor:"pointer", textAlign:"left", transition:"border-color .15s" }, onMouseOver: e=>e.currentTarget.style.borderColor="rgba(190,160,128,.4)", onMouseOut: e=>e.currentTarget.style.borderColor="rgba(190,160,128,.18)" }, React.createElement("div", { style:{ fontSize:".8rem", fontWeight:600, color:"var(--warm)", marginBottom:".25rem", fontFamily:"'DM Sans',sans-serif" } }, "Remortgage"), React.createElement("div", { style:{ fontSize:".72rem", color:"rgba(190,160,128,.55)", lineHeight:1.5 } }, "Same property, new lender. Carries over client details, property, loan, income and commitments. Clears lender, solicitor, docs and compliance." ) ), React.createElement("button", { onClick: () => cloneCase('purchase'), style:{ background:"rgba(190,160,128,.06)", border:"1px solid rgba(190,160,128,.18)", borderRadius:"8px", padding:"1rem 1.25rem", cursor:"pointer", textAlign:"left", transition:"border-color .15s" }, onMouseOver: e=>e.currentTarget.style.borderColor="rgba(190,160,128,.4)", onMouseOut: e=>e.currentTarget.style.borderColor="rgba(190,160,128,.18)" }, React.createElement("div", { style:{ fontSize:".8rem", fontWeight:600, color:"var(--warm)", marginBottom:".25rem", fontFamily:"'DM Sans',sans-serif" } }, "New Purchase"), React.createElement("div", { style:{ fontSize:".72rem", color:"rgba(190,160,128,.55)", lineHeight:1.5 } }, "New property entirely. Carries over client contact details, income and commitments only. All property and mortgage fields start fresh." ) ) ) ) ), showNewCase && React.createElement("div", { className:"modal-overlay", onClick:e => e.target===e.currentTarget&&setShowNewCase(false) }, React.createElement("div", { className:"modal" }, React.createElement("div", { className:"modal-head" }, React.createElement("div", null, React.createElement("div", { className:"modal-title" }, "New Case"), React.createElement("div", { className:"modal-sub" }, "Create client · SharePoint folder auto-created") ), React.createElement("button", { className:"modal-close", onClick:()=>setShowNewCase(false) }, "×") ), React.createElement("div", { className:"modal-body" }, React.createElement("div", { className:"form-grid" }, React.createElement("div", { className:"form-group" }, React.createElement("label", { className:"form-label" }, "Surname", React.createElement("span", null, " *")), React.createElement("input", { className:"form-input", placeholder:"e.g. Smith", value:ncData.sur, onChange:e=>setNcData(p=>({...p,sur:e.target.value})) }) ), React.createElement("div", { className:"form-group" }, React.createElement("label", { className:"form-label" }, "First Name", React.createElement("span", null, " *")), React.createElement("input", { className:"form-input", placeholder:"e.g. James", value:ncData.first, onChange:e=>setNcData(p=>({...p,first:e.target.value})) }) ), React.createElement("div", { className:"form-group" }, React.createElement("label", { className:"form-label" }, "Case Type"), React.createElement("select", { className:"form-select", value:ncData.type, onChange:e=>setNcData(p=>({...p,type:e.target.value})) }, Object.keys(TYPES).map(t => React.createElement("option", { key:t, value:t }, t)) ) ), React.createElement("div", { className:"form-group" }, React.createElement("label", { className:"form-label" }, "Starting Stage"), React.createElement("select", { className:"form-select", value:newCaseStage, onChange:e=>setNewCaseStage(e.target.value) }, STAGES.map(s => React.createElement("option", { key:s, value:s }, s)) ) ), React.createElement("div", { className:"form-group" }, React.createElement("label", { className:"form-label" }, "Phone"), React.createElement("input", { className:"form-input", placeholder:"07700 900000", value:ncData.phone, onChange:e=>setNcData(p=>({...p,phone:e.target.value})) }) ), React.createElement("div", { className:"form-group" }, React.createElement("label", { className:"form-label" }, "Email"), React.createElement("input", { className:"form-input", type:"email", placeholder:"client@email.com", value:ncData.email, onChange:e=>setNcData(p=>({...p,email:e.target.value})) }) ), React.createElement("div", { className:"form-group" }, React.createElement("label", { className:"form-label" }, "Company Name (Ltd BTL / SPV — optional)"), React.createElement("input", { className:"form-input", placeholder:"e.g. Myers Properties Ltd", value:ncData.company, onChange:e=>setNcData(p=>({...p,company:e.target.value})) }) ), React.createElement("div", { className:"form-group" }, React.createElement("label", { className:"form-label" }, "Loan Amount", React.createElement("span", null, " *")), React.createElement("input", { className:"form-input", placeholder:"e.g. 350,000", value:ncData.loan, onChange:e=>handleNcLoan(e.target.value) }) ), React.createElement("div", { className:"form-group" }, React.createElement("label", { className:"form-label" }, "LTV — auto-calculates deposit"), React.createElement("input", { className:"form-input", placeholder:"e.g. 75", value:ncData.ltv, onChange:e=>handleNcLtv(e.target.value) }) ), React.createElement("div", { className:"form-group" }, React.createElement("label", { className:"form-label" }, "Deposit — auto-calculates LTV"), React.createElement("input", { className:"form-input", placeholder:"e.g. 50,000", value:ncData.deposit, onChange:e=>handleNcDeposit(e.target.value) }) ), React.createElement("div", { className:"form-group full" }, React.createElement("label", { className:"form-label" }, "Security / Property Address"), React.createElement("input", { className:"form-input", placeholder:"Full address of the property", value:ncData.propAddr, onChange:e=>setNcData(p=>({...p,propAddr:e.target.value})) }) ), React.createElement("div", { className:"joint-section" }, React.createElement("div", { className:"joint-toggle", onClick:()=>setNcData(p=>({...p,isJoint:!p.isJoint})) }, React.createElement("div", { className:"joint-toggle-chk"+(ncData.isJoint?" on":"") }, ncData.isJoint && React.createElement("span",{style:{color:"#fff",fontSize:".65rem",fontWeight:700}},"✓") ), React.createElement("span", { className:"joint-label" }, "Joint application — add second applicant") ), ncData.isJoint && React.createElement("div", { className:"form-grid" }, React.createElement("div", { className:"form-group" }, React.createElement("label", { className:"form-label" }, "Applicant 2 Surname"), React.createElement("input", { className:"form-input", placeholder:"Surname", value:ncData.ap2sur, onChange:e=>setNcData(p=>({...p,ap2sur:e.target.value})) }) ), React.createElement("div", { className:"form-group" }, React.createElement("label", { className:"form-label" }, "Applicant 2 First Name"), React.createElement("input", { className:"form-input", placeholder:"First name", value:ncData.ap2first, onChange:e=>setNcData(p=>({...p,ap2first:e.target.value})) }) ) ) ) ), ncStatus && React.createElement("div", { className:"form-status " + ncStatus.type }, ncStatus.msg), React.createElement("div", { className:"modal-footer" }, React.createElement("button", { className:"modal-btn-sec", onClick:()=>setShowNewCase(false) }, "Cancel"), React.createElement("button", { className:"modal-btn-pri", disabled:ncSaving, onClick:createCase }, ncSaving ? "Creating..." : "Create Case + SharePoint Folder") ) ) ) ), showCompose && React.createElement("div", { className:"modal-overlay", onClick:e => e.target===e.currentTarget&&setShowCompose(false) }, React.createElement("div", { className:"modal" }, React.createElement("div", { className:"modal-head" }, React.createElement("div", null, React.createElement("div", { className:"modal-title" }, "Send Email"), React.createElement("div", { className:"modal-sub" }, "Via Outlook · Nathan@lawesfinancial.com") ), React.createElement("button", { className:"modal-close", onClick:()=>setShowCompose(false) }, "×") ), React.createElement("div", { className:"modal-body" }, React.createElement("div", { className:"compose-to" }, "To: ", composeData.to || "No email on file"), React.createElement("div", { className:"form-group", style:{ marginBottom:".85rem" } }, React.createElement("label", { className:"form-label" }, "Subject"), React.createElement("input", { className:"form-input", value:composeData.subject, onChange:e=>setComposeData(p=>({...p,subject:e.target.value})) }) ), React.createElement("div", { className:"form-group" }, React.createElement("label", { className:"form-label" }, "Message"), React.createElement("textarea", { className:"note-area", rows:8, value:composeData.body, onChange:e=>setComposeData(p=>({...p,body:e.target.value})), placeholder:"Type your message..." }) ), composeStatus && React.createElement("div", { className:"form-status " + composeStatus.type }, composeStatus.msg), React.createElement("div", { className:"modal-footer" }, React.createElement("button", { className:"modal-btn-sec", onClick:()=>setShowCompose(false) }, "Cancel"), React.createElement("a", { href: "https://outlook.office.com/mail/deeplink/compose?to=" + encodeURIComponent(composeData.to) + "&subject=" + encodeURIComponent(composeData.subject) + "&body=" + encodeURIComponent(composeData.body), target:"_blank", className:"modal-btn-sec", style:{ textDecoration:"none", display:"flex", alignItems:"center" } }, "Open in Outlook"), React.createElement("button", { className:"modal-btn-pri", disabled:composeSending, onClick:sendEmail }, composeSending ? "Sending..." : "Send via Worker") ) ) ) ), showDecline && selected && React.createElement("div", { className:"modal-overlay", onClick:e=>e.target===e.currentTarget&&setShowDecline(false) }, React.createElement("div", { className:"modal", style:{maxWidth:420} }, React.createElement("div", { className:"modal-head" }, React.createElement("div", null, React.createElement("div", { className:"modal-title" }, "Log Lender Decline"), React.createElement("div", { className:"modal-sub" }, cName(selected)) ), React.createElement("button", { className:"modal-close", onClick:()=>setShowDecline(false) }, "×") ), React.createElement("div", { className:"modal-body" }, React.createElement("div", { className:"form-group", style:{marginBottom:".85rem"} }, React.createElement("label", { className:"form-label" }, "Lender Declined", React.createElement("span", null, " *")), React.createElement("input", { className:"form-input", placeholder:"e.g. Nationwide", value:declineData.lender, onChange:e=>setDeclineData(p=>({...p,lender:e.target.value})) }) ), React.createElement("div", { className:"form-group" }, React.createElement("label", { className:"form-label" }, "Reason / Notes"), React.createElement("textarea", { className:"note-area", rows:3, placeholder:"e.g. DTI too high, minimum income not met...", value:declineData.reason, onChange:e=>setDeclineData(p=>({...p,reason:e.target.value})) }) ), React.createElement("div", { className:"modal-footer" }, React.createElement("button", { className:"modal-btn-sec", onClick:()=>setShowDecline(false) }, "Cancel"), React.createElement("button", { className:"modal-btn-pri", style:{background:"#8C3518"}, onClick:()=>logDecline(selected.id) }, "Log Decline to Activity") ) ) ) ), cmdCentre && React.createElement("div", { className: "cc-overlay" }, React.createElement("div", { className: "cc-header" }, React.createElement("div", null, React.createElement("div", { className: "cc-title" }, "Command Centre"), React.createElement("div", { className: "cc-sub" }, cases.filter(c => c.stage !== "Completed").length, " active · ", cases.length, " total · £", cases.filter(c => c.stage !== "Completed").reduce((a, c) => a + parseInt((c.fee || '0').replace(/,/g,'') || 0), 0).toLocaleString(), " expected" ) ), React.createElement("div", { style:{display:"flex",gap:".6rem",alignItems:"center"} }, React.createElement("button", { className:"cc-close", style:{background: cmdShowAll?"rgba(196,120,72,.15)":"rgba(190,160,128,.08)", color: cmdShowAll?"var(--copper)":"var(--stone)"}, onClick:()=>setCmdShowAll(p=>!p) }, cmdShowAll ? "Showing All" : "Active Only"), React.createElement("button", { className: "cc-close", onClick: () => setCmdCentre(false) }, "✕ Close") ) ), React.createElement("div", { className: "cc-grid" }, cases.filter(c => cmdShowAll || c.stage !== "Completed") .sort((a, b) => { if (!cmdShowAll) { const hOrder = { r: 0, a: 1, g: 2 }; return hOrder[calcHealth(a)] - hOrder[calcHealth(b)]; } if (a.stage === "Completed" && b.stage !== "Completed") return 1; if (b.stage === "Completed" && a.stage !== "Completed") return -1; return 0; }) .map(c => { const t = TYPES[c.type] || TYPES["Remortgage"]; const h = calcHealth(c); const d = getDays(c); const u = urgencyInfo(c); const openTodos = (c.todos || []).filter(t => !t.done).length; return React.createElement("div", { key: c.id, className: "cc-card", onClick: () => { setSel(c.id); setPTab("case"); setCmdCentre(false); setView("pipeline"); } }, React.createElement("div", { className: "cc-card-bar", style: { background: t.bar } }), React.createElement("div", { style: { display: "flex", alignItems: "flex-start", justifyContent: "space-between", marginBottom: ".2rem" } }, React.createElement("div", { className: "cc-name" }, cSur(c), React.createElement("span", { style: { fontWeight: 300, color: "rgba(253,250,246,.4)" } }, cRest(c))), React.createElement("div", { className: "hlth hlth-" + h, style: { position: "relative", top: 0, right: 0, flexShrink: 0, marginTop: ".35rem" } }) ), React.createElement("div", { className: "cc-meta" }, c.type, " · ", c.lender), React.createElement("div", { className: "cc-row" }, React.createElement("span", { className: "cc-label" }, "Loan"), React.createElement("span", { className: "cc-val" }, "£", c.loan) ), React.createElement("div", { className: "cc-row" }, React.createElement("span", { className: "cc-label" }, "LTV"), React.createElement("span", { className: "cc-val" }, c.ltv) ), u && React.createElement("div", { className: "card-urgency " + u.cls, style: { margin: ".4rem 0 0" } }, React.createElement("span", { className: "urg-dot" }), u.msg ), React.createElement("div", { className: "cc-action " + (d > 7 ? "ca-warn" : "ca-muted") }, c.action), React.createElement("div", { className: "cc-footer" }, React.createElement("span", { className: "cc-stage-pill" }, c.stage), React.createElement("div", { style: { display: "flex", gap: ".6rem", alignItems: "center" } }, openTodos > 0 && React.createElement("span", { className: "todo-badge" }, "◦ ", openTodos), React.createElement("span", { className: "cc-days days" + (d > 7 ? " late" : "") }, d, "d") ) ) ); }) ) ), toasts.length > 0 && React.createElement("div", { className:"toast-wrap" }, toasts.map(t => React.createElement("div", { key:t.id, className:"toast toast-"+t.type }, t.msg)) ) ); } const root = ReactDOM.createRoot(document.getElementById('root')); root.render(React.createElement(App));