Skip to content

Commit d3fb3a4

Browse files
committed
feat: Improve approval page UI with progress indicator and section headers
- Add visual approval progress bar showing all steps with current/completed status - Add 'Submission Data' section header with icon for student-submitted data - Show current approval step with 'Your Action Required' badge - Display future approval steps greyed out to show the workflow - Add 'Current Step: X of Y' in submission info - Consistent styling with icons throughout Bump version to 0.7.2
1 parent e14a56d commit d3fb3a4

4 files changed

Lines changed: 127 additions & 18 deletions

File tree

django_forms_workflows/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Enterprise-grade, database-driven form builder with approval workflows
44
"""
55

6-
__version__ = "0.7.1"
6+
__version__ = "0.7.2"
77
__author__ = "Django Forms Workflows Contributors"
88
__license__ = "LGPL-3.0-only"
99

django_forms_workflows/templates/django_forms_workflows/approve.html

Lines changed: 97 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,39 @@
77
<div class="row">
88
<div class="col-lg-10 mx-auto">
99
<h1 class="mb-4">Review Submission</h1>
10+
11+
<!-- Approval Progress (only show for sequential workflows with steps) -->
12+
{% if approval_steps %}
13+
<div class="card mb-4">
14+
<div class="card-header bg-light">
15+
<h5 class="mb-0"><i class="bi bi-diagram-3"></i> Approval Progress</h5>
16+
</div>
17+
<div class="card-body">
18+
<div class="d-flex justify-content-between align-items-center flex-wrap">
19+
{% for step in approval_steps %}
20+
<div class="text-center flex-fill p-2">
21+
<div class="rounded-circle d-inline-flex justify-content-center align-items-center mb-2 {% if step.is_completed %}bg-success text-white{% elif step.is_current %}bg-primary text-white{% elif step.is_rejected %}bg-danger text-white{% else %}bg-light text-muted border{% endif %}" style="width: 40px; height: 40px;">
22+
{% if step.is_completed %}<i class="bi bi-check"></i>{% elif step.is_rejected %}<i class="bi bi-x"></i>{% else %}{{ step.number }}{% endif %}
23+
</div>
24+
<div class="{% if step.is_current %}fw-bold text-primary{% elif step.is_pending %}text-muted{% endif %}">
25+
<small>{{ step.group_name }}</small>
26+
</div>
27+
</div>
28+
{% if not forloop.last %}
29+
<div class="flex-shrink-1 px-2 text-muted d-none d-md-block">
30+
<i class="bi bi-arrow-right"></i>
31+
</div>
32+
{% endif %}
33+
{% endfor %}
34+
</div>
35+
</div>
36+
</div>
37+
{% endif %}
38+
1039
<!-- Submission Info -->
1140
<div class="card mb-4">
1241
<div class="card-header">
13-
<h5 class="mb-0">Submission Information</h5>
42+
<h5 class="mb-0"><i class="bi bi-info-circle"></i> Submission Information</h5>
1443
</div>
1544
<div class="card-body">
1645
<div class="row">
@@ -22,32 +51,84 @@ <h5 class="mb-0">Submission Information</h5>
2251
<div class="col-md-6">
2352
<p><strong>Submitted:</strong> {{ submission.submitted_at|date:"Y-m-d H:i" }}</p>
2453
<p><strong>Status:</strong> {{ submission.get_status_display }}</p>
25-
<p><strong>Approval Step:</strong> {{ task.step_name }}</p>
54+
{% if current_step_number and total_steps %}
55+
<p><strong>Current Step:</strong> {{ current_step_number }} of {{ total_steps }}</p>
56+
{% endif %}
2657
</div>
2758
</div>
2859
</div>
2960
</div>
3061

3162
{% if has_approval_step_fields and approval_step_form %}
63+
<!-- Submission Data Section (Read-only) -->
64+
<div class="card mb-4">
65+
<div class="card-header bg-secondary text-white">
66+
<h5 class="mb-0"><i class="bi bi-file-text"></i> Submission Data</h5>
67+
</div>
68+
<div class="card-body">
69+
<table class="table table-bordered table-sm">
70+
{% for key, value in submission.form_data.items %}
71+
{% if not key|slice:":7" == "advisor" and not key|slice:":2" == "fa" and not key|slice:":9" == "registrar" and not key|slice:":7" == "manager" %}
72+
<tr>
73+
<th style="width: 30%;">{{ key|title }}</th>
74+
<td>
75+
{% if value.url %}
76+
{% if value.content_type and 'image' in value.content_type %}
77+
<img src="{{ value.url }}" alt="{{ value.filename }}" class="img-fluid" style="max-width: 200px; max-height: 200px;">
78+
{% endif %}
79+
<a href="{{ value.url }}" target="_blank" class="btn btn-sm btn-outline-primary">
80+
<i class="bi bi-download"></i> {{ value.filename }}
81+
</a>
82+
{% elif value.path %}
83+
<span class="text-muted">{{ value.filename }}</span>
84+
{% else %}
85+
{{ value }}
86+
{% endif %}
87+
</td>
88+
</tr>
89+
{% endif %}
90+
{% endfor %}
91+
</table>
92+
</div>
93+
</div>
94+
3295
<!-- Approval Form with Step Fields -->
3396
<form method="post" data-loading-message="Processing your decision...">
3497
{% csrf_token %}
3598

36-
<!-- Step Fields Section -->
37-
<div class="card mb-4">
99+
<!-- Current Step Fields Section -->
100+
<div class="card mb-4 border-primary">
38101
<div class="card-header bg-primary text-white">
39-
<h5 class="mb-0"><i class="bi bi-pencil-square"></i> {{ task.step_name }}</h5>
102+
<h5 class="mb-0"><i class="bi bi-pencil-square"></i> Step {{ current_step_number }}: {{ task.step_name }}
103+
<span class="badge bg-light text-primary float-end">Your Action Required</span>
104+
</h5>
40105
</div>
41106
<div class="card-body">
42-
<p class="text-muted mb-4">Please review the submission and fill in the required fields below.</p>
107+
<p class="text-muted mb-4">Please review the submission above and fill in the required fields below.</p>
43108
{{ approval_step_form|crispy }}
44109
</div>
45110
</div>
46111

112+
<!-- Future Steps Preview (greyed out) -->
113+
{% for step in approval_steps %}
114+
{% if step.number > current_step_number %}
115+
<div class="card mb-4 border-secondary opacity-50">
116+
<div class="card-header bg-light text-muted">
117+
<h5 class="mb-0"><i class="bi bi-hourglass"></i> Step {{ step.number }}: {{ step.group_name }}
118+
<span class="badge bg-secondary float-end">Pending</span>
119+
</h5>
120+
</div>
121+
<div class="card-body">
122+
<p class="text-muted mb-0"><em>This step will be available after all previous steps are approved.</em></p>
123+
</div>
124+
</div>
125+
{% endif %}
126+
{% endfor %}
127+
47128
<!-- Decision Section -->
48-
<div class="card mb-4">
49-
<div class="card-header">
50-
<h5 class="mb-0">Your Decision</h5>
129+
<div class="card mb-4 border-success">
130+
<div class="card-header bg-light">
131+
<h5 class="mb-0"><i class="bi bi-clipboard-check"></i> Your Decision</h5>
51132
</div>
52133
<div class="card-body">
53134
<div class="mb-3">
@@ -71,10 +152,10 @@ <h5 class="mb-0">Your Decision</h5>
71152

72153
{% else %}
73154
<!-- Standard Approval (No Step Fields) -->
74-
<!-- Form Data -->
155+
<!-- Submission Data -->
75156
<div class="card mb-4">
76-
<div class="card-header">
77-
<h5 class="mb-0">Submitted Data</h5>
157+
<div class="card-header bg-secondary text-white">
158+
<h5 class="mb-0"><i class="bi bi-file-text"></i> Submission Data</h5>
78159
</div>
79160
<div class="card-body">
80161
<table class="table table-bordered">
@@ -108,10 +189,10 @@ <h5 class="mb-0">Submitted Data</h5>
108189
</table>
109190
</div>
110191
</div>
111-
<!-- Approval Form -->
112-
<div class="card mb-4">
113-
<div class="card-header">
114-
<h5 class="mb-0">Your Decision</h5>
192+
<!-- Approval Decision -->
193+
<div class="card mb-4 border-success">
194+
<div class="card-header bg-light">
195+
<h5 class="mb-0"><i class="bi bi-clipboard-check"></i> Your Decision</h5>
115196
</div>
116197
<div class="card-body">
117198
<form method="post" data-loading-message="Processing your decision...">

django_forms_workflows/views.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,31 @@ def approve_submission(request, task_id):
408408
user=request.user,
409409
)
410410

411+
# Build approval steps info for display
412+
approval_steps = []
413+
workflow = form_def.workflow
414+
if workflow and workflow.approval_logic == "sequence":
415+
# Get all approval groups in order
416+
approval_groups = list(workflow.approval_groups.all())
417+
total_steps = len(approval_groups)
418+
419+
# Get all tasks for this submission to check completion status
420+
all_tasks = {t.step_number: t for t in submission.approval_tasks.all()}
421+
422+
for idx, group in enumerate(approval_groups, start=1):
423+
step_task = all_tasks.get(idx)
424+
step_info = {
425+
"number": idx,
426+
"total": total_steps,
427+
"group_name": group.name,
428+
"is_current": idx == task.step_number,
429+
"is_completed": step_task.status == "approved" if step_task else False,
430+
"is_rejected": step_task.status == "rejected" if step_task else False,
431+
"is_pending": idx > task.step_number,
432+
"task": step_task,
433+
}
434+
approval_steps.append(step_info)
435+
411436
return render(
412437
request,
413438
"django_forms_workflows/approve.html",
@@ -416,6 +441,9 @@ def approve_submission(request, task_id):
416441
"submission": submission,
417442
"approval_step_form": approval_step_form,
418443
"has_approval_step_fields": has_approval_step_fields,
444+
"approval_steps": approval_steps,
445+
"current_step_number": task.step_number,
446+
"total_steps": len(approval_steps) if approval_steps else 0,
419447
},
420448
)
421449

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "django-forms-workflows"
3-
version = "0.7.1"
3+
version = "0.7.2"
44
description = "Enterprise-grade, database-driven form builder with approval workflows and external data integration"
55
license = "LGPL-3.0-only"
66
readme = "README.md"

0 commit comments

Comments
 (0)