Add bulk firm selection to service catalogue
This commit is contained in:
@@ -19,6 +19,20 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if request.query_params.get('bulk_action') == 'enable' or request.query_params.get('bulk_action') == 'disable' %}
|
||||
<div class="rounded-2xl border border-emerald-200 bg-emerald-50 p-4 text-sm text-emerald-800">
|
||||
Bulk update completed: <span class="font-semibold">{{ request.query_params.get('bulk_updated', '0') }}</span> changed,
|
||||
<span class="font-semibold">{{ request.query_params.get('bulk_unchanged', '0') }}</span> already in the requested state,
|
||||
and <span class="font-semibold">{{ request.query_params.get('bulk_skipped', '0') }}</span> skipped.
|
||||
</div>
|
||||
{% elif request.query_params.get('bulk_action') == 'no_selection' %}
|
||||
<div class="rounded-2xl border border-amber-200 bg-amber-50 p-4 text-sm text-amber-800">Select at least one service before applying a bulk action.</div>
|
||||
{% elif request.query_params.get('bulk_action') == 'invalid_branch' %}
|
||||
<div class="rounded-2xl border border-red-200 bg-red-50 p-4 text-sm text-red-800">The selected default branch is not available for the active firm.</div>
|
||||
{% elif request.query_params.get('bulk_action') == 'invalid' %}
|
||||
<div class="rounded-2xl border border-red-200 bg-red-50 p-4 text-sm text-red-800">The requested bulk action is invalid.</div>
|
||||
{% endif %}
|
||||
|
||||
{% if can_manage_firm_services %}
|
||||
<div class="rounded-2xl border border-emerald-200 bg-emerald-50 p-4 text-sm text-emerald-800">
|
||||
Select the services your firm provides. Once selected, you can open <span class="font-semibold">Firm Task Templates</span> and customise tasks for your firm.
|
||||
@@ -59,10 +73,41 @@
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% if can_manage_firm_services %}
|
||||
<form id="bulk-service-form" method="post" action="/services/catalogue/bulk-selection" class="rounded-2xl border border-slate-200 bg-white p-4 shadow-soft">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
||||
<input type="hidden" name="return_q" value="{{ q }}">
|
||||
<input type="hidden" name="return_category_id" value="{{ category_id or '' }}">
|
||||
<input type="hidden" name="return_recurrence_type" value="{{ recurrence_type }}">
|
||||
<input type="hidden" name="return_engagement_type" value="{{ engagement_type }}">
|
||||
<input type="hidden" name="return_page" value="{{ page }}">
|
||||
<input type="hidden" name="return_per_page" value="{{ per_page }}">
|
||||
<div class="flex flex-wrap items-center gap-3">
|
||||
<div class="mr-auto text-sm font-medium text-slate-700"><span id="bulk-selected-count">0</span> services selected</div>
|
||||
{% if branches %}
|
||||
<select name="default_branch_id" class="rounded-xl border border-slate-300 px-3 py-2 text-sm">
|
||||
<option value="">Keep existing branch / no default for new selections</option>
|
||||
{% for branch in branches %}<option value="{{ branch.id }}">{{ branch.name }}</option>{% endfor %}
|
||||
</select>
|
||||
{% else %}
|
||||
<input type="hidden" name="default_branch_id" value="">
|
||||
{% endif %}
|
||||
<button type="submit" name="bulk_action" value="enable" class="rounded-xl bg-emerald-600 px-4 py-2 text-sm font-semibold text-white hover:bg-emerald-700">Select for Firm</button>
|
||||
<button type="submit" name="bulk_action" value="disable" class="rounded-xl border border-amber-300 px-4 py-2 text-sm font-semibold text-amber-700 hover:bg-amber-50">Disable for Firm</button>
|
||||
<button type="button" id="bulk-clear-selection" class="rounded-xl border border-slate-300 px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-50">Clear Selection</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
<div class="overflow-hidden rounded-2xl bg-white shadow-soft">
|
||||
<table class="min-w-full divide-y divide-slate-200">
|
||||
<thead class="bg-slate-50">
|
||||
<tr>
|
||||
{% if can_manage_firm_services %}
|
||||
<th class="w-12 px-4 py-3 text-left">
|
||||
<input id="bulk-select-all-visible" type="checkbox" class="h-4 w-4 rounded border-slate-300 text-brand-600" aria-label="Select all services visible on this page">
|
||||
</th>
|
||||
{% endif %}
|
||||
<th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-slate-500">Code</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-slate-500">Name</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-slate-500">Category</th>
|
||||
@@ -75,6 +120,11 @@
|
||||
{% for row in rows %}
|
||||
{% set selection = firm_selection_by_catalogue.get(row.id) if firm_selection_by_catalogue else None %}
|
||||
<tr>
|
||||
{% if can_manage_firm_services %}
|
||||
<td class="px-4 py-3 align-top">
|
||||
<input type="checkbox" name="service_ids" value="{{ row.id }}" form="bulk-service-form" class="bulk-service-checkbox h-4 w-4 rounded border-slate-300 text-brand-600" aria-label="Select {{ row.service_name }}">
|
||||
</td>
|
||||
{% endif %}
|
||||
<td class="px-4 py-3 text-sm font-medium text-slate-900">{{ row.service_code }}</td>
|
||||
<td class="px-4 py-3 text-sm text-slate-700">
|
||||
<div class="font-medium text-slate-900">{{ row.service_name }}</div>
|
||||
@@ -118,7 +168,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="6" class="px-4 py-8 text-center text-sm text-slate-500">No catalogue services found.</td></tr>
|
||||
<tr><td colspan="{{ 7 if can_manage_firm_services else 6 }}" class="px-4 py-8 text-center text-sm text-slate-500">No catalogue services found.</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -190,4 +240,41 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if can_manage_firm_services %}
|
||||
<script>
|
||||
(() => {
|
||||
const selectAll = document.getElementById('bulk-select-all-visible');
|
||||
const clearButton = document.getElementById('bulk-clear-selection');
|
||||
const countLabel = document.getElementById('bulk-selected-count');
|
||||
const checkboxes = Array.from(document.querySelectorAll('.bulk-service-checkbox'));
|
||||
|
||||
const refreshSelectionState = () => {
|
||||
const selectedCount = checkboxes.filter((checkbox) => checkbox.checked).length;
|
||||
countLabel.textContent = String(selectedCount);
|
||||
if (selectAll) {
|
||||
selectAll.checked = checkboxes.length > 0 && selectedCount === checkboxes.length;
|
||||
selectAll.indeterminate = selectedCount > 0 && selectedCount < checkboxes.length;
|
||||
}
|
||||
};
|
||||
|
||||
if (selectAll) {
|
||||
selectAll.addEventListener('change', () => {
|
||||
checkboxes.forEach((checkbox) => { checkbox.checked = selectAll.checked; });
|
||||
refreshSelectionState();
|
||||
});
|
||||
}
|
||||
|
||||
checkboxes.forEach((checkbox) => checkbox.addEventListener('change', refreshSelectionState));
|
||||
|
||||
if (clearButton) {
|
||||
clearButton.addEventListener('click', () => {
|
||||
checkboxes.forEach((checkbox) => { checkbox.checked = false; });
|
||||
refreshSelectionState();
|
||||
});
|
||||
}
|
||||
|
||||
refreshSelectionState();
|
||||
})();
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user