Clients list: tags under industry with add-tag pill, whole client cell opens the client, gray header and select column

This commit is contained in:
johnnyq
2026-10-10 14:43:35 -04:00
parent 362a6dcfd2
commit 2b2a6c8c42
4 changed files with 67 additions and 19 deletions

View File

@@ -446,9 +446,9 @@ $total_leads = intval($row['leads']);
<!-- position-relative keeps the visually-hidden labels inside the scroll box on narrow screens -->
<div class="table-responsive position-relative">
<table class="table table-hover align-middle mb-0 itflow-clients">
<thead class="<?php if ($num_rows[0] == 0) { echo "d-none"; } ?>">
<thead class="<?php if ($num_rows[0] == 0) { echo "d-none"; } ?> table-light">
<tr>
<th class="checkbox-column">
<th class="checkbox-column border-end">
<div class="form-check">
<input class="form-check-input" id="selectAllCheckbox" type="checkbox" title="Select all on this page">
</div>
@@ -502,22 +502,23 @@ $total_leads = intval($row['leads']);
}
?>
<tr>
<td class="checkbox-column">
<td class="checkbox-column bg-light border-end">
<div class="form-check">
<input class="form-check-input bulk-select" type="checkbox" name="client_ids[]" value="<?= $client_id ?>" aria-label="Select <?= $client_name ?>">
</div>
</td>
<td>
<!-- The whole cell opens the client (stretched-link); the tag pills and + sit above it -->
<td class="position-relative itflow-clients-client">
<div class="d-flex align-items-center gap-3">
<span class="itflow-client-avatar d-none d-sm-inline-flex bg-<?= $client_tone ?>-subtle text-<?= $client_tone ?>-emphasis" aria-hidden="true"><?= $client_initials ?></span>
<div class="overflow-hidden">
<a class="fw-semibold" href="client_overview.php?client_id=<?= $client_id ?>"><?= $client_name ?></a>
<?php if ($client_type || !empty($client_tags[$client_id])) { ?>
<div class="d-flex flex-wrap align-items-center column-gap-2 row-gap-1 small text-body-secondary mt-1">
<?php if ($client_type) { ?>
<span><?= $client_type ?></span>
<?php } ?>
<a class="fw-semibold stretched-link" href="client_overview.php?client_id=<?= $client_id ?>"><?= $client_name ?></a>
<?php if ($client_type) { ?>
<div class="small text-body-secondary"><?= $client_type ?></div>
<?php } ?>
<?php if (!empty($client_tags[$client_id]) || $can_edit) { ?>
<div class="d-flex flex-wrap align-items-center gap-1 mt-1">
<?php foreach ($client_tags[$client_id] ?? [] as $tag) {
$tag_id = intval($tag['tag_id']);
$tag_name = escapeHtml($tag['tag_name']);
@@ -528,6 +529,11 @@ $total_leads = intval($row['leads']);
<i class="fas fa-fw fa-<?= $tag_icon ?>" aria-hidden="true"></i><?= $tag_name ?>
</a>
<?php } ?>
<?php if ($can_edit) { ?>
<a href="#" class="itflow-client-tag itflow-client-tag-add ajax-modal" data-modal-url="modals/client/client_bulk_assign_tags.php?client_ids[]=<?= $client_id ?>" title="Add tags to <?= $client_name ?>">
<i class="fas fa-fw fa-plus" aria-hidden="true"></i><?php if (empty($client_tags[$client_id])) { ?>Tag<?php } else { ?><span class="visually-hidden">Add tags</span><?php } ?>
</a>
<?php } ?>
</div>
<?php } ?>
</div>

View File

@@ -2,16 +2,32 @@
require_once '../../../includes/modal_header.php';
enforceUserPermission('module_client', 2);
$client_ids = array_map('intval', $_GET['client_ids'] ?? []);
$count = count($client_ids);
// Opened from the + beside one client's tags on the clients list: name the
// client, and offer only the tags it doesn't already have
$single_client_id = $count === 1 ? $client_ids[0] : 0;
$existing_tags_query = '';
if ($single_client_id) {
enforceClientAccess($single_client_id);
$single_client_name = escapeHtml(getFieldById('clients', $single_client_id, 'client_name'));
$existing_tags_query = "AND tag_id NOT IN (SELECT tag_id FROM client_tags WHERE client_id = $single_client_id)";
}
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-tags me-2"></i>Assign Tags for <strong><?= $count ?></strong> Clients</h5>
<?php if ($single_client_id) { ?>
<h5 class="modal-title text-truncate"><i class="fa fa-fw fa-tags me-2"></i>Add Tags to <strong><?= $single_client_name ?></strong></h5>
<?php } else { ?>
<h5 class="modal-title"><i class="fa fa-fw fa-tags me-2"></i>Assign Tags for <strong><?= $count ?></strong> Clients</h5>
<?php } ?>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
@@ -22,10 +38,12 @@ ob_start();
<div class="modal-body">
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" name="bulk_remove_tags" value="1">
<label class="form-check-label text-danger">Remove Existing Tags</label>
</div>
<?php if (!$single_client_id) { ?>
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" name="bulk_remove_tags" value="1">
<label class="form-check-label text-danger">Remove Existing Tags</label>
</div>
<?php } ?>
<div class="mb-3">
<label>Tags</label>
@@ -34,7 +52,7 @@ ob_start();
<select class="form-select select2" name="bulk_tags[]" data-placeholder="Add some tags" multiple>
<?php
$sql_tags_select = mysqli_query($mysqli, "SELECT tag_id, tag_name FROM tags WHERE tag_type = 1 ORDER BY tag_name ASC");
$sql_tags_select = mysqli_query($mysqli, "SELECT tag_id, tag_name FROM tags WHERE tag_type = 1 $existing_tags_query ORDER BY tag_name ASC");
while ($row = mysqli_fetch_assoc($sql_tags_select)) {
$tag_id_select = intval($row['tag_id']);
$tag_name_select = escapeHtml($row['tag_name']);
@@ -44,12 +62,15 @@ ob_start();
</select>
</div>
<?php if ($single_client_id && mysqli_num_rows($sql_tags_select) === 0) { ?>
<div class="form-text">This client already has every client tag.</div>
<?php } ?>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="bulk_assign_client_tags" class="btn btn-primary text-bold"><i class="fas fa-check me-2"></i>Assign</button>
<button type="submit" name="bulk_assign_client_tags" class="btn btn-primary text-bold"><i class="fas fa-check me-2"></i><?= $single_client_id ? 'Add Tags' : 'Assign' ?></button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fa fa-times me-2"></i>Cancel</button>
</div>
</form>

View File

@@ -1044,9 +1044,13 @@ if (isset($_POST['bulk_assign_client_tags'])) {
}
logAudit("Client", "Bulk Edit", "$session_name added tags for $count clients", $client_id);
if ($count === 1) {
flashAlert("Tags added to <strong>" . escapeHtml($row['client_name']) . "</strong>");
} else {
logAudit("Client", "Bulk Edit", "$session_name added tags for $count clients", $client_id);
flashAlert("Assigned tags for <strong>$count</strong> clients");
flashAlert("Assigned tags for <strong>$count</strong> clients");
}
}
redirect();

View File

@@ -2370,6 +2370,23 @@ a.itflow-dash-rail-item:focus-visible {
color: #fff;
filter: brightness(1.1);
}
/* The whole client cell is a link (stretched-link); the pills stay clickable on top of it */
.itflow-clients-client .itflow-client-tag {
position: relative;
z-index: 2;
}
/* "+ Tag" / "+" at the end of the tags - opens Add Tags for that client */
.itflow-client-tag-add {
color: var(--bs-secondary-color);
background-color: transparent;
border: 1px dashed var(--bs-border-color);
}
.itflow-client-tag-add:hover,
.itflow-client-tag-add:focus-visible {
color: var(--itflow-accent, var(--bs-primary));
border-color: currentColor;
filter: none;
}
.itflow-clients-street {
max-width: 16rem;
}