589 lines
26 KiB
JavaScript
589 lines
26 KiB
JavaScript
// Modal Management for Lookup Tables
|
|
// This file contains modal-specific functions that replace the old prompt() dialogs
|
|
// Include this AFTER company-settings-lookups.js
|
|
|
|
(function() {
|
|
'use strict';
|
|
|
|
// ====================
|
|
// JOB STATUS MODAL
|
|
// ====================
|
|
|
|
window.showJobStatusModal = function(item) {
|
|
const modal = new bootstrap.Modal(document.getElementById('jobStatusModal'));
|
|
const form = document.getElementById('jobStatusForm');
|
|
|
|
// Reset form
|
|
form.reset();
|
|
document.getElementById('jobStatusId').value = '';
|
|
|
|
if (item) {
|
|
// Edit mode
|
|
document.getElementById('jobStatusModalTitle').textContent = 'Edit Job Status';
|
|
document.getElementById('jobStatusId').value = item.id;
|
|
document.getElementById('jobStatusCode').value = item.statusCode;
|
|
document.getElementById('jobStatusCode').disabled = true; // Cannot change code
|
|
document.getElementById('jobStatusDisplayName').value = item.displayName;
|
|
document.getElementById('jobStatusColorClass').value = item.colorClass;
|
|
document.getElementById('jobStatusCategory').value = item.workflowCategory || '';
|
|
document.getElementById('jobStatusIsTerminal').checked = item.isTerminalStatus;
|
|
document.getElementById('jobStatusIsWIP').checked = item.isWorkInProgressStatus;
|
|
document.getElementById('jobStatusDescription').value = item.description || '';
|
|
} else {
|
|
// Add mode
|
|
document.getElementById('jobStatusModalTitle').textContent = 'Add Job Status';
|
|
document.getElementById('jobStatusCode').disabled = false;
|
|
}
|
|
|
|
modal.show();
|
|
};
|
|
|
|
document.getElementById('saveJobStatusBtn').addEventListener('click', function() {
|
|
const form = document.getElementById('jobStatusForm');
|
|
if (!form.checkValidity()) {
|
|
form.reportValidity();
|
|
return;
|
|
}
|
|
|
|
const id = document.getElementById('jobStatusId').value;
|
|
const data = {
|
|
statusCode: document.getElementById('jobStatusCode').value.toUpperCase(),
|
|
displayName: document.getElementById('jobStatusDisplayName').value,
|
|
colorClass: document.getElementById('jobStatusColorClass').value,
|
|
workflowCategory: document.getElementById('jobStatusCategory').value || null,
|
|
isTerminalStatus: document.getElementById('jobStatusIsTerminal').checked,
|
|
isWorkInProgressStatus: document.getElementById('jobStatusIsWIP').checked,
|
|
description: document.getElementById('jobStatusDescription').value || null,
|
|
displayOrder: 999 // Will be set by server
|
|
};
|
|
|
|
if (id) {
|
|
// Update
|
|
data.id = parseInt(id);
|
|
data.isActive = true;
|
|
$.ajax({
|
|
url: '/CompanySettings/UpdateJobStatus',
|
|
type: 'POST',
|
|
contentType: 'application/json',
|
|
data: JSON.stringify(data),
|
|
success: function(response) {
|
|
if (response.success) {
|
|
bootstrap.Modal.getInstance(document.getElementById('jobStatusModal')).hide();
|
|
window.showToast('success', response.message);
|
|
window.loadJobStatuses();
|
|
} else {
|
|
window.showToast('error', response.message);
|
|
}
|
|
},
|
|
error: function() {
|
|
window.showToast('error', 'Failed to update job status');
|
|
}
|
|
});
|
|
} else {
|
|
// Create
|
|
$.ajax({
|
|
url: '/CompanySettings/CreateJobStatus',
|
|
type: 'POST',
|
|
contentType: 'application/json',
|
|
data: JSON.stringify(data),
|
|
success: function(response) {
|
|
if (response.success) {
|
|
bootstrap.Modal.getInstance(document.getElementById('jobStatusModal')).hide();
|
|
window.showToast('success', response.message);
|
|
window.loadJobStatuses();
|
|
} else {
|
|
window.showToast('error', response.message);
|
|
}
|
|
},
|
|
error: function() {
|
|
window.showToast('error', 'Failed to create job status');
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
// ====================
|
|
// JOB PRIORITY MODAL
|
|
// ====================
|
|
|
|
window.showJobPriorityModal = function(item) {
|
|
const modal = new bootstrap.Modal(document.getElementById('jobPriorityModal'));
|
|
const form = document.getElementById('jobPriorityForm');
|
|
|
|
// Reset form
|
|
form.reset();
|
|
document.getElementById('jobPriorityId').value = '';
|
|
|
|
if (item) {
|
|
// Edit mode
|
|
document.getElementById('jobPriorityModalTitle').textContent = 'Edit Job Priority';
|
|
document.getElementById('jobPriorityId').value = item.id;
|
|
document.getElementById('jobPriorityCode').value = item.priorityCode;
|
|
document.getElementById('jobPriorityCode').disabled = true; // Cannot change code
|
|
document.getElementById('jobPriorityDisplayName').value = item.displayName;
|
|
document.getElementById('jobPriorityColorClass').value = item.colorClass;
|
|
document.getElementById('jobPriorityDescription').value = item.description || '';
|
|
} else {
|
|
// Add mode
|
|
document.getElementById('jobPriorityModalTitle').textContent = 'Add Job Priority';
|
|
document.getElementById('jobPriorityCode').disabled = false;
|
|
}
|
|
|
|
modal.show();
|
|
};
|
|
|
|
document.getElementById('saveJobPriorityBtn').addEventListener('click', function() {
|
|
const form = document.getElementById('jobPriorityForm');
|
|
if (!form.checkValidity()) {
|
|
form.reportValidity();
|
|
return;
|
|
}
|
|
|
|
const id = document.getElementById('jobPriorityId').value;
|
|
const data = {
|
|
priorityCode: document.getElementById('jobPriorityCode').value.toUpperCase(),
|
|
displayName: document.getElementById('jobPriorityDisplayName').value,
|
|
colorClass: document.getElementById('jobPriorityColorClass').value,
|
|
description: document.getElementById('jobPriorityDescription').value || null,
|
|
displayOrder: 999 // Will be set by server
|
|
};
|
|
|
|
if (id) {
|
|
// Update
|
|
data.id = parseInt(id);
|
|
data.isActive = true;
|
|
$.ajax({
|
|
url: '/CompanySettings/UpdateJobPriority',
|
|
type: 'POST',
|
|
contentType: 'application/json',
|
|
data: JSON.stringify(data),
|
|
success: function(response) {
|
|
if (response.success) {
|
|
bootstrap.Modal.getInstance(document.getElementById('jobPriorityModal')).hide();
|
|
window.showToast('success', response.message);
|
|
window.loadJobPriorities();
|
|
} else {
|
|
window.showToast('error', response.message);
|
|
}
|
|
},
|
|
error: function() {
|
|
window.showToast('error', 'Failed to update job priority');
|
|
}
|
|
});
|
|
} else {
|
|
// Create
|
|
$.ajax({
|
|
url: '/CompanySettings/CreateJobPriority',
|
|
type: 'POST',
|
|
contentType: 'application/json',
|
|
data: JSON.stringify(data),
|
|
success: function(response) {
|
|
if (response.success) {
|
|
bootstrap.Modal.getInstance(document.getElementById('jobPriorityModal')).hide();
|
|
window.showToast('success', response.message);
|
|
window.loadJobPriorities();
|
|
} else {
|
|
window.showToast('error', response.message);
|
|
}
|
|
},
|
|
error: function() {
|
|
window.showToast('error', 'Failed to create job priority');
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
// ====================
|
|
// QUOTE STATUS MODAL
|
|
// ====================
|
|
|
|
window.showQuoteStatusModal = function(item) {
|
|
const modal = new bootstrap.Modal(document.getElementById('quoteStatusModal'));
|
|
const form = document.getElementById('quoteStatusForm');
|
|
|
|
// Reset form
|
|
form.reset();
|
|
document.getElementById('quoteStatusId').value = '';
|
|
|
|
if (item) {
|
|
// Edit mode
|
|
document.getElementById('quoteStatusModalTitle').textContent = 'Edit Quote Status';
|
|
document.getElementById('quoteStatusId').value = item.id;
|
|
document.getElementById('quoteStatusCode').value = item.statusCode;
|
|
document.getElementById('quoteStatusCode').disabled = true; // Cannot change code
|
|
document.getElementById('quoteStatusDisplayName').value = item.displayName;
|
|
document.getElementById('quoteStatusColorClass').value = item.colorClass;
|
|
document.getElementById('quoteStatusIsApproved').checked = item.isApprovedStatus;
|
|
document.getElementById('quoteStatusIsConverted').checked = item.isConvertedStatus;
|
|
document.getElementById('quoteStatusIsDraft').checked = item.isDraftStatus;
|
|
document.getElementById('quoteStatusDescription').value = item.description || '';
|
|
} else {
|
|
// Add mode
|
|
document.getElementById('quoteStatusModalTitle').textContent = 'Add Quote Status';
|
|
document.getElementById('quoteStatusCode').disabled = false;
|
|
}
|
|
|
|
modal.show();
|
|
};
|
|
|
|
document.getElementById('saveQuoteStatusBtn').addEventListener('click', function() {
|
|
const form = document.getElementById('quoteStatusForm');
|
|
if (!form.checkValidity()) {
|
|
form.reportValidity();
|
|
return;
|
|
}
|
|
|
|
const id = document.getElementById('quoteStatusId').value;
|
|
const data = {
|
|
statusCode: document.getElementById('quoteStatusCode').value.toUpperCase(),
|
|
displayName: document.getElementById('quoteStatusDisplayName').value,
|
|
colorClass: document.getElementById('quoteStatusColorClass').value,
|
|
isApprovedStatus: document.getElementById('quoteStatusIsApproved').checked,
|
|
isConvertedStatus: document.getElementById('quoteStatusIsConverted').checked,
|
|
isDraftStatus: document.getElementById('quoteStatusIsDraft').checked,
|
|
description: document.getElementById('quoteStatusDescription').value || null,
|
|
displayOrder: 999 // Will be set by server
|
|
};
|
|
|
|
if (id) {
|
|
// Update
|
|
data.id = parseInt(id);
|
|
data.isActive = true;
|
|
$.ajax({
|
|
url: '/CompanySettings/UpdateQuoteStatus',
|
|
type: 'POST',
|
|
contentType: 'application/json',
|
|
data: JSON.stringify(data),
|
|
success: function(response) {
|
|
if (response.success) {
|
|
bootstrap.Modal.getInstance(document.getElementById('quoteStatusModal')).hide();
|
|
window.showToast('success', response.message);
|
|
window.loadQuoteStatuses();
|
|
} else {
|
|
window.showToast('error', response.message);
|
|
}
|
|
},
|
|
error: function() {
|
|
window.showToast('error', 'Failed to update quote status');
|
|
}
|
|
});
|
|
} else {
|
|
// Create
|
|
$.ajax({
|
|
url: '/CompanySettings/CreateQuoteStatus',
|
|
type: 'POST',
|
|
contentType: 'application/json',
|
|
data: JSON.stringify(data),
|
|
success: function(response) {
|
|
if (response.success) {
|
|
bootstrap.Modal.getInstance(document.getElementById('quoteStatusModal')).hide();
|
|
window.showToast('success', response.message);
|
|
window.loadQuoteStatuses();
|
|
} else {
|
|
window.showToast('error', response.message);
|
|
}
|
|
},
|
|
error: function() {
|
|
window.showToast('error', 'Failed to create quote status');
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
// ====================
|
|
// INVENTORY CATEGORY MODAL
|
|
// ====================
|
|
|
|
window.showInventoryCategoryModal = function(item) {
|
|
const modal = new bootstrap.Modal(document.getElementById('inventoryCategoryModal'));
|
|
const form = document.getElementById('inventoryCategoryForm');
|
|
|
|
// Reset form
|
|
form.reset();
|
|
document.getElementById('inventoryCategoryId').value = '';
|
|
|
|
if (item) {
|
|
// Edit mode
|
|
document.getElementById('inventoryCategoryModalTitle').textContent = 'Edit Inventory Category';
|
|
document.getElementById('inventoryCategoryId').value = item.id;
|
|
document.getElementById('inventoryCategoryCode').value = item.categoryCode;
|
|
document.getElementById('inventoryCategoryCode').disabled = true; // Cannot change code
|
|
document.getElementById('inventoryCategoryDisplayName').value = item.displayName;
|
|
document.getElementById('inventoryCategoryIsCoating').checked = item.isCoating;
|
|
document.getElementById('inventoryCategoryDescription').value = item.description || '';
|
|
} else {
|
|
// Add mode
|
|
document.getElementById('inventoryCategoryModalTitle').textContent = 'Add Inventory Category';
|
|
document.getElementById('inventoryCategoryCode').disabled = false;
|
|
}
|
|
|
|
modal.show();
|
|
};
|
|
|
|
document.getElementById('saveInventoryCategoryBtn').addEventListener('click', function() {
|
|
const form = document.getElementById('inventoryCategoryForm');
|
|
if (!form.checkValidity()) {
|
|
form.reportValidity();
|
|
return;
|
|
}
|
|
|
|
const id = document.getElementById('inventoryCategoryId').value;
|
|
const data = {
|
|
categoryCode: document.getElementById('inventoryCategoryCode').value.toUpperCase(),
|
|
displayName: document.getElementById('inventoryCategoryDisplayName').value,
|
|
isCoating: document.getElementById('inventoryCategoryIsCoating').checked,
|
|
description: document.getElementById('inventoryCategoryDescription').value || null,
|
|
displayOrder: 999 // Will be set by server
|
|
};
|
|
|
|
if (id) {
|
|
// Update
|
|
data.id = parseInt(id);
|
|
data.isActive = true;
|
|
$.ajax({
|
|
url: '/CompanySettings/UpdateInventoryCategory',
|
|
type: 'POST',
|
|
contentType: 'application/json',
|
|
data: JSON.stringify(data),
|
|
success: function(response) {
|
|
if (response.success) {
|
|
bootstrap.Modal.getInstance(document.getElementById('inventoryCategoryModal')).hide();
|
|
window.showToast('success', response.message);
|
|
window.loadInventoryCategories();
|
|
} else {
|
|
window.showToast('error', response.message);
|
|
}
|
|
},
|
|
error: function() {
|
|
window.showToast('error', 'Failed to update inventory category');
|
|
}
|
|
});
|
|
} else {
|
|
// Create
|
|
$.ajax({
|
|
url: '/CompanySettings/CreateInventoryCategory',
|
|
type: 'POST',
|
|
contentType: 'application/json',
|
|
data: JSON.stringify(data),
|
|
success: function(response) {
|
|
if (response.success) {
|
|
bootstrap.Modal.getInstance(document.getElementById('inventoryCategoryModal')).hide();
|
|
window.showToast('success', response.message);
|
|
window.loadInventoryCategories();
|
|
} else {
|
|
window.showToast('error', response.message);
|
|
}
|
|
},
|
|
error: function() {
|
|
window.showToast('error', 'Failed to create inventory category');
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
// Update the window-level edit functions to use the new modals
|
|
const originalEditJobStatus = window.editJobStatus;
|
|
window.editJobStatus = function(id) {
|
|
// Find the item from the global arrays
|
|
const item = window.jobStatuses ? window.jobStatuses.find(s => s.id === id) : null;
|
|
if (item) window.showJobStatusModal(item);
|
|
};
|
|
|
|
const originalEditJobPriority = window.editJobPriority;
|
|
window.editJobPriority = function(id) {
|
|
const item = window.jobPriorities ? window.jobPriorities.find(p => p.id === id) : null;
|
|
if (item) window.showJobPriorityModal(item);
|
|
};
|
|
|
|
const originalEditQuoteStatus = window.editQuoteStatus;
|
|
window.editQuoteStatus = function(id) {
|
|
const item = window.quoteStatuses ? window.quoteStatuses.find(s => s.id === id) : null;
|
|
if (item) window.showQuoteStatusModal(item);
|
|
};
|
|
|
|
const originalEditInventoryCategory = window.editInventoryCategory;
|
|
window.editInventoryCategory = function(id) {
|
|
const item = window.inventoryCategories ? window.inventoryCategories.find(c => c.id === id) : null;
|
|
if (item) window.showInventoryCategoryModal(item);
|
|
};
|
|
|
|
// ====================
|
|
// APPOINTMENT TYPE MODAL
|
|
// ====================
|
|
|
|
window.showAppointmentTypeModal = function(item) {
|
|
const modal = new bootstrap.Modal(document.getElementById('appointmentTypeModal'));
|
|
const form = document.getElementById('appointmentTypeForm');
|
|
|
|
// Reset form
|
|
form.reset();
|
|
document.getElementById('appointmentTypeId').value = '';
|
|
document.getElementById('appointmentTypeActiveField').style.display = 'none';
|
|
|
|
if (item) {
|
|
// Edit mode
|
|
document.getElementById('appointmentTypeModalTitle').textContent = 'Edit Appointment Type';
|
|
document.getElementById('appointmentTypeId').value = item.id;
|
|
document.getElementById('appointmentTypeCode').value = item.typeCode;
|
|
document.getElementById('appointmentTypeCode').disabled = true; // Cannot change code
|
|
document.getElementById('appointmentTypeDisplayName').value = item.displayName;
|
|
document.getElementById('appointmentTypeColorClass').value = item.colorClass;
|
|
document.getElementById('appointmentTypeIconClass').value = item.iconClass || '';
|
|
document.getElementById('appointmentTypeRequiresJob').checked = item.requiresJobLink;
|
|
document.getElementById('appointmentTypeIsActive').checked = item.isActive;
|
|
document.getElementById('appointmentTypeDescription').value = item.description || '';
|
|
document.getElementById('appointmentTypeActiveField').style.display = 'block';
|
|
} else {
|
|
// Add mode
|
|
document.getElementById('appointmentTypeModalTitle').textContent = 'Add Appointment Type';
|
|
document.getElementById('appointmentTypeCode').disabled = false;
|
|
document.getElementById('appointmentTypeIsActive').checked = true;
|
|
}
|
|
|
|
modal.show();
|
|
|
|
// Update color preview after modal is shown
|
|
window.updateAppointmentTypeColorPreview();
|
|
};
|
|
|
|
// Update the appointment type color preview badge
|
|
window.updateAppointmentTypeColorPreview = function() {
|
|
const colorSelect = document.getElementById('appointmentTypeColorClass');
|
|
const previewBadge = document.getElementById('appointmentTypeColorPreview');
|
|
|
|
if (!colorSelect || !previewBadge) return;
|
|
|
|
const selectedColor = colorSelect.value;
|
|
|
|
// Remove all existing Bootstrap color classes
|
|
const colorClasses = [
|
|
'bg-purple', 'bg-green', 'bg-blue', 'bg-orange', 'bg-red', 'bg-yellow',
|
|
'bg-pink', 'bg-cyan', 'bg-teal', 'bg-indigo', 'bg-lime', 'bg-brown', 'bg-gray',
|
|
'bg-success', 'bg-danger', 'bg-warning', 'bg-info', 'bg-primary', 'bg-secondary', 'bg-dark',
|
|
'text-dark', 'text-white'
|
|
];
|
|
previewBadge.classList.remove(...colorClasses);
|
|
|
|
// Add the selected color class
|
|
previewBadge.classList.add('bg-' + selectedColor);
|
|
|
|
// Add text-dark for warning (yellow) to ensure visibility
|
|
if (selectedColor === 'warning' || selectedColor === 'yellow' || selectedColor === 'lime') {
|
|
previewBadge.classList.add('text-dark');
|
|
} else {
|
|
previewBadge.classList.add('text-white');
|
|
}
|
|
};
|
|
|
|
document.getElementById('saveAppointmentTypeBtn').addEventListener('click', function() {
|
|
const form = document.getElementById('appointmentTypeForm');
|
|
if (!form.checkValidity()) {
|
|
form.reportValidity();
|
|
return;
|
|
}
|
|
|
|
const id = document.getElementById('appointmentTypeId').value;
|
|
const data = {
|
|
typeCode: document.getElementById('appointmentTypeCode').value.toUpperCase(),
|
|
displayName: document.getElementById('appointmentTypeDisplayName').value,
|
|
colorClass: document.getElementById('appointmentTypeColorClass').value,
|
|
iconClass: document.getElementById('appointmentTypeIconClass').value || null,
|
|
requiresJobLink: document.getElementById('appointmentTypeRequiresJob').checked,
|
|
description: document.getElementById('appointmentTypeDescription').value || null,
|
|
displayOrder: 999 // Will be set by server
|
|
};
|
|
|
|
if (id) {
|
|
// Update
|
|
data.id = parseInt(id);
|
|
data.isActive = document.getElementById('appointmentTypeIsActive').checked;
|
|
$.ajax({
|
|
url: '/CompanySettings/UpdateAppointmentType',
|
|
type: 'POST',
|
|
contentType: 'application/json',
|
|
headers: {
|
|
'RequestVerificationToken': $('input[name="__RequestVerificationToken"]').val()
|
|
},
|
|
data: JSON.stringify(data),
|
|
success: function(response) {
|
|
if (response.success) {
|
|
bootstrap.Modal.getInstance(document.getElementById('appointmentTypeModal')).hide();
|
|
window.showToast('success', response.message);
|
|
window.loadAppointmentTypes();
|
|
} else {
|
|
window.showToast('error', response.message);
|
|
}
|
|
},
|
|
error: function() {
|
|
window.showToast('error', 'Failed to update appointment type');
|
|
}
|
|
});
|
|
} else {
|
|
// Create
|
|
$.ajax({
|
|
url: '/CompanySettings/CreateAppointmentType',
|
|
type: 'POST',
|
|
contentType: 'application/json',
|
|
headers: {
|
|
'RequestVerificationToken': $('input[name="__RequestVerificationToken"]').val()
|
|
},
|
|
data: JSON.stringify(data),
|
|
success: function(response) {
|
|
if (response.success) {
|
|
bootstrap.Modal.getInstance(document.getElementById('appointmentTypeModal')).hide();
|
|
window.showToast('success', response.message);
|
|
window.loadAppointmentTypes();
|
|
} else {
|
|
window.showToast('error', response.message);
|
|
}
|
|
},
|
|
error: function() {
|
|
window.showToast('error', 'Failed to create appointment type');
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
// Override the editAppointmentType function to use modal
|
|
window.editAppointmentType = function(id) {
|
|
const item = window.appointmentTypes ? window.appointmentTypes.find(t => t.id === id) : null;
|
|
if (item) window.showAppointmentTypeModal(item);
|
|
};
|
|
|
|
// ====================
|
|
// AUTO-DERIVE CODE FROM DISPLAY NAME
|
|
// When adding a new lookup item, auto-populate the Code field from the Display Name:
|
|
// "In Progress" → "IN_PROGRESS". Also auto-uppercases direct code field edits.
|
|
// Only fires when the code field is enabled (add mode — disabled in edit mode).
|
|
// ====================
|
|
|
|
function deriveCode(displayName) {
|
|
return displayName.trim().toUpperCase().replace(/\s+/g, '_').replace(/[^A-Z0-9_]/g, '');
|
|
}
|
|
|
|
function wireCodeAutoFill(nameFieldId, codeFieldId) {
|
|
const nameField = document.getElementById(nameFieldId);
|
|
const codeField = document.getElementById(codeFieldId);
|
|
if (!nameField || !codeField) return;
|
|
|
|
nameField.addEventListener('input', function() {
|
|
if (!codeField.disabled) {
|
|
codeField.value = deriveCode(this.value);
|
|
}
|
|
});
|
|
|
|
codeField.addEventListener('input', function() {
|
|
const pos = this.selectionStart;
|
|
this.value = this.value.toUpperCase().replace(/[^A-Z0-9_]/g, '');
|
|
this.setSelectionRange(pos, pos);
|
|
});
|
|
}
|
|
|
|
wireCodeAutoFill('jobStatusDisplayName', 'jobStatusCode');
|
|
wireCodeAutoFill('jobPriorityDisplayName', 'jobPriorityCode');
|
|
wireCodeAutoFill('quoteStatusDisplayName', 'quoteStatusCode');
|
|
wireCodeAutoFill('inventoryCategoryDisplayName','inventoryCategoryCode');
|
|
wireCodeAutoFill('appointmentTypeDisplayName', 'appointmentTypeCode');
|
|
|
|
})();
|