Restore missing UI templates
This commit is contained in:
@@ -0,0 +1 @@
|
||||
{% extends "ui/templates/base/layout.html" %}{% block content %}<div class="mx-auto max-w-lg rounded-3xl bg-white p-6 shadow-soft"><h2 class="text-xl font-semibold">Set a New Password</h2><p class="mt-1 text-sm text-slate-500">Your account requires a password update before proceeding.</p><form method="post" class="mt-6 space-y-4"><input type="hidden" name="csrf_token" value="{{ csrf_token }}" /><div><label class="mb-2 block text-sm font-medium text-slate-700">New Password</label><input type="password" name="password" class="w-full rounded-2xl border border-slate-300 px-4 py-2.5 text-sm" required /></div><button class="rounded-xl bg-brand-600 px-4 py-2.5 text-sm font-medium text-white">Save password</button></form></div>{% endblock %}
|
||||
@@ -0,0 +1,20 @@
|
||||
{% extends "ui/templates/base/layout.html" %}
|
||||
{% block content %}
|
||||
<div class="mx-auto max-w-lg rounded-3xl bg-white p-6 shadow-soft">
|
||||
<h2 class="text-xl font-semibold text-slate-900">Accept Invite</h2>
|
||||
<p class="mt-1 text-sm text-slate-500">Set your password to activate your account.</p>
|
||||
{% if flash %}
|
||||
<div class="mt-4 rounded-2xl border border-red-200 bg-red-50 p-3 text-sm text-red-800">{{ flash }}</div>
|
||||
{% endif %}
|
||||
<form method="post" class="mt-6 space-y-4">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token }}" />
|
||||
<input type="hidden" name="token" value="{{ token }}" />
|
||||
<div>
|
||||
<label class="mb-2 block text-sm font-medium text-slate-700">Password</label>
|
||||
<input type="password" name="password" class="w-full rounded-2xl border border-slate-300 px-4 py-2.5 text-sm" required />
|
||||
<p class="mt-1 text-xs text-slate-500">Use at least 8 characters with letters and numbers.</p>
|
||||
</div>
|
||||
<button class="rounded-xl bg-brand-600 px-4 py-2.5 text-sm font-medium text-white hover:bg-brand-700">Accept invite</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,23 @@
|
||||
{% extends "ui/templates/base/layout.html" %}
|
||||
{% block content %}
|
||||
<div class="max-w-md rounded-2xl bg-white border p-6">
|
||||
<h1 class="text-2xl font-semibold">Login</h1>
|
||||
<p class="text-slate-600 mt-1 text-sm">Use bootstrap admin (first run). Lockout + CSRF are enabled.</p>
|
||||
|
||||
<form method="post" class="mt-5 grid gap-3">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token }}" />
|
||||
|
||||
<label class="grid gap-1">
|
||||
<span class="text-sm text-slate-600">Email</span>
|
||||
<input class="border rounded-xl px-3 py-2" name="email" type="email" required />
|
||||
</label>
|
||||
|
||||
<label class="grid gap-1">
|
||||
<span class="text-sm text-slate-600">Password</span>
|
||||
<input class="border rounded-xl px-3 py-2" name="password" type="password" required />
|
||||
</label>
|
||||
|
||||
<button class="rounded-xl bg-slate-900 text-white px-4 py-2 text-sm mt-2" type="submit">Sign in</button>
|
||||
</form><div class="mt-4 text-sm"><a class="text-brand-700 underline" href="/forgot-password">Forgot password?</a></div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,5 @@
|
||||
{% extends "ui/templates/base/layout.html" %}
|
||||
{% block content %}
|
||||
<h1 class="text-2xl font-semibold">Create User</h1>
|
||||
<p class="text-slate-600 mt-1">User creation UI will be expanded in the next hardening step.</p>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,303 @@
|
||||
{% extends "ui/templates/base/layout.html" %}
|
||||
{% block content %}
|
||||
<div class="grid gap-6 lg:grid-cols-[minmax(0,1fr)_320px]">
|
||||
<div class="rounded-3xl bg-white p-6 shadow-soft">
|
||||
<h2 class="text-xl font-semibold">{{ "Create User" if form_mode == "create" else "Edit User" }}</h2>
|
||||
<p class="mt-1 text-sm text-slate-500">Audit Firm and branch scope follow the logged-in user's access level.</p>
|
||||
|
||||
{% if flash %}
|
||||
<div class="mt-4 rounded-2xl border border-rose-200 bg-rose-50 px-4 py-3 text-sm text-rose-800">
|
||||
{{ flash }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form method="post" class="mt-6 space-y-5">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token }}" />
|
||||
|
||||
<div class="grid gap-5 md:grid-cols-2">
|
||||
<div>
|
||||
<label class="mb-2 block text-sm font-medium text-slate-700">Email</label>
|
||||
<input
|
||||
{% if form_mode == 'edit' %}readonly{% endif %}
|
||||
type="email"
|
||||
name="email"
|
||||
value="{{ user_obj.email if user_obj else '' }}"
|
||||
class="w-full rounded-2xl border border-slate-300 px-4 py-2.5 text-sm {% if form_mode == 'edit' %}bg-slate-100{% endif %}"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="mb-2 block text-sm font-medium text-slate-700">Full Name</label>
|
||||
<input
|
||||
type="text"
|
||||
name="full_name"
|
||||
value="{{ user_obj.full_name if user_obj else '' }}"
|
||||
class="w-full rounded-2xl border border-slate-300 px-4 py-2.5 text-sm"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-5 md:grid-cols-4">
|
||||
<div>
|
||||
<label class="mb-2 block text-sm font-medium text-slate-700">
|
||||
Password {% if form_mode == 'edit' %}<span class="text-slate-400">(leave blank to keep existing)</span>{% endif %}
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
name="password"
|
||||
class="w-full rounded-2xl border border-slate-300 px-4 py-2.5 text-sm"
|
||||
{% if form_mode == 'create' %}required{% endif %}
|
||||
/>
|
||||
</div>
|
||||
<div class="flex items-end">
|
||||
<label class="inline-flex items-center gap-3 rounded-2xl border border-slate-200 px-4 py-3 text-sm font-medium text-slate-700">
|
||||
<input type="checkbox" name="is_active" {% if user_obj is none or user_obj.is_active %}checked{% endif %} class="h-4 w-4 rounded border-slate-300" />
|
||||
User is active
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex items-end">
|
||||
<label class="inline-flex items-center gap-3 rounded-2xl border border-slate-200 px-4 py-3 text-sm font-medium text-slate-700">
|
||||
<input type="checkbox" name="allow_login" {% if user_obj is none or user_obj.allow_login %}checked{% endif %} class="h-4 w-4 rounded border-slate-300" />
|
||||
Allow login
|
||||
</label>
|
||||
</div>
|
||||
{% if form_mode == "create" %}
|
||||
<div class="flex items-end">
|
||||
<label class="inline-flex items-center gap-3 rounded-2xl border border-slate-200 px-4 py-3 text-sm font-medium text-slate-700">
|
||||
<input type="checkbox" name="invite_user" class="h-4 w-4 rounded border-slate-300" />
|
||||
Invite user
|
||||
</label>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="grid gap-5 md:grid-cols-2">
|
||||
<div>
|
||||
<label class="mb-2 block text-sm font-medium text-slate-700">Audit Firm</label>
|
||||
|
||||
{% if can_change_tenant %}
|
||||
<select
|
||||
id="tenant_id"
|
||||
name="tenant_id"
|
||||
class="w-full rounded-2xl border border-slate-300 px-4 py-2.5 text-sm"
|
||||
required
|
||||
>
|
||||
{% for tenant in tenants %}
|
||||
<option value="{{ tenant.id }}" {% if selected_tenant_id == tenant.id %}selected{% endif %}>
|
||||
{{ tenant.name }} ({{ tenant.code }})
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% else %}
|
||||
<input type="hidden" name="tenant_id" value="{{ selected_tenant_id }}" />
|
||||
<div class="rounded-2xl border border-slate-200 bg-slate-50 px-4 py-2.5 text-sm text-slate-700">
|
||||
{{ selected_tenant_name or current_user.tenant_id }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="mb-2 block text-sm font-medium text-slate-700">Branch</label>
|
||||
|
||||
{% if scope.branch_scoped %}
|
||||
<input type="hidden" name="branch_id" value="{{ selected_branch_id }}" />
|
||||
<div class="rounded-2xl border border-slate-200 bg-slate-50 px-4 py-2.5 text-sm text-slate-700">
|
||||
{{ selected_branch_name or current_user.branch_id }}
|
||||
</div>
|
||||
{% else %}
|
||||
<select
|
||||
id="branch_id"
|
||||
name="branch_id"
|
||||
class="w-full rounded-2xl border border-slate-300 px-4 py-2.5 text-sm"
|
||||
required
|
||||
>
|
||||
{% for branch in branches %}
|
||||
<option value="{{ branch.id }}" {% if selected_branch_id == branch.id %}selected{% endif %}>
|
||||
{{ branch.name }} ({{ branch.code }})
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="mb-3 block text-sm font-medium text-slate-700">Role Assignment</label>
|
||||
<div class="grid gap-3 sm:grid-cols-2">
|
||||
{% for role in roles %}
|
||||
<label class="flex items-start gap-3 rounded-2xl border border-slate-200 p-4 hover:border-brand-300 hover:bg-brand-50/50">
|
||||
<input type="checkbox" name="role_ids" value="{{ role.id }}" data-role-name="{{ role.name }}" {% if role.id in assigned_role_ids %}checked{% endif %} class="mt-1 h-4 w-4 rounded border-slate-300" />
|
||||
<span>
|
||||
<span class="block text-sm font-medium text-slate-800">{{ role.name }}</span>
|
||||
<span class="block text-xs text-slate-500">{{ 'Assignable role' if role.is_active else 'Inactive role' }}</span>
|
||||
</span>
|
||||
</label>
|
||||
{% else %}
|
||||
<div class="rounded-2xl border border-amber-200 bg-amber-50 p-4 text-sm text-amber-900">
|
||||
No roles are available for your scope.
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="employee-auto-profile-box" class="rounded-3xl border border-emerald-200 bg-emerald-50/60 p-5">
|
||||
<div class="flex items-start justify-between gap-4">
|
||||
<div>
|
||||
<h3 class="text-sm font-semibold uppercase tracking-[0.14em] text-emerald-700">Employee profile auto-link</h3>
|
||||
<p class="mt-2 text-sm text-emerald-900">
|
||||
For internal firm roles such as Firm Admin, Partner, Manager, Branch Manager and Staff, an employee profile will be created and linked automatically.
|
||||
System Admin, Client and Consultant-only users are excluded.
|
||||
</p>
|
||||
</div>
|
||||
<span class="rounded-full bg-white px-3 py-1 text-xs font-semibold text-emerald-700 shadow-sm">Phase 7F</span>
|
||||
</div>
|
||||
|
||||
<div class="mt-5 grid gap-5 md:grid-cols-3">
|
||||
<div>
|
||||
<label class="mb-2 block text-sm font-medium text-slate-700">Employee Code</label>
|
||||
<input
|
||||
type="text"
|
||||
name="employee_code"
|
||||
class="w-full rounded-2xl border border-slate-300 px-4 py-2.5 text-sm"
|
||||
placeholder="Auto if blank, e.g. EMP00001"
|
||||
/>
|
||||
<p class="mt-1 text-xs text-slate-500">Leave blank to auto-generate.</p>
|
||||
</div>
|
||||
<div>
|
||||
<label class="mb-2 block text-sm font-medium text-slate-700">Mobile</label>
|
||||
<input
|
||||
type="text"
|
||||
name="employee_mobile"
|
||||
class="w-full rounded-2xl border border-slate-300 px-4 py-2.5 text-sm"
|
||||
placeholder="Optional"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="mb-2 block text-sm font-medium text-slate-700">Date of Joining</label>
|
||||
<input
|
||||
type="date"
|
||||
name="employee_date_of_joining"
|
||||
class="w-full rounded-2xl border border-slate-300 px-4 py-2.5 text-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-5 grid gap-5 md:grid-cols-3">
|
||||
<div>
|
||||
<label class="mb-2 block text-sm font-medium text-slate-700">Department</label>
|
||||
<input
|
||||
type="text"
|
||||
name="employee_department"
|
||||
class="w-full rounded-2xl border border-slate-300 px-4 py-2.5 text-sm"
|
||||
placeholder="Audit / GST / Admin"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="mb-2 block text-sm font-medium text-slate-700">Designation</label>
|
||||
<input
|
||||
type="text"
|
||||
name="employee_designation"
|
||||
class="w-full rounded-2xl border border-slate-300 px-4 py-2.5 text-sm"
|
||||
placeholder="Staff / Manager / Partner"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="mb-2 block text-sm font-medium text-slate-700">Employment Type</label>
|
||||
<select name="employee_employment_type" class="w-full rounded-2xl border border-slate-300 px-4 py-2.5 text-sm">
|
||||
<option value="full_time">Full Time</option>
|
||||
<option value="part_time">Part Time</option>
|
||||
<option value="article_assistant">Article Assistant</option>
|
||||
<option value="intern">Intern</option>
|
||||
<option value="contract">Contract</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 rounded-2xl border border-amber-200 bg-amber-50 px-4 py-3 text-xs text-amber-900">
|
||||
Branch is mandatory for internal employee users because attendance geofencing, branch timezone, leave approvals and work allocation depend on the employee's branch.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-end gap-3 border-t border-slate-200 pt-5">
|
||||
<a href="/system-settings/users" class="rounded-xl border border-slate-300 px-4 py-2.5 text-sm font-medium text-slate-700 hover:bg-slate-50">Cancel</a>
|
||||
<button type="submit" class="rounded-xl bg-brand-600 px-4 py-2.5 text-sm font-medium text-white hover:bg-brand-700">
|
||||
{{ 'Create User' if form_mode == 'create' else 'Save Changes' }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="rounded-3xl bg-white p-6 shadow-soft">
|
||||
<h3 class="text-sm font-semibold uppercase tracking-[0.16em] text-slate-500">Scope rules</h3>
|
||||
<ul class="mt-4 space-y-3 text-sm text-slate-600">
|
||||
<li>System Admin can create and manage users across all audit firms and branches.</li>
|
||||
<li>Firm Admin can create and manage users only inside their own audit firm and its branches.</li>
|
||||
<li>Partner and Branch Manager cannot create users in this version.</li>
|
||||
<li>Audit Firm changes refresh branch options automatically for System Admin.</li>
|
||||
<li>Role assignment is limited by the creator's scope.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const box = document.getElementById("employee-auto-profile-box");
|
||||
const roleInputs = Array.from(document.querySelectorAll('input[name="role_ids"]'));
|
||||
const internalRoles = new Set(["Firm Admin", "Partner", "Manager", "Branch Manager", "Staff"]);
|
||||
|
||||
function refreshEmployeeBox() {
|
||||
if (!box) return;
|
||||
const selectedNames = roleInputs
|
||||
.filter((input) => input.checked)
|
||||
.map((input) => input.getAttribute("data-role-name") || "");
|
||||
const needsEmployee = selectedNames.some((name) => internalRoles.has(name)) && !selectedNames.includes("System Admin");
|
||||
box.classList.toggle("hidden", !needsEmployee);
|
||||
}
|
||||
|
||||
roleInputs.forEach((input) => input.addEventListener("change", refreshEmployeeBox));
|
||||
refreshEmployeeBox();
|
||||
});
|
||||
</script>
|
||||
|
||||
{% if can_change_tenant and not scope.branch_scoped %}
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const tenantSelect = document.getElementById("tenant_id");
|
||||
const branchSelect = document.getElementById("branch_id");
|
||||
|
||||
if (!tenantSelect || !branchSelect) return;
|
||||
|
||||
async function loadBranches(tenantId) {
|
||||
branchSelect.innerHTML = '<option value="">Select branch</option>';
|
||||
|
||||
if (!tenantId) return;
|
||||
|
||||
try {
|
||||
const response = await fetch(`/system-settings/users/branch-options?tenant_id=${encodeURIComponent(tenantId)}`, {
|
||||
headers: {
|
||||
"X-Requested-With": "XMLHttpRequest"
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) return;
|
||||
|
||||
const rows = await response.json();
|
||||
for (const row of rows) {
|
||||
const option = document.createElement("option");
|
||||
option.value = row.id;
|
||||
option.textContent = `${row.name} (${row.code})`;
|
||||
branchSelect.appendChild(option);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Failed to load branches:", err);
|
||||
}
|
||||
}
|
||||
|
||||
tenantSelect.addEventListener("change", function () {
|
||||
loadBranches(this.value);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,56 @@
|
||||
{% extends "ui/templates/base/layout.html" %}
|
||||
{% import "ui/templates/components/macros.html" as ui %}
|
||||
{% block content %}
|
||||
<div class="space-y-6">
|
||||
{{ ui.page_shell('User Management', 'Audit Firm- and branch-scoped users with filters, pagination, lifecycle controls, role bindings, and invite actions.', '<a href="/system-settings/users/new" class="inline-flex items-center justify-center rounded-xl bg-brand-600 px-4 py-2.5 text-sm font-medium text-white hover:bg-brand-700">Create user</a>' if can_manage_users(current_user, current_user_permissions or []) else '') }}
|
||||
{% if flash %}{{ ui.alert(flash) }}{% endif %}
|
||||
|
||||
<div class="overflow-hidden rounded-3xl bg-white shadow-soft">
|
||||
{{ ui.search_bar('/system-settings/users', filters.q, filters.per_page) }}
|
||||
{% if users %}
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-slate-200 text-sm">
|
||||
<thead class="bg-slate-50">
|
||||
<tr>
|
||||
<th class="px-4 py-3 text-left font-semibold text-slate-600">User</th>
|
||||
<th class="px-4 py-3 text-left font-semibold text-slate-600">Audit Firm / Branch</th>
|
||||
<th class="px-4 py-3 text-left font-semibold text-slate-600">Roles</th>
|
||||
<th class="px-4 py-3 text-left font-semibold text-slate-600">Lifecycle</th>
|
||||
<th class="px-4 py-3 text-right font-semibold text-slate-600">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-slate-100">
|
||||
{% for row in users %}
|
||||
<tr>
|
||||
<td class="px-4 py-4"><div class="font-medium text-slate-900">{{ row.full_name or '-' }}</div><div class="text-slate-500">{{ row.email }}</div></td>
|
||||
<td class="px-4 py-4 text-slate-600"><div>{{ tenants[row.tenant_id].name if row.tenant_id in tenants else '-' }}</div><div class="text-xs text-slate-400">{{ branches[row.branch_id].name if row.branch_id in branches else '-' }}</div></td>
|
||||
<td class="px-4 py-4"><div class="flex flex-wrap gap-2">{% for role_name in user_roles.get(row.id, []) %}{{ ui.badge(role_name, 'brand') }}{% else %}<span class="text-slate-400">No roles</span>{% endfor %}</div></td>
|
||||
<td class="px-4 py-4"><div class="flex flex-wrap gap-2">{% if row.deleted_at %}{{ ui.badge('Deleted') }}{% endif %}{% if row.is_active %}{{ ui.badge('Active','emerald') }}{% else %}{{ ui.badge('Inactive','rose') }}{% endif %}{% if row.allow_login %}{{ ui.badge('Login enabled','sky') }}{% else %}{{ ui.badge('Login disabled','amber') }}{% endif %}{% if row.is_locked %}{{ ui.badge('Locked','amber') }}{% else %}{{ ui.badge('Unlocked') }}{% endif %}</div></td>
|
||||
<td class="px-4 py-4 text-right">
|
||||
{% if "users.manage" in current_user_permissions %}
|
||||
<div class="flex flex-wrap justify-end gap-2">
|
||||
<a href="/system-settings/users/{{ row.id }}/edit" class="inline-flex rounded-xl border border-slate-300 px-3 py-2 text-xs font-medium text-slate-700 hover:bg-slate-50">Edit</a>
|
||||
{% if row.deleted_at %}
|
||||
<form method="post" action="/system-settings/users/{{ row.id }}/restore"><input type="hidden" name="csrf_token" value="{{ csrf_token }}" /><button class="inline-flex rounded-xl border border-slate-300 px-3 py-2 text-xs font-medium text-slate-700 hover:bg-slate-50">Restore</button></form>
|
||||
{% else %}
|
||||
{% if row.is_active %}<form method="post" action="/system-settings/users/{{ row.id }}/deactivate"><input type="hidden" name="csrf_token" value="{{ csrf_token }}" /><button class="inline-flex rounded-xl border border-slate-300 px-3 py-2 text-xs font-medium text-slate-700 hover:bg-slate-50">Deactivate</button></form>{% else %}<form method="post" action="/system-settings/users/{{ row.id }}/activate"><input type="hidden" name="csrf_token" value="{{ csrf_token }}" /><button class="inline-flex rounded-xl border border-slate-300 px-3 py-2 text-xs font-medium text-slate-700 hover:bg-slate-50">Activate</button></form>{% endif %}
|
||||
{% if row.allow_login %}<form method="post" action="/system-settings/users/{{ row.id }}/disable-login"><input type="hidden" name="csrf_token" value="{{ csrf_token }}" /><button class="inline-flex rounded-xl border border-slate-300 px-3 py-2 text-xs font-medium text-slate-700 hover:bg-slate-50">Disable Login</button></form>{% else %}<form method="post" action="/system-settings/users/{{ row.id }}/enable-login"><input type="hidden" name="csrf_token" value="{{ csrf_token }}" /><button class="inline-flex rounded-xl border border-slate-300 px-3 py-2 text-xs font-medium text-slate-700 hover:bg-slate-50">Enable Login</button></form>{% endif %}
|
||||
{% if row.is_locked %}<form method="post" action="/system-settings/users/{{ row.id }}/unlock"><input type="hidden" name="csrf_token" value="{{ csrf_token }}" /><button class="inline-flex rounded-xl border border-slate-300 px-3 py-2 text-xs font-medium text-slate-700 hover:bg-slate-50">Unlock</button></form>{% else %}<form method="post" action="/system-settings/users/{{ row.id }}/lock"><input type="hidden" name="csrf_token" value="{{ csrf_token }}" /><button class="inline-flex rounded-xl border border-slate-300 px-3 py-2 text-xs font-medium text-slate-700 hover:bg-slate-50">Lock</button></form>{% endif %}
|
||||
{% if "users.invite" in current_user_permissions and row.allow_login and not row.deleted_at %}<form method="post" action="/system-settings/users/{{ row.id }}/invite"><input type="hidden" name="csrf_token" value="{{ csrf_token }}" /><button class="inline-flex rounded-xl border border-slate-300 px-3 py-2 text-xs font-medium text-slate-700 hover:bg-slate-50">Invite</button></form>{% endif %}
|
||||
<form method="post" action="/system-settings/users/{{ row.id }}/delete"><input type="hidden" name="csrf_token" value="{{ csrf_token }}" /><button class="inline-flex rounded-xl border border-rose-300 px-3 py-2 text-xs font-medium text-rose-700 hover:bg-rose-50">Soft Delete</button></form>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{ ui.pagination(users_page, '/system-settings/users', request.url.query) }}
|
||||
{% else %}
|
||||
<div class="p-6">{{ ui.empty_state('No users matched the current filter.', 'Try a different search term or create a new user.') }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user