Skip to content

Commit 9760d8c

Browse files
committed
feat: phone format (###) ###-#### + discard-draft UX on submission detail (v0.37.11)
Phone field: - Enforce (###) ###-#### format with optional +## country-code prefix - Updated HTML pattern, RegexValidator, placeholder, and error message - Applied to both DynamicForm.add_field and ApprovalStepForm._create_field Discard draft: - submission_detail.html now shows Continue Editing + Discard Draft action buttons when the viewing user owns a draft submission - Links to existing form_submit and discard_draft views respectively Bump version 0.37.10 → 0.37.11
1 parent c9a617a commit 9760d8c

File tree

4 files changed

+39
-9
lines changed

4 files changed

+39
-9
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.37.11] - 2026-03-27
11+
12+
### Added
13+
- **Phone field format**`phone` field type now enforces the `(###) ###-####` format with an optional international country-code prefix (`+## `). The HTML `pattern` attribute, the server-side `RegexValidator`, the placeholder, and the error message all reflect the new format. Valid examples: `(555) 867-5309`, `+1 (555) 867-5309`, `+44 (555) 867-5309`.
14+
- **Discard Draft on submission detail page** — when a user views their own draft via `submission_detail`, the action bar now shows a **Continue Editing** button (links back to the form to resume editing) and a **Discard Draft** button (links to the existing confirmation page at `submissions/<id>/discard/`). Both buttons are only shown when `submission.submitter == user`.
15+
1016
## [0.37.10] - 2026-03-27
1117

1218
### Added

django_forms_workflows/forms.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -342,11 +342,15 @@ def add_field(self, field_def, initial_data):
342342
)
343343

344344
elif field_def.field_type == "phone":
345+
# Format: optional country code (+## ) then (###) ###-####
346+
# Examples: (555) 867-5309 | +1 (555) 867-5309 | +44 (555) 867-5309
347+
_phone_pattern = r"(\+[0-9]{1,3} )?\([0-9]{3}\) [0-9]{3}-[0-9]{4}"
345348
widget_attrs.update(
346349
{
347350
"type": "tel",
348351
"inputmode": "tel",
349-
"pattern": r"[+]?[(]?[0-9]{3}[)]?[-\s.]?[0-9]{3}[-\s.]?[0-9]{4,6}",
352+
"pattern": _phone_pattern,
353+
"placeholder": widget_attrs.get("placeholder", "(555) 867-5309"),
350354
}
351355
)
352356
field = forms.CharField(
@@ -356,8 +360,11 @@ def add_field(self, field_def, initial_data):
356360
)
357361
field.validators.append(
358362
RegexValidator(
359-
regex=r"^\+?[(]?[0-9]{3}[)]?[-\s.]?[0-9]{3}[-\s.]?[0-9]{4,6}$",
360-
message="Enter a valid phone number (e.g. 555-555-5555 or +1 555 555 5555).",
363+
regex=r"^(\+[0-9]{1,3} )?\([0-9]{3}\) [0-9]{3}-[0-9]{4}$",
364+
message=(
365+
"Enter a phone number in the format (555) 867-5309 "
366+
"or +1 (555) 867-5309 for international numbers."
367+
),
361368
)
362369
)
363370
self.fields[field_def.field_name] = field
@@ -1141,11 +1148,14 @@ def _create_field(self, field_def, field_args, widget_attrs, is_editable):
11411148
)
11421149

11431150
elif field_def.field_type == "phone":
1151+
# Format: optional country code (+## ) then (###) ###-####
1152+
_phone_pattern = r"(\+[0-9]{1,3} )?\([0-9]{3}\) [0-9]{3}-[0-9]{4}"
11441153
widget_attrs.update(
11451154
{
11461155
"type": "tel",
11471156
"inputmode": "tel",
1148-
"pattern": r"[+]?[(]?[0-9]{3}[)]?[-\s.]?[0-9]{3}[-\s.]?[0-9]{4,6}",
1157+
"pattern": _phone_pattern,
1158+
"placeholder": widget_attrs.get("placeholder", "(555) 867-5309"),
11491159
}
11501160
)
11511161
field = forms.CharField(
@@ -1155,8 +1165,11 @@ def _create_field(self, field_def, field_args, widget_attrs, is_editable):
11551165
)
11561166
field.validators.append(
11571167
RegexValidator(
1158-
regex=r"^\+?[(]?[0-9]{3}[)]?[-\s.]?[0-9]{3}[-\s.]?[0-9]{4,6}$",
1159-
message="Enter a valid phone number (e.g. 555-555-5555 or +1 555 555 5555).",
1168+
regex=r"^(\+[0-9]{1,3} )?\([0-9]{3}\) [0-9]{3}-[0-9]{4}$",
1169+
message=(
1170+
"Enter a phone number in the format (555) 867-5309 "
1171+
"or +1 (555) 867-5309 for international numbers."
1172+
),
11601173
)
11611174
)
11621175
self.fields[field_def.field_name] = field

django_forms_workflows/templates/django_forms_workflows/submission_detail.html

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -483,10 +483,22 @@ <h5 class="mb-0">Approval History</h5>
483483

484484

485485
<!-- Actions -->
486-
<div class="mt-3">
486+
<div class="mt-3 d-flex flex-wrap gap-2 align-items-center">
487487
<a href="{% url 'forms_workflows:my_submissions' %}" class="btn btn-outline-secondary">
488488
<i class="bi bi-arrow-left"></i> Back to My Submissions
489489
</a>
490+
{% if submission.status == 'draft' %}
491+
{% if submission.submitter == user %}
492+
<a href="{% url 'forms_workflows:form_submit' submission.form_definition.slug %}"
493+
class="btn btn-primary">
494+
<i class="bi bi-pencil"></i> Continue Editing
495+
</a>
496+
<a href="{% url 'forms_workflows:discard_draft' submission.id %}"
497+
class="btn btn-outline-danger">
498+
<i class="bi bi-trash"></i> Discard Draft
499+
</a>
500+
{% endif %}
501+
{% endif %}
490502
{% if submission.status == 'submitted' or submission.status == 'pending_approval' %}
491503
{% if submission.form_definition.allow_withdrawal and submission.submitter == user %}
492504
<a href="{% url 'forms_workflows:withdraw_submission' submission.id %}" class="btn btn-outline-danger">
@@ -501,7 +513,6 @@ <h5 class="mb-0">Approval History</h5>
501513
</a>
502514
{% endif %}
503515
{% endif %}
504-
505516
</div>
506517
</div>
507518
</div>

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.37.10"
3+
version = "0.37.11"
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)