Add admin task category import
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
{% extends "base/layout.html" %}
|
||||
{% block content %}
|
||||
<div class="max-w-6xl mx-auto px-4 py-6 space-y-6">
|
||||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||||
<div>
|
||||
<h1 class="text-2xl font-semibold text-slate-900">Task Categories Import</h1>
|
||||
<p class="mt-1 text-sm text-slate-600">Assign task categories to existing firm task templates for the active tenant. Validation is performed before any update.</p>
|
||||
</div>
|
||||
<a href="/services/task-categories/template.csv" class="inline-flex items-center rounded-lg border border-slate-300 bg-white px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-50">Download CSV template</a>
|
||||
</div>
|
||||
|
||||
<div class="rounded-xl border border-amber-200 bg-amber-50 p-4 text-sm text-amber-900">
|
||||
<strong>Active tenant:</strong> {{ tenant_id }}. Required columns are <code>service_code</code>, <code>task_name</code> and <code>task_category</code>. Matching is intentionally strict so an incorrect category is never applied to the wrong task.
|
||||
</div>
|
||||
|
||||
{% if errors %}
|
||||
<div class="rounded-xl border border-red-200 bg-red-50 p-4">
|
||||
<div class="font-semibold text-red-800">Import not applied</div>
|
||||
<ul class="mt-2 list-disc pl-5 text-sm text-red-700">
|
||||
{% for error in errors %}<li>{{ error }}</li>{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if imported %}
|
||||
<div class="rounded-xl border border-emerald-200 bg-emerald-50 p-4 text-sm text-emerald-800">
|
||||
Task categories were updated successfully.
|
||||
{% if summary and summary.existing_tasks_updated %} {{ summary.existing_tasks_updated }} existing generated task(s) were also synchronized.{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form method="post" enctype="multipart/form-data" class="rounded-xl border border-slate-200 bg-white p-5 shadow-sm space-y-4">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700">CSV or XLSX file</label>
|
||||
<input type="file" name="file" accept=".csv,.xlsx" required class="mt-2 block w-full rounded-lg border border-slate-300 px-3 py-2 text-sm">
|
||||
</div>
|
||||
<label class="flex items-start gap-3 text-sm text-slate-700">
|
||||
<input type="checkbox" name="update_existing_tasks" value="1" class="mt-1 rounded border-slate-300">
|
||||
<span><strong>Also update existing generated tasks.</strong><br><span class="text-slate-500">Leave this unticked if you only want future/generated tasks to use the new categories. Existing completed/history tasks are otherwise left untouched.</span></span>
|
||||
</label>
|
||||
<div class="flex flex-wrap gap-3">
|
||||
<button type="submit" name="action" value="validate" class="rounded-lg border border-slate-300 bg-white px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-50">Validate only</button>
|
||||
<button type="submit" name="action" value="import" class="rounded-lg bg-slate-900 px-4 py-2 text-sm font-medium text-white hover:bg-slate-800">Import now</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% if summary %}
|
||||
<div class="grid grid-cols-2 gap-3 md:grid-cols-4">
|
||||
<div class="rounded-xl border bg-white p-4"><div class="text-xs uppercase text-slate-500">Rows</div><div class="text-xl font-semibold">{{ summary.total }}</div></div>
|
||||
<div class="rounded-xl border bg-white p-4"><div class="text-xs uppercase text-slate-500">Ready</div><div class="text-xl font-semibold">{{ summary.ready }}</div></div>
|
||||
<div class="rounded-xl border bg-white p-4"><div class="text-xs uppercase text-slate-500">Unchanged</div><div class="text-xl font-semibold">{{ summary.unchanged }}</div></div>
|
||||
<div class="rounded-xl border bg-white p-4"><div class="text-xs uppercase text-slate-500">Errors</div><div class="text-xl font-semibold">{{ summary.errors }}</div></div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if results %}
|
||||
<div class="overflow-x-auto rounded-xl border border-slate-200 bg-white shadow-sm">
|
||||
<table class="min-w-full divide-y divide-slate-200 text-sm">
|
||||
<thead class="bg-slate-50 text-left text-xs font-semibold uppercase tracking-wide text-slate-500">
|
||||
<tr><th class="px-4 py-3">Row</th><th class="px-4 py-3">Service</th><th class="px-4 py-3">Task</th><th class="px-4 py-3">Category</th><th class="px-4 py-3">Status</th><th class="px-4 py-3">Result</th></tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-slate-100">
|
||||
{% for row in results %}
|
||||
<tr>
|
||||
<td class="px-4 py-3">{{ row.row_no }}</td><td class="px-4 py-3 font-medium">{{ row.service_code }}</td><td class="px-4 py-3">{{ row.task_name }}</td><td class="px-4 py-3">{{ row.task_category }}</td>
|
||||
<td class="px-4 py-3"><span class="rounded-full px-2 py-1 text-xs {% if row.status == 'error' %}bg-red-100 text-red-700{% elif row.status == 'updated' %}bg-emerald-100 text-emerald-700{% elif row.status == 'ready' %}bg-blue-100 text-blue-700{% else %}bg-slate-100 text-slate-700{% endif %}">{{ row.status }}</span></td>
|
||||
<td class="px-4 py-3 text-slate-600">{{ row.message }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user