(function(){ var STEPS=["Order placed","Cutting material","Sewing","Shipped","In GVA store"]; var PW="couture2026"; var orders={}; var smsList=[]; var loggedIn=false; function save(){ try{localStorage.setItem('ac_orders',JSON.stringify(orders));}catch(e){} try{localStorage.setItem('ac_sms',JSON.stringify(smsList));}catch(e){} } function load(){ try{var o=localStorage.getItem('ac_orders');if(o)orders=JSON.parse(o);}catch(e){} try{var s=localStorage.getItem('ac_sms');if(s)smsList=JSON.parse(s);}catch(e){} if(!Object.keys(orders).length)seed(); } function seed(){ orders={ "AC-1001":{customer:"Sophie Martin",phone:"+41 79 123 45 67",items:[{ref:"SGEAP001",description:"Bespoke wool suit",fabric:"DAV7601"}],placed:"3 March 2026",eta:"2026-03-28",currentStep:2,steps:[{date:"3 March 2026",note:"Order created."},{date:"8 March 2026",note:"Fabric cut and ready."},{date:null,note:null},{date:null,note:null},{date:null,note:null}]}, }; save(); } function formatEta(iso){ if(!iso)return null; try{var d=new Date(iso);return d.toLocaleDateString('en-GB',{day:'numeric',month:'long',year:'numeric'});}catch(e){return iso;} } function etaStatus(iso){ if(!iso)return ''; var d=new Date(iso),now=new Date(); var diff=Math.ceil((d-now)/(1000*60*60*24)); if(diff<0)return 'En retard'; if(diff===0)return 'Aujourd\'hui'; if(diff<=3)return 'Dans '+diff+' jour'+(diff>1?'s':'')+''; return 'Dans '+diff+' jours'; } function itemsHtml(o){ if(!o.items||!o.items.length)return '
'+o.item+'
'; return o.items.map(function(it){ return '
'+ ''+it.ref+''+ ''+it.description+(it.fabric?' · '+it.fabric+'':'')+''+ '
'; }).join(''); } function showTab(t){ ['customer','admin'].forEach(function(x){document.getElementById('ac-tab-'+x).style.display=x===t?'block':'none';}); document.querySelectorAll('.ac-tab').forEach(function(el){el.classList.toggle('active',el.getAttribute('data-tab')===t);}); if(t==='admin')renderAdminView(); } function doLogin(){ if(document.getElementById('ac-pw').value===PW){ loggedIn=true;document.getElementById('ac-pw').value='';document.getElementById('ac-pw-err').innerHTML='';renderAdminView(); }else{document.getElementById('ac-pw-err').innerHTML='
Incorrect password.
';} } function renderAdminView(){ document.getElementById('ac-admin-lock').style.display=loggedIn?'none':'block'; document.getElementById('ac-admin-panel').style.display=loggedIn?'block':'none'; if(loggedIn){renderOrderList();renderSMS();} } function doTrack(){ var key=document.getElementById('ac-track-input').value.trim().toUpperCase(); var box=document.getElementById('ac-track-result'); var o=orders[key]; if(!o){box.innerHTML='
Commande introuvable. Vérifiez votre numéro de commande.
';return;} var bc=o.currentStep===4?'ac-badge-done':o.currentStep===3?'ac-badge-shipped':'ac-badge-progress'; var bl=o.currentStep===4?'In GVA store':o.currentStep===3?'Shipped':'In progress'; var etaF=formatEta(o.eta); var etaBar=etaF&&o.currentStep<4?'
Estimated delivery:'+etaF+''+etaStatus(o.eta)+'
':''; var tl='
'; STEPS.forEach(function(s,i){ var done=iExpected: '+etaF+etaStatus(o.eta)+'
':''; tl+='
'+ (done?'':'')+ '
'+(last?'':'
')+'
'+ '
'+s+'
'+ (o.steps[i].date?'
'+o.steps[i].date+'
':'')+etaHint+ (o.steps[i].note?'
'+o.steps[i].note+'
':'')+ '
'; }); tl+=''; var itemsBlock='
Articles commandés
'+itemsHtml(o)+'
'; box.innerHTML='
'+ '
'+ '
'+key+'
'+o.customer+'
'+ ''+bl+'
'+ itemsBlock+etaBar+tl+'
'+ '
'+ '
Client'+o.customer+'
'+ '
Order placed'+o.placed+'
'+ (etaF?'
Estimated delivery'+etaF+etaStatus(o.eta)+'
':'')+ '
Articles'+((o.items&&o.items.length)||1)+' article'+(((o.items&&o.items.length)||1)>1?'s':'')+'
'+ '
'; } function createOrder(){ var id=document.getElementById('ac-new-id').value.trim().toUpperCase(); var customer=document.getElementById('ac-new-customer').value.trim(); var item=document.getElementById('ac-new-item').value.trim(); var eta=document.getElementById('ac-new-eta').value; var phone=document.getElementById('ac-new-phone').value.trim(); var msg=document.getElementById('ac-create-msg'); if(!id||!customer||!item||!phone){msg.innerHTML='
Please fill in all required fields.
';return;} if(orders[id]){msg.innerHTML='
Order ID already exists.
';return;} var today=new Date().toLocaleDateString('en-GB',{day:'numeric',month:'long',year:'numeric'}); orders[id]={customer:customer,phone:phone,items:[{ref:id,description:item,fabric:''}],placed:today,eta:eta||null,currentStep:0, steps:[{date:today,note:'Order created.'},{date:null,note:null},{date:null,note:null},{date:null,note:null},{date:null,note:null}]}; addSMS(id,orders[id],0);save();renderOrderList(); msg.innerHTML='
Order '+id+' created.
'; ['ac-new-id','ac-new-customer','ac-new-item','ac-new-phone'].forEach(function(f){document.getElementById(f).value='';}); document.getElementById('ac-new-eta').value=''; setTimeout(function(){msg.innerHTML='';},3500); } function updateOrder(){ var id=document.getElementById('ac-upd-id').value.trim().toUpperCase(); var step=parseInt(document.getElementById('ac-upd-status').value); var note=document.getElementById('ac-upd-note').value.trim(); var eta=document.getElementById('ac-upd-eta').value; var msg=document.getElementById('ac-update-msg'); if(!id){msg.innerHTML='
Please enter an order ID.
';return;} if(!orders[id]){msg.innerHTML='
Order not found.
';return;} var today=new Date().toLocaleDateString('en-GB',{day:'numeric',month:'long',year:'numeric'}); orders[id].currentStep=step;orders[id].steps[step].date=today; if(note)orders[id].steps[step].note=note; if(eta)orders[id].eta=eta; addSMS(id,orders[id],step);save();renderOrderList(); msg.innerHTML='
Order '+id+' updated to "'+STEPS[step]+'".
'; document.getElementById('ac-upd-note').value='';document.getElementById('ac-upd-eta').value=''; setTimeout(function(){msg.innerHTML='';},3500); } /* ---- EXCEL IMPORT avec regroupement par client ---- */ function loadSheetJS(cb){ if(window.XLSX){cb();return;} var s=document.createElement('script'); s.src='https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js'; s.onload=cb;document.head.appendChild(s); } function normalizeClient(name){ return (name||'').toString().trim().toUpperCase().replace(/\s+/g,' '); } function generateACId(clientName){ // Generate AC-XXXX from first letters + timestamp var parts=clientName.trim().split(' '); var initials=parts.map(function(p){return p[0]||'';}).join('').toUpperCase().slice(0,3); var num=Date.now().toString().slice(-4); var id='AC-'+initials+num; // ensure unique while(orders[id]){num=(parseInt(num)+1).toString().padStart(4,'0');id='AC-'+initials+num;} return id; } function parseDate(val){ if(!val)return null; if(val instanceof Date)return val.toISOString().split('T')[0]; var s=val.toString().trim(); if(/^\d{4}-\d{2}-\d{2}/.test(s))return s.slice(0,10); var d=new Date(s); return isNaN(d)?null:d.toISOString().split('T')[0]; } function statusToStep(status){ if(!status)return 0; var s=status.toLowerCase(); if(s.includes('pattern'))return 1; if(s.includes('produc')||s.includes('sew'))return 2; if(s.includes('ship')||s.includes('logist'))return 3; if(s.includes('arriv')||s.includes('pickup')||s.includes('store'))return 4; return 0; } function handleExcelFile(file){ var msg=document.getElementById('ac-import-msg'); msg.innerHTML='
Lecture du fichier en cours...
'; loadSheetJS(function(){ var reader=new FileReader(); reader.onload=function(e){ try{ var wb=window.XLSX.read(e.target.result,{type:'array',cellDates:true}); var ws=wb.Sheets[wb.SheetNames[0]]; var rows=window.XLSX.utils.sheet_to_json(ws,{defval:''}); if(!rows.length){msg.innerHTML='
Le fichier est vide.
';return;} // Group rows by client name var byClient={}; rows.forEach(function(row){ var client=normalizeClient( row['Client Name']||row['client_name']||row['Nom']||row['Client']||'' ); if(!client)return; if(!byClient[client])byClient[client]={rows:[]}; byClient[client].rows.push(row); }); var today=new Date().toLocaleDateString('en-GB',{day:'numeric',month:'long',year:'numeric'}); var imported=0,skipped=0; // Build a lookup: normalized client name -> existing AC order id var existingByClient={}; Object.keys(orders).forEach(function(acId){ var norm=normalizeClient(orders[acId].customer); existingByClient[norm]=acId; }); Object.keys(byClient).forEach(function(clientKey){ var clientRows=byClient[clientKey].rows; var firstRow=clientRows[0]; var customerName=( firstRow['Client Name']||firstRow['client_name']||firstRow['Nom']||firstRow['Client']||clientKey ).toString().trim(); var phone=(firstRow['Phone']||firstRow['phone']||firstRow['Téléphone']||'').toString().trim(); // Earliest eta var etaDates=clientRows.map(function(r){ return parseDate(r['Deal Date']||r['Pick up Date']||r['ETA']||r['Estimated Delivery']||''); }).filter(Boolean).sort(); var eta=etaDates.length?etaDates[0]:null; // Items var items=clientRows.map(function(r){ return { ref:(r['OrderNO.']||r['Order ID']||r['order_id']||'').toString().trim(), description:(r['Clothing Category']||r['Item Description']||r['Article']||'').toString().trim(), fabric:(r['Fabric']||r['fabric']||'').toString().trim() }; }).filter(function(it){return it.description;}); // Most advanced status var maxStep=0; clientRows.forEach(function(r){ var s=statusToStep(r['Status']||r['status']||''); if(s>maxStep)maxStep=s; }); var normClient=normalizeClient(customerName); if(existingByClient[normClient]){ // UPDATE existing order var acId=existingByClient[normClient]; var existing=orders[acId]; var prevStep=existing.currentStep; // Update ETA if(eta)existing.eta=eta; // Update items existing.items=items; // Update status only if more advanced if(maxStep>prevStep){ existing.currentStep=maxStep; existing.steps[maxStep]={date:today,note:'Updated from Excel import.'}; addSMS(acId,existing,maxStep); } imported++; } else { // CREATE new order var acId=generateACId(customerName); var placedDates=clientRows.map(function(r){ return parseDate(r['Create Date']||r['Order Date']||''); }).filter(Boolean).sort(); var placedIso=placedDates.length?placedDates[0]:null; var placedFmt=placedIso?new Date(placedIso).toLocaleDateString('en-GB',{day:'numeric',month:'long',year:'numeric'}):today; var steps=[{date:placedFmt,note:'Imported from Excel. '+items.length+' article'+(items.length>1?'s':'')+'.'},{date:null,note:null},{date:null,note:null},{date:null,note:null},{date:null,note:null}]; if(maxStep>0)steps[maxStep]={date:today,note:'Status at import.'}; orders[acId]={ customer:customerName, phone:phone, items:items, placed:placedFmt, eta:eta, currentStep:maxStep, steps:steps }; imported++; } }); save();renderOrderList(); msg.innerHTML='
'+imported+' commande'+(imported>1?'s':'')+' importée'+(imported>1?'s':'')+' avec succès'+(skipped?' ('+skipped+' ignorée'+(skipped>1?'s':'')+')':'')+'
'; setTimeout(function(){msg.innerHTML='';},6000); }catch(err){msg.innerHTML='
Erreur : '+err.message+'
';} }; reader.readAsArrayBuffer(file); }); } function initDropzone(){ var btn=document.getElementById('ac-btn-import-pick'); var input=document.getElementById('ac-import-file'); var fname=document.getElementById('ac-import-filename'); if(!btn||!input)return; btn.addEventListener('click',function(){input.click();}); input.addEventListener('change',function(){ if(input.files[0]){ if(fname)fname.textContent=input.files[0].name; handleExcelFile(input.files[0]); } }); } /* ---- END EXCEL IMPORT ---- */ function smsText(id,o,step){ var name=o.customer.split(' ')[0]; var etaStr=o.eta?formatEta(o.eta):''; var nbItems=o.items&&o.items.length?o.items.length:1; var itemsStr=nbItems>1?nbItems+' articles':((o.items&&o.items[0]&&o.items[0].description)||o.item||'your order'); if(step===0)return 'Hi '+name+', your order '+id+' has been placed at Anastasi Couture ('+itemsStr+').'+(etaStr?' Estimated delivery: '+etaStr+'.':'')+' Track anytime with your order number.'; if(step===1)return 'Hi '+name+', we have started cutting the fabric for your order '+id+' ('+itemsStr+').'+(etaStr?' Estimated delivery: '+etaStr+'.':''); if(step===2)return 'Hi '+name+', sewing is now underway for your order '+id+' ('+itemsStr+').'+(etaStr?' Estimated delivery: '+etaStr+'.':''); if(step===3)return 'Hi '+name+', your order '+id+' ('+itemsStr+') has been shipped.'+(etaStr?' Expected at our Geneva store: '+etaStr+'.':''); if(step===4)return 'Hi '+name+', your order '+id+' ('+itemsStr+') is ready for pickup at our Geneva store. We look forward to seeing you! \u2014 Anastasi Couture'; } function addSMS(id,o,step){ var now=new Date().toLocaleTimeString('en-GB',{hour:'2-digit',minute:'2-digit'}); var today=new Date().toLocaleDateString('en-GB',{day:'numeric',month:'short',year:'numeric'}); smsList.unshift({id:id,customer:o.customer,phone:o.phone,text:smsText(id,o,step),time:today+' at '+now}); if(smsList.length>100)smsList.pop(); updateSMSCount();save(); } function whatsappUrl(phone, text){ var clean=phone.replace(/[\s\-\(\)]/g,''); if(clean.startsWith('+')){clean=clean.slice(1);} return 'https://wa.me/'+clean+'?text='+encodeURIComponent(text); } function updateSMSCount(){var c=document.getElementById('ac-sms-count');if(c)c.textContent=smsList.length?'('+smsList.length+')':'';} function renderSMS(){ var box=document.getElementById('ac-sms-list');if(!box)return;updateSMSCount(); if(!smsList.length){box.innerHTML='
No messages yet.
';return;} box.innerHTML=smsList.map(function(s,i){ var waUrl=whatsappUrl(s.phone, s.text); return '
'+ '
To: '+s.customer+'  ·  '+s.phone+'
'+ '
'+s.text+'
'+ '
'+ ''+s.time+''+ ''+ ''+ ''+ 'Envoyer WhatsApp'+ ''+ '
'+ '
'; }).join(''); box.querySelectorAll('.ac-copy-btn').forEach(function(btn){ btn.addEventListener('click',function(){ var i=parseInt(btn.getAttribute('data-i')); try{navigator.clipboard.writeText(smsList[i].text);}catch(e){} btn.textContent='Copié !';setTimeout(function(){btn.textContent='Copier';},2000); }); }); } function renderOrderList(){ var box=document.getElementById('ac-order-list');if(!box)return; var keys=Object.keys(orders); if(!keys.length){box.innerHTML='
No orders yet.
';return;} box.innerHTML=keys.map(function(k){ var o=orders[k]; var bc=o.currentStep===4?'ac-badge-done':o.currentStep===3?'ac-badge-shipped':'ac-badge-progress'; var nb=o.items&&o.items.length?o.items.length:1; var lastStep=o.currentStep; var lastMsg=smsText(k,o,lastStep); var waUrl=whatsappUrl(o.phone||'', lastMsg); var waBtn=o.phone?''+ 'WA':''; var etaStr=o.eta?'
ETA: '+formatEta(o.eta)+'
':''; return '
'+ '
'+k+'
'+ '
'+o.customer+' — '+nb+' article'+(nb>1?'s':'')+'
'+etaStr+'
'+ '
'+ ''+STEPS[o.currentStep]+''+ waBtn+ ''+ '
'; }).join(''); box.querySelectorAll('.ac-prefill-btn').forEach(function(btn){ btn.addEventListener('click',function(){ var id=btn.getAttribute('data-id'); document.getElementById('ac-upd-id').value=id; document.getElementById('ac-upd-status').value=orders[id].currentStep; document.getElementById('ac-upd-note').value=''; document.getElementById('ac-upd-eta').value=orders[id].eta||''; document.getElementById('ac-upd-id').scrollIntoView({behavior:'smooth',block:'center'}); }); }); box.querySelectorAll('.ac-del-btn').forEach(function(btn){ btn.addEventListener('click',function(){ var id=btn.getAttribute('data-id'); if(!confirm('Delete order '+id+'? This cannot be undone.'))return; delete orders[id];save();renderOrderList(); }); }); } function init(){ if(!document.getElementById('ac-btn-track')){setTimeout(init,200);return;} document.querySelectorAll('.ac-tab').forEach(function(btn){btn.addEventListener('click',function(){showTab(btn.getAttribute('data-tab'));});}); document.getElementById('ac-btn-track').addEventListener('click',doTrack); document.getElementById('ac-btn-login').addEventListener('click',doLogin); document.getElementById('ac-btn-logout').addEventListener('click',function(){loggedIn=false;renderAdminView();}); document.getElementById('ac-btn-create').addEventListener('click',createOrder); document.getElementById('ac-btn-update').addEventListener('click',updateOrder); document.addEventListener('keydown',function(e){ if(e.key==='Enter'){ if(document.activeElement.id==='ac-track-input')doTrack(); if(document.activeElement.id==='ac-pw')doLogin(); } }); initDropzone(); load();renderOrderList();renderSMS(); } if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',init);}else{init();} })();