mirror of
https://github.com/itflow-org/itflow
synced 2026-08-15 20:15:12 +00:00
Fixed Asset section in recurring ticket if global added project in add ticket
This commit is contained in:
@@ -324,6 +324,9 @@ if (isset($_GET['get_client_contacts'])) {
|
|||||||
ORDER BY contact_primary DESC, contact_technical DESC, contact_important DESC, contact_name"
|
ORDER BY contact_primary DESC, contact_technical DESC, contact_important DESC, contact_name"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Always return the key, so a client with none gives [] rather than null
|
||||||
|
$response['contacts'] = [];
|
||||||
|
|
||||||
while ($row = mysqli_fetch_assoc($contact_sql)) {
|
while ($row = mysqli_fetch_assoc($contact_sql)) {
|
||||||
$response['contacts'][] = $row;
|
$response['contacts'][] = $row;
|
||||||
}
|
}
|
||||||
@@ -343,14 +346,17 @@ if (isset($_GET['get_client_assets'])) {
|
|||||||
|
|
||||||
$asset_sql = mysqli_query(
|
$asset_sql = mysqli_query(
|
||||||
$mysqli,
|
$mysqli,
|
||||||
"SELECT asset_id, asset_name, contact_name FROM assets
|
"SELECT asset_id, asset_name, asset_type, asset_make, asset_model, contact_name FROM assets
|
||||||
LEFT JOIN clients on asset_client_id = client_id
|
LEFT JOIN clients on asset_client_id = client_id
|
||||||
LEFT JOIN contacts ON contact_id = asset_contact_id
|
LEFT JOIN contacts ON contact_id = asset_contact_id
|
||||||
WHERE assets.asset_archived_at IS NULL AND asset_client_id = $client_id
|
WHERE assets.asset_archived_at IS NULL AND asset_client_id = $client_id
|
||||||
$access_permission_query
|
$access_permission_query
|
||||||
ORDER BY asset_favorite DESC, asset_name"
|
ORDER BY asset_type ASC, asset_favorite DESC, asset_name"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Always return the key, so a client with no assets gives [] rather than null
|
||||||
|
$response['assets'] = [];
|
||||||
|
|
||||||
while ($row = mysqli_fetch_assoc($asset_sql)) {
|
while ($row = mysqli_fetch_assoc($asset_sql)) {
|
||||||
$response['assets'][] = $row;
|
$response['assets'][] = $row;
|
||||||
}
|
}
|
||||||
@@ -377,6 +383,9 @@ if (isset($_GET['get_client_locations'])) {
|
|||||||
ORDER BY location_primary DESC, location_name ASC"
|
ORDER BY location_primary DESC, location_name ASC"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Always return the key, so a client with none gives [] rather than null
|
||||||
|
$response['locations'] = [];
|
||||||
|
|
||||||
while ($row = mysqli_fetch_assoc($locations_sql)) {
|
while ($row = mysqli_fetch_assoc($locations_sql)) {
|
||||||
$response['locations'][] = $row;
|
$response['locations'][] = $row;
|
||||||
}
|
}
|
||||||
@@ -403,6 +412,9 @@ if (isset($_GET['get_client_vendors'])) {
|
|||||||
ORDER BY vendor_name ASC"
|
ORDER BY vendor_name ASC"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Always return the key, so a client with none gives [] rather than null
|
||||||
|
$response['vendors'] = [];
|
||||||
|
|
||||||
while ($row = mysqli_fetch_assoc($vendors_sql)) {
|
while ($row = mysqli_fetch_assoc($vendors_sql)) {
|
||||||
$response['vendors'][] = $row;
|
$response['vendors'][] = $row;
|
||||||
}
|
}
|
||||||
@@ -410,6 +422,35 @@ if (isset($_GET['get_client_vendors'])) {
|
|||||||
echo json_encode($response);
|
echo json_encode($response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns open projects for a specified client
|
||||||
|
*/
|
||||||
|
if (isset($_GET['get_client_projects'])) {
|
||||||
|
enforceUserPermission('module_client');
|
||||||
|
|
||||||
|
$client_id = intval($_GET['client_id']);
|
||||||
|
|
||||||
|
enforceClientAccess();
|
||||||
|
|
||||||
|
$projects_sql = mysqli_query(
|
||||||
|
$mysqli,
|
||||||
|
"SELECT project_id, project_name FROM projects
|
||||||
|
LEFT JOIN clients on project_client_id = client_id
|
||||||
|
WHERE projects.project_archived_at IS NULL AND projects.project_completed_at IS NULL AND project_client_id = $client_id
|
||||||
|
$access_permission_query
|
||||||
|
ORDER BY project_name ASC"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Always return the key, so a client with none gives [] rather than null
|
||||||
|
$response['projects'] = [];
|
||||||
|
|
||||||
|
while ($row = mysqli_fetch_assoc($projects_sql)) {
|
||||||
|
$response['projects'][] = $row;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode($response);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* NEW TOTP getter for client login/passwords page
|
* NEW TOTP getter for client login/passwords page
|
||||||
* When provided with a login ID, checks permissions and returns the 6-digit code
|
* When provided with a login ID, checks permissions and returns the 6-digit code
|
||||||
|
|||||||
@@ -1,42 +1,156 @@
|
|||||||
// Used to populate dynamic content in recurring_ticket_add_modal and ticket_add_modal_v2 based on selected client
|
// Used to populate dynamic content in recurring_ticket_add_modal and ticket_add_modal_v2 based on selected client
|
||||||
|
|
||||||
|
// Not every modal that loads this script has every dropdown, and a modal opened
|
||||||
|
// from a contact page has no client selector at all - the client arrives as a
|
||||||
|
// hidden field. Everything below therefore checks that an element exists before
|
||||||
|
// touching it, rather than assuming the full set is present.
|
||||||
|
|
||||||
// Client selected listener
|
// Client selected listener
|
||||||
// We seem to have to use jQuery to listen for events, as the client input is a select2 component?
|
// We seem to have to use jQuery to listen for events, as the client input is a select2 component?
|
||||||
|
|
||||||
const clientSelectDropdown = document.getElementById("changeClientSelect"); // Define client selector
|
const clientSelectDropdown = document.getElementById("changeClientSelect"); // Define client selector
|
||||||
|
|
||||||
// // If the client selector is disabled, we must be on a client-specific page instead. Trigger the lists to update.
|
if (clientSelectDropdown) {
|
||||||
if (clientSelectDropdown.disabled) {
|
|
||||||
|
|
||||||
let client_id = $(clientSelectDropdown).find(':selected').val();
|
// If the client selector is disabled, we must be on a client-specific page instead. Trigger the lists to update.
|
||||||
|
if (clientSelectDropdown.disabled) {
|
||||||
|
|
||||||
|
let client_id = $(clientSelectDropdown).find(':selected').val();
|
||||||
|
|
||||||
|
populateLists(client_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listener for client selection. Populate select lists when a client is selected
|
||||||
|
$(clientSelectDropdown).on('select2:select', function (e) {
|
||||||
|
let client_id = $(this).find(':selected').val();
|
||||||
|
|
||||||
|
// Update the dependent dropdown lists
|
||||||
|
populateLists(client_id);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// No client selector - the modal was opened from a contact page, where the
|
||||||
|
// client is fixed and arrives as a hidden field instead
|
||||||
|
const clientIdHiddenField = document.getElementById("clientIdHidden");
|
||||||
|
|
||||||
|
if (clientIdHiddenField && clientIdHiddenField.value) {
|
||||||
|
populateLists(clientIdHiddenField.value);
|
||||||
|
}
|
||||||
|
|
||||||
populateLists(client_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Listener for client selection. Populate select lists when a client is selected
|
|
||||||
$(clientSelectDropdown).on('select2:select', function (e) {
|
|
||||||
let client_id = $(this).find(':selected').val();
|
|
||||||
|
|
||||||
// Update the contacts dropdown list
|
|
||||||
populateLists(client_id);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
// Populates dropdowns with dynamic content based on the client ID
|
// Populates dropdowns with dynamic content based on the client ID
|
||||||
// Called the client select dropdown is used or if the client select is disabled
|
// Called when the client select dropdown is used or if the client select is disabled
|
||||||
function populateLists(client_id) {
|
function populateLists(client_id) {
|
||||||
|
|
||||||
populateContactsDropdown(client_id);
|
populateContactsDropdown(client_id);
|
||||||
|
|
||||||
populateAssetsDropdown(client_id);
|
populateAssetsDropdowns(client_id);
|
||||||
|
|
||||||
populateLocationsDropdown(client_id);
|
populateLocationsDropdown(client_id);
|
||||||
|
|
||||||
populateVendorsDropdown(client_id);
|
populateVendorsDropdown(client_id);
|
||||||
|
|
||||||
|
populateProjectsDropdown(client_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empties a dropdown and adds its placeholder, returning the element - or null if
|
||||||
|
// this modal doesn't have it. Pass null as the label for a multi-select, which has
|
||||||
|
// no placeholder option of its own.
|
||||||
|
function resetDropdown(id, placeholderLabel, placeholderValue) {
|
||||||
|
|
||||||
|
const dropdown = document.getElementById(id);
|
||||||
|
|
||||||
|
if (!dropdown) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// innerHTML rather than removing options one by one, which leaves empty optgroups behind
|
||||||
|
dropdown.innerHTML = '';
|
||||||
|
|
||||||
|
// A multi-select keeps showing its old selections until select2 is told the value changed
|
||||||
|
$(dropdown).val(null).trigger('change.select2');
|
||||||
|
|
||||||
|
if (placeholderLabel !== null) {
|
||||||
|
dropdown[dropdown.length] = new Option(placeholderLabel, placeholderValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dropdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redraws a select2 component after its options have been replaced
|
||||||
|
function refreshDropdown(dropdown) {
|
||||||
|
if (dropdown) {
|
||||||
|
$(dropdown).trigger('change.select2');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-applies the value the modal was opened with (e.g. ticket_add_v2.php?project_id=4),
|
||||||
|
// which can only be selected once the options it refers to exist
|
||||||
|
function applyPreselection(dropdown) {
|
||||||
|
|
||||||
|
// '0' is the "none selected" value every one of these dropdowns uses, and is
|
||||||
|
// truthy as a string - so it has to be excluded explicitly
|
||||||
|
if (!dropdown || !dropdown.dataset.selected || dropdown.dataset.selected === '0') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dropdown.value = dropdown.dataset.selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds an optgroup to a dropdown and returns it, so options can be appended into it
|
||||||
|
function appendOptionGroup(dropdown, label) {
|
||||||
|
|
||||||
|
if (!dropdown) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const group = document.createElement("optgroup");
|
||||||
|
group.label = label;
|
||||||
|
dropdown.appendChild(group);
|
||||||
|
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds an option to an optgroup
|
||||||
|
function appendGroupedOption(group, label, value) {
|
||||||
|
|
||||||
|
if (!group) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
group.appendChild(new Option(label, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Builds the asset label as "Name - Make Model - (Contact)", matching how assets read elsewhere
|
||||||
|
function buildAssetLabel(asset) {
|
||||||
|
|
||||||
|
let label = asset.asset_name;
|
||||||
|
|
||||||
|
if (asset.asset_make) {
|
||||||
|
label = label + " - " + asset.asset_make;
|
||||||
|
|
||||||
|
if (asset.asset_model) {
|
||||||
|
label = label + " " + asset.asset_model;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (asset.contact_name) {
|
||||||
|
label = label + " - (" + asset.contact_name + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populate client contacts
|
// Populate client contacts
|
||||||
function populateContactsDropdown(client_id) {
|
function populateContactsDropdown(client_id) {
|
||||||
|
|
||||||
|
if (!document.getElementById("contactSelect")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Send a GET request to ajax.php as ajax.php?get_client_contacts=true&client_id=NUM
|
// Send a GET request to ajax.php as ajax.php?get_client_contacts=true&client_id=NUM
|
||||||
jQuery.get(
|
jQuery.get(
|
||||||
"ajax.php",
|
"ajax.php",
|
||||||
@@ -47,17 +161,10 @@ function populateContactsDropdown(client_id) {
|
|||||||
const response = JSON.parse(data);
|
const response = JSON.parse(data);
|
||||||
|
|
||||||
// Access the data for contacts (multiple)
|
// Access the data for contacts (multiple)
|
||||||
const contacts = response.contacts;
|
const contacts = response.contacts || [];
|
||||||
|
|
||||||
// Contacts dropdown
|
// Contacts dropdown
|
||||||
const contactSelectDropdown = document.getElementById("contactSelect");
|
const contactSelectDropdown = resetDropdown("contactSelect", '- Contact -', '0');
|
||||||
|
|
||||||
// Clear dropdown
|
|
||||||
let i, L = contactSelectDropdown.options.length - 1;
|
|
||||||
for (i = L; i >= 0; i--) {
|
|
||||||
contactSelectDropdown.remove(i);
|
|
||||||
}
|
|
||||||
contactSelectDropdown[contactSelectDropdown.length] = new Option('- Contact -', '0');
|
|
||||||
|
|
||||||
// Populate dropdown
|
// Populate dropdown
|
||||||
contacts.forEach(contact => {
|
contacts.forEach(contact => {
|
||||||
@@ -70,12 +177,22 @@ function populateContactsDropdown(client_id) {
|
|||||||
contactSelectDropdown[contactSelectDropdown.length] = new Option(contact.contact_name + appendText, contact.contact_id);
|
contactSelectDropdown[contactSelectDropdown.length] = new Option(contact.contact_name + appendText, contact.contact_id);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
applyPreselection(contactSelectDropdown);
|
||||||
|
|
||||||
|
refreshDropdown(contactSelectDropdown);
|
||||||
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populate client assets
|
// Populate client assets - feeds both the single asset picker and the additional assets
|
||||||
function populateAssetsDropdown(client_id) {
|
// multi-select from one request, as both need the same list
|
||||||
|
function populateAssetsDropdowns(client_id) {
|
||||||
|
|
||||||
|
if (!document.getElementById("assetSelect") && !document.getElementById("additionalAssetsSelect")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
jQuery.get(
|
jQuery.get(
|
||||||
"ajax.php",
|
"ajax.php",
|
||||||
{get_client_assets: 'true', client_id: client_id},
|
{get_client_assets: 'true', client_id: client_id},
|
||||||
@@ -85,34 +202,47 @@ function populateAssetsDropdown(client_id) {
|
|||||||
const response = JSON.parse(data);
|
const response = JSON.parse(data);
|
||||||
|
|
||||||
// Access the data for assets (multiple)
|
// Access the data for assets (multiple)
|
||||||
const assets = response.assets;
|
const assets = response.assets || [];
|
||||||
|
|
||||||
// Assets dropdown
|
const assetSelectDropdown = resetDropdown("assetSelect", '- None -', '0');
|
||||||
const assetSelectDropdown = document.getElementById("assetSelect");
|
const additionalAssetsDropdown = resetDropdown("additionalAssetsSelect", null, null);
|
||||||
|
|
||||||
// Clear dropdown
|
// Assets arrive ordered by type, so a change of type starts a new group
|
||||||
let i, L = assetSelectDropdown.options.length - 1;
|
let currentType = null;
|
||||||
for (i = L; i >= 0; i--) {
|
let assetGroup = null;
|
||||||
assetSelectDropdown.remove(i);
|
let additionalAssetGroup = null;
|
||||||
}
|
|
||||||
assetSelectDropdown[assetSelectDropdown.length] = new Option('- Asset -', '0');
|
|
||||||
|
|
||||||
// Populate dropdown with asset name (and contact, if set)
|
|
||||||
assets.forEach(asset => {
|
assets.forEach(asset => {
|
||||||
let displayText = asset.asset_name;
|
const assetType = asset.asset_type || 'Uncategorized';
|
||||||
if (asset.contact_name !== null) {
|
|
||||||
displayText = asset.asset_name + " - " + asset.contact_name;
|
if (assetType !== currentType) {
|
||||||
|
currentType = assetType;
|
||||||
|
assetGroup = appendOptionGroup(assetSelectDropdown, assetType);
|
||||||
|
additionalAssetGroup = appendOptionGroup(additionalAssetsDropdown, assetType);
|
||||||
}
|
}
|
||||||
|
|
||||||
assetSelectDropdown[assetSelectDropdown.length] = new Option(displayText, asset.asset_id);
|
const assetLabel = buildAssetLabel(asset);
|
||||||
|
|
||||||
|
appendGroupedOption(assetGroup, assetLabel, asset.asset_id);
|
||||||
|
appendGroupedOption(additionalAssetGroup, assetLabel, asset.asset_id);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
applyPreselection(assetSelectDropdown);
|
||||||
|
|
||||||
|
refreshDropdown(assetSelectDropdown);
|
||||||
|
refreshDropdown(additionalAssetsDropdown);
|
||||||
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populate client locations
|
// Populate client locations
|
||||||
function populateLocationsDropdown(client_id) {
|
function populateLocationsDropdown(client_id) {
|
||||||
|
|
||||||
|
if (!document.getElementById("locationSelect")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
jQuery.get(
|
jQuery.get(
|
||||||
"ajax.php",
|
"ajax.php",
|
||||||
{get_client_locations: 'true', client_id: client_id},
|
{get_client_locations: 'true', client_id: client_id},
|
||||||
@@ -122,29 +252,31 @@ function populateLocationsDropdown(client_id) {
|
|||||||
const response = JSON.parse(data);
|
const response = JSON.parse(data);
|
||||||
|
|
||||||
// Access the data for locations (multiple)
|
// Access the data for locations (multiple)
|
||||||
const locations = response.locations;
|
const locations = response.locations || [];
|
||||||
|
|
||||||
// Locations dropdown
|
// Locations dropdown
|
||||||
const locationSelectDropdown = document.getElementById("locationSelect");
|
const locationSelectDropdown = resetDropdown("locationSelect", '- Location -', '0');
|
||||||
|
|
||||||
// Clear dropdown
|
|
||||||
let i, L = locationSelectDropdown.options.length - 1;
|
|
||||||
for (i = L; i >= 0; i--) {
|
|
||||||
locationSelectDropdown.remove(i);
|
|
||||||
}
|
|
||||||
locationSelectDropdown[locationSelectDropdown.length] = new Option('- Location -', '0');
|
|
||||||
|
|
||||||
// Populate dropdown
|
// Populate dropdown
|
||||||
locations.forEach(location => {
|
locations.forEach(location => {
|
||||||
locationSelectDropdown[locationSelectDropdown.length] = new Option(location.location_name, location.location_id);
|
locationSelectDropdown[locationSelectDropdown.length] = new Option(location.location_name, location.location_id);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
applyPreselection(locationSelectDropdown);
|
||||||
|
|
||||||
|
refreshDropdown(locationSelectDropdown);
|
||||||
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populate client vendors
|
// Populate client vendors
|
||||||
function populateVendorsDropdown(client_id) {
|
function populateVendorsDropdown(client_id) {
|
||||||
|
|
||||||
|
if (!document.getElementById("vendorSelect")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
jQuery.get(
|
jQuery.get(
|
||||||
"ajax.php",
|
"ajax.php",
|
||||||
{get_client_vendors: 'true', client_id: client_id},
|
{get_client_vendors: 'true', client_id: client_id},
|
||||||
@@ -153,24 +285,55 @@ function populateVendorsDropdown(client_id) {
|
|||||||
// If we get a response from ajax.php, parse it as JSON
|
// If we get a response from ajax.php, parse it as JSON
|
||||||
const response = JSON.parse(data);
|
const response = JSON.parse(data);
|
||||||
|
|
||||||
// Access the data for locations (multiple)
|
// Access the data for vendors (multiple)
|
||||||
const vendors = response.vendors;
|
const vendors = response.vendors || [];
|
||||||
|
|
||||||
// Locations dropdown
|
// Vendors dropdown
|
||||||
const vendorSelectDropdown = document.getElementById("vendorSelect");
|
const vendorSelectDropdown = resetDropdown("vendorSelect", '- Vendor -', '0');
|
||||||
|
|
||||||
// Clear dropdown
|
|
||||||
let i, L = vendorSelectDropdown.options.length - 1;
|
|
||||||
for (i = L; i >= 0; i--) {
|
|
||||||
vendorSelectDropdown.remove(i);
|
|
||||||
}
|
|
||||||
vendorSelectDropdown[vendorSelectDropdown.length] = new Option('- Vendor -', '0');
|
|
||||||
|
|
||||||
// Populate dropdown
|
// Populate dropdown
|
||||||
vendors.forEach(vendor => {
|
vendors.forEach(vendor => {
|
||||||
vendorSelectDropdown[vendorSelectDropdown.length] = new Option(vendor.vendor_name, vendor.vendor_id);
|
vendorSelectDropdown[vendorSelectDropdown.length] = new Option(vendor.vendor_name, vendor.vendor_id);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
applyPreselection(vendorSelectDropdown);
|
||||||
|
|
||||||
|
refreshDropdown(vendorSelectDropdown);
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate client projects
|
||||||
|
function populateProjectsDropdown(client_id) {
|
||||||
|
|
||||||
|
if (!document.getElementById("projectSelect")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
jQuery.get(
|
||||||
|
"ajax.php",
|
||||||
|
{get_client_projects: 'true', client_id: client_id},
|
||||||
|
function(data) {
|
||||||
|
|
||||||
|
// If we get a response from ajax.php, parse it as JSON
|
||||||
|
const response = JSON.parse(data);
|
||||||
|
|
||||||
|
// Access the data for projects (multiple)
|
||||||
|
const projects = response.projects || [];
|
||||||
|
|
||||||
|
// Projects dropdown
|
||||||
|
const projectSelectDropdown = resetDropdown("projectSelect", '- Select Project -', '0');
|
||||||
|
|
||||||
|
// Populate dropdown
|
||||||
|
projects.forEach(project => {
|
||||||
|
projectSelectDropdown[projectSelectDropdown.length] = new Option(project.project_name, project.project_id);
|
||||||
|
});
|
||||||
|
|
||||||
|
applyPreselection(projectSelectDropdown);
|
||||||
|
|
||||||
|
refreshDropdown(projectSelectDropdown);
|
||||||
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ ob_start();
|
|||||||
</div>
|
</div>
|
||||||
<form action="post.php" method="post" autocomplete="off">
|
<form action="post.php" method="post" autocomplete="off">
|
||||||
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
|
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
|
||||||
<?php if (isset($client_id)) { ?>
|
<?php if ($client_id) { ?>
|
||||||
<input type="hidden" name="client_id" value="<?= $client_id ?>>">
|
<input type="hidden" name="client_id" id="clientIdHidden" value="<?= $client_id ?>">
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
<input type="hidden" name="billable" value="0">
|
<input type="hidden" name="billable" value="0">
|
||||||
|
|
||||||
@@ -41,7 +41,6 @@ ob_start();
|
|||||||
<div class="tab-pane fade show active" id="pills-add-details">
|
<div class="tab-pane fade show active" id="pills-add-details">
|
||||||
|
|
||||||
<?php if ($contact_id) { ?>
|
<?php if ($contact_id) { ?>
|
||||||
<input type="hidden" name="client_id" value="<?= $client_id ?>">
|
|
||||||
<input type="hidden" name="contact_id" value="<?= $contact_id ?>">
|
<input type="hidden" name="contact_id" value="<?= $contact_id ?>">
|
||||||
<?php } else { ?>
|
<?php } else { ?>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -264,113 +263,30 @@ ob_start();
|
|||||||
|
|
||||||
<div class="tab-pane fade" id="pills-add-assets">
|
<div class="tab-pane fade" id="pills-add-assets">
|
||||||
|
|
||||||
<?php if ($client_id) { ?>
|
<div class="form-group">
|
||||||
|
<label>Asset</label>
|
||||||
<div class="form-group">
|
<div class="input-group">
|
||||||
<label>Asset</label>
|
<div class="input-group-prepend">
|
||||||
<div class="input-group">
|
<span class="input-group-text"><i class="fa fa-fw fa-desktop"></i></span>
|
||||||
<div class="input-group-prepend">
|
|
||||||
<span class="input-group-text"><i class="fa fa-fw fa-desktop"></i></span>
|
|
||||||
</div>
|
|
||||||
<select class="form-control select2" name="asset_id">
|
|
||||||
<option value="0">- None -</option>
|
|
||||||
|
|
||||||
<?php
|
|
||||||
// Query assets ordered by type, then name
|
|
||||||
$sql_assets = mysqli_query($mysqli, "
|
|
||||||
SELECT asset_id, asset_name, asset_type, asset_make, asset_model, contact_name
|
|
||||||
FROM assets
|
|
||||||
LEFT JOIN contacts ON contact_id = asset_contact_id
|
|
||||||
WHERE asset_client_id = $client_id
|
|
||||||
AND asset_archived_at IS NULL
|
|
||||||
ORDER BY asset_type ASC, asset_name ASC
|
|
||||||
");
|
|
||||||
|
|
||||||
$current_type = null; // Track which optgroup we're in
|
|
||||||
|
|
||||||
while ($row = mysqli_fetch_assoc($sql_assets)) {
|
|
||||||
$asset_id_select = intval($row['asset_id']);
|
|
||||||
$asset_name_select = escapeHtml($row['asset_name']);
|
|
||||||
$asset_type_select = escapeHtml($row['asset_type']);
|
|
||||||
$asset_make_select = escapeHtml($row['asset_make']);
|
|
||||||
$asset_model_select = escapeHtml($row['asset_model']);
|
|
||||||
$contact_name_select = escapeHtml($row['contact_name']);
|
|
||||||
|
|
||||||
// Start new optgroup if type changes
|
|
||||||
if ($asset_type_select !== $current_type) {
|
|
||||||
if ($current_type !== null) echo "</optgroup>";
|
|
||||||
echo "<optgroup label=\"" . ($asset_type_select ?: 'Uncategorized') . "\">";
|
|
||||||
$current_type = $asset_type_select;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build full display
|
|
||||||
$full_name = $asset_name_select . ($asset_make_select ? " - $asset_make_select" . ($asset_model_select ? " $asset_model_select" : '') : '')
|
|
||||||
. ($contact_name_select ? " - ($contact_name_select)" : '');
|
|
||||||
?>
|
|
||||||
|
|
||||||
<option <?php if ($asset_id == $asset_id_select) { echo "selected"; } ?> value="<?= $asset_id_select ?>"><?= $full_name ?></option>
|
|
||||||
|
|
||||||
<?php }
|
|
||||||
|
|
||||||
if ($current_type_select !== null) echo "</optgroup>";
|
|
||||||
?>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
|
<select class="form-control select2" name="asset_id" id="assetSelect" data-selected="<?= $asset_id ?>">
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Additional Assets</label>
|
<label>Additional Assets</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
<span class="input-group-text"><i class="fa fa-fw fa-desktop"></i></span>
|
<span class="input-group-text"><i class="fa fa-fw fa-desktop"></i></span>
|
||||||
</div>
|
|
||||||
<select class="form-control select2" name="additional_assets[]" data-tags="true" data-placeholder="- Select Additional Assets -" multiple>
|
|
||||||
<option value=""></option>
|
|
||||||
|
|
||||||
<?php
|
|
||||||
// Query assets ordered by type then name
|
|
||||||
$sql_assets = mysqli_query($mysqli, "
|
|
||||||
SELECT asset_id, asset_name, asset_type, asset_make, asset_model, contact_name
|
|
||||||
FROM assets
|
|
||||||
LEFT JOIN contacts ON contact_id = asset_contact_id
|
|
||||||
WHERE asset_client_id = $client_id
|
|
||||||
AND asset_archived_at IS NULL
|
|
||||||
ORDER BY asset_type ASC, asset_name ASC
|
|
||||||
");
|
|
||||||
|
|
||||||
$current_type = null;
|
|
||||||
|
|
||||||
while ($row = mysqli_fetch_assoc($sql_assets)) {
|
|
||||||
$asset_id_select = intval($row['asset_id']);
|
|
||||||
$asset_name_select = escapeHtml($row['asset_name']);
|
|
||||||
$asset_type_select = escapeHtml($row['asset_type']);
|
|
||||||
$asset_make_select = escapeHtml($row['asset_make']);
|
|
||||||
$asset_model_select = escapeHtml($row['asset_model']);
|
|
||||||
$contact_name_select = escapeHtml($row['contact_name']);
|
|
||||||
|
|
||||||
// Start new optgroup if type changes
|
|
||||||
if ($asset_type_select !== $current_type) {
|
|
||||||
if ($current_type !== null) echo "</optgroup>";
|
|
||||||
echo "<optgroup label=\"" . ($asset_type_select ?: 'Uncategorized') . "\">";
|
|
||||||
$current_type = $asset_type_select;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build full display
|
|
||||||
$full_name = $asset_name_select . ($asset_make_select ? " - $asset_make_select" . ($asset_model_select ? " $asset_model_select" : '') : '')
|
|
||||||
. ($contact_name_select ? " - ($contact_name_select)" : '');
|
|
||||||
?>
|
|
||||||
|
|
||||||
<option value="<?= $asset_id_select ?>"><?= $full_name ?></option>
|
|
||||||
|
|
||||||
<?php }
|
|
||||||
|
|
||||||
if ($current_type !== null) echo "</optgroup>";
|
|
||||||
?>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
|
<select class="form-control select2" name="additional_assets[]" id="additionalAssetsSelect" data-placeholder="- Select Additional Assets -" multiple>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if (!$client_id) { ?>
|
||||||
|
<small class="form-text text-muted">Select a client on the Details tab to load its assets.</small>
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -23,10 +23,7 @@ ob_start();
|
|||||||
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
|
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
|
||||||
<!-- Hidden/System fields -->
|
<!-- Hidden/System fields -->
|
||||||
<?php if ($client_id) { ?>
|
<?php if ($client_id) { ?>
|
||||||
<input type="hidden" name="client_id" value="<?= $client_id ?>">
|
<input type="hidden" name="client_id" id="clientIdHidden" value="<?= $client_id ?>">
|
||||||
<?php } ?>
|
|
||||||
<?php if ($project_id) { ?>
|
|
||||||
<input type="hidden" name="project_id" value="<?= $project_id ?>">
|
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
<input type="hidden" name="billable" value="0">
|
<input type="hidden" name="billable" value="0">
|
||||||
|
|
||||||
@@ -234,7 +231,17 @@ ob_start();
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tab-pane fade" id="pills-add-relationships">
|
<div class="tab-pane fade" id="pills-add-relationships">
|
||||||
To-do: project, etc.
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Project</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<div class="input-group-prepend">
|
||||||
|
<span class="input-group-text"><i class="fa fa-fw fa-project-diagram"></i></span>
|
||||||
|
</div>
|
||||||
|
<select class="form-control select2" name="project_id" id="projectSelect" data-selected="<?= $project_id ?>">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Asset</label>
|
<label>Asset</label>
|
||||||
@@ -247,6 +254,17 @@ ob_start();
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Additional Assets</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<div class="input-group-prepend">
|
||||||
|
<span class="input-group-text"><i class="fa fa-fw fa-desktop"></i></span>
|
||||||
|
</div>
|
||||||
|
<select class="form-control select2" name="additional_assets[]" id="additionalAssetsSelect" data-placeholder="- Select Additional Assets -" multiple>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Location</label>
|
<label>Location</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
|
|||||||
Reference in New Issue
Block a user