Save worktree changes

This commit is contained in:
2026-07-25 02:29:19 +01:00
parent 8d3b7d557b
commit db9d718cd8
170 changed files with 11719 additions and 3414 deletions
@@ -0,0 +1,264 @@
<div id="background-tasks-state" data-state-version="{{stateVersion}}" hidden></div>
<div class="page-header">
<div>
<h2>Background tasks</h2>
<p>Queued data refreshes run in the web process. Finished items stay here until you clear them.</p>
</div>
</div>
<div class="row g-3 mb-4">
<div class="col-6 col-xl-3">
<div class="card card-outline card-primary h-100">
<div class="card-body">
<div class="text-muted small text-uppercase">Queued</div>
<div class="fs-3 fw-semibold">{{summary.counts.queued}}</div>
</div>
</div>
</div>
<div class="col-6 col-xl-3">
<div class="card card-outline card-info h-100">
<div class="card-body">
<div class="text-muted small text-uppercase">Running</div>
<div class="fs-3 fw-semibold">{{summary.counts.running}}</div>
</div>
</div>
</div>
<div class="col-6 col-xl-3">
<div class="card card-outline card-danger h-100">
<div class="card-body">
<div class="text-muted small text-uppercase">Failed</div>
<div class="fs-3 fw-semibold">{{summary.counts.failed}}</div>
</div>
</div>
</div>
<div class="col-6 col-xl-3">
<div class="card card-outline card-success h-100">
<div class="card-body">
<div class="text-muted small text-uppercase">Finished</div>
<div class="fs-3 fw-semibold">{{summary.counts.completed}}</div>
</div>
</div>
</div>
</div>
<div class="card card-outline card-primary mb-4">
<div class="card-header d-flex align-items-center gap-2">
<h3 class="card-title mb-0">Task queue</h3>
<div class="ms-auto d-flex align-items-center gap-2">
{{#if canManage}}
{{#if canClearFinished}}
<form method="post" action="/settings/background-tasks/clear-finished">
<button type="submit" class="btn btn-outline-secondary btn-sm" data-confirm-message="Clear finished background tasks?">Clear finished</button>
</form>
{{/if}}
{{/if}}
<span class="text-muted small">Active workers: {{summary.activeCount}}</span>
</div>
</div>
<div class="card-body table-responsive p-0">
<table class="table table-striped w-100 mb-0 align-middle">
<thead>
<tr>
<th>Task</th>
<th>Category</th>
<th>Status</th>
<th>Queued</th>
<th>Started</th>
<th>Finished</th>
<th>Source</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{{#if tasks.length}}
{{#each tasks}}
<tr>
<td data-label="Task">
<div class="fw-semibold">{{title}}</div>
{{#if key}}
<div class="text-muted small text-break">{{key}}</div>
{{/if}}
{{#if errorMessage}}
<div class="text-danger small mt-1 text-break">{{errorMessage}}</div>
{{/if}}
</td>
<td data-label="Category">{{category}}</td>
<td data-label="Status">
{{#if (eq status 'queued')}}
<span class="badge text-bg-secondary">Queued</span>
{{else if (eq status 'running')}}
<span class="badge text-bg-primary">Running</span>
{{else if (eq status 'completed')}}
<span class="badge text-bg-success">Completed</span>
{{else if (eq status 'failed')}}
<span class="badge text-bg-danger">Failed</span>
{{else if (eq status 'canceled')}}
<span class="badge text-bg-warning">Canceled</span>
{{else}}
<span class="badge text-bg-light text-dark">{{status}}</span>
{{/if}}
</td>
<td data-label="Queued">
{{#if createdAt}}
<time data-local-datetime datetime="{{createdAt}}">{{createdAt}}</time>
{{else}}
-
{{/if}}
</td>
<td data-label="Started">
{{#if startedAt}}
<time data-local-datetime datetime="{{startedAt}}">{{startedAt}}</time>
{{else}}
-
{{/if}}
</td>
<td data-label="Finished">
{{#if finishedAt}}
<time data-local-datetime datetime="{{finishedAt}}">{{finishedAt}}</time>
{{else}}
-
{{/if}}
</td>
<td data-label="Source">
{{#if metadata.sourceName}}
<div>{{metadata.sourceName}}</div>
<div class="text-muted small">{{metadata.sourceType}} #{{metadata.sourceId}}</div>
{{else}}
<span class="empty">-</span>
{{/if}}
</td>
<td data-label="Actions">
<div class="d-flex flex-wrap gap-2">
{{#if canManage}}
{{#if (eq status 'queued')}}
<form method="post" action="/settings/background-tasks/{{id}}/cancel">
<button type="submit" class="btn btn-outline-danger btn-sm" data-confirm-message="Cancel this queued task?">Cancel</button>
</form>
{{/if}}
{{#if (eq status 'failed')}}
<form method="post" action="/settings/background-tasks/{{id}}/retry">
<button type="submit" class="btn btn-outline-primary btn-sm">Retry</button>
</form>
{{/if}}
{{/if}}
</div>
</td>
</tr>
{{/each}}
{{else}}
<tr>
<td colspan="8" class="empty">No background tasks are queued right now.</td>
</tr>
{{/if}}
</tbody>
</table>
</div>
{{#if pagination.hasMultiplePages}}
<div class="card-footer d-flex flex-wrap align-items-center justify-content-between gap-3">
<div class="text-muted small">
Showing {{pagination.startItem}}-{{pagination.endItem}} of {{pagination.totalItems}} tasks
</div>
<nav aria-label="Background task pages">
<ul class="pagination pagination-sm mb-0">
<li class="page-item {{#unless pagination.hasPrevious}}disabled{{/unless}}">
<a class="page-link" href="{{#if pagination.hasPrevious}}/settings/background-tasks{{pagination.previousUrl}}{{else}}#{{/if}}" aria-label="Previous page">Previous</a>
</li>
{{#each pagination.pages}}
<li class="page-item {{#if active}}active{{/if}}">
<a class="page-link" href="/settings/background-tasks{{url}}">{{number}}</a>
</li>
{{/each}}
<li class="page-item {{#unless pagination.hasNext}}disabled{{/unless}}">
<a class="page-link" href="{{#if pagination.hasNext}}/settings/background-tasks{{pagination.nextUrl}}{{else}}#{{/if}}" aria-label="Next page">Next</a>
</li>
</ul>
</nav>
</div>
{{/if}}
</div>
<div class="card card-outline card-secondary">
<div class="card-header">
<h3 class="card-title mb-0">Scheduled refreshes</h3>
</div>
<div class="card-body table-responsive p-0">
<table class="table table-striped w-100 mb-0 align-middle">
<thead>
<tr>
<th>Task</th>
<th>Interval</th>
<th>Next run</th>
<th>Last run</th>
<th>Last result</th>
<th>Source</th>
</tr>
</thead>
<tbody>
{{#if recurringTasks.length}}
{{#each recurringTasks}}
<tr>
<td data-label="Task">
<div class="fw-semibold">{{title}}</div>
<div class="text-muted small text-break">{{key}}</div>
</td>
<td data-label="Interval">{{intervalLabel}}</td>
<td data-label="Next run">
{{#if nextRunAt}}
<time data-local-datetime datetime="{{nextRunAt}}">{{nextRunAt}}</time>
{{else}}-{{/if}}
</td>
<td data-label="Last run">
{{#if lastRunAt}}
<time data-local-datetime datetime="{{lastRunAt}}">{{lastRunAt}}</time>
{{else}}-{{/if}}
</td>
<td data-label="Last result">
{{#if lastStatus}}
<span class="badge {{#if (eq lastStatus 'failed')}}text-bg-danger{{else if (eq lastStatus 'completed')}}text-bg-success{{else}}text-bg-secondary{{/if}}">{{lastStatus}}</span>
{{#if lastError}}<div class="text-danger small mt-1 text-break">{{lastError}}</div>{{/if}}
{{else}}
<span class="empty">-</span>
{{/if}}
</td>
<td data-label="Source">
{{#if metadata.sourceName}}
<div>{{metadata.sourceName}}</div>
<div class="text-muted small">{{metadata.sourceType}} #{{metadata.sourceId}}</div>
{{else}}
<span class="empty">-</span>
{{/if}}
</td>
</tr>
{{/each}}
{{else}}
<tr>
<td colspan="6" class="empty">No scheduled refreshes are registered yet.</td>
</tr>
{{/if}}
</tbody>
</table>
</div>
{{#if recurringPagination.hasMultiplePages}}
<div class="card-footer d-flex flex-wrap align-items-center justify-content-between gap-3">
<div class="text-muted small">
Showing {{recurringPagination.startItem}}-{{recurringPagination.endItem}} of {{recurringPagination.totalItems}} scheduled refreshes
</div>
<nav aria-label="Scheduled refresh pages">
<ul class="pagination pagination-sm mb-0">
<li class="page-item {{#unless recurringPagination.hasPrevious}}disabled{{/unless}}">
<a class="page-link" href="{{#if recurringPagination.hasPrevious}}/settings/background-tasks{{recurringPagination.previousUrl}}{{else}}#{{/if}}" aria-label="Previous page">Previous</a>
</li>
{{#each recurringPagination.pages}}
<li class="page-item {{#if active}}active{{/if}}">
<a class="page-link" href="/settings/background-tasks{{url}}">{{number}}</a>
</li>
{{/each}}
<li class="page-item {{#unless recurringPagination.hasNext}}disabled{{/unless}}">
<a class="page-link" href="{{#if recurringPagination.hasNext}}/settings/background-tasks{{recurringPagination.nextUrl}}{{else}}#{{/if}}" aria-label="Next page">Next</a>
</li>
</ul>
</nav>
</div>
{{/if}}
</div>
+81
View File
@@ -0,0 +1,81 @@
<div class="page-header">
<div>
<h2>Create role</h2>
<p>Define a new role and choose its permissions before saving.</p>
</div>
</div>
<form method="post" action="/rbac" id="role-create-form" data-async-save data-async-save-close-url="/rbac">
<div class="row g-4">
<div class="col-12 col-xl-4 d-flex flex-column gap-4">
<div class="card card-outline card-primary admin-form-card">
<div class="card-header">
<h3 class="card-title">Role details</h3>
</div>
<div class="card-body">
<div class="mb-3">
<label for="role-name" class="form-label">Role name</label>
<input id="role-name" name="name" class="form-control" value="{{formValues.name}}" required />
</div>
<div class="mb-0">
<label for="role-description" class="form-label">Description</label>
<textarea id="role-description" name="description" class="form-control" rows="5">{{formValues.description}}</textarea>
</div>
</div>
</div>
</div>
<div class="col-12 col-xl-8 d-flex flex-column gap-4">
<div class="card card-outline card-secondary admin-form-card">
<div class="card-header">
<h3 class="card-title">Permissions</h3>
</div>
<div class="card-body">
<p class="text-body-secondary mb-3">Pick the permissions this role should have now.</p>
{{#if permissionGroups.length}}
<div class="accordion" id="role-create-permissions-accordion">
{{#each permissionGroups}}
<div class="accordion-item">
<h2 class="accordion-header" id="create-heading-{{id}}">
<button class="accordion-button {{#unless @first}}collapsed{{/unless}}" type="button" data-bs-toggle="collapse" data-bs-target="#create-collapse-{{id}}" aria-expanded="{{#if @first}}true{{else}}false{{/if}}" aria-controls="create-collapse-{{id}}">
<span>{{title}}</span>
{{#if categoryName}}
<span class="badge text-bg-secondary ms-2">{{categoryName}}</span>
{{/if}}
</button>
</h2>
<div id="create-collapse-{{id}}" class="accordion-collapse collapse {{#if @first}}show{{/if}}" aria-labelledby="create-heading-{{id}}" data-bs-parent="#role-create-permissions-accordion">
<div class="accordion-body">
<div class="row g-3">
{{#each permissions}}
<div class="col-12 col-md-6">
<label class="form-check card card-outline card-secondary position-relative p-3 h-100 mb-0">
<input class="form-check-input position-absolute top-0 end-0 m-3" type="checkbox" name="permission_keys[]" value="{{permission_key}}" data-permission-key="{{permission_key}}" {{#if isSelected}}checked{{/if}} />
<span class="form-check-label pe-4">
<strong>{{actionLabel}}</strong>
<span class="d-block text-body-secondary small">{{#if description}}{{description}}{{else}}No description provided.{{/if}}</span>
</span>
</label>
</div>
{{/each}}
</div>
</div>
</div>
</div>
{{/each}}
</div>
{{else}}
<div class="alert alert-warning mb-0">No permissions are available.</div>
{{/if}}
</div>
</div>
</div>
</div>
<div class="d-flex justify-content-end mt-4">
<div class="btn-group" role="group" aria-label="Role actions">
{{{saveActionButtons formId="role-create-form" saveLabel="Save" saveAndCloseLabel="Save and Close" saveAndCloseValue="close" showSaveAndNew=false}}}
<a class="btn btn-warning" href="/rbac" data-confirm-unsaved="You have unsaved changes. Leave this page?">Cancel</a>
</div>
</div>
</form>
+133
View File
@@ -0,0 +1,133 @@
<div class="page-header">
<div>
<h2>Edit role - {{role.name}}</h2>
<p>Choose which permissions and users this role receives.</p>
</div>
</div>
<form method="post" action="/rbac/{{role.id}}" id="role-edit-form" data-async-save data-async-save-close-url="/rbac">
<input type="hidden" name="permissions_present" value="1" />
<input type="hidden" name="users_present" value="1" />
<div class="row g-4">
<div class="col-12 col-xl-4 d-flex flex-column gap-4">
<div class="card card-outline card-primary admin-form-card">
<div class="card-header">
<h3 class="card-title">Role details</h3>
</div>
<div class="card-body">
<div class="mb-3">
<label for="role-name-edit" class="form-label">Role Name</label>
<input id="role-name-edit" name="name" class="form-control" value="{{role.name}}" required />
</div>
<div class="mb-0">
<label for="role-description-edit" class="form-label">Description</label>
<textarea id="role-description-edit" name="description" class="form-control" rows="5">{{role.description}}</textarea>
</div>
</div>
<div class="card-footer d-flex justify-content-end gap-2 flex-wrap">
<div class="btn-group" role="group" aria-label="Role actions">
{{{saveActionButtons formId="role-edit-form" saveLabel="Save" saveAndCloseLabel="Save and Close" saveAndCloseValue="close" showSaveAndNew=false}}}
</div>
{{#unless role.user_count}}
<button type="submit" class="btn btn-danger" form="role-delete-form">Delete</button>
{{/unless}}
</div>
</div>
<div class="card card-outline card-secondary admin-form-card">
<div class="card-header">
<h3 class="card-title">Users</h3>
</div>
<div class="card-body p-0">
<div class="px-3 pt-3">
<p class="text-body-secondary mb-3">Choose the users who should belong to this role.</p>
</div>
{{#if users.length}}
<div class="table-responsive">
<table class="table table-sm align-middle mb-0 w-100">
<thead>
<tr>
<th style="width: 1%" scope="col">Select</th>
<th scope="col">Name</th>
<th scope="col">Username</th>
<th scope="col">Roles</th>
</tr>
</thead>
<tbody>
{{#each users}}
<tr>
<td>
<input class="form-check-input" type="checkbox" name="user_ids[]" value="{{id}}" {{#if isSelected}}checked{{/if}} />
</td>
<td>{{name}}</td>
<td class="text-body-secondary">{{username}}</td>
<td class="text-body-secondary">{{#if roleNames}}{{roleNames}}{{else}}No roles assigned{{/if}}</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
{{else}}
<div class="px-3 pb-3">
<div class="alert alert-warning mb-0">Create at least one user before assigning this role.</div>
</div>
{{/if}}
</div>
</div>
</div>
<div class="col-12 col-xl-8 d-flex flex-column gap-4">
<div class="card card-outline card-secondary admin-form-card">
<div class="card-header">
<h3 class="card-title">Permissions</h3>
</div>
<div class="card-body">
<p class="text-body-secondary mb-3">Each section below is a resource. Pick the actions this role should have for that resource.</p>
{{#if permissionGroups.length}}
<div class="accordion" id="role-permissions-accordion">
{{#each permissionGroups}}
<div class="accordion-item">
<h2 class="accordion-header" id="heading-{{id}}">
<button class="accordion-button {{#unless @first}}collapsed{{/unless}}" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-{{id}}" aria-expanded="{{#if @first}}true{{else}}false{{/if}}" aria-controls="collapse-{{id}}">
<span>{{title}}</span>
{{#if categoryName}}
<span class="badge text-bg-secondary ms-2">{{categoryName}}</span>
{{/if}}
</button>
</h2>
<div id="collapse-{{id}}" class="accordion-collapse collapse {{#if @first}}show{{/if}}" aria-labelledby="heading-{{id}}" data-bs-parent="#role-permissions-accordion">
<div class="accordion-body">
<div class="row g-3">
{{#each permissions}}
<div class="col-12 col-md-6">
<label class="form-check card card-outline card-secondary position-relative p-3 h-100 mb-0">
<input class="form-check-input position-absolute top-0 end-0 m-3" type="checkbox" name="permission_keys[]" value="{{permission_key}}" data-permission-key="{{permission_key}}" {{#if isSelected}}checked{{/if}} />
<span class="form-check-label pe-4">
<strong>{{actionLabel}}</strong>
<span class="d-block text-body-secondary small">{{#if description}}{{description}}{{else}}No description provided.{{/if}}</span>
</span>
</label>
</div>
{{/each}}
</div>
</div>
</div>
</div>
{{/each}}
</div>
{{else}}
<div class="alert alert-warning mb-0">No permissions are available.</div>
{{/if}}
</div>
<div class="card-footer d-flex justify-content-end">
<div class="btn-group" role="group" aria-label="Permission actions">
{{{saveActionButtons formId="role-edit-form" saveLabel="Save" saveAndCloseLabel="Save and Close" saveAndCloseValue="close" showSaveAndNew=false}}}
</div>
</div>
</div>
</div>
</div>
</form>
<form method="post" action="/rbac/{{role.id}}/delete" id="role-delete-form" class="d-none" data-confirm-message="Delete {{role.name}}?"></form>
+78
View File
@@ -0,0 +1,78 @@
<div class="page-header">
<div>
<h2>Roles and permissions</h2>
<p>Define roles here, then assign those roles to users from either the role or users screen.</p>
</div>
</div>
<div class="row g-4">
<div class="col-12">
<div class="card card-outline card-primary admin-form-card">
<div class="card-header">
<h3 class="card-title">Existing roles</h3>
{{#if (hasPermission currentUser 'rbac.create')}}
<div class="card-tools">
<a class="btn btn-primary btn-sm" href="/rbac/new">Add role</a>
</div>
{{/if}}
</div>
<div class="card-body table-responsive p-0">
<table class="table table-striped w-100 mb-0 users-table">
<thead>
<tr>
<th>Role</th>
<th>Description</th>
<th>Users</th>
<th>Permissions</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{{#if roles.length}}
{{#each roles}}
<tr>
<td data-label="Role">
<div class="user-cell">
<div>
<strong>{{name}}</strong>
</div>
</div>
</td>
<td data-label="Description" title="{{description}}">
{{#if description}}
<span class="subtle">{{truncateText description 80}}</span>
{{else}}
<span class="empty">No description provided.</span>
{{/if}}
</td>
<td data-label="Users">{{user_count}}</td>
<td data-label="Permissions">{{permission_count}}</td>
<td data-label="Actions">
{{#if (anyPermission ../currentUser 'rbac.update' 'rbac.delete')}}
<div class="actions users-row-actions">
{{#if (hasPermission ../currentUser 'rbac.update')}}
<a class="btn btn-sm btn-primary" href="/rbac/{{id}}/edit">Edit</a>
{{/if}}
{{#if (hasPermission ../currentUser 'rbac.delete')}}
{{#unless user_count}}
<form method="post" action="/rbac/{{id}}/delete" class="inline-form" data-confirm-message="Delete {{name}}?">
<button type="submit" class="btn btn-sm btn-danger">Delete</button>
</form>
{{else}}
<span class="empty">In use</span>
{{/unless}}
{{/if}}
</div>
{{/if}}
</td>
</tr>
{{/each}}
{{else}}
<tr><td colspan="5" class="empty">No roles found.</td></tr>
{{/if}}
</tbody>
</table>
</div>
</div>
</div>
</div>
+74
View File
@@ -0,0 +1,74 @@
<div class="page-header">
<div>
<h2>Add user</h2>
<p>Create a new login for the web UI.</p>
</div>
</div>
<div class="row g-4">
<div class="col-12 col-xl-4">
<div class="card card-outline card-primary admin-form-card">
<div class="card-header">
<h3 class="card-title">User details</h3>
</div>
<form id="user-form" method="post" action="/users">
<div class="card-body">
<div class="row g-3">
<div class="col-12 col-md-6">
<label for="user-username" class="form-label">Username</label>
<input id="user-username" type="text" name="username" class="form-control" autocomplete="username" value="{{formValues.username}}" required />
</div>
<div class="col-12 col-md-6">
<label for="user-name" class="form-label">Name</label>
<input id="user-name" type="text" name="name" class="form-control" autocomplete="name" value="{{formValues.name}}" required />
</div>
<div class="col-12 col-md-6">
<label for="user-password" class="form-label">Password</label>
<input id="user-password" type="password" name="password" class="form-control" autocomplete="new-password" minlength="8" required />
</div>
<div class="col-12 col-md-6">
<label for="user-confirm-password" class="form-label">Confirm password</label>
<input id="user-confirm-password" type="password" name="confirm_password" class="form-control" autocomplete="new-password" minlength="8" required />
</div>
</div>
</div>
<div class="card-footer d-flex justify-content-end">
<div class="btn-group" role="group" aria-label="User actions">
{{{saveActionButtons formId="user-form" saveLabel="Save" showSaveAndClose=false showSaveAndNew=false}}}
<a class="btn btn-warning" href="/users" data-confirm-unsaved="You have unsaved changes. Leave this page?">Cancel</a>
</div>
</div>
</form>
</div>
</div>
<div class="col-12 col-xl-8">
<div class="card card-outline card-secondary admin-form-card">
<div class="card-header">
<h3 class="card-title">Roles</h3>
</div>
<div class="card-body">
<div class="row g-3">
{{#if roles.length}}
{{#each roles}}
<div class="col-4">
<label class="form-check card card-outline card-secondary position-relative p-3 h-100 mb-0">
<input class="form-check-input position-absolute top-0 end-0 m-3" type="checkbox" name="role_ids[]" value="{{id}}" {{#if isSelected}}checked{{/if}} />
<span class="form-check-label pe-4">
<strong>{{name}}</strong>
<span class="d-block text-body-secondary small">{{#if description}}{{description}}{{else}}No description provided.{{/if}}</span>
</span>
</label>
</div>
{{/each}}
{{else}}
<div class="col-12">
<div class="alert alert-warning mb-0">Create at least one role before adding users.</div>
</div>
{{/if}}
</div>
<div class="form-text mt-2">Select at least one role so the new user can sign in with access.</div>
</div>
</div>
</div>
</div>
+103
View File
@@ -0,0 +1,103 @@
<div class="page-header">
<div>
<h2>Edit user - {{user.name}}</h2>
<p>Update the name or password for {{user.username}}.</p>
</div>
</div>
<div class="card card-outline card-primary admin-form-card">
<div class="card-body">
<div class="row g-4">
<div class="col-12 col-xl-6">
<div class="card card-outline card-secondary admin-form-card mb-0">
<div class="card-header">
<h3 class="card-title">Profile</h3>
</div>
<form id="user-profile-form" method="post" action="/users/{{user.id}}/username" data-async-save data-async-save-close-url="/users">
<div class="card-body">
<div class="mb-3">
<label for="edit-user-username" class="form-label">Username</label>
<input id="edit-user-username" type="text" name="username" value="{{user.username}}" class="form-control" autocomplete="username" required disabled />
</div>
<div class="mb-0">
<label for="edit-user-name" class="form-label">Name</label>
<input id="edit-user-name" type="text" name="name" value="{{user.name}}" class="form-control" autocomplete="name" required />
</div>
</div>
<div class="card-footer d-flex justify-content-end">
<div class="btn-group" role="group" aria-label="Profile actions">
{{{saveActionButtons formId="user-profile-form" saveLabel="Save" saveAndCloseLabel="Save and Close" saveAndCloseValue="close" showSaveAndNew=false}}}
<a class="btn btn-warning" href="/users" data-confirm-unsaved="You have unsaved changes. Leave this page?">Cancel</a>
<button type="submit" class="btn btn-danger" form="delete-user-form">Delete</button>
</div>
</div>
</form>
</div>
</div>
<div class="col-12 col-xl-6">
<div class="card card-outline card-warning admin-form-card h-100 mb-0">
<div class="card-header">
<h3 class="card-title">Reset password</h3>
</div>
<form id="user-password-form" method="post" action="/users/{{user.id}}/password" data-async-save data-async-save-close-url="/users">
<div class="card-body">
<div class="mb-3">
<label for="edit-user-password" class="form-label">New password</label>
<input id="edit-user-password" type="password" name="password" class="form-control" autocomplete="new-password" minlength="8" required />
</div>
<div class="mb-0">
<label for="edit-user-confirm-password" class="form-label">Confirm password</label>
<input id="edit-user-confirm-password" type="password" name="confirm_password" class="form-control" autocomplete="new-password" minlength="8" required />
</div>
</div>
<div class="card-footer d-flex justify-content-end">
<div class="btn-group" role="group" aria-label="Password actions">
{{{saveActionButtons formId="user-password-form" saveLabel="Save" saveAndCloseLabel="Save and Close" saveAndCloseValue="close" showSaveAndNew=false}}}
</div>
</div>
</form>
</div>
</div>
<div class="col-12 col-xl-6">
<div class="card card-outline card-secondary admin-form-card h-100 mb-0">
<div class="card-header">
<h3 class="card-title">Roles</h3>
</div>
<form id="user-roles-form" method="post" action="/users/{{user.id}}/roles" data-async-save data-async-save-close-url="/users">
<div class="card-body">
<div class="row g-3">
{{#if roles.length}}
{{#each roles}}
<div class="col-12 col-md-6">
<label class="form-check card card-outline card-secondary p-3 h-100 mb-0">
<input class="form-check-input" type="checkbox" name="role_ids[]" value="{{id}}" {{#if isSelected}}checked{{/if}} />
<span class="form-check-label ms-2">
<strong>{{name}}</strong>
<span class="d-block text-body-secondary small">{{#if description}}{{description}}{{else}}No description provided.{{/if}}</span>
</span>
</label>
</div>
{{/each}}
{{else}}
<div class="col-12">
<div class="alert alert-warning mb-0">Create at least one user before assigning this role.</div>
</div>
{{/if}}
</div>
</div>
<div class="card-footer d-flex justify-content-end">
<div class="btn-group" role="group" aria-label="Role actions">
{{{saveActionButtons formId="user-roles-form" saveLabel="Save" saveAndCloseLabel="Save and Close" saveAndCloseValue="close" showSaveAndNew=false}}}
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<form id="delete-user-form" method="post" action="/users/{{user.id}}/delete" class="d-none" data-confirm-message="Delete {{user.username}}?"></form>
+93
View File
@@ -0,0 +1,93 @@
<div class="page-header">
<div>
<h2>Users</h2>
<p>Create accounts, rename users, reset passwords, and remove unused logins.</p>
</div>
</div>
<div class="card card-outline card-primary">
<div class="card-header">
<h3 class="card-title">Existing users</h3>
{{#if (hasPermission currentUser 'users.create')}}
<div class="card-tools">
<a class="btn btn-primary btn-sm" href="/users/new">Add user</a>
</div>
{{/if}}
</div>
<div class="card-body table-responsive p-0">
<table class="table table-striped w-100 mb-0 users-table">
<thead>
<tr>
<th>User</th>
<th>Name</th>
<th>Roles</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{{#if users.length}}
{{#each users}}
<tr>
<td data-label="User">
<div class="user-cell">
<div>
{{#if isCurrentUser}}
<i>{{username}}</i>
{{else}}
{{username}}
{{/if}}
</div>
</div>
</td>
<td data-label="Name">
<div class="user-cell">
<div>
{{#if isCurrentUser}}
<i>{{#if name}}{{name}}{{else}}-{{/if}}</i>
{{else}}
{{#if name}}{{name}}{{else}}-{{/if}}
{{/if}}
</div>
</div>
</td>
<td data-label="Roles">
<div class="user-cell">
<div>
{{#if isCurrentUser}}
<i>{{#if roleNames}}{{roleNames}}{{else}}No roles assigned{{/if}}</i>
{{else}}
{{#if roleNames}}{{roleNames}}{{else}}No roles assigned{{/if}}
{{/if}}
</div>
</div>
</td>
<td data-label="Actions">
{{#if (anyPermission ../currentUser 'users.update' 'users.delete')}}
<div class="actions users-row-actions">
{{#unless isCurrentUser}}
{{#if (hasPermission ../currentUser 'users.update')}}
<a class="btn btn-sm btn-primary" href="/users/{{id}}/edit">Edit</a>
{{/if}}
{{#if (hasPermission ../currentUser 'users.delete')}}
<form method="post" action="/users/{{id}}/delete" class="inline-form" data-confirm-message="Delete {{username}}?">
<button type="submit" class="btn btn-sm btn-danger">Delete</button>
</form>
{{/if}}
{{else}}
<span class="empty">Protected</span>
{{/unless}}
</div>
{{else}}
<span class="empty">-</span>
{{/if}}
</td>
</tr>
{{/each}}
{{else}}
<tr><td colspan="4" class="empty">No users found.</td></tr>
{{/if}}
</tbody>
</table>
</div>
</div>