feat: submission confirmation and payment locking

This commit is contained in:
Victor Andersson
2025-11-08 20:19:31 +01:00
parent 4bd04c5f43
commit 02bbda562e
16 changed files with 675 additions and 171 deletions

View File

@@ -34,8 +34,8 @@
<div class="space-y-6">
{% for claim in claims %}
<article class="rounded-3xl bg-white shadow-sm ring-1 ring-gray-100">
<div class="flex flex-col gap-4 border-b border-gray-100 px-6 py-5 md:flex-row md:items-center md:justify-between">
<div class="space-y-2">
<div class="flex flex-col gap-4 border-b border-gray-100 px-6 py-5 lg:flex-row lg:justify-between">
<div class="space-y-3">
<div class="flex flex-wrap items-center gap-3 text-sm text-gray-500">
<span class="rounded-full bg-slate-100 px-3 py-1 font-semibold text-gray-700">
{{ claim.amount }} {{ claim.currency }}
@@ -47,25 +47,67 @@
{% endif %}
<span class="text-xs text-gray-400">Skapad {{ claim.created_at|date:"Y-m-d H:i" }}</span>
</div>
<h2 class="text-xl font-semibold text-gray-900">{{ claim.full_name }}</h2>
<p class="text-sm text-gray-600">
{{ claim.email }} · Konto: {{ claim.account_number }}<br>
{% if claim.submitted_by %}
<span class="text-xs uppercase tracking-wide text-green-600">Inloggad användare: {{ claim.submitted_by.get_username }}</span>
{% else %}
<span class="text-xs uppercase tracking-wide text-gray-500">Inskickad av gäst</span>
{% endif %}
</p>
<div class="space-y-1">
<p class="text-sm font-semibold uppercase tracking-wide text-gray-500">Person</p>
<h2 class="text-2xl font-semibold text-gray-900">{{ claim.full_name }}</h2>
<p class="text-sm text-gray-600">
{{ claim.email }} · Konto: <span class="font-mono text-gray-900">{{ claim.account_number }}</span><br>
{% if claim.submitted_by %}
<span class="text-xs uppercase tracking-wide text-green-600">Inloggad användare: {{ claim.submitted_by.get_username }}</span>
{% else %}
<span class="text-xs uppercase tracking-wide text-gray-500">Inskickad av gäst</span>
{% endif %}
</p>
</div>
</div>
<div class="flex flex-col items-start gap-2 text-sm md:items-end">
<div class="flex flex-col items-start gap-2 text-sm lg:items-end">
<span class="rounded-full px-4 py-2 text-sm font-semibold {% if claim.status == 'approved' %}bg-green-50 text-green-700 border border-green-200{% elif claim.status == 'rejected' %}bg-rose-50 text-rose-700 border border-rose-200{% else %}bg-amber-50 text-amber-800 border border-amber-200{% endif %}">
{{ claim.get_status_display }}
</span>
{% if claim.decision_note %}
<p class="text-xs text-gray-500">Kommentar: {{ claim.decision_note }}</p>
{% endif %}
{% if payments_enabled and claim.status == 'approved' %}
{% if claim.is_paid %}
<span class="rounded-full bg-emerald-100 px-3 py-1 text-xs font-semibold text-emerald-800">
Betald {{ claim.paid_at|date:"Y-m-d H:i" }}
{% if claim.paid_by %}av {{ claim.paid_by.get_username }}{% endif %}
</span>
{% else %}
<span class="text-xs text-gray-500">Ej markerad som betald</span>
{% endif %}
{% endif %}
</div>
</div>
{% if claim.status == 'approved' %}
<div class="mx-6 mt-4 rounded-2xl border border-green-100 bg-green-50 px-6 py-4 text-sm text-green-900">
<div class="flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
<div>
<p class="text-xs font-semibold uppercase tracking-wide text-green-600">Sammanfattning</p>
<p class="text-lg font-semibold text-green-900">{{ claim.full_name }}</p>
<p class="text-sm text-green-800">Belopp: <strong>{{ claim.amount }} {{ claim.currency }}</strong> · Konto: <span class="font-mono">{{ claim.account_number }}</span></p>
</div>
{% if payments_enabled %}
{% if claim.is_paid %}
<p class="text-xs uppercase tracking-wide text-emerald-600">Markerad som betald</p>
{% else %}
<form method="post" onsubmit="return confirm('Är du säker på att du har lagt upp betalningen? Markera endast som betald om beloppet skickas till banken.');">
{% csrf_token %}
<input type="hidden" name="action_type" value="payment">
<input type="hidden" name="payment_claim_id" value="{{ claim.id }}">
<button type="submit" class="inline-flex items-center gap-2 rounded-full bg-emerald-600 px-4 py-2 text-xs font-semibold uppercase tracking-wide text-white transition hover:bg-emerald-700">
Betala
</button>
</form>
{% endif %}
{% else %}
<p class="text-xs text-green-700">Intern betalningshantering är av markera betalning i ekonomisystemet.</p>
{% endif %}
</div>
</div>
{% endif %}
<div class="grid gap-6 px-6 py-6 lg:grid-cols-3">
<div class="lg:col-span-2">
<p class="text-sm font-semibold text-gray-500">Beskrivning</p>
@@ -110,24 +152,31 @@
</details>
{% if can_change %}
<form method="post" class="space-y-3">
{% csrf_token %}
<input type="hidden" name="claim_id" value="{{ claim.id }}">
{% if claim.is_paid %}
<p class="rounded-lg bg-slate-100 px-3 py-2 text-xs text-slate-600">
Utlägget är markerat som betalt. Ändringar av beslut/kommentar är låsta.
</p>
{% else %}
<form method="post" class="space-y-3">
{% csrf_token %}
<input type="hidden" name="claim_id" value="{{ claim.id }}">
<label class="block text-sm font-medium text-gray-700">Åtgärd</label>
<select name="action" class="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm text-gray-900 focus:border-brand-600 focus:outline-none focus:ring-2 focus:ring-brand-600">
{% for value, label in decision_choices %}
<option value="{{ value }}">{{ label }}</option>
{% endfor %}
</select>
<label class="block text-sm font-medium text-gray-700">Åtgärd</label>
<select name="action" class="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm text-gray-900 focus:border-brand-600">
{% for value, label in decision_choices %}
<option value="{{ value }}">{{ label }}</option>
{% endfor %}
</select>
<label class="block text-sm font-medium text-gray-700">Kommentar</label>
<textarea name="decision_note" rows="3" class="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm text-gray-900 focus:border-brand-600 focus:outline-none focus:ring-2 focus:ring-brand-600">{{ claim.decision_note }}</textarea>
<label class="block text-sm font-medium text-gray-700">Kommentar</label>
<textarea name="decision_note" rows="3" class="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm text-gray-900 focus:border-brand-600">{{ claim.decision_note }}</textarea>
<button type="submit" class="w-full rounded-full bg-brand-600 px-4 py-2 text-sm font-semibold text-white transition hover:bg-brand-700">
Uppdatera beslut
</button>
</form>
<input type="hidden" name="action_type" value="decision">
<button type="submit" class="w-full rounded-full bg-brand-600 px-4 py-2 text-sm font-semibold text-white transition hover:bg-brand-700">
Uppdatera beslut
</button>
</form>
{% endif %}
{% endif %}
</div>
</div>

View File

@@ -24,21 +24,27 @@
<body class="min-h-screen bg-slate-50 text-gray-900">
<header class="bg-white shadow-sm">
<div class="mx-auto flex max-w-6xl items-center justify-between px-4 py-4">
<div class="text-lg font-semibold text-gray-900">
claims-system
</div>
<nav class="flex flex-wrap items-center gap-4 text-sm font-medium text-gray-600">
<a class="hover:text-gray-900" href="{% url 'claims:submit' %}">Skicka utlägg</a>
<a class="hover:text-gray-900" href="{% url 'claims:admin-list' %}">Admin</a>
<a class="hover:text-gray-900" href="{% url 'claims:export' %}">Export</a>
<a href="{% url 'claims:submit' %}" class="text-lg font-semibold text-gray-900 hover:text-brand-700">claims-system</a>
<nav class="flex flex-wrap items-center gap-3 text-sm font-medium text-gray-600">
<div class="flex items-center gap-2">
<span class="rounded-full border border-gray-200 bg-gray-50 px-3 py-1 text-xs uppercase tracking-wide text-gray-500">Offentlig</span>
<a class="hover:text-gray-900" href="{% url 'claims:submit' %}">Skicka utlägg</a>
</div>
{% if user.is_authenticated %}
<div class="flex flex-wrap items-center gap-3">
<span class="rounded-full border border-gray-200 bg-gray-50 px-3 py-1 text-xs uppercase tracking-wide text-gray-500">Intern</span>
<a class="hover:text-gray-900" href="{% url 'claims:admin-list' %}">Utläggslista</a>
<a class="hover:text-gray-900" href="{% url 'claims:my-claims' %}">Mina utlägg</a>
{% if perms.auth.view_user %}
<a class="hover:text-gray-900" href="{% url 'claims:user-manage' %}">Användare</a>
{% endif %}
<a class="hover:text-gray-900" href="{% url 'claims:export' %}">Export</a>
{% if user.is_staff %}
<a class="hover:text-gray-900" href="{% url 'admin:index' %}">Django admin</a>
{% endif %}
</div>
{% endif %}
{% if user.is_authenticated %}
<a class="hover:text-gray-900" href="{% url 'claims:my-claims' %}">Mina utlägg</a>
{% if perms.auth.view_user %}
<a class="hover:text-gray-900" href="{% url 'claims:user-manage' %}">Användare</a>
{% endif %}
{% if user.is_staff %}
<a class="hover:text-gray-900" href="{% url 'admin:index' %}">Kontohantering</a>
{% endif %}
<span class="hidden text-xs text-gray-400 sm:inline">|</span>
<span class="text-xs text-gray-500">Inloggad som {{ user.get_username }}</span>
<form action="{% url 'logout' %}" method="post" class="inline">

View File

@@ -3,61 +3,80 @@
{% block title %}Mina utlägg{% endblock %}
{% block content %}
<h1>Mina utlägg</h1>
<p>Här ser du status för de utlägg du skickat in när du varit inloggad.</p>
<section class="space-y-6 py-6">
<header class="max-w-3xl">
<p class="text-sm font-semibold uppercase tracking-wide text-brand-600">Översikt</p>
<h1 class="text-3xl font-semibold text-gray-900">Mina utlägg</h1>
<p class="mt-2 text-sm text-gray-600">Här ser du alla utlägg du skickat in när du varit inloggad, inklusive status, kvitton och loggar.</p>
</header>
{% if claims %}
<table>
<thead>
<tr>
<th>Skickad</th>
<th>Beskrivning</th>
<th>Belopp</th>
<th>Projekt</th>
<th>Status</th>
<th>Kvitto</th>
<th>Logg</th>
</tr>
</thead>
<tbody>
<div class="grid gap-6">
{% for claim in claims %}
<tr>
<td>{{ claim.created_at|date:"Y-m-d H:i" }}</td>
<td>{{ claim.description|linebreaksbr }}</td>
<td>{{ claim.amount }} {{ claim.currency }}</td>
<td>{{ claim.project|default:"-" }}</td>
<td>{{ claim.get_status_display }}</td>
<td>
{% if claim.receipt %}
<a href="{{ claim.receipt.url }}" target="_blank" rel="noopener">Visa fil</a>
{% else %}
{% endif %}
</td>
<td>
<details>
<summary>Visa logg</summary>
<ul>
{% for log in claim.logs.all %}
<li>
{{ log.created_at|date:"Y-m-d H:i" }}
{{ log.get_action_display }}
{% if log.from_status %} ({{ log.get_from_status_display }} → {{ log.get_to_status_display }}){% endif %}
{% if log.note %}
"{{ log.note }}"
{% endif %}
</li>
{% empty %}
<li>Ingen logg än.</li>
{% endfor %}
</ul>
</details>
</td>
</tr>
<article class="rounded-3xl bg-white px-6 py-6 shadow-sm ring-1 ring-gray-100">
<div class="flex flex-col gap-4 border-b border-gray-100 pb-4 lg:flex-row lg:items-center lg:justify-between">
<div>
<p class="text-xs uppercase tracking-wide text-gray-500">Skickad {{ claim.created_at|date:"Y-m-d H:i" }}</p>
<h2 class="mt-1 text-2xl font-semibold text-gray-900">{{ claim.description|default:"Utlägg" }}</h2>
<p class="mt-2 text-sm text-gray-600">
Belopp: <strong>{{ claim.amount }} {{ claim.currency }}</strong><br>
Projekt: {{ claim.project|default:"-" }}
</p>
</div>
<div class="flex flex-col items-start gap-2 text-sm lg:items-end">
<span class="rounded-full px-4 py-2 text-sm font-semibold {% if claim.status == 'approved' %}bg-green-50 text-green-700 border border-green-200{% elif claim.status == 'rejected' %}bg-rose-50 text-rose-700 border border-rose-200{% else %}bg-amber-50 text-amber-800 border border-amber-200{% endif %}">
{{ claim.get_status_display }}
</span>
{% if claim.paid_at %}
<span class="rounded-full bg-emerald-50 px-3 py-1 text-xs font-semibold text-emerald-800">
Betald {{ claim.paid_at|date:"Y-m-d" }}
</span>
{% endif %}
</div>
</div>
<div class="mt-4 grid gap-4 lg:grid-cols-[2fr,1fr]">
<div>
<p class="text-sm font-semibold text-gray-500">Detaljer</p>
<p class="mt-2 whitespace-pre-wrap text-gray-800">{{ claim.description }}</p>
</div>
<div class="rounded-2xl bg-slate-50 p-4 text-sm text-gray-600">
<p class="font-semibold text-gray-800">Kvitto</p>
{% if claim.receipt %}
<a class="mt-2 inline-flex items-center gap-2 text-brand-600 hover:text-brand-700" href="{{ claim.receipt.url }}" target="_blank" rel="noopener">
Visa fil
</a>
{% else %}
<p class="mt-2 text-xs text-gray-400">Inget kvitto bifogat.</p>
{% endif %}
</div>
</div>
<details class="mt-4 rounded-2xl border border-dashed border-gray-200 bg-gray-50 p-4 text-sm text-gray-700">
<summary class="cursor-pointer select-none text-sm font-semibold text-gray-800">Visa logg</summary>
<ul class="mt-3 space-y-2 text-sm text-gray-600">
{% for log in claim.logs.all %}
<li class="rounded-lg bg-white px-3 py-2 shadow-sm">
<p class="font-semibold text-gray-900">{{ log.get_action_display }}</p>
<p class="text-xs text-gray-500">{{ log.created_at|date:"Y-m-d H:i" }}</p>
{% if log.from_status %}
<p class="text-xs text-gray-500">Status: {{ log.get_from_status_display }} → {{ log.get_to_status_display }}</p>
{% endif %}
{% if log.note %}
<p class="mt-1 text-xs text-gray-600">"{{ log.note }}"</p>
{% endif %}
</li>
{% empty %}
<li class="text-xs text-gray-400">Ingen logg än.</li>
{% endfor %}
</ul>
</details>
</article>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<p>Du har inte skickat in några utlägg ännu eller så gjordes de utan inloggning.</p>
<div class="rounded-3xl border border-dashed border-gray-200 bg-white px-6 py-10 text-center text-gray-500">
<p class="text-lg font-semibold text-gray-900">Inga utlägg ännu</p>
<p class="mt-2 text-sm">När du skickar in utlägg medan du är inloggad dyker de upp här.</p>
</div>
{% endif %}
</section>
{% endblock %}

View File

@@ -0,0 +1,26 @@
{% extends "claims/base.html" %}
{% block title %}Tack för ditt utlägg{% endblock %}
{% block content %}
<section class="flex min-h-[60vh] items-center justify-center py-10">
<div class="w-full max-w-lg rounded-3xl bg-white px-8 py-10 text-center shadow-lg ring-1 ring-gray-100">
<p class="text-sm font-semibold uppercase tracking-wide text-brand-600">Tack!</p>
<h1 class="mt-2 text-3xl font-semibold text-gray-900">Utlägget är skickat</h1>
<p class="mt-3 text-sm text-gray-600">
Vi har tagit emot underlaget. Om du har fler kvitton kan du fylla i ett nytt formulär direkt,
annars kan du logga in för att följa statusen.
</p>
<div class="mt-6 flex flex-col gap-3 sm:flex-row sm:justify-center">
<a href="{% url 'claims:submit' %}"
class="inline-flex items-center justify-center rounded-full bg-brand-600 px-5 py-3 text-sm font-semibold text-white transition hover:bg-brand-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-brand-600 focus-visible:ring-offset-2">
Skicka nytt utlägg
</a>
<a href="{% url 'login' %}"
class="inline-flex items-center justify-center rounded-full border border-gray-200 px-5 py-3 text-sm font-semibold text-gray-700 transition hover:bg-gray-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-brand-600 focus-visible:ring-offset-2">
Logga in
</a>
</div>
</div>
</section>
{% endblock %}

View File

@@ -3,76 +3,175 @@
{% block title %}Användarhantering{% endblock %}
{% block content %}
<h1>Användare & behörigheter</h1>
<p>Skapa nya konton, underhåll behörigheter och ta bort användare kopplat till utläggssystemet.</p>
<section class="space-y-10 py-8">
<header class="max-w-3xl">
<p class="text-sm font-semibold uppercase tracking-wide text-brand-600">Konton & behörigheter</p>
<h1 class="mt-2 text-3xl font-semibold text-gray-900">Hantera användare</h1>
<p class="mt-3 text-sm text-gray-600">
Skapa nya konton, justera rättigheter för claim-flödet och ta bort användare som inte längre ska ha åtkomst.
</p>
<div class="mt-4 rounded-2xl border border-dashed border-amber-200 bg-amber-50 px-4 py-3 text-xs text-amber-800">
Notis: denna sida styr direkta behörigheter. Rättigheter via grupper eller superuser-status gäller även om kryssrutorna avmarkeras.
</div>
</header>
<p><small>Notis: sidan hanterar direkta behörigheter. Behörigheter via grupper eller superuser-status gäller även om kryssrutorna avmarkeras.</small></p>
<section>
<h2>Skapa ny användare</h2>
<form method="post">
{% csrf_token %}
<input type="hidden" name="action" value="create">
{{ create_form.as_p }}
<button type="submit">Skapa användare</button>
</form>
</section>
<hr>
<section>
<h2>Befintliga användare</h2>
<table>
<thead>
<tr>
<th>Användare</th>
<th>Namn</th>
<th>E-post</th>
<th>Behörigheter</th>
<th>Ta bort</th>
</tr>
</thead>
<tbody>
{% for row in user_rows %}
{% with user=row.user %}
<tr>
<td>
{{ user.username }}
{% if user.is_superuser %}<span title="Superuser"></span>{% endif %}
</td>
<td>{{ user.get_full_name|default:"-" }}</td>
<td>{{ user.email|default:"-" }}</td>
<td>
{% with form=row.permission_form %}
<form method="post">
{% csrf_token %}
<input type="hidden" name="action" value="update">
{{ form.user_id }}
<label>{{ form.is_staff }} {{ form.is_staff.label }}</label><br>
<label>{{ form.grant_view }} {{ form.grant_view.label }}</label><br>
<label>{{ form.grant_change }} {{ form.grant_change.label }}</label><br>
<button type="submit">Spara</button>
</form>
{% endwith %}
</td>
<td>
{% if row.delete_form %}
<form method="post" onsubmit="return confirm('Ta bort {{ user.username }}?');">
{% csrf_token %}
<input type="hidden" name="action" value="delete">
{{ row.delete_form.user_id }}
<button type="submit">Ta bort</button>
</form>
<div class="grid gap-8 lg:grid-cols-2">
<div class="rounded-3xl bg-white px-6 py-8 shadow-sm ring-1 ring-gray-100">
<div class="border-b border-gray-100 pb-4">
<p class="text-sm font-semibold uppercase tracking-wide text-gray-500">Nytt konto</p>
<h2 class="mt-1 text-2xl font-semibold text-gray-900">Skapa användare</h2>
<p class="mt-1 text-sm text-gray-600">Lösenordet valideras mot Djangos standardregler.</p>
</div>
<form method="post" class="mt-6 space-y-4">
{% csrf_token %}
<input type="hidden" name="action" value="create">
{% for field in create_form %}
<div>
<label class="text-sm font-medium text-gray-700" for="{{ field.id_for_label }}">{{ field.label }}</label>
{% if field.is_hidden %}
{{ field }}
{% else %}
<em></em>
<input
type="{{ field.field.widget.input_type|default:'text' }}"
name="{{ field.html_name }}"
id="{{ field.id_for_label }}"
value="{{ field.value|default_if_none:'' }}"
class="mt-1 block w-full rounded-xl border border-gray-200 px-3 py-2 text-sm shadow-sm focus:border-brand-600 focus:outline-none focus:ring-2 focus:ring-brand-600"
{% if field.field.required %}required{% endif %}
{% if field.field.widget.attrs.autocomplete %}autocomplete="{{ field.field.widget.attrs.autocomplete }}"{% endif %}
{% if field.field.widget.attrs.autofocus %}autofocus{% endif %}
/>
{% endif %}
</td>
</tr>
{% if field.help_text %}
<p class="mt-1 text-xs text-gray-500">{{ field.help_text }}</p>
{% endif %}
{% for error in field.errors %}
<p class="mt-1 text-sm text-rose-600">{{ error }}</p>
{% endfor %}
</div>
{% endfor %}
<button type="submit" class="w-full rounded-2xl bg-brand-600 px-4 py-3 text-sm font-semibold text-white transition hover:bg-brand-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-brand-600 focus-visible:ring-offset-2">
Skapa användare
</button>
</form>
</div>
<div class="rounded-3xl bg-slate-900 px-6 py-8 text-slate-100 shadow-sm ring-1 ring-slate-800">
<h3 class="text-2xl font-semibold">Tips för kontohantering</h3>
<ul class="mt-4 space-y-3 text-sm text-slate-300 break-words">
<li class="flex items-start gap-3">
<span class="mt-1 h-2 w-2 rounded-full bg-brand-400"></span>
<span class="min-w-0">Lägg användare i grupper via Django admin om flera personer ska dela samma roll.</span>
</li>
<li class="flex items-start gap-3">
<span class="mt-1 h-2 w-2 rounded-full bg-brand-400"></span>
<span class="min-w-0">
Behörigheterna <code class="break-normal rounded bg-slate-800 px-2 py-1 text-xs">claims.view_claim</code>
och <code class="break-normal rounded bg-slate-800 px-2 py-1 text-xs">claims.change_claim</code>
styr åtkomst till adminvyn respektive beslutsflödet.
</span>
</li>
<li class="flex items-start gap-3">
<span class="mt-1 h-2 w-2 rounded-full bg-brand-400"></span>
<span class="min-w-0">En markerad <strong>Admin/staff</strong>-användare kan nå Django admin och skapa projekt, exportflöden m.m.</span>
</li>
<li class="flex items-start gap-3">
<span class="mt-1 h-2 w-2 rounded-full bg-brand-400"></span>
<span class="min-w-0">Ta bara bort konton du är säker på historik försvinner inte, men personen tappar all åtkomst.</span>
</li>
</ul>
</div>
</div>
<section class="space-y-6">
<div>
<p class="text-sm font-semibold uppercase tracking-wide text-gray-500">Befintliga användare</p>
<h2 class="text-2xl font-semibold text-gray-900">Justera behörigheter</h2>
</div>
<div class="space-y-4">
{% for row in user_rows %}
{% with user=row.user form=row.permission_form delete_form=row.delete_form %}
<article class="rounded-3xl bg-white px-6 py-6 shadow-sm ring-1 ring-gray-100">
<div class="flex flex-col gap-4 border-b border-gray-100 pb-4 md:flex-row md:items-center md:justify-between">
<div>
<div class="flex items-center gap-3">
<h3 class="text-xl font-semibold text-gray-900">{{ user.username }}</h3>
{% if user.is_superuser %}
<span class="rounded-full bg-emerald-50 px-3 py-1 text-xs font-semibold uppercase tracking-wide text-emerald-700">Superuser</span>
{% endif %}
</div>
<p class="text-sm text-gray-600">
{{ user.get_full_name|default:"Saknar namn" }} · {{ user.email|default:"Ingen e-post" }}
</p>
</div>
<p class="text-xs uppercase tracking-wide text-gray-400">
ID: {{ user.id }}
</p>
</div>
<div class="mt-4 grid gap-6 lg:grid-cols-[2fr,1fr]">
<form method="post" class="space-y-4 rounded-2xl bg-slate-50 p-4">
{% csrf_token %}
<input type="hidden" name="action" value="update">
{{ form.user_id }}
<div class="space-y-3 text-sm text-gray-700">
<label class="flex items-center gap-2" for="{{ form.is_staff.id_for_label }}">
<input type="checkbox"
id="{{ form.is_staff.id_for_label }}"
name="{{ form.is_staff.html_name }}"
value="on"
{% if form.is_staff.value %}checked{% endif %}
class="h-4 w-4 rounded border-gray-300 text-brand-600 focus:ring-brand-600">
<span>Admin/staff</span>
</label>
<label class="flex items-center gap-2" for="{{ form.grant_view.id_for_label }}">
<input type="checkbox"
id="{{ form.grant_view.id_for_label }}"
name="{{ form.grant_view.html_name }}"
value="on"
{% if form.grant_view.value %}checked{% endif %}
class="h-4 w-4 rounded border-gray-300 text-brand-600 focus:ring-brand-600">
<span>Får se utlägg</span>
</label>
<label class="flex items-center gap-2" for="{{ form.grant_change.id_for_label }}">
<input type="checkbox"
id="{{ form.grant_change.id_for_label }}"
name="{{ form.grant_change.html_name }}"
value="on"
{% if form.grant_change.value %}checked{% endif %}
class="h-4 w-4 rounded border-gray-300 text-brand-600 focus:ring-brand-600">
<span>Får besluta utlägg</span>
</label>
</div>
<button type="submit" class="w-full rounded-2xl bg-brand-600 px-4 py-2 text-sm font-semibold text-white transition hover:bg-brand-700">
Spara behörigheter
</button>
</form>
<div class="rounded-2xl border border-red-100 bg-red-50 p-4 text-sm text-red-800">
<p class="font-semibold">Ta bort konto</p>
{% if delete_form %}
<p class="mt-1 text-xs text-red-700">Åtgärden går inte att ångra. Användaren förlorar omedelbart åtkomst.</p>
<form method="post" class="mt-4" onsubmit="return confirm('Ta bort {{ user.username }}?');">
{% csrf_token %}
<input type="hidden" name="action" value="delete">
{{ delete_form.user_id }}
<button type="submit" class="w-full rounded-full bg-red-600 px-4 py-2 text-xs font-semibold text-white transition hover:bg-red-700">
Ta bort användare
</button>
</form>
{% else %}
<p class="mt-2 text-xs text-red-700">Kan inte tas bort (antingen du själv eller superuser).</p>
{% endif %}
</div>
</div>
</article>
{% endwith %}
{% empty %}
<tr><td colspan="5">Inga användare upplagda.</td></tr>
<div class="rounded-3xl border border-dashed border-gray-200 bg-white px-6 py-10 text-center text-gray-500">
<p class="text-lg font-semibold text-gray-900">Inga användare ännu</p>
<p class="mt-2 text-sm">Skapa det första kontot via formuläret ovan.</p>
</div>
{% endfor %}
</tbody>
</table>
</div>
</section>
</section>
{% endblock %}