Files
itflow/agent/modals/ticket/ticket_edit.php
johnnyq 234afd25fb Contacts overhaul: rebuilt contacts list with role tabs and counts, avatars, flag pills, hover copy on phone/mobile/email, inline location picker, linked asset/credential/license/ticket chips and tag filter; contact page rebuilt to match the asset page with header actions, Reach/Details/Quick notes cards and tabbed Assets, Credentials, Tickets, Recurring, Licenses, Documents, Files, Services and Notes; add and edit contact share one tabbed form (Details, Roles, Access, Notes) with field icons, role switches, password generator, duplicate email check and photo removal; contact quick look redone to match the asset quick look; contact avatars now used everywhere a contact appears - contact pickers, ticket header, contact card, client replies, ticket list, kanban, assets, clients list, client header, overview key contacts, credentials, documents, services, invoice/quote/statement recipients, license users and global search - with each contact keeping the same colour throughout; ticket contact pickers show the name with title and role underneath; fixes: permission checks on the add contact modal and contact page/list actions, contact location limited to the contact's own client on add/edit/bulk assign, contacts list no longer selects user account columns, archived assets and credentials left off the contact page and quick look, saving a contact with the client portal disabled no longer clears their portal login method, replacing a photo no longer tries to delete a directory, sort column whitelisted, list counts and tags batched instead of per row, recurring ticket menu markup, undefined index notices, contacts list no longer scrolls sideways on phones, Edit Service shows avatars on unselected contacts and assets, license user labels toggle their checkbox; new functions/contact.php helpers (contactAvatar, contactLink, contactFlagPills, contactPhoneLink, contactAvatarData, contactOptionDescription), shared contact_form.php, contact_location quick pick, removed unused assetContactAvatar and clientOverviewPersonInitials
2026-10-14 18:33:18 -04:00

374 lines
21 KiB
PHP

<?php
require_once '../../../includes/modal_header.php';
enforceUserPermission('module_support', 2);
// Tickets with no client stay visible to restricted agents - clientScopeSql() includes 0
$access_permission_query_overide = clientScopeSql('ticket_client_id');
$ticket_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT client_id, client_name, ticket_asset_id, ticket_assigned_to, ticket_billable,
ticket_category, ticket_contact_id, ticket_created_at, ticket_details, ticket_due_at,
ticket_location_id, ticket_number, ticket_prefix, ticket_priority, ticket_project_id,
ticket_subject, ticket_vendor_id, ticket_vendor_ticket_number FROM tickets LEFT JOIN clients ON client_id = ticket_client_id WHERE ticket_id = $ticket_id $access_permission_query_overide LIMIT 1");
$row = mysqli_fetch_assoc($sql);
$client_id = intval($row['client_id']);
$client_name = escapeHtml($row['client_name']);
$ticket_prefix = escapeHtml($row['ticket_prefix']);
$ticket_number = intval($row['ticket_number']);
$ticket_category = intval($row['ticket_category']);
$ticket_subject = escapeHtml($row['ticket_subject']);
$ticket_details = escapeHtml($row['ticket_details']);
$ticket_priority = escapeHtml($row['ticket_priority']);
$ticket_billable = intval($row['ticket_billable']);
$ticket_vendor_ticket_number = escapeHtml($row['ticket_vendor_ticket_number']);
$ticket_created_at = escapeHtml($row['ticket_created_at']);
$ticket_due_at = escapeHtml($row['ticket_due_at']);
$ticket_assigned_to = intval($row['ticket_assigned_to']);
$contact_id = intval($row['ticket_contact_id']);
$asset_id = intval($row['ticket_asset_id']);
$location_id = intval($row['ticket_location_id']);
$vendor_id = intval($row['ticket_vendor_id']);
$project_id = intval($row['ticket_project_id']);
if ($client_id) {
enforceClientAccess();
}
// Additional Assets Selected
$additional_assets_array = array();
$sql_additional_assets = mysqli_query($mysqli, "SELECT asset_id FROM ticket_assets WHERE ticket_id = $ticket_id");
while ($row = mysqli_fetch_assoc($sql_additional_assets)) {
$additional_asset_id = intval($row['asset_id']);
$additional_assets_array[] = $additional_asset_id;
}
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-life-ring me-2"></i>Edit ticket <small class="ms-1 opacity-75"><?= "$ticket_prefix$ticket_number" ?></small></h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<input type="hidden" name="ticket_id" value="<?= $ticket_id ?>">
<div class="modal-body">
<?php if ($client_id) { ?>
<ul class="nav nav-pills nav-justified mb-3">
<li class="nav-item">
<a class="nav-link active" data-bs-toggle="pill" href="#pills-details"><i class="fa fa-fw fa-life-ring me-2"></i>Details</a>
</li>
<li class="nav-item">
<a class="nav-link" data-bs-toggle="pill" href="#pills-contacts"><i class="fa fa-fw fa-user me-2"></i>Contact</a>
</li>
<li class="nav-item">
<a class="nav-link" data-bs-toggle="pill" href="#pills-assignment"><i class="fa fa-fw fa-link me-2"></i>Related</a>
</li>
</ul>
<hr>
<?php } ?>
<div class="tab-content" <?php if (lookupUserPermission('module_support') <= 1) { echo 'inert'; } ?>>
<div class="tab-pane fade show active" id="pills-details">
<div class="mb-3">
<label>Subject <strong class="text-danger">*</strong></label>
<div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-tag"></i></span>
<input type="text" class="form-control" name="subject" maxlength="500" value="<?= $ticket_subject ?>" placeholder="Subject" required>
</div>
</div>
<div class="mb-3">
<textarea class="form-control tinymceTicket" rows="8" name="details"><?= $ticket_details ?></textarea>
</div>
<div class="row">
<div class="col">
<div class="mb-3">
<label>Priority <strong class="text-danger">*</strong></label>
<div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-thermometer-half"></i></span>
<select class="form-select select2" name="priority" required>
<option <?php if ($ticket_priority == 'Low') { echo "selected"; } ?> data-color="var(--bs-info)">Low</option>
<option <?php if ($ticket_priority == 'Medium') { echo "selected"; } ?> data-color="var(--bs-warning)">Medium</option>
<option <?php if ($ticket_priority == 'High') { echo "selected"; } ?> data-color="var(--bs-danger)">High</option>
<option <?php if ($ticket_priority == 'Urgent') { echo "selected"; } ?> data-color="var(--bs-emphasis-color)">Urgent</option>
</select>
</div>
</div>
</div>
<div class="col">
<div class="mb-3">
<label>Category</label>
<div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-layer-group"></i></span>
<select class="form-select select2" name="category_id">
<option value="0">- Uncategorized -</option>
<?php
$sql_categories = mysqli_query($mysqli, "SELECT category_id, category_name, category_color, category_icon FROM categories WHERE category_type = 'Ticket' AND category_archived_at IS NULL ORDER BY category_name ASC");
while ($row = mysqli_fetch_assoc($sql_categories)) {
$category_id = intval($row['category_id']);
$category_name = escapeHtml($row['category_name']);
?>
<option <?php if ($ticket_category == $category_id) {echo "selected";} ?> value="<?= $category_id ?>"<?= selectOptionColor($row['category_color'] ?? '', $row['category_icon'] ?? '') ?>><?= $category_name ?></option>
<?php } ?>
</select>
<button class="btn btn-secondary ajax-modal" title="New category" type="button"
data-modal-url="../admin/modals/category/category_add.php?category=Ticket">
<i class="fas fa-fw fa-plus"></i>
</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col">
<div class="mb-3">
<label>Assigned to</label>
<div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-user-check"></i></span>
<select class="form-select select2" name="assigned_to">
<option value="0">Unassigned</option>
<?php
$sql = mysqli_query(
$mysqli,
"SELECT user_id, user_name, user_avatar FROM users
WHERE user_role_id > 1
AND user_type = 1
AND user_status = 1
AND user_archived_at IS NULL
ORDER BY user_name ASC"
);
while ($row = mysqli_fetch_assoc($sql)) {
$user_id = intval($row['user_id']);
$user_name = escapeHtml($row['user_name']); ?>
<option <?php if ($ticket_assigned_to === $user_id) { echo "selected"; } ?> value="<?= $user_id ?>"<?= selectOptionUser($row['user_id'], $row['user_name'], $row['user_avatar'] ?? '') ?>><?= $user_name ?></option>
<?php } ?>
</select>
</div>
</div>
</div>
<div class="col">
<div class="mb-3">
<label>Due date</label>
<div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-calendar-check"></i></span>
<input type="datetime-local" class="form-control" name="due" value="<?= $ticket_due_at ?>">
</div>
</div>
</div>
</div>
<?php if ($config_module_enable_accounting && lookupUserPermission("module_sales") >= 2) { ?>
<div class="mb-3">
<div class="form-check form-switch">
<input type="checkbox" class="form-check-input" name="billable" <?php if ($ticket_billable == 1) { echo "checked"; } ?> value="1" id="billableSwitch<?= $ticket_id ?>">
<label class="form-check-label" for="billableSwitch<?= $ticket_id ?>">Billable</label>
</div>
</div>
<?php } else { ?>
<!-- No switch for this user, so carry the current value through untouched -->
<input type="hidden" name="billable" value="<?= $ticket_billable ?>">
<?php } ?>
</div>
<?php if ($client_id) { ?>
<div class="tab-pane fade" id="pills-contacts">
<div class="mb-3">
<label>Contact</label>
<div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-user"></i></span>
<select class="form-select select2" name="contact_id">
<option value="0">No contact</option>
<?php
$sql_client_contacts_select = mysqli_query($mysqli, "SELECT contact_id, contact_name, contact_client_id, contact_photo, contact_title, contact_primary, contact_technical FROM contacts WHERE contact_client_id = $client_id AND contact_archived_at IS NULL ORDER BY contact_primary DESC, contact_technical DESC, contact_name ASC");
while ($row = mysqli_fetch_assoc($sql_client_contacts_select)) {
$contact_id_select = intval($row['contact_id']);
$contact_name_select = escapeHtml($row['contact_name']);
?>
<option value="<?= $contact_id_select ?>" <?php if ($contact_id_select == $contact_id) { echo "selected"; } ?><?= selectOptionContact($row['contact_client_id'] ?? 0, $row['contact_name'], $row['contact_photo'] ?? '', contactOptionDescription($row), $row['contact_id'] ?? 0) ?>><?= $contact_name_select ?></option>
<?php } ?>
</select>
</div>
</div>
<?php if (!empty($config_smtp_provider)) { ?>
<div class="mb-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="contact_notify" value="1" id="checkNotifyContact">
<label class="form-check-label" for="checkNotifyContact">
Email the contact a link to this ticket
</label>
</div>
</div>
<?php } ?>
</div>
<div class="tab-pane fade" id="pills-assignment">
<div class="mb-3">
<label>Primary asset</label>
<div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-desktop"></i></span>
<select class="form-select select2" name="asset_id">
<option value="0">- None -</option>
<?php
$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_name ASC");
while ($row = mysqli_fetch_assoc($sql_assets)) {
$asset_id_select = intval($row['asset_id']);
$asset_name_select = escapeHtml($row['asset_name']);
$asset_contact_name_select = escapeHtml($row['contact_name']);
?>
<option <?php if ($asset_id == $asset_id_select) { echo "selected"; } ?> value="<?= $asset_id_select ?>"<?= selectOptionAsset($row['asset_type'] ?? '', trim(($row['asset_make'] ?? '') . ' ' . ($row['asset_model'] ?? ''))) ?>><?= $asset_contact_name_select ? "$asset_name_select ($asset_contact_name_select)" : $asset_name_select ?></option>
<?php
}
?>
</select>
</div>
</div>
<div class="mb-3">
<label>Other assets</label>
<div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-desktop"></i></span>
<select class="form-select select2" name="additional_assets[]" data-placeholder="- Any other assets involved -" multiple>
<option value=""></option>
<?php
$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_id != $asset_id AND asset_archived_at IS NULL ORDER BY asset_name ASC");
while ($row = mysqli_fetch_assoc($sql_assets)) {
$asset_id_select = intval($row['asset_id']);
$asset_name_select = escapeHtml($row['asset_name']);
$asset_contact_name_select = escapeHtml($row['contact_name']);
?>
<option value="<?= $asset_id_select ?>"
<?php if (in_array($asset_id_select, $additional_assets_array)) { echo "selected"; } ?>
<?= selectOptionAsset($row['asset_type'] ?? '', trim(($row['asset_make'] ?? '') . ' ' . ($row['asset_model'] ?? ''))) ?>><?= $asset_contact_name_select ? "$asset_name_select ($asset_contact_name_select)" : $asset_name_select ?></option>
<?php } ?>
</select>
</div>
</div>
<div class="mb-3">
<label>Location</label>
<div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-map-marker-alt"></i></span>
<select class="form-select select2" name="location_id">
<option value="0">- None -</option>
<?php
$sql_locations = mysqli_query($mysqli, "SELECT location_id, location_name FROM locations WHERE location_client_id = $client_id AND location_archived_at IS NULL ORDER BY location_name ASC");
while ($row = mysqli_fetch_assoc($sql_locations)) {
$location_id_select = intval($row['location_id']);
$location_name_select = escapeHtml($row['location_name']);
?>
<option <?php if ($location_id == $location_id_select) { echo "selected"; } ?> value="<?= $location_id_select ?>"><?= $location_name_select ?></option>
<?php
}
?>
</select>
</div>
</div>
<div class="row">
<div class="col">
<div class="mb-3">
<label>Vendor</label>
<div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-building"></i></span>
<select class="form-select select2" name="vendor_id">
<option value="0">- None -</option>
<?php
$sql_vendors = mysqli_query($mysqli, "SELECT vendor_id, vendor_name FROM vendors WHERE vendor_client_id = $client_id AND vendor_archived_at IS NULL ORDER BY vendor_name ASC");
while ($row = mysqli_fetch_assoc($sql_vendors)) {
$vendor_id_select = intval($row['vendor_id']);
$vendor_name_select = escapeHtml($row['vendor_name']);
?>
<option <?php if ($vendor_id == $vendor_id_select) { echo "selected"; } ?> value="<?= $vendor_id_select ?>"><?= $vendor_name_select ?></option>
<?php
}
?>
</select>
</div>
</div>
</div>
<div class="col">
<div class="mb-3">
<label>Vendor's ticket number</label>
<div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-tag"></i></span>
<input type="text" class="form-control" name="vendor_ticket_number" placeholder="Their reference for this issue" maxlength="255" value="<?= $ticket_vendor_ticket_number ?>">
</div>
</div>
</div>
</div>
<div class="mb-3">
<label>Project</label>
<div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-project-diagram"></i></span>
<select class="form-select select2" name="project_id">
<option value="0">- None -</option>
<?php
$sql_projects = mysqli_query($mysqli, "SELECT project_id, project_name FROM projects WHERE (project_client_id = $client_id OR project_client_id = 0) AND project_completed_at IS NULL AND project_archived_at IS NULL ORDER BY project_name ASC");
while ($row = mysqli_fetch_assoc($sql_projects)) {
$project_id_select = intval($row['project_id']);
$project_name_select = escapeHtml($row['project_name']); ?>
<option <?php if ($project_id == $project_id_select) { echo "selected"; } ?> value="<?= $project_id_select ?>"><?= $project_name_select ?></option>
<?php } ?>
</select>
</div>
</div>
</div>
<?php } // End client_id check ?>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="edit_ticket" class="btn btn-primary text-bold"><i class="fa fa-check me-2"></i>Save changes</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fa fa-times me-2"></i>Cancel</button>
</div>
</form>
<?php
require_once '../../../includes/modal_footer.php';