';
}).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)+'
';
}
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':'')+')':'')+'
';}
};
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 '
';
}).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();}
})();