Skip to content

Commit d3b0a4e

Browse files
authored
Merge pull request #95 from AAdewunmi/chore/build-claim-detail-page-shell
Chore/build claim detail page shell
2 parents ebd5f5e + 6374c98 commit d3b0a4e

File tree

2 files changed

+171
-74
lines changed

2 files changed

+171
-74
lines changed
Lines changed: 168 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,196 @@
1+
<!-- path: policylens/apps/ops/templates/ops/claim_detail.html -->
12
{% extends "ops/base.html" %}
3+
{% load static %}
24

35
{% block title %}Claim #{{ claim.id }}, PolicyLens Ops{% endblock %}
46

57
{% block content %}
6-
<div class="mb-3">
7-
<a class="text-decoration-none small" href="{% url 'ops:queue' %}">&larr; Back to queue</a>
8-
</div>
9-
10-
<div class="d-flex align-items-center justify-content-between mb-3">
8+
<div class="d-flex align-items-start justify-content-between mb-3">
119
<div>
10+
<div class="text-muted small">
11+
<a href="{% url 'ops:queue' %}" class="text-decoration-none">Queue</a>
12+
<span class="mx-2">/</span>
13+
<span>Claim #{{ claim.id }}</span>
14+
</div>
1215
<h1 class="h3 mb-1">Claim #{{ claim.id }}</h1>
13-
<div class="text-muted">Claim detail placeholder.</div>
16+
<div class="text-muted">
17+
Policy <span class="fw-semibold">{{ claim.policy.policy_number }}</span>
18+
<span class="mx-2">·</span>
19+
{{ claim.status }}
20+
<span class="mx-2">·</span>
21+
{{ claim.priority }}
22+
</div>
1423
</div>
15-
<div>
16-
<span class="badge text-bg-secondary">{{ claim.priority }}</span>
17-
{% if claim.status == "DECIDED" %}
18-
<span class="badge text-bg-success">Decided</span>
19-
{% elif claim.status == "IN_REVIEW" %}
20-
<span class="badge text-bg-warning">In review</span>
21-
{% else %}
22-
<span class="badge text-bg-primary">New</span>
23-
{% endif %}
24+
<div class="text-end">
25+
<div class="small text-muted">Created</div>
26+
<div class="fw-semibold">{{ claim.created_at }}</div>
2427
</div>
2528
</div>
2629

27-
<div class="row g-3 mb-3">
28-
<div class="col-6 col-md-3">
29-
<div class="card shadow-sm h-100">
30+
<div class="row g-3">
31+
<div class="col-12 col-lg-4">
32+
<div class="card shadow-sm mb-3">
3033
<div class="card-body">
31-
<div class="text-muted small">Documents</div>
32-
<div class="h4 mb-0">{{ claim.documents.all|length }}</div>
34+
<div class="d-flex align-items-center justify-content-between">
35+
<div class="h6 mb-0">SLA</div>
36+
{% if claim.sla_clock and claim.sla_clock.breached_at %}
37+
<span class="badge text-bg-danger">Breached</span>
38+
{% else %}
39+
<span class="badge text-bg-success">Active</span>
40+
{% endif %}
41+
</div>
42+
<hr class="my-3">
43+
{% if claim.sla_clock %}
44+
<div class="small text-muted">Due at</div>
45+
<div class="fw-semibold">{{ claim.sla_clock.due_at|default:"(not set)" }}</div>
46+
{% if claim.sla_clock.breached_at %}
47+
<div class="small text-muted mt-2">Breached at</div>
48+
<div class="fw-semibold">{{ claim.sla_clock.breached_at }}</div>
49+
{% endif %}
50+
{% else %}
51+
<div class="text-muted">No SLA clock found.</div>
52+
{% endif %}
3353
</div>
3454
</div>
35-
</div>
36-
<div class="col-6 col-md-3">
37-
<div class="card shadow-sm h-100">
55+
56+
<div class="card shadow-sm">
3857
<div class="card-body">
39-
<div class="text-muted small">Notes</div>
40-
<div class="h4 mb-0">{{ claim.notes.all|length }}</div>
58+
<div class="d-flex align-items-center justify-content-between">
59+
<div class="h6 mb-0">ML completeness</div>
60+
{% if claim.ml_score %}
61+
{% if claim.ml_score.label == "LIKELY_INCOMPLETE" %}
62+
<span class="badge text-bg-warning">Likely incomplete</span>
63+
{% else %}
64+
<span class="badge text-bg-success">Likely complete</span>
65+
{% endif %}
66+
{% else %}
67+
<span class="badge text-bg-secondary">Not scored</span>
68+
{% endif %}
69+
</div>
70+
<hr class="my-3">
71+
{% if claim.ml_score %}
72+
<div class="small text-muted">Score</div>
73+
<div class="fw-semibold">{{ claim.ml_score.score }}</div>
74+
75+
<div class="small text-muted mt-2">Reason codes</div>
76+
{% if claim.ml_score.reason_codes|length == 0 %}
77+
<div class="text-muted">(none)</div>
78+
{% else %}
79+
<ul class="mb-0">
80+
{% for r in claim.ml_score.reason_codes %}
81+
<li class="small">{{ r }}</li>
82+
{% endfor %}
83+
</ul>
84+
{% endif %}
85+
86+
<div class="small text-muted mt-2">Model</div>
87+
<div class="text-muted small">{{ claim.ml_score.model_version }}</div>
88+
{% else %}
89+
<div class="text-muted">No score recorded yet.</div>
90+
{% endif %}
4191
</div>
4292
</div>
4393
</div>
44-
<div class="col-6 col-md-3">
45-
<div class="card shadow-sm h-100">
94+
95+
<div class="col-12 col-lg-8">
96+
<div class="card shadow-sm mb-3">
4697
<div class="card-body">
47-
<div class="text-muted small">Decisions</div>
48-
<div class="h4 mb-0">{{ claim.decisions.all|length }}</div>
98+
<div class="h6 mb-2">Summary</div>
99+
<div class="text-muted">{{ claim.summary|default:"(no summary)" }}</div>
49100
</div>
50101
</div>
51-
</div>
52-
<div class="col-6 col-md-3">
53-
<div class="card shadow-sm h-100">
102+
103+
<div class="card shadow-sm mb-3">
54104
<div class="card-body">
55-
<div class="text-muted small">Audit Events</div>
56-
<div class="h4 mb-0">{{ claim.audit_events.all|length }}</div>
105+
<div class="h6 mb-2">Documents</div>
106+
{% if claim.documents.all|length == 0 %}
107+
<div class="text-muted">No documents uploaded.</div>
108+
{% else %}
109+
<div class="table-responsive">
110+
<table class="table mb-0 align-middle">
111+
<thead class="table-light">
112+
<tr>
113+
<th>Filename</th>
114+
<th>Type</th>
115+
<th class="text-end">Bytes</th>
116+
<th class="text-end">Uploaded</th>
117+
</tr>
118+
</thead>
119+
<tbody>
120+
{% for d in claim.documents.all %}
121+
<tr>
122+
<td class="fw-semibold">{{ d.original_filename }}</td>
123+
<td class="text-muted small">{{ d.content_type }}</td>
124+
<td class="text-end text-muted small">{{ d.size_bytes }}</td>
125+
<td class="text-end text-muted small">{{ d.uploaded_at }}</td>
126+
</tr>
127+
{% endfor %}
128+
</tbody>
129+
</table>
130+
</div>
131+
{% endif %}
132+
</div>
133+
</div>
134+
135+
<div class="card shadow-sm mb-3">
136+
<div class="card-body">
137+
<div class="h6 mb-2">Notes</div>
138+
{% if claim.notes.all|length == 0 %}
139+
<div class="text-muted">No notes yet.</div>
140+
{% else %}
141+
<div class="vstack gap-2">
142+
{% for n in claim.notes.all %}
143+
<div class="border rounded p-2 bg-white">
144+
<div class="small text-muted">{{ n.created_at }} · {{ n.created_by|default:"(unknown)" }}</div>
145+
<div>{{ n.body }}</div>
146+
</div>
147+
{% endfor %}
148+
</div>
149+
{% endif %}
150+
</div>
151+
</div>
152+
153+
<div class="card shadow-sm mb-3">
154+
<div class="card-body">
155+
<div class="h6 mb-2">Decisions</div>
156+
{% if claim.decisions.all|length == 0 %}
157+
<div class="text-muted">No decisions recorded.</div>
158+
{% else %}
159+
<div class="vstack gap-2">
160+
{% for d in claim.decisions.all %}
161+
<div class="border rounded p-2 bg-white">
162+
<div class="small text-muted">{{ d.decided_at }} · {{ d.decided_by|default:"(unknown)" }}</div>
163+
<div class="fw-semibold">{{ d.decision }}</div>
164+
{% if d.notes %}
165+
<div class="text-muted">{{ d.notes }}</div>
166+
{% endif %}
167+
</div>
168+
{% endfor %}
169+
</div>
170+
{% endif %}
57171
</div>
58172
</div>
59-
</div>
60-
</div>
61173

62-
<div class="card shadow-sm">
63-
<div class="card-body">
64-
<div><strong>Policy:</strong> {{ claim.policy.policy_number }}</div>
65-
<div><strong>Holder:</strong> {{ claim.policy.holder.full_name }}</div>
66-
<div><strong>Created:</strong> {{ claim.created_at }}</div>
67-
<div class="mt-2 text-muted">{{ claim.summary|default:"(no summary)" }}</div>
174+
<div class="card shadow-sm">
175+
<div class="card-body">
176+
<div class="h6 mb-2">Audit timeline</div>
177+
{% if claim.audit_events.all|length == 0 %}
178+
<div class="text-muted">No audit events recorded.</div>
179+
{% else %}
180+
<div class="vstack gap-2">
181+
{% for e in claim.audit_events.all %}
182+
<div class="border rounded p-2 bg-white">
183+
<div class="small text-muted">{{ e.created_at }} · {{ e.actor|default:"(unknown)" }}</div>
184+
<div class="fw-semibold">{{ e.event_type }}</div>
185+
{% if e.payload %}
186+
<pre class="small mb-0 mt-2 bg-body-tertiary p-2 rounded">{{ e.payload|safe }}</pre>
187+
{% endif %}
188+
</div>
189+
{% endfor %}
190+
</div>
191+
{% endif %}
192+
</div>
193+
</div>
68194
</div>
69195
</div>
70196
{% endblock %}

policylens/apps/ops/views.py

Lines changed: 3 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,10 @@
1010
from __future__ import annotations
1111

1212
from django.contrib.auth.decorators import login_required
13-
from django.db.models import Prefetch
1413
from django.http import HttpRequest, HttpResponse
1514
from django.shortcuts import get_object_or_404, redirect, render
1615

17-
from policylens.apps.claims.models import (
18-
AuditEvent,
19-
Claim,
20-
ClaimDocument,
21-
InternalNote,
22-
ReviewDecision,
23-
)
16+
from policylens.apps.claims.models import Claim
2417
from policylens.apps.claims.queue import build_queue_queryset
2518

2619

@@ -58,28 +51,6 @@ def queue_view(request: HttpRequest) -> HttpResponse:
5851

5952
@login_required
6053
def claim_detail_view(request: HttpRequest, claim_id: int) -> HttpResponse:
61-
"""Claim detail placeholder. Full content lands Wednesday."""
62-
claim = get_object_or_404(
63-
Claim.objects.select_related(
64-
"policy", "policy__holder", "sla_clock", "ml_score"
65-
).prefetch_related(
66-
Prefetch(
67-
"documents",
68-
queryset=ClaimDocument.objects.order_by("-uploaded_at"),
69-
),
70-
Prefetch(
71-
"notes",
72-
queryset=InternalNote.objects.order_by("-created_at"),
73-
),
74-
Prefetch(
75-
"decisions",
76-
queryset=ReviewDecision.objects.order_by("-decided_at"),
77-
),
78-
Prefetch(
79-
"audit_events",
80-
queryset=AuditEvent.objects.order_by("-created_at"),
81-
),
82-
),
83-
pk=claim_id,
84-
)
54+
"""Render claim detail shape with basic claim fetch only."""
55+
claim = get_object_or_404(Claim, pk=claim_id)
8556
return render(request, "ops/claim_detail.html", context={"claim": claim})

0 commit comments

Comments
 (0)