feat: complete session — catalog, marketplace, WhatsApp, peer-to-peer, install scripts
Major features: - Pixel-Perfect glassmorphism design (landing + POS + public catalog) - OEM/Local catalog toggle with Nexpart taxonomy (14 groups, 108 subgroups, 558 part types) - Marketplace B2B Phase 1 (bodegas, POs, status machine, WA+email notifications) - Peer-to-peer inventory (multi-instance, LAN discovery) - WhatsApp: photo→Vision AI, voice→Whisper, conversational quotations - Smart unified search (VIN/plate/part_number/keyword auto-detect) - Shop Supplies tab (vehicle-independent parts) - Chatbot AI fallback chain (5 models) + response cache - CSV inventory import tool + setup_instance.sh installer - Tablet-responsive CSS + sidebar toggle - Filters, export CSV, employee edit, business data save - Quotation system (WA→POS) with auto-print on confirmation - Live stats on landing page Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -256,7 +256,7 @@ const Config = (() => {
|
||||
+ '<td>' + escHtml(emp.branch_name || 'Todas') + '</td>'
|
||||
+ '<td>' + statusBadge + '</td>'
|
||||
+ '<td>' + (emp.max_discount_pct || 0) + '%</td>'
|
||||
+ '<td><button class="btn btn--ghost btn--sm" disabled>Editar</button></td>'
|
||||
+ '<td><button class="btn btn--ghost btn--sm" onclick="Config.editEmployee(' + emp.id + ')">Editar</button></td>'
|
||||
+ '</tr>';
|
||||
});
|
||||
|
||||
@@ -265,8 +265,21 @@ const Config = (() => {
|
||||
}
|
||||
|
||||
async function saveEmployee(data) {
|
||||
var res = await fetch(API + '/employees', {
|
||||
method: 'POST',
|
||||
// Check if we're editing (modal has editId) or creating
|
||||
var modal = document.getElementById('employee-modal');
|
||||
var editId = modal ? modal.dataset.editId : null;
|
||||
var url = API + '/employees';
|
||||
var method = 'POST';
|
||||
|
||||
if (editId) {
|
||||
url = API + '/employees/' + editId;
|
||||
method = 'PUT';
|
||||
// Clear the edit marker so next use is a fresh create
|
||||
delete modal.dataset.editId;
|
||||
}
|
||||
|
||||
var res = await fetch(url, {
|
||||
method: method,
|
||||
headers: headers(),
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
@@ -302,6 +315,95 @@ const Config = (() => {
|
||||
if (el) el.value = v || '';
|
||||
}
|
||||
|
||||
function getVal(id) {
|
||||
var el = document.getElementById(id);
|
||||
return el ? el.value.trim() : '';
|
||||
}
|
||||
|
||||
async function editEmployee(empId) {
|
||||
if (!checkAuth()) return;
|
||||
// Find the employee in the loaded data by re-fetching
|
||||
try {
|
||||
var res = await fetch(API + '/employees', { headers: headers() });
|
||||
if (!res.ok) throw new Error('Failed to load employees');
|
||||
var json = await res.json();
|
||||
var emp = (json.data || []).find(function(e) { return e.id === empId; });
|
||||
if (!emp) { toast('Empleado no encontrado', 'error'); return; }
|
||||
|
||||
// Pre-fill the "new employee" modal with existing data for editing
|
||||
setVal('new-emp-name', emp.name);
|
||||
setVal('new-emp-email', emp.email || '');
|
||||
var roleSelect = document.getElementById('new-emp-role');
|
||||
if (roleSelect) roleSelect.value = emp.role || 'cashier';
|
||||
var branchSelect = document.getElementById('new-emp-branch');
|
||||
if (branchSelect) branchSelect.value = emp.branch_id || '';
|
||||
setVal('new-emp-discount', emp.max_discount_pct || '');
|
||||
setVal('new-emp-pin', ''); // Don't pre-fill PIN for security
|
||||
|
||||
// Store the ID so saveEmployee knows it's an update
|
||||
var modal = document.getElementById('employee-modal');
|
||||
if (modal) {
|
||||
modal.dataset.editId = empId;
|
||||
var title = modal.querySelector('.modal-title, h3');
|
||||
if (title) title.textContent = 'Editar Empleado';
|
||||
}
|
||||
openModal('employee-modal');
|
||||
} catch (e) {
|
||||
toast('Error: ' + e.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function saveTaxParams() {
|
||||
if (!checkAuth()) return;
|
||||
var data = {
|
||||
tax_iva: getVal('tax-iva') || '16',
|
||||
tax_ieps: getVal('tax-ieps') || '0',
|
||||
invoice_serie: getVal('tax-serie') || 'FA',
|
||||
invoice_folio: getVal('tax-folio') || '1',
|
||||
default_currency: document.getElementById('tax-moneda') ? document.getElementById('tax-moneda').value : 'MXN',
|
||||
default_payment_method: document.getElementById('tax-forma-pago') ? document.getElementById('tax-forma-pago').value : '01',
|
||||
};
|
||||
try {
|
||||
// Use the business PUT endpoint with tax_ prefixed keys
|
||||
var res = await fetch(API + '/business', {
|
||||
method: 'PUT',
|
||||
headers: headers(),
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
if (!res.ok) throw new Error('Error al guardar');
|
||||
toast('Parámetros de impuestos guardados', 'ok');
|
||||
} catch (e) {
|
||||
toast(e.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function saveBusiness() {
|
||||
if (!checkAuth()) return;
|
||||
var data = {
|
||||
razon_social: getVal('biz-razon-social'),
|
||||
nombre: getVal('biz-nombre'),
|
||||
rfc: getVal('biz-rfc'),
|
||||
regimen_fiscal: getVal('biz-regimen'),
|
||||
direccion: getVal('biz-direccion'),
|
||||
telefono: getVal('biz-telefono'),
|
||||
email: getVal('biz-email'),
|
||||
};
|
||||
try {
|
||||
var res = await fetch(API + '/business', {
|
||||
method: 'PUT',
|
||||
headers: headers(),
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
if (!res.ok) {
|
||||
var err = await res.json().catch(function() { return { error: res.statusText }; });
|
||||
throw new Error(err.error || 'Error al guardar');
|
||||
}
|
||||
toast('Datos de empresa guardados', 'ok');
|
||||
} catch (e) {
|
||||
toast(e.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Event bindings
|
||||
// -------------------------------------------------------------------------
|
||||
@@ -525,7 +627,8 @@ const Config = (() => {
|
||||
|
||||
return {
|
||||
init, setTheme, selectThemeOption,
|
||||
loadBranches, loadEmployees, saveBranch, saveEmployee,
|
||||
loadBranches, loadEmployees, saveBranch, saveEmployee, editEmployee,
|
||||
loadBusiness, saveBusiness, saveTaxParams,
|
||||
loadCurrency, saveCurrency,
|
||||
openModal, closeModal
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user