18 lines
12 KiB
HTML
18 lines
12 KiB
HTML
{% extends "templates/web.html" %}
|
||
{% block title %}Customer Onboarding · MAKAP{% endblock %}
|
||
{% block page_content %}
|
||
<div id="makap-onboarding" class="onboarding-page">
|
||
<style>
|
||
#makap-onboarding{font-family:Inter,system-ui,sans-serif;background:#f3f5f9;color:#12182b;min-height:calc(100vh - 60px);padding:28px 34px 55px}#makap-onboarding *{box-sizing:border-box}.onb-head{display:flex;justify-content:space-between;align-items:flex-end;gap:20px;max-width:1280px;margin:auto}.onb-eyebrow{text-transform:uppercase;letter-spacing:.1em;font-size:11px;font-weight:700;color:#6f7b91;margin:0 0 8px}.onb-head h1{font-size:30px;margin:0 0 7px;letter-spacing:-.04em}.onb-head p{color:#667086;margin:0;font-size:14px}.onb-actions{display:flex;gap:9px}.onb-btn{border:1px solid #dfe4ed;border-radius:8px;background:#fff;color:#26324b;padding:11px 15px;font-weight:700;cursor:pointer}.onb-btn.primary{background:#2e6be6;border-color:#2e6be6;color:#fff}.onb-btn:hover{filter:brightness(.96)}.onb-summary{max-width:1280px;margin:26px auto 18px;display:grid;grid-template-columns:repeat(6,1fr);gap:12px}.onb-card{background:#fff;border:1px solid #e7eaf1;border-radius:12px;padding:16px 17px;cursor:pointer}.onb-card.active{border-color:#2e6be6;box-shadow:0 0 0 2px #dbe6ff}.onb-card label{display:block;color:#707b91;font-size:12px;margin-bottom:10px}.onb-card strong{font-size:27px;letter-spacing:-.04em}.onb-toolbar{max-width:1280px;margin:0 auto 12px;background:#fff;border:1px solid #e7eaf1;border-radius:12px;padding:13px;display:flex;gap:10px;align-items:center}.onb-search{flex:1;border:1px solid #dfe4ed;border-radius:7px;padding:11px 13px;font-size:13px;min-width:170px}.onb-select{border:1px solid #dfe4ed;border-radius:7px;background:#fff;padding:11px 13px;font-size:13px}.onb-table-wrap{max-width:1280px;margin:auto;background:#fff;border:1px solid #e7eaf1;border-radius:12px;overflow:auto}.onb-table{border-collapse:collapse;width:100%;font-size:13px}.onb-table th{text-align:left;text-transform:uppercase;letter-spacing:.08em;color:#8791a4;font-size:10px;padding:14px 15px;border-bottom:1px solid #e7eaf1;white-space:nowrap}.onb-table td{padding:15px;border-bottom:1px solid #f0f2f6;white-space:nowrap}.onb-table tr:hover{background:#f8faff}.onb-id{color:#1e4fbe;font-weight:800;cursor:pointer}.onb-muted{color:#69748b}.onb-badge{border-radius:20px;padding:5px 9px;font-size:11px;font-weight:700;background:#eef3ff;color:#2e6be6}.onb-badge.review{background:#fff5e5;color:#b46b17}.onb-badge.approved{background:#eaf8f0;color:#1f8a5c}.onb-badge.rejected{background:#fff0f2;color:#c23f60}.onb-empty{text-align:center;padding:42px;color:#8a94a7}.onb-modal-backdrop{position:fixed;inset:0;background:#0f1b2d99;z-index:99;display:none;align-items:center;justify-content:center;padding:20px}.onb-modal-backdrop.open{display:flex}.onb-modal{background:#fff;border-radius:14px;width:min(760px,100%);max-height:90vh;overflow:auto;padding:25px;box-shadow:0 25px 70px #0f1b2d44}.onb-modal-head{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:22px}.onb-modal h2{margin:0;font-size:22px}.onb-close{border:0;background:transparent;font-size:25px;cursor:pointer;color:#6b7588}.onb-form-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px}.onb-field{display:flex;flex-direction:column;gap:6px}.onb-field.full{grid-column:1/-1}.onb-field label{font-size:12px;font-weight:700;color:#47536b}.onb-field input,.onb-field select,.onb-field textarea{font:inherit;border:1px solid #dfe4ed;border-radius:7px;padding:10px 11px}.onb-field textarea{min-height:74px;resize:vertical}.onb-form-actions{display:flex;justify-content:flex-end;gap:9px;margin-top:21px}.onb-detail{display:grid;grid-template-columns:1fr 1fr;gap:14px}.onb-detail div{background:#f7f9fc;border-radius:8px;padding:11px}.onb-detail small{color:#7d879a;display:block;font-size:10px;text-transform:uppercase;letter-spacing:.07em;margin-bottom:4px}.onb-detail span{font-size:13px}.onb-loading{padding:40px;text-align:center;color:#7c879b}@media(max-width:900px){.onb-summary{grid-template-columns:repeat(3,1fr)}.onb-head{display:block}.onb-actions{margin-top:18px}}@media(max-width:600px){ #makap-onboarding{padding:20px 13px 40px}.onb-summary{grid-template-columns:repeat(2,1fr)}.onb-toolbar{display:block}.onb-search,.onb-select{width:100%;margin-bottom:8px}.onb-form-grid,.onb-detail{grid-template-columns:1fr}}
|
||
</style>
|
||
<div class="onb-head"><div><p class="onb-eyebrow">Customer & commercial · M1–2</p><h1>Customer onboarding</h1><p>Capture, review, and convert new customer requests.</p></div><div class="onb-actions"><button class="onb-btn" id="onb-refresh">Refresh</button><button class="onb-btn primary" id="onb-new">+ New onboarding request</button></div></div>
|
||
<div class="onb-summary" id="onb-summary"></div>
|
||
<div class="onb-toolbar"><input class="onb-search" id="onb-search" placeholder="Search organisation, contact, email, or request ID…"><select class="onb-select" id="onb-status"><option>All</option><option>Submitted</option><option>Under Review</option><option>Approved</option><option>Completed</option><option>Rejected</option></select></div>
|
||
<div class="onb-table-wrap"><table class="onb-table"><thead><tr><th>Request</th><th>Organisation</th><th>Contact</th><th>Location</th><th>Status</th><th>Customer match</th><th>Created</th></tr></thead><tbody id="onb-rows"><tr><td class="onb-loading" colspan="7">Loading onboarding requests…</td></tr></tbody></table></div>
|
||
<div class="onb-modal-backdrop" id="onb-modal"><div class="onb-modal"><div class="onb-modal-head"><div><p class="onb-eyebrow">MAKAP request</p><h2 id="onb-modal-title">New onboarding request</h2></div><button class="onb-close" id="onb-close">×</button></div><form id="onb-form"><div class="onb-form-grid"><div class="onb-field"><label>Organisation name *</label><input name="organisation_name" required></div><div class="onb-field"><label>Trading name</label><input name="trading_name"></div><div class="onb-field"><label>Customer type *</label><select name="customer_type" required><option value="Company">Company</option><option value="Individual">Individual</option></select></div><div class="onb-field"><label>Business tax identifier</label><input name="business_tax_identifier"></div><div class="onb-field"><label>Contact name *</label><input name="contact_name" required></div><div class="onb-field"><label>Email address *</label><input name="email_id" type="email" required></div><div class="onb-field"><label>Telephone</label><input name="telephone"></div><div class="onb-field"><label>Location</label><input name="location"></div><div class="onb-field full"><label>Submitted address</label><textarea name="submitted_address"></textarea></div><div class="onb-field full"><label>Product interest</label><textarea name="product_interest"></textarea></div><div class="onb-field full"><label>Initial enquiry</label><textarea name="initial_enquiry"></textarea></div><div class="onb-field"><label>Registration source *</label><select name="registration_source" required><option value="Website">Website</option><option value="Assisted">Assisted</option></select></div><div class="onb-field"><label>Assisted channel</label><input name="assisted_channel"></div><div class="onb-field full"><label><input type="checkbox" name="privacy_notice_acknowledged" value="1" required> Privacy notice acknowledged *</label></div></div><div class="onb-form-actions"><button type="button" class="onb-btn" id="onb-cancel">Cancel</button><button class="onb-btn primary" type="submit">Create request</button></div></form><div id="onb-detail-content"></div></div></div>
|
||
</div>
|
||
<script>
|
||
(function(){const A='/api/method/';const call=(method,args={})=>fetch(A+method,{method:'POST',headers:{'Content-Type':'application/json','X-Frappe-CSRF-Token':window.frappe?.csrf_token||''},body:JSON.stringify(args)}).then(r=>r.json()).then(r=>{if(r.exc)throw Error('Request failed');return r.message});const $=s=>document.querySelector(s);const esc=v=>String(v??'—').replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));let selected='All';const labels={all:'All requests',submitted:'Submitted',under_review:'Under review',approved:'Approved',completed:'Completed',rejected:'Rejected'};function badge(s){let c=s==='Approved'||s==='Completed'?'approved':s==='Under Review'?'review':s==='Rejected'?'rejected':'';return `<span class="onb-badge ${c}">${esc(s||'—')}</span>`}function date(v){return v?new Date(String(v).replace(' ','T')).toLocaleDateString(): '—'}function summary(d){$('#onb-summary').innerHTML=Object.entries(labels).map(([k,l])=>`<div class="onb-card ${selected===(k==='all'?'All':k==='under_review'?'Under Review':k[0].toUpperCase()+k.slice(1))?'active':''}" data-status="${k==='all'?'All':k==='under_review'?'Under Review':k[0].toUpperCase()+k.slice(1)}"><label>${l}</label><strong>${Number(d[k]||0).toLocaleString()}</strong></div>`).join('');document.querySelectorAll('.onb-card').forEach(x=>x.onclick=()=>{selected=x.dataset.status;$('#onb-status').value=selected;load()})}function rows(data){$('#onb-rows').innerHTML=data.length?data.map(x=>`<tr><td class="onb-id" data-id="${esc(x.name)}">${esc(x.name)}</td><td><strong>${esc(x.organisation_name)}</strong><br><span class="onb-muted">${esc(x.trading_name||'')}</span></td><td>${esc(x.contact_name)}<br><span class="onb-muted">${esc(x.email_id)}</span></td><td>${esc(x.location)}</td><td>${badge(x.onboarding_status)}</td><td>${esc(x.customer_match_result)}</td><td>${date(x.creation)}</td></tr>`).join(''):'<tr><td colspan="7" class="onb-empty">No onboarding requests match your filters.</td></tr>';document.querySelectorAll('.onb-id').forEach(x=>x.onclick=()=>detail(x.dataset.id))}async function load(){try{const [s,r]=await Promise.all([call('makap_backend.makap_backend.onboarding_api.get_onboarding_summary'),call('makap_backend.makap_backend.onboarding_api.get_onboarding_requests',{status:$('#onb-status').value,search:$('#onb-search').value})]);summary(s);rows(r)}catch(e){$('#onb-rows').innerHTML='<tr><td colspan="7" class="onb-empty">Unable to load onboarding data. Check your Frappe permissions.</td></tr>'}}function open(){ $('#onb-modal').classList.add('open');$('#onb-form').style.display='block';$('#onb-detail-content').innerHTML='';$('#onb-modal-title').textContent='New onboarding request'}function close(){ $('#onb-modal').classList.remove('open')}async function detail(name){try{const x=await call('makap_backend.makap_backend.onboarding_api.get_onboarding_request',{name});$('#onb-modal-title').textContent=x.organisation_name||x.name;$('#onb-form').style.display='none';$('#onb-detail-content').innerHTML='<div class="onb-detail">'+[['Request',x.name],['Status',x.onboarding_status],['Organisation',x.organisation_name],['Trading name',x.trading_name],['Customer type',x.customer_type],['Tax identifier',x.business_tax_identifier],['Contact',x.contact_name],['Email',x.email_id],['Telephone',x.telephone],['Location',x.location],['Customer match',x.customer_match_result],['Portal access',x.portal_access_status],['Product interest',x.product_interest],['Initial enquiry',x.initial_enquiry],['Review notes',x.review_notes]].map(([a,b])=>`<div><small>${a}</small><span>${esc(b)}</span></div>`).join('')+'</div>';$('#onb-modal').classList.add('open')}catch(e){alert('Unable to open this request')}}$('#onb-new').onclick=open;$('#onb-close').onclick=close;$('#onb-cancel').onclick=close;$('#onb-refresh').onclick=load;$('#onb-status').onchange=()=>{selected=$('#onb-status').value;load()};let timer;$('#onb-search').oninput=()=>{clearTimeout(timer);timer=setTimeout(load,250)};$('#onb-form').onsubmit=async e=>{e.preventDefault();const data=Object.fromEntries(new FormData(e.target));data.privacy_notice_acknowledged=data.privacy_notice_acknowledged?1:0;try{const name=await call('makap_backend.makap_backend.onboarding_api.create_onboarding_request',{data:JSON.stringify(data)});close();await load();detail(name)}catch(err){alert('Unable to create request. Check required fields and permissions.')}};load()})();
|
||
</script>
|
||
{% endblock %}
|