window.updateSaveButtonState = window.updateSaveButtonState || function () {};
window.markOrderDirty = window.markOrderDirty || function () {};
(function () {
function initWorkspace() {
const wrap = document.getElementById('ass-client-workspace');
if (!wrap) return;
const ajaxUrl = wrap.dataset.ajaxUrl;
const nonce = wrap.dataset.nonce;
const screen = wrap.dataset.screen || 'workspace';
const initialOrderId = parseInt(wrap.dataset.orderId || '0', 10);
const initialOrderMode = ['view', 'edit', 'repeat', 'new'].includes(wrap.dataset.orderMode || 'edit') ? (wrap.dataset.orderMode || 'edit') : 'edit';
const initialCustomerId = parseInt(wrap.dataset.customerId || '0', 10);
const workspaceBaseUrl = wrap.dataset.workspaceUrl || (window.location.origin + window.location.pathname);
const workspaceScreen = document.getElementById('asscw-workspace-screen');
const orderScreen = document.getElementById('asscw-order-screen');
const orderScreenContent = document.getElementById('asscw-order-screen-content');
const orderTitle = document.getElementById('asscw-order-title');
const saveOrderButton = document.getElementById('asscw-save-order-button');
const noticeBox = document.getElementById('asscw-notice');
const backToCustomerBtn = document.getElementById('asscw-back-to-customer-button');
const backToWorkspaceLink = document.getElementById('asscw-back-to-workspace-link');
const input = document.getElementById('asscw-search-input');
const clearBtn = document.getElementById('asscw-clear-search');
const resultsBox = document.getElementById('asscw-search-results');
const detailBox = document.getElementById('asscw-client-detail');
const ordersBox = document.getElementById('asscw-orders');
const selectedClientBar = document.getElementById('asscw-selected-client-bar');
const selectedClientName = document.getElementById('asscw-selected-client-name');
const changeClientBtn = document.getElementById('asscw-change-client-btn');
let timer = null;
let productTimer = null;
let selectedCustomerId = 0;
let selectedCustomerLabel = '';
let keyboardIndex = -1;
let currentOrderData = null;
let currentOrderMode = initialOrderMode;
let currentCustomerId = initialCustomerId || 0;
let orderHasUnsavedChanges = false;
let isSavingOrder = false;
let productSearchRequestId = 0;
let currentProductPreviewId = 0;
let clientSearchRequestId = 0;
let lastSearchTerm = '';
let lastSearchResults = [];
const searchStateStorageKey = 'asscw_last_search_state';
const defaultDetailHtml = 'Sélectionne un client pour afficher sa fiche.';
const defaultOrdersHtml = 'Aucune commande à afficher.';
function escapeHtml(value) {
return String(value || '')
.replace(/&/g, '&')
.replace(//g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
function showNotice(message, type) {
if (!noticeBox) return;
noticeBox.className = 'asscw-notice ' + (type === 'error' ? 'is-error' : 'is-success');
noticeBox.textContent = message || '';
noticeBox.style.display = 'block';
window.clearTimeout(showNotice._timer);
showNotice._timer = window.setTimeout(function () {
noticeBox.style.display = 'none';
}, 5000);
}
function updateSaveButtonState() {
if (!saveOrderButton) return;
if (currentOrderMode === 'view' || !currentOrderData) {
saveOrderButton.style.display = 'none';
return;
}
saveOrderButton.style.display = 'inline-flex';
if (isSavingOrder) {
saveOrderButton.disabled = true;
saveOrderButton.textContent = 'Enregistrement...';
return;
}
saveOrderButton.disabled = !orderHasUnsavedChanges;
if (currentOrderMode === 'repeat' || currentOrderMode === 'new') {
saveOrderButton.textContent = orderHasUnsavedChanges ? 'Créer la commande' : 'Enregistré';
return;
}
saveOrderButton.textContent = orderHasUnsavedChanges ? 'Enregistrer' : 'Enregistré';
}
function markOrderDirty() {
if (currentOrderMode === 'view') return;
orderHasUnsavedChanges = true;
updateSaveButtonState();
}
window.updateSaveButtonState = updateSaveButtonState;
window.markOrderDirty = markOrderDirty;
function post(action, payload) {
const form = new URLSearchParams();
form.append('action', action);
form.append('nonce', nonce);
Object.keys(payload || {}).forEach(function (key) {
const value = payload[key];
if (typeof value === 'object') {
form.append(key, JSON.stringify(value));
} else {
form.append(key, value);
}
});
return fetch(ajaxUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
body: form.toString(),
credentials: 'same-origin'
}).then(async function (response) {
const text = await response.text();
try {
return JSON.parse(text);
} catch (e) {
throw new Error('La requête a renvoyé une réponse invalide.');
}
});
}
function buildCustomerUrl(customerId) {
const url = new URL(workspaceBaseUrl, window.location.origin);
url.searchParams.delete('asscw_screen');
url.searchParams.delete('asscw_order');
url.searchParams.delete('asscw_mode');
url.searchParams.delete('asscw_customer');
if (customerId) {
url.searchParams.set('asscw_customer', String(customerId));
}
return url.toString();
}
function buildWorkspaceUrl() {
const url = new URL(workspaceBaseUrl, window.location.origin);
url.searchParams.delete('asscw_screen');
url.searchParams.delete('asscw_order');
url.searchParams.delete('asscw_mode');
url.searchParams.delete('asscw_customer');
return url.toString();
}
function updateWorkspaceUrl(customerId) {
window.history.replaceState({}, '', customerId ? buildCustomerUrl(customerId) : buildWorkspaceUrl());
}
function updateClearButton() {
if (!clearBtn || !input) return;
clearBtn.style.display = input.value.trim().length ? 'block' : 'none';
}
function showResultsBox() {
if (resultsBox) resultsBox.style.display = 'block';
}
function hideResultsBox() {
if (resultsBox) resultsBox.style.display = 'none';
}
function showSelectedClientBar(label) {
if (!selectedClientBar || !selectedClientName) return;
selectedClientName.textContent = label || '';
selectedClientBar.style.display = 'flex';
}
function hideSelectedClientBar() {
if (!selectedClientBar || !selectedClientName) return;
selectedClientName.textContent = '';
selectedClientBar.style.display = 'none';
}
function getResultItems() {
return Array.from(resultsBox.querySelectorAll('.asscw-result-item[data-customer-id]'));
}
function resetKeyboardSelection() {
keyboardIndex = -1;
clearSelectionHighlight();
}
function applyKeyboardHighlight() {
const items = getResultItems();
items.forEach(function (item, index) {
if (index === keyboardIndex) {
item.classList.add('asscw-selected');
item.scrollIntoView({ block: 'nearest' });
} else if (parseInt(item.getAttribute('data-customer-id'), 10) !== parseInt(selectedCustomerId, 10)) {
item.classList.remove('asscw-selected');
}
});
}
function setKeyboardIndex(index) {
const items = getResultItems();
if (!items.length) { keyboardIndex = -1; return; }
if (index < 0) index = items.length - 1;
if (index >= items.length) index = 0;
keyboardIndex = index;
applyKeyboardHighlight();
}
function clearSelectionHighlight() {
resultsBox.querySelectorAll('.asscw-result-item.asscw-selected').forEach(function (item) {
item.classList.remove('asscw-selected');
});
}
function highlightSelectedCustomer() {
clearSelectionHighlight();
if (!selectedCustomerId) return;
const item = resultsBox.querySelector('.asscw-result-item[data-customer-id="' + selectedCustomerId + '"]');
if (item) item.classList.add('asscw-selected');
}
function resetWorkspace() {
selectedCustomerId = 0;
selectedCustomerLabel = '';
clearLastSearchState();
resultsBox.innerHTML = '';
showResultsBox();
hideSelectedClientBar();
resetKeyboardSelection();
detailBox.innerHTML = '
' + defaultDetailHtml + '
';
ordersBox.innerHTML = '' + defaultOrdersHtml + '
';
updateClearButton();
updateWorkspaceUrl(0);
updateBackButtons(0);
}
function switchToWorkspace() {
if (workspaceScreen) workspaceScreen.style.display = '';
if (orderScreen) orderScreen.style.display = 'none';
}
function switchToOrderScreen() {
if (workspaceScreen) workspaceScreen.style.display = 'none';
if (orderScreen) orderScreen.style.display = '';
}
function updateBackButtons(customerId) {
currentCustomerId = parseInt(customerId || '0', 10) || 0;
if (backToCustomerBtn) backToCustomerBtn.style.display = currentCustomerId ? '' : 'none';
if (backToWorkspaceLink) backToWorkspaceLink.href = buildWorkspaceUrl();
}
function renderStockBadge(product) {
const status = product && product.stock_status ? product.stock_status : '';
if (!status) return '';
const labelMap = { instock: 'En stock', onbackorder: 'Sur commande', outofstock: 'Rupture' };
return '' + escapeHtml(labelMap[status] || status) + '';
}
function renderProductPreviewButton(product) {
if (!product || !product.id) return '';
return '';
}
function renderProductThumb(product, extraClass) {
if (!product) return '';
const className = extraClass ? 'asscw-product-thumb ' + extraClass : 'asscw-product-thumb';
const productId = escapeHtml(product.id || product.product_id || '');
if (product.image_url) {
return '';
}
return '';
}
function renderConditioningBadge(product) {
if (!product || !product.conditioning_label) return '';
return '' + escapeHtml(product.conditioning_label) + '
';
}
function getProductPreviewHost() {
const workspaceHost = document.getElementById('asscw-workspace-product-preview-host');
const orderHost = document.getElementById('asscw-product-preview-host') || orderScreenContent;
const isWorkspaceVisible = workspaceScreen && workspaceScreen.style.display !== 'none';
if (isWorkspaceVisible && workspaceHost) return workspaceHost;
return orderHost || workspaceHost || null;
}
function isLandscapeTabletLayout() {
return window.matchMedia('(min-width: 981px)').matches && window.matchMedia('(orientation: landscape)').matches;
}
function syncProductPreviewLayout() {
const panel = document.getElementById('asscw-product-preview-panel');
if (!panel) return;
panel.classList.toggle('is-side-drawer', isLandscapeTabletLayout());
panel.classList.toggle('is-bottom-sheet', !isLandscapeTabletLayout());
}
function closeProductPreview() {
const preview = document.getElementById('asscw-product-preview-panel');
if (preview) preview.remove();
const orderLayout = document.querySelector('.asscw-order-layout');
if (orderLayout) orderLayout.classList.remove('has-product-preview');
currentProductPreviewId = 0;
}
function renderProductPreview(product) {
closeProductPreview();
if (!product) return;
currentProductPreviewId = parseInt(product.id || '0', 10) || 0;
const attributes = Array.isArray(product.attributes) ? product.attributes : [];
const qtyText = (product.stock_qty === null || typeof product.stock_qty === 'undefined') ? '' : String(product.stock_qty);
const imageHtml = product.image_url ? '' : '';
const links = [
product.permalink ? 'Voir la page' : '',
product.edit_url ? 'Éditer' : ''
].filter(Boolean).join(' ');
const panel = document.createElement('div');
panel.id = 'asscw-product-preview-panel';
panel.className = 'asscw-panel asscw-product-preview-panel';
panel.innerHTML = '' +
imageHtml +
'' +
'
' + escapeHtml(product.name || '') + '
' +
(product.sku ? '
SKU : ' + escapeHtml(product.sku) + '
' : '') +
'
' +
(attributes.length ? '
' + attributes.map(function(attr){ return '
' + escapeHtml(attr.label || '') + ' : ' + escapeHtml(attr.value || '') + '
'; }).join('') + '
' : '') +
(product.short_description ? '
Résumé :
' + escapeHtml(product.short_description) + '
' : '') +
(product.description ? '
Description :
' + escapeHtml(product.description) + '
' : '') +
(links ? '
' + links + '
' : '') +
'
';
const host = getProductPreviewHost();
if (!host) return;
host.appendChild(panel);
const orderLayout = document.querySelector('.asscw-order-layout');
if (orderLayout) orderLayout.classList.add('has-product-preview');
syncProductPreviewLayout();
const closeBtn = panel.querySelector('.asscw-product-preview-close');
if (closeBtn) closeBtn.addEventListener('click', closeProductPreview);
}
function openProductPreview(productId) {
productId = parseInt(productId || '0', 10) || 0;
if (!productId) return;
if (currentProductPreviewId === productId) {
closeProductPreview();
return;
}
closeProductPreview();
const loading = document.createElement('div');
loading.id = 'asscw-product-preview-panel';
loading.className = 'asscw-panel asscw-product-preview-panel';
loading.innerHTML = 'Chargement de la fiche produit...
';
const host = getProductPreviewHost();
if (!host) return;
host.appendChild(loading);
const orderLayout = document.querySelector('.asscw-order-layout');
if (orderLayout) orderLayout.classList.add('has-product-preview');
syncProductPreviewLayout();
post('ass_get_product_detail', { product_id: productId })
.then(function(data){
if (!data || !data.success) throw new Error(data && data.data && data.data.message ? data.data.message : 'Erreur de chargement produit.');
renderProductPreview(data.data.product || null);
})
.catch(function(error){
const panel = document.getElementById('asscw-product-preview-panel');
if (panel) panel.innerHTML = '' + escapeHtml(error.message || 'Erreur de chargement produit.') + '
';
currentProductPreviewId = productId;
});
}
function bindProductPreviewButtons(scope) {
if (!scope) return;
scope.querySelectorAll('.asscw-view-product-button').forEach(function(button){
button.addEventListener('click', function(){
openProductPreview(button.getAttribute('data-product-id'));
});
});
}
function persistLastSearchState() {
try {
window.sessionStorage.setItem(searchStateStorageKey, JSON.stringify({
term: lastSearchTerm || '',
results: Array.isArray(lastSearchResults) ? lastSearchResults : [],
selected_customer_id: selectedCustomerId || 0,
selected_customer_label: selectedCustomerLabel || ''
}));
} catch (error) {}
}
function hydrateLastSearchState() {
try {
const raw = window.sessionStorage.getItem(searchStateStorageKey);
if (!raw) return;
const parsed = JSON.parse(raw);
if (parsed && typeof parsed.term === 'string') {
lastSearchTerm = parsed.term;
}
if (parsed && Array.isArray(parsed.results)) {
lastSearchResults = parsed.results;
}
if (!selectedCustomerId && parsed && parsed.selected_customer_id) {
selectedCustomerId = parseInt(parsed.selected_customer_id, 10) || 0;
}
if (!selectedCustomerLabel && parsed && typeof parsed.selected_customer_label === 'string') {
selectedCustomerLabel = parsed.selected_customer_label;
}
} catch (error) {}
}
function clearLastSearchState() {
lastSearchTerm = '';
lastSearchResults = [];
try {
window.sessionStorage.removeItem(searchStateStorageKey);
} catch (error) {}
}
function renderSearchResults(items) {
resetKeyboardSelection();
showResultsBox();
if (!items || !items.length) {
resultsBox.innerHTML = 'Aucun client trouvé.
';
return;
}
resultsBox.innerHTML = items.map(function (item) {
const selectedClass = parseInt(item.id, 10) === parseInt(selectedCustomerId, 10) ? ' asscw-selected' : '';
return `
${escapeHtml(item.label)}
${item.contact_name ? 'Contact : ' + escapeHtml(item.contact_name) + ' · ' : ''}
${item.phone ? 'Tél : ' + escapeHtml(item.phone) + ' · ' : ''}
${item.email ? 'Email : ' + escapeHtml(item.email) : ''}
`;
}).join('');
}
function renderFrequentProducts(products) {
if (!products || !products.length) return '';
return `
Produits fréquents
${products.map(function (product) {
return `
${renderProductThumb(product, 'is-inline')}${renderProductPreviewButton(product)}${renderConditioningBadge(product)}
`;
}).join('')}
`;
}
function buildClassificationHtml(workspaceDetail, customer) {
const master = workspaceDetail && workspaceDetail.master ? workspaceDetail.master : null;
const parts = [];
if (master) {
if (master.category_label) parts.push(master.category_label);
if (master.subcategory_label) parts.push(master.subcategory_label);
if (master.subsubcategory_label) parts.push(master.subsubcategory_label);
}
if (!parts.length) {
if (customer && customer.customer_category) parts.push(customer.customer_category);
if (customer && customer.customer_subcategory) parts.push(customer.customer_subcategory);
}
if (!parts.length) {
return '—';
}
return escapeHtml(parts.join(' → '));
}
function buildCustomerTabsNav() {
return `
`;
}
function bindCustomerTabs(defaultTab) {
const tabWrap = document.getElementById('asscw-customer-tabs');
if (!tabWrap) return;
const buttons = Array.from(tabWrap.querySelectorAll('.asscw-tab-button'));
const panels = Array.from(document.querySelectorAll('.asscw-tab-panel'));
const firstTab = defaultTab || 'summary';
function activate(tabName) {
buttons.forEach(function (button) {
button.classList.toggle('is-active', button.getAttribute('data-tab') === tabName);
});
panels.forEach(function (panel) {
panel.style.display = panel.getAttribute('data-tab-panel') === tabName ? '' : 'none';
});
}
buttons.forEach(function (button) {
button.addEventListener('click', function () {
activate(button.getAttribute('data-tab'));
});
});
activate(firstTab);
}
function buildEditableContactRowsHtml(contacts) {
if (!contacts || !contacts.length) {
return 'Aucun contact opérationnel.
';
}
return `
${contacts.map(function (contact) {
const id = parseInt(contact.id || '0', 10) || 0;
return `
`;
}).join('')}
`;
}
function buildEditableAddressRowsHtml(addresses) {
if (!addresses || !addresses.length) {
return 'Aucune adresse de livraison.
';
}
return `
${addresses.map(function (address) {
const id = parseInt(address.id || '0', 10) || 0;
return `
`;
}).join('')}
`;
}
function buildAddContactFormHtml() {
return `
`;
}
function buildAddAddressFormHtml() {
return `
`;
}
function renderCustomerDetail(customer, frequentProducts, workspaceDetail, activeTab) {
const billingLines = (customer.billing_lines || []).map(function (line) {
return '' + escapeHtml(line) + '';
}).join('');
const shippingLines = (customer.shipping_lines || []).map(function (line) {
return '' + escapeHtml(line) + '';
}).join('');
const pricelist = customer.pricelist_label || '';
const category = customer.customer_category || '';
const subcategory = customer.customer_subcategory || '';
const shippingAddresses = Array.isArray(customer.shipping_addresses) ? customer.shipping_addresses : [];
const workspaceContacts = workspaceDetail && Array.isArray(workspaceDetail.contacts) ? workspaceDetail.contacts : [];
const workspaceAddresses = workspaceDetail && Array.isArray(workspaceDetail.addresses) ? workspaceDetail.addresses : [];
let shippingSelectorHtml = '';
if (shippingAddresses.length > 0) {
shippingSelectorHtml = `
`;
}
detailBox.innerHTML = `
${buildCustomerTabsNav()}
Client
${escapeHtml(customer.label || '')}
Contact
${escapeHtml(customer.contact_name || '') || '—'}
Téléphone
${escapeHtml(customer.phone || '') || '—'}
Email
${escapeHtml(customer.email || '') || '—'}
TVA
${escapeHtml(customer.vat_number || '') || '—'}
ID Odoo
${escapeHtml(customer.odoo_partner_id || '') || '—'}
Source
${escapeHtml(customer.client_source || '') || '—'}
Dernière synchro
${escapeHtml(customer.odoo_last_sync || '') || '—'}
Tarif
${escapeHtml(pricelist) || '—'}
Catégorie
${escapeHtml(category) || '—'}
Sous-catégorie
${escapeHtml(subcategory) || '—'}
Classification
${buildClassificationHtml(workspaceDetail, customer)}
Adresse facturation
${billingLines ? '
' : '
—'}
Adresse livraison par défaut
${shippingLines ? '
' : '
—'}
${shippingSelectorHtml}
${renderFrequentProducts(frequentProducts)}
Contacts opérationnels
${buildEditableContactRowsHtml(workspaceContacts)}
${buildAddContactFormHtml()}
Adresses de livraison
${buildEditableAddressRowsHtml(workspaceAddresses)}
${buildAddAddressFormHtml()}
`;
bindCustomerTabs(activeTab || 'summary');
const createBtn = document.getElementById('asscw-create-order-button');
bindProductPreviewButtons(detailBox);
if (createBtn) {
createBtn.addEventListener('click', function () {
let selectedAddressId = '';
const selectedAddressInput = document.querySelector('input[name="asscw_shipping_address"]:checked');
if (selectedAddressInput) {
selectedAddressId = selectedAddressInput.value || '';
}
openNewOrderScreen(customer, frequentProducts, selectedAddressId);
});
}
const addContactBtn = document.getElementById('asscw-add-contact-button');
if (addContactBtn) {
addContactBtn.addEventListener('click', function () {
post('ass_add_customer_contact', {
customer_id: customer.id,
contact_type: document.getElementById('asscw-new-contact-type').value || 'other',
first_name: document.getElementById('asscw-new-contact-first-name').value || '',
last_name: document.getElementById('asscw-new-contact-last-name').value || '',
job_title: document.getElementById('asscw-new-contact-job-title').value || '',
email: document.getElementById('asscw-new-contact-email').value || '',
phone: document.getElementById('asscw-new-contact-phone').value || '',
is_primary: document.getElementById('asscw-new-contact-primary').checked ? 1 : 0
})
.then(function (data) {
if (!data || !data.success) {
throw new Error(data && data.data && data.data.message ? data.data.message : 'Erreur lors de l’ajout du contact.');
}
showNotice(data.data.message || 'Contact ajouté.', 'success');
renderCustomerDetail(customer, frequentProducts, data.data.workspace_detail || workspaceDetail, 'contacts');
})
.catch(function (error) {
showNotice(error.message || 'Erreur lors de l’ajout du contact.', 'error');
});
});
}
detailBox.querySelectorAll('.asscw-save-contact-button').forEach(function (button) {
button.addEventListener('click', function () {
const contactId = parseInt(button.getAttribute('data-contact-id'), 10);
const box = button.closest('[data-contact-id]');
if (!contactId || !box) return;
post('ass_update_customer_contact', {
customer_id: customer.id,
contact_id: contactId,
contact_type: (box.querySelector('.asscw-edit-contact-type') || {}).value || 'other',
first_name: (box.querySelector('.asscw-edit-contact-first-name') || {}).value || '',
last_name: (box.querySelector('.asscw-edit-contact-last-name') || {}).value || '',
job_title: (box.querySelector('.asscw-edit-contact-job-title') || {}).value || '',
email: (box.querySelector('.asscw-edit-contact-email') || {}).value || '',
phone: (box.querySelector('.asscw-edit-contact-phone') || {}).value || '',
is_primary: (box.querySelector('.asscw-edit-contact-primary') || {}).checked ? 1 : 0
})
.then(function (data) {
if (!data || !data.success) {
throw new Error(data && data.data && data.data.message ? data.data.message : 'Erreur lors de la mise à jour du contact.');
}
showNotice(data.data.message || 'Contact mis à jour.', 'success');
renderCustomerDetail(customer, frequentProducts, data.data.workspace_detail || workspaceDetail, 'contacts');
})
.catch(function (error) {
showNotice(error.message || 'Erreur lors de la mise à jour du contact.', 'error');
});
});
});
const addAddressBtn = document.getElementById('asscw-add-address-button');
if (addAddressBtn) {
addAddressBtn.addEventListener('click', function () {
post('ass_add_delivery_address', {
customer_id: customer.id,
label: document.getElementById('asscw-new-address-label').value || '',
company: document.getElementById('asscw-new-address-company').value || '',
attention_to: document.getElementById('asscw-new-address-attention').value || '',
address_1: document.getElementById('asscw-new-address-1').value || '',
address_2: document.getElementById('asscw-new-address-2').value || '',
postcode: document.getElementById('asscw-new-address-postcode').value || '',
city: document.getElementById('asscw-new-address-city').value || '',
country_code: document.getElementById('asscw-new-address-country').value || '',
phone: document.getElementById('asscw-new-address-phone').value || '',
email: document.getElementById('asscw-new-address-email').value || '',
instructions: document.getElementById('asscw-new-address-instructions').value || '',
is_default: document.getElementById('asscw-new-address-default').checked ? 1 : 0
})
.then(function (data) {
if (!data || !data.success) {
throw new Error(data && data.data && data.data.message ? data.data.message : 'Erreur lors de l’ajout de l’adresse.');
}
showNotice(data.data.message || 'Adresse ajoutée.', 'success');
renderCustomerDetail(customer, frequentProducts, data.data.workspace_detail || workspaceDetail, 'addresses');
})
.catch(function (error) {
showNotice(error.message || 'Erreur lors de l’ajout de l’adresse.', 'error');
});
});
}
detailBox.querySelectorAll('.asscw-save-address-button').forEach(function (button) {
button.addEventListener('click', function () {
const addressId = parseInt(button.getAttribute('data-address-id'), 10);
const box = button.closest('[data-address-id]');
if (!addressId || !box) return;
post('ass_update_delivery_address', {
customer_id: customer.id,
address_id: addressId,
label: (box.querySelector('.asscw-edit-address-label') || {}).value || '',
company: (box.querySelector('.asscw-edit-address-company') || {}).value || '',
attention_to: (box.querySelector('.asscw-edit-address-attention') || {}).value || '',
address_1: (box.querySelector('.asscw-edit-address-1') || {}).value || '',
address_2: (box.querySelector('.asscw-edit-address-2') || {}).value || '',
postcode: (box.querySelector('.asscw-edit-address-postcode') || {}).value || '',
city: (box.querySelector('.asscw-edit-address-city') || {}).value || '',
country_code: (box.querySelector('.asscw-edit-address-country') || {}).value || '',
phone: (box.querySelector('.asscw-edit-address-phone') || {}).value || '',
email: (box.querySelector('.asscw-edit-address-email') || {}).value || '',
instructions: (box.querySelector('.asscw-edit-address-instructions') || {}).value || '',
is_default: (box.querySelector('.asscw-edit-address-default') || {}).checked ? 1 : 0
})
.then(function (data) {
if (!data || !data.success) {
throw new Error(data && data.data && data.data.message ? data.data.message : 'Erreur lors de la mise à jour de l’adresse.');
}
showNotice(data.data.message || 'Adresse mise à jour.', 'success');
renderCustomerDetail(customer, frequentProducts, data.data.workspace_detail || workspaceDetail, 'addresses');
})
.catch(function (error) {
showNotice(error.message || 'Erreur lors de la mise à jour de l’adresse.', 'error');
});
});
});
}
function openNewOrderScreen(customer, frequentProducts, selectedAddressId) {
if (!customer || !customer.id) {
showNotice('Client invalide.', 'error');
return;
}
const payload = {
order_id: 0,
order_number: 'Nouveau',
status: 'Brouillon local',
status_key: 'local-new',
currency: 'EUR',
date_created: '',
customer_id: customer.id,
customer: customer,
frequent_products: frequentProducts || [],
items: [],
fees: [],
shipping: [],
discount_total: '',
total_tax: '',
total: '',
total_amount: 0,
customer_note: '',
selected_shipping_address_id: selectedAddressId || ''
};
switchToOrderScreen();
renderOrderScreen(payload, 'new');
}
function bindRepeatButtons() {
ordersBox.querySelectorAll('.asscw-repeat-order-button').forEach(function (button) {
button.addEventListener('click', function () {
const orderId = parseInt(button.getAttribute('data-order-id'), 10);
if (!orderId) {
showNotice('Commande invalide.', 'error');
return;
}
button.disabled = true;
button.textContent = 'Reprise...';
persistLastSearchState();
const url = new URL(workspaceBaseUrl, window.location.origin);
url.searchParams.set('asscw_screen', 'order');
url.searchParams.set('asscw_order', String(orderId));
url.searchParams.set('asscw_mode', 'repeat');
if (selectedCustomerId) url.searchParams.set('asscw_customer', String(selectedCustomerId));
window.location.href = url.toString();
});
});
}
function renderOrders(orders) {
if (!orders || !orders.length) {
ordersBox.innerHTML = 'Aucune commande trouvée pour ce client.
';
return;
}
ordersBox.innerHTML = `
| Commande | Date | Statut | Actions |
${orders.map(function (order) {
const viewUrl = order.frontend_view_url || order.view_url || order.edit_url || '#';
return `
| #${escapeHtml(order.number || '')} |
${escapeHtml(order.date || '')} |
${escapeHtml(order.status || '')} |
|
`;
}).join('')}
`;
bindRepeatButtons();
}
function loadCustomer(customerId, options) {
options = options || {};
selectedCustomerId = customerId;
highlightSelectedCustomer();
detailBox.innerHTML = 'Chargement du client...
';
ordersBox.innerHTML = 'Chargement des commandes...
';
if (!options.keepResultsOpen) {
hideResultsBox();
}
post('ass_get_client_detail', { customer_id: customerId, workspace_url: workspaceBaseUrl })
.then(function (data) {
if (!data || !data.success) throw new Error(data && data.data && data.data.message ? data.data.message : 'Erreur de chargement.');
selectedCustomerLabel = (data.data.customer && data.data.customer.label) ? data.data.customer.label : selectedCustomerLabel;
if (selectedCustomerLabel) {
input.value = selectedCustomerLabel;
showSelectedClientBar(selectedCustomerLabel);
}
renderCustomerDetail(
data.data.customer,
data.data.frequent_products || [],
data.data.workspace_detail || null,
options.activeTab || 'summary'
);
renderOrders(data.data.orders || []);
persistLastSearchState();
highlightSelectedCustomer();
updateBackButtons(customerId);
if (!options.skipHistory) updateWorkspaceUrl(customerId);
})
.catch(function (error) {
showResultsBox();
detailBox.innerHTML = '' + escapeHtml(error.message || 'Erreur de chargement.') + '
';
ordersBox.innerHTML = 'Impossible de charger les commandes.
';
showNotice(error.message || 'Erreur de chargement du client.', 'error');
});
}
function renderOrderScreen(order, mode) {
currentOrderData = order;
currentOrderMode = ['view', 'edit', 'repeat', 'new'].includes(mode) ? mode : 'edit';
updateBackButtons(order.customer_id || 0);
const isReadOnly = currentOrderMode === 'view';
const isRepeatMode = currentOrderMode === 'repeat';
const isNewMode = currentOrderMode === 'new';
if (orderTitle) orderTitle.innerHTML = (isNewMode ? 'Nouvelle commande' : (isRepeatMode ? 'Reprise de la commande #' : 'Commande #') + escapeHtml(order.order_number || '')) + ' ' + escapeHtml(order.status || '') + '';
orderHasUnsavedChanges = false;
isSavingOrder = false;
updateSaveButtonState();
const customer = order.customer || {};
const billingLines = (customer.billing_lines || []).map(function (line) { return '' + escapeHtml(line) + ''; }).join('');
const shippingLines = (customer.shipping_lines || []).map(function (line) { return '' + escapeHtml(line) + ''; }).join('');
orderScreenContent.innerHTML = `
Lignes de commande
| Produit | Qté | Total ligne | ${isReadOnly ? '' : 'Action | '}
${(order.items || []).map(function (item) {
const metaHtml = (item.meta_lines || []).length ? '' + item.meta_lines.map(function (line) { return escapeHtml(line); }).join('
') + '
' : '';
return `
${renderProductThumb({id: item.product_id || '', image_url: item.image_url || '', name: item.name || ''}, 'is-inline')} ${escapeHtml(item.name || '')} ${item.sku ? ' SKU : ' + escapeHtml(item.sku) + ' ' : ''}${renderConditioningBadge(item)}${metaHtml} |
${isReadOnly ? escapeHtml(item.qty) : ''} |
${item.line_total || ''} |
${isReadOnly ? '' : '' + renderProductPreviewButton({id: item.product_id || ''}) + ' | '}
`;
}).join('')}
${!isReadOnly && order.frequent_products && order.frequent_products.length ? `
Produits fréquents du client
${order.frequent_products.map(function (product) {
return `
${renderProductThumb(product, 'is-inline') }${renderProductPreviewButton(product)}${renderConditioningBadge(product)}
`;
}).join('')}
` : ''}
${isReadOnly ? '' : '
Ajouter un produit simple
'}
Informations commande
Numéro :
${isNewMode ? '-' : '#' + escapeHtml(order.order_number || '')}
Date :
${escapeHtml(order.date_created || '') || '-'}
Total :${order.total || ''}${(typeof order.total_amount === 'number' && order.total_amount < 250) ? '
+ frais de port
' : ''}
Taxes :
${order.total_tax || ''}
Bonus acquis (avoir) :
-
Statut :
${escapeHtml(order.status || '')}
Client
${escapeHtml(customer.label || '')}
${customer.contact_name ? '
Contact : ' + escapeHtml(customer.contact_name) + '
' : ''}${customer.phone ? '
Téléphone : ' + escapeHtml(customer.phone) + '
' : ''}${customer.email ? '
Email : ' + escapeHtml(customer.email) + '
' : ''}
Facturation
${billingLines ? '
' : '
—'}
Livraison
${shippingLines ? '
' : '
—'}
${(order.fees || []).length ? '
Frais
' + order.fees.map(function (fee) { return '
' + escapeHtml(fee.name || '') + ' : ' + fee.total + '
'; }).join('') + '
' : ''}
${(order.shipping || []).length ? '
Expédition
' + order.shipping.map(function (row) { return '
' + escapeHtml(row.name || '') + ' : ' + row.total + '
'; }).join('') + '
' : ''}
Note client
${isReadOnly ? ((order.customer_note && order.customer_note.length) ? '
' + escapeHtml(order.customer_note) + '
' : '
Aucune note.') : '
'}
`;
if (!isReadOnly) bindOrderEditorEvents();
}
function bindOrderEditorEvents() {
orderScreenContent.querySelectorAll('.asscw-item-qty, .asscw-new-product-qty, #asscw-order-note').forEach(function (field) {
field.addEventListener('input', markOrderDirty);
field.addEventListener('change', markOrderDirty);
});
orderScreenContent.querySelectorAll('.asscw-remove-item-button').forEach(function (button) {
button.addEventListener('click', function () {
const itemId = parseInt(button.getAttribute('data-item-id'), 10);
if (!itemId) return;
const row = orderScreenContent.querySelector('tr[data-item-id="' + itemId + '"]');
if (row) {
const qtyInput = row.querySelector('.asscw-item-qty');
if (qtyInput) qtyInput.value = '0';
row.style.opacity = '0.45';
markOrderDirty();
}
});
});
bindProductPreviewButtons(orderScreenContent);
orderScreenContent.querySelectorAll('.asscw-frequent-product').forEach(function (button) {
button.addEventListener('click', function () {
appendNewProductRow({ product_id: parseInt(button.getAttribute('data-product-id'), 10), name: button.getAttribute('data-product-name') || '', sku: button.getAttribute('data-product-sku') || '', price_html: button.getAttribute('data-price-html') || '', image_url: button.getAttribute('data-product-image') || '', conditioning_label: button.getAttribute('data-conditioning-label') || '' });
markOrderDirty();
});
});
const productInput = document.getElementById('asscw-product-search-input');
const productResults = document.getElementById('asscw-product-results');
if (productInput && productResults) {
productInput.addEventListener('input', function () {
const term = productInput.value.trim();
clearTimeout(productTimer);
if (term.length < 2) { productResults.innerHTML = '';
markOrderDirty(); return; }
const requestId = ++productSearchRequestId;
productTimer = setTimeout(function () {
productResults.innerHTML = 'Recherche...
';
post('ass_search_products', { term: term })
.then(function (data) {
if (requestId !== productSearchRequestId) return;
if (!data || !data.success) throw new Error(data && data.data && data.data.message ? data.data.message : 'Erreur produit.');
const items = data.data.items || [];
if (!items.length) { productResults.innerHTML = 'Aucun produit trouvé.
'; return; }
productResults.innerHTML = items.map(function (item) {
const qtyText = (item.stock_qty === null || typeof item.stock_qty === 'undefined') ? '' : ' · Qté : ' + escapeHtml(item.stock_qty);
return `${renderProductThumb(item, 'is-inline')}
${escapeHtml(item.name || '')}
${renderConditioningBadge(item)}
${item.sku ? 'SKU : ' + escapeHtml(item.sku) + ' · ' : ''}${escapeHtml(item.price_html || '')}${qtyText}${renderStockBadge(item)}
${renderProductPreviewButton(item)}
`;
}).join('');
bindProductPreviewButtons(productResults);
productResults.querySelectorAll('.asscw-add-product-button').forEach(function (button) {
button.addEventListener('click', function () {
const productId = parseInt(button.getAttribute('data-product-id'), 10);
if (!productId) return;
appendNewProductRow({ product_id: productId, name: button.getAttribute('data-product-name') || '', sku: button.getAttribute('data-product-sku') || '', price_html: button.getAttribute('data-price-html') || '', image_url: button.getAttribute('data-product-image') || '', conditioning_label: button.getAttribute('data-conditioning-label') || '' });
productInput.value = '';
productResults.innerHTML = '';
});
});
})
.catch(function (error) {
if (requestId !== productSearchRequestId) return;
productResults.innerHTML = '' + escapeHtml(error.message || 'Erreur de recherche produit.') + '
';
});
}, 150);
});
}
}
function appendNewProductRow(product) {
const tbody = document.getElementById('asscw-editor-items-body');
if (!tbody || !product || !product.product_id) return;
const productId = String(product.product_id || '');
const existingOrderQtyInput = tbody.querySelector('tr[data-product-id="' + productId + '"] .asscw-item-qty');
if (existingOrderQtyInput) { const currentValue = parseInt(existingOrderQtyInput.value || '0', 10); existingOrderQtyInput.value = String((isNaN(currentValue) ? 0 : currentValue) + 1); return; }
const existingNewQtyInput = tbody.querySelector('.asscw-new-product-qty[data-product-id="' + productId + '"]');
if (existingNewQtyInput) { const currentValue = parseInt(existingNewQtyInput.value || '0', 10); existingNewQtyInput.value = String((isNaN(currentValue) ? 0 : currentValue) + 1); return; }
const row = document.createElement('tr');
row.setAttribute('data-new-product-id', String(product.product_id || ''));
row.innerHTML = `${renderProductThumb({id: product.product_id || '', image_url: product.image_url || '', name: product.name || ''}, 'is-inline')} ${escapeHtml(product.name || '')} ${product.sku ? ' SKU : ' + escapeHtml(product.sku) + ' ' : ''}${renderConditioningBadge(product)} Nouveau produit simple | | ${escapeHtml(product.price_html || '')} | ${renderProductPreviewButton({id: product.product_id || ''})} | `;
tbody.appendChild(row);
const removeBtn = row.querySelector('.asscw-remove-new-item-button');
if (removeBtn) removeBtn.addEventListener('click', function () { row.remove(); markOrderDirty(); });
bindProductPreviewButtons(row);
}
function gatherOrderPayloadForSave() {
const items = [];
orderScreenContent.querySelectorAll('.asscw-item-qty').forEach(function (inputField) {
const itemId = parseInt(inputField.getAttribute('data-item-id'), 10);
if (itemId) items.push({ item_id: itemId, qty: String(inputField.value || '0').replace(',', '.') });
});
const newProducts = [];
orderScreenContent.querySelectorAll('.asscw-new-product-qty').forEach(function (inputField) {
const productId = parseInt(inputField.getAttribute('data-product-id'), 10);
const qty = parseInt(String(inputField.value || '0').replace(',', '.'), 10);
if (productId && qty > 0) newProducts.push({ product_id: productId, qty: String(qty) });
});
const noteField = document.getElementById('asscw-order-note');
return { order_id: currentOrderData ? currentOrderData.order_id : 0, mode: currentOrderMode, workspace_url: workspaceBaseUrl, customer_id: currentOrderData ? currentOrderData.customer_id : 0, shipping_address_id: currentOrderData ? (currentOrderData.selected_shipping_address_id || '') : '', items: items, new_products: newProducts, customer_note: noteField ? noteField.value : '' };
}
function loadFrontOrder(orderId, mode) {
switchToOrderScreen();
if (orderScreenContent) orderScreenContent.innerHTML = 'Chargement de la commande...
';
if (saveOrderButton) saveOrderButton.style.display = 'none';
post('ass_get_front_order', { order_id: orderId, mode: mode, workspace_url: workspaceBaseUrl })
.then(function (data) {
if (!data || !data.success) throw new Error(data && data.data && data.data.message ? data.data.message : 'Erreur de chargement de commande.');
renderOrderScreen(data.data.order, mode);
})
.catch(function (error) {
if (orderScreenContent) orderScreenContent.innerHTML = '' + escapeHtml(error.message || 'Erreur de chargement de commande.') + '
';
showNotice(error.message || 'Erreur de chargement de la commande.', 'error');
});
}
if (saveOrderButton) {
saveOrderButton.addEventListener('click', function () {
if (!currentOrderData || currentOrderMode === 'view') return;
const payload = gatherOrderPayloadForSave();
if (payload.mode !== 'new' && !payload.order_id) { showNotice('Commande invalide.', 'error'); return; }
isSavingOrder = true;
updateSaveButtonState();
post(payload.mode === 'repeat' ? 'ass_create_repeat_order' : (payload.mode === 'new' ? 'ass_create_front_order' : 'ass_save_front_order'), payload)
.then(function (data) {
if (!data || !data.success) throw new Error(data && data.data && data.data.message ? data.data.message : 'Erreur d’enregistrement.');
if (payload.mode === 'repeat' || payload.mode === 'new') {
if (!data.data || !data.data.order_id) throw new Error('Commande créée mais impossible de récupérer son identifiant.');
showNotice('Commande créée.', 'success');
currentOrderMode = 'edit';
orderHasUnsavedChanges = false;
return post('ass_get_front_order', { order_id: data.data.order_id, mode: 'edit', workspace_url: workspaceBaseUrl }).then(function (refreshData) {
if (!refreshData || !refreshData.success) throw new Error(refreshData && refreshData.data && refreshData.data.message ? refreshData.data.message : 'Erreur de rechargement.');
renderOrderScreen(refreshData.data.order, 'edit');
});
}
showNotice('Commande enregistrée.', 'success');
orderHasUnsavedChanges = false;
return post('ass_get_front_order', { order_id: payload.order_id, mode: 'edit', workspace_url: workspaceBaseUrl }).then(function (refreshData) {
if (!refreshData || !refreshData.success) throw new Error(refreshData && refreshData.data && refreshData.data.message ? refreshData.data.message : 'Erreur de rechargement.');
renderOrderScreen(refreshData.data.order, 'edit');
});
})
.catch(function (error) { showNotice(error.message || 'Erreur lors de l’enregistrement.', 'error'); })
.finally(function () { isSavingOrder = false; updateSaveButtonState(); });
});
}
if (backToCustomerBtn) backToCustomerBtn.addEventListener('click', function () {
switchToWorkspace();
if (currentCustomerId) {
loadCustomer(currentCustomerId, { skipHistory: true });
} else {
updateWorkspaceUrl(0);
updateBackButtons(0);
}
});
updateBackButtons(initialCustomerId || 0);
if (input) {
input.addEventListener('input', function () {
const term = input.value.trim();
clearTimeout(timer);
updateClearButton();
if (selectedCustomerLabel && term !== selectedCustomerLabel) {
hideSelectedClientBar();
selectedCustomerId = 0;
showResultsBox();
}
if (term.length < 2) { resetWorkspace(); return; }
const requestId = ++clientSearchRequestId;
timer = setTimeout(function () {
resultsBox.innerHTML = 'Recherche...
';
post('ass_search_clients', { term: term })
.then(function (data) {
if (requestId !== clientSearchRequestId) return;
if (!data || !data.success) throw new Error(data && data.data && data.data.message ? data.data.message : 'Erreur de recherche.');
lastSearchTerm = term;
lastSearchResults = data.data.items || [];
persistLastSearchState();
renderSearchResults(lastSearchResults);
highlightSelectedCustomer();
})
.catch(function (error) {
if (requestId !== clientSearchRequestId) return;
resultsBox.innerHTML = '' + escapeHtml(error.message || 'Erreur de recherche.') + '
';
});
}, 220);
});
}
if (input) {
input.addEventListener('keydown', function (event) {
const items = getResultItems();
if (event.key === 'ArrowDown') {
if (!items.length || resultsBox.style.display === 'none') return;
event.preventDefault();
setKeyboardIndex(keyboardIndex + 1);
return;
}
if (event.key === 'ArrowUp') {
if (!items.length || resultsBox.style.display === 'none') return;
event.preventDefault();
setKeyboardIndex(keyboardIndex <= 0 ? items.length - 1 : keyboardIndex - 1);
return;
}
if (event.key === 'Enter') {
if (keyboardIndex >= 0 && items[keyboardIndex]) {
event.preventDefault();
const item = items[keyboardIndex];
const customerId = parseInt(item.getAttribute('data-customer-id'), 10);
if (customerId) {
selectedCustomerLabel = (item.querySelector('.asscw-result-title') || {}).textContent || '';
loadCustomer(customerId);
}
}
return;
}
if (event.key === 'Escape') {
event.preventDefault();
input.value = '';
resetWorkspace();
input.focus();
}
});
}
if (clearBtn) clearBtn.addEventListener('click', function () { input.value = ''; resetWorkspace(); input.focus(); });
if (changeClientBtn) changeClientBtn.addEventListener('click', function () {
hydrateLastSearchState();
selectedCustomerId = 0;
selectedCustomerLabel = '';
hideSelectedClientBar();
showResultsBox();
resetKeyboardSelection();
if (lastSearchTerm.length >= 2 && Array.isArray(lastSearchResults) && lastSearchResults.length) {
input.value = lastSearchTerm;
renderSearchResults(lastSearchResults);
} else {
resultsBox.innerHTML = '';
input.value = '';
}
updateClearButton();
input.focus();
});
if (resultsBox) resultsBox.addEventListener('click', function (event) {
const item = event.target.closest('.asscw-result-item[data-customer-id]');
if (!item) return;
const customerId = parseInt(item.getAttribute('data-customer-id'), 10);
if (!customerId) return;
selectedCustomerLabel = ((item.querySelector('.asscw-result-title') || {}).textContent || '').trim();
clearSelectionHighlight();
item.classList.add('asscw-selected');
loadCustomer(customerId);
});
hydrateLastSearchState();
hideSelectedClientBar();
updateClearButton();
window.addEventListener('resize', syncProductPreviewLayout);
window.addEventListener('orientationchange', syncProductPreviewLayout);
if (screen === 'order' && initialOrderId > 0) {
switchToOrderScreen();
loadFrontOrder(initialOrderId, initialOrderMode);
} else {
switchToWorkspace();
if (initialCustomerId > 0) {
loadCustomer(initialCustomerId, { skipHistory: true });
}
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initWorkspace);
} else {
initWorkspace();
}
})();