diff --git a/backend/api/base/modal.py b/backend/api/base/modal.py index 3f52a39e6..82c3255e4 100644 --- a/backend/api/base/modal.py +++ b/backend/api/base/modal.py @@ -1,7 +1,7 @@ from django.http import HttpRequest, HttpResponseBadRequest from django.shortcuts import render -from backend.models import UserSettings +from backend.models import UserSettings, Invoice # Still working on @@ -19,6 +19,29 @@ def open_modal(request: HttpRequest, modal_name, context_type=None, context_valu ] = request.user.user_profile.profile_picture_url except UserSettings.DoesNotExist: pass + elif context_type == "edit_invoice_to": + invoice = context_value + try: + invoice = Invoice.objects.get(user=request.user, id=invoice) + except: + return render(request, template_name, context) + + if invoice.client_to: + context["to_name"] = invoice.client_to.name + context["to_company"] = invoice.client_to.company + context["to_address"] = invoice.client_to.address + context["existing_client_id"] = invoice.client_to.id + # context["to_city"] = invoice.client_to.city + # context["to_county"] = invoice.client_to.county + # context["to_country"] = invoice.client_to.country + else: + context["to_name"] = invoice.client_name + context["to_company"] = invoice.client_company + context["to_address"] = invoice.client_address + # context["to_city"] = invoice.client_city + # context["to_county"] = invoice.client_county + # context["to_country"] = invoice.client_country + return render(request, template_name, context) except: return HttpResponseBadRequest("Something went wrong") diff --git a/backend/api/clients/fetch.py b/backend/api/clients/fetch.py index b14d6000a..e42eaf424 100644 --- a/backend/api/clients/fetch.py +++ b/backend/api/clients/fetch.py @@ -1,3 +1,5 @@ +import time + from django.db.models import Q from django.http import HttpRequest from django.shortcuts import render, redirect @@ -23,3 +25,18 @@ def fetch_all_clients(request: HttpRequest): ) return render(request, "pages/clients/dashboard/_table.html", {"clients": clients}) + + +@require_http_methods(["GET"]) +def fetch_clients_dropdown(request: HttpRequest): + if not request.htmx: + return redirect("clients dashboard") + + selected_client = request.GET.get("existing_client_id") or None + clients = Client.objects.filter(user=request.user, active=True) + + return render( + request, + "pages/invoices/create/_view_clients_dropdown.html", + {"clients": clients, "selected_client": selected_client}, + ) diff --git a/backend/api/clients/urls.py b/backend/api/clients/urls.py index 395c0244c..6ae98023d 100644 --- a/backend/api/clients/urls.py +++ b/backend/api/clients/urls.py @@ -7,6 +7,11 @@ fetch.fetch_all_clients, name="fetch", ), + path( + "fetch/dropdown/", + fetch.fetch_clients_dropdown, + name="fetch dropdown", + ), ] app_name = "clients" diff --git a/backend/api/invoices/create/set_destination.py b/backend/api/invoices/create/set_destination.py index eac11b1e0..5e59411b3 100644 --- a/backend/api/invoices/create/set_destination.py +++ b/backend/api/invoices/create/set_destination.py @@ -1,8 +1,11 @@ +from django.contrib import messages from django.http import HttpRequest from django.shortcuts import render from django.views.decorators.http import require_http_methods -to_get = ["name", "address", "city", "country"] +from backend.models import Client + +to_get = ["name", "address", "city", "country", "company", "is_representative"] @require_http_methods(["POST"]) @@ -11,6 +14,16 @@ def set_destination_to(request: HttpRequest): context.update({key: request.POST.get(key) for key in to_get}) + use_existing = True if request.POST.get("use_existing") == "true" else False + selected_client = request.POST.get("selected_client") if use_existing else None + + if selected_client: + try: + client = Client.objects.get(user=request.user, id=selected_client) + context["existing_client"] = client + except: + messages.error("Client not found") + return render(request, "pages/invoices/create/_to_destination.html", context) diff --git a/backend/api/invoices/delete.py b/backend/api/invoices/delete.py index eb5a635ff..d6fee2ced 100644 --- a/backend/api/invoices/delete.py +++ b/backend/api/invoices/delete.py @@ -9,7 +9,6 @@ @require_http_methods(["DELETE"]) def delete_invoice(request: HttpRequest): delete_items = QueryDict(request.body) - print("del items", delete_items) invoice = delete_items.get("invoice") diff --git a/backend/migrations/0003_client_company_client_is_representative.py b/backend/migrations/0003_client_company_client_is_representative.py new file mode 100644 index 000000000..8e6bc3a65 --- /dev/null +++ b/backend/migrations/0003_client_company_client_is_representative.py @@ -0,0 +1,22 @@ +# Generated by Django 4.1.7 on 2023-12-12 14:37 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("backend", "0002_alter_receipt_date_uploaded"), + ] + + operations = [ + migrations.AddField( + model_name="client", + name="company", + field=models.CharField(blank=True, max_length=100, null=True), + ), + migrations.AddField( + model_name="client", + name="is_representative", + field=models.BooleanField(default=False), + ), + ] diff --git a/backend/migrations/0004_invoice_client_is_representative.py b/backend/migrations/0004_invoice_client_is_representative.py new file mode 100644 index 000000000..4b5d16191 --- /dev/null +++ b/backend/migrations/0004_invoice_client_is_representative.py @@ -0,0 +1,17 @@ +# Generated by Django 4.1.7 on 2023-12-15 08:40 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("backend", "0003_client_company_client_is_representative"), + ] + + operations = [ + migrations.AddField( + model_name="invoice", + name="client_is_representative", + field=models.BooleanField(default=False), + ), + ] diff --git a/backend/models.py b/backend/models.py index f655df3b5..9c237e36c 100644 --- a/backend/models.py +++ b/backend/models.py @@ -128,6 +128,8 @@ class Client(models.Model): name = models.CharField(max_length=64) phone_number = models.CharField(max_length=100, blank=True, null=True) email = models.EmailField(blank=True, null=True) + company = models.CharField(max_length=100, blank=True, null=True) + is_representative = models.BooleanField(default=False) address = models.CharField(max_length=100, blank=True, null=True) city = models.CharField(max_length=100, blank=True, null=True) @@ -181,6 +183,7 @@ class Invoice(models.Model): client_city = models.CharField(max_length=100, blank=True, null=True) client_county = models.CharField(max_length=100, blank=True, null=True) client_country = models.CharField(max_length=100, blank=True, null=True) + client_is_representative = models.BooleanField(default=False) self_name = models.CharField(max_length=100, blank=True, null=True) self_company = models.CharField(max_length=100, blank=True, null=True) @@ -218,6 +221,21 @@ def dynamic_payment_status(self): else: return self.payment_status + @property + def get_to_details(self) -> tuple[str, dict[str, str]]: + """ + Returns the client details for the invoice + "client" and Client object if client_to + "manual" and dict of details if client_name + """ + if self.client_to: + return "client", self.client_to + else: + return "manual", { + "name": self.client_name, + "company": self.client_company, + } + def __str__(self): invoice_id = self.invoice_id or self.id if self.client_name: diff --git a/backend/views/core/clients/create.py b/backend/views/core/clients/create.py index b53c1374b..bc114375e 100644 --- a/backend/views/core/clients/create.py +++ b/backend/views/core/clients/create.py @@ -9,28 +9,47 @@ def create_client(request: HttpRequest): if request.method == "GET": return render(request, "pages/clients/create/create.html") - client_name = request.POST.get("client_name") - client_email = request.POST.get("client_email") - client_address = request.POST.get("client_address") - client_phone = request.POST.get("client_phone") + client_details = { + "name": request.POST.get("client_name"), + "email": request.POST.get("client_email"), + "address": request.POST.get("client_address"), + "phone_number": request.POST.get("client_phone"), + "company": request.POST.get("company_name"), + "is_representative": True + if request.POST.get("is_representative") == "on" + else False, + } - if not client_name: - messages.error(request, "Please provide at least a client name") - return redirect("clients create") + error = validate_client_create(client_details) - if len(client_name) < 3: - messages.error(request, "Client name must be at least 3 characters") + if error: + messages.error(request, error) return redirect("clients create") client = Client.objects.create( user=request.user, - name=client_name, - email=client_email, - address=client_address, - phone_number=client_phone, ) + + for model_field, new_value in client_details.items(): + setattr(client, model_field, new_value) + + client.save() + if client: messages.success(request, f"Client created successfully (#{client.id})") else: messages.error(request, "Failed to create client - an unknown error occurred") return redirect("clients dashboard") + + +def validate_client_create(client_details): + if not client_details.get("name"): + return "Please provide at least a client name" + + if len(client_details.get("name")) < 3: + return "Client name must be at least 3 characters" + + if client_details.get("is_representiative") and not client_details.get("company"): + return "Please provide a company name if you are creating a representative" + + return None diff --git a/backend/views/core/invoices/create.py b/backend/views/core/invoices/create.py index f701dc6e4..0a4e64d10 100644 --- a/backend/views/core/invoices/create.py +++ b/backend/views/core/invoices/create.py @@ -1,3 +1,4 @@ +from django.contrib import messages from django.http import HttpRequest from django.shortcuts import render, redirect from django.urls import reverse_lazy @@ -7,8 +8,9 @@ def invoice_page_get(request: HttpRequest): - context = {} - + context = { + "clients": Client.objects.filter(user=request.user), + } return render(request, "pages/invoices/create/create.html", context) @@ -23,36 +25,57 @@ def invoice_page_post(request: HttpRequest): request.POST.getlist("hours[]"), request.POST.getlist("price_per_hour[]"), ) - ] # TODO: add products to this for logic (so that they can be loaded without manually making each time) + ] invoice = Invoice.objects.create( user=request.user, date_due=datetime.strptime(request.POST.get("date_due"), "%Y-%m-%d").date(), date_issued=request.POST.get("date_issued"), - client_name=request.POST.get("to_name"), - client_company=request.POST.get("to_company"), - client_address=request.POST.get("to_address"), - client_city=request.POST.get("to_city"), - client_county=request.POST.get("to_county"), - client_country=request.POST.get("to_country"), - self_name=request.POST.get("from_name"), - self_company=request.POST.get("from_company"), - self_address=request.POST.get("from_address"), - self_city=request.POST.get("from_city"), - self_county=request.POST.get("from_county"), - self_country=request.POST.get("from_country"), - notes=request.POST.get("notes"), - invoice_number=request.POST.get("invoice_number"), - vat_number=request.POST.get("vat_number"), - # logo = request.POST.get("logo"), - reference=request.POST.get("reference"), - sort_code=request.POST.get("sort_code"), - account_number=request.POST.get("account_number"), - account_holder_name=request.POST.get("account_holder_name"), ) + is_existing_client = True if request.POST.get("selected_client") else False + + if is_existing_client: + try: + client = Client.objects.get( + user=request.user, id=request.POST.get("selected_client") + ) + except: + messages.error(request, "Client not found") + invoice.delete() + return render(request, "pages/invoices/create/create.html") + invoice.client_to = client + + else: + invoice.client_name = request.POST.get("to_name") + invoice.client_company = request.POST.get("to_company") + invoice.client_address = request.POST.get("to_address") + invoice.client_city = request.POST.get("to_city") + invoice.client_county = request.POST.get("to_county") + invoice.client_country = request.POST.get("to_country") + if request.POST.get("is_representative") == "on": + invoice.client_is_representative = True + else: + invoice.client_is_representative = False + + invoice.self_name = request.POST.get("from_name") + invoice.self_company = request.POST.get("from_company") + invoice.self_address = request.POST.get("from_address") + invoice.self_city = request.POST.get("from_city") + invoice.self_county = request.POST.get("from_county") + invoice.self_country = request.POST.get("from_country") + + invoice.notes = request.POST.get("notes") + invoice.invoice_number = request.POST.get("invoice_number") + invoice.vat_number = request.POST.get("vat_number") + invoice.logo = request.FILES.get("logo") + invoice.reference = request.POST.get("reference") + invoice.sort_code = request.POST.get("sort_code") + invoice.account_number = request.POST.get("account_number") + invoice.account_holder_name = request.POST.get("account_holder_name") + invoice.payment_status = invoice.dynamic_payment_status - print(invoice.date_due) + invoice.save() invoice.items.set(invoice_items) diff --git a/backend/views/core/invoices/edit.py b/backend/views/core/invoices/edit.py index 4ea19f9b6..38424b93c 100644 --- a/backend/views/core/invoices/edit.py +++ b/backend/views/core/invoices/edit.py @@ -3,7 +3,7 @@ from django.shortcuts import render from django.views.decorators.http import require_http_methods -from backend.models import Invoice +from backend.models import Invoice, Client from datetime import datetime # RELATED PATH FILES : \frontend\templates\pages\invoices\dashboard\_fetch_body.html, \backend\urls.py @@ -12,21 +12,39 @@ # Function that takes an invoice object and makes a dict of its attributes def invoice_get_existing_data(invoice_obj): stored_data = { - "og_name": invoice_obj.self_name, - "og_company": invoice_obj.self_company, - "og_address": invoice_obj.self_address, - "og_city": invoice_obj.self_city, - "og_county": invoice_obj.self_county, - "og_country": invoice_obj.self_country, - "og_cilent_name": invoice_obj.client_name, - "og_cilent_company": invoice_obj.client_company, - "og_cilent_address": invoice_obj.client_address, - "og_cilent_city": invoice_obj.client_city, - "og_cilent_county": invoice_obj.client_county, - "og_cilent_country": invoice_obj.client_country, - "og_date_issued": invoice_obj.date_issued, - "og_date_due": invoice_obj.date_due, + "from_name": invoice_obj.self_name, + "from_company": invoice_obj.self_company, + "from_address": invoice_obj.self_address, + "from_city": invoice_obj.self_city, + "from_county": invoice_obj.self_county, + "from_country": invoice_obj.self_country, + "from_date_issued": invoice_obj.date_issued, + "from_date_due": invoice_obj.date_due, + "invoice_object": invoice_obj, + "og_issue_date": invoice_obj.date_issued, + "og_due_date": invoice_obj.date_due, + "invoice_object": invoice_obj, } + if invoice_obj.client_to: + stored_data["to_name"] = invoice_obj.client_to.name + stored_data["to_company"] = invoice_obj.client_to.company + stored_data["is_representative"] = invoice_obj.client_to.is_representative + # stored_data["to_address"] = invoice_obj.client_to.address + # stored_data["to_city"] = invoice_obj.client_to.city + # stored_data["to_county"] = invoice_obj.client_to.county + # stored_data["to_country"] = invoice_obj.client_to.country + else: + stored_data["to_name"] = invoice_obj.client_name + stored_data["to_company"] = invoice_obj.client_company + stored_data["to_address"] = invoice_obj.client_address + stored_data["to_city"] = invoice_obj.client_city + stored_data["to_county"] = invoice_obj.client_county + stored_data["to_country"] = invoice_obj.client_country + stored_data["is_representative"] = invoice_obj.client_is_representative + + if invoice_obj.client_to: + stored_data["existing_client"] = invoice_obj.client_to + return stored_data @@ -53,12 +71,6 @@ def edit_invoice(request: HttpRequest, invoice_id): attributes_to_updates = { "date_due": datetime.strptime(request.POST.get("date_due"), "%Y-%m-%d").date(), "date_issued": request.POST.get("date_issued"), - "client_name": request.POST.get("to_name"), - "client_company": request.POST.get("to_company"), - "client_address": request.POST.get("to_address"), - "client_city": request.POST.get("to_city"), - "client_county": request.POST.get("to_county"), - "client_country": request.POST.get("to_country"), "self_name": request.POST.get("from_name"), "self_company": request.POST.get("from_company"), "self_address": request.POST.get("from_address"), @@ -74,6 +86,26 @@ def edit_invoice(request: HttpRequest, invoice_id): "account_holder_name": request.POST.get("account_holder_name"), } + client_to_id = request.POST.get("selected_client") + try: + client_to_obj = Client.objects.get(id=client_to_id, user=request.user) + except: + client_to_obj = None + + if client_to_obj: + invoice.client_to = client_to_obj + else: + attributes_to_updates.update( + { + "client_name": request.POST.get("to_name"), + "client_company": request.POST.get("to_company"), + "client_address": request.POST.get("to_address"), + "client_city": request.POST.get("to_city"), + "client_county": request.POST.get("to_county"), + "client_country": request.POST.get("to_country"), + } + ) + for column_name, new_value in attributes_to_updates.items(): setattr(invoice, column_name, new_value) @@ -83,8 +115,7 @@ def edit_invoice(request: HttpRequest, invoice_id): messages.success(request, "Invoice edited") return render(request, "partials/base/toasts.html") - return render(request, "pages/invoices/dashboard/dashboard.html") - # return JsonResponse({"message": "Invoice successfully edited"}, status=200) + return invoice_edit_page_get(request, invoice_id) # decorator & view function for rendering page and updating invoice items in the backend diff --git a/frontend/static/src/input.css b/frontend/static/src/input.css index 79cf21213..079829f31 100644 --- a/frontend/static/src/input.css +++ b/frontend/static/src/input.css @@ -2,6 +2,17 @@ @tailwind components; @tailwind utilities; +.circle-tick { + @apply inline-flex; + @apply p-1; + @apply mr-3; + @apply rounded-full; + @apply w-6; + @apply h-6; + @apply items-center; + @apply justify-center; +} + .modal-responsive { @apply modal-bottom; @apply sm:modal-middle; diff --git a/frontend/static/src/output.css b/frontend/static/src/output.css index 48ab39999..657c90ba3 100644 --- a/frontend/static/src/output.css +++ b/frontend/static/src/output.css @@ -2003,6 +2003,30 @@ html { background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity))); } +.toggle { + flex-shrink: 0; + --tglbg: var(--fallback-b1,oklch(var(--b1)/1)); + --handleoffset: 1.5rem; + --handleoffsetcalculator: calc(var(--handleoffset) * -1); + --togglehandleborder: 0 0; + height: 1.5rem; + width: 3rem; + cursor: pointer; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + border-radius: var(--rounded-badge, 1.9rem); + border-width: 1px; + border-color: currentColor; + background-color: currentColor; + color: var(--fallback-bc,oklch(var(--bc)/0.5)); + transition: background, + box-shadow var(--animation-input, 0.2s) ease-out; + box-shadow: var(--handleoffsetcalculator) 0 0 2px var(--tglbg) inset, + 0 0 0 2px var(--tglbg) inset, + var(--togglehandleborder); +} + .alert-info { border-color: var(--fallback-in,oklch(var(--in)/0.2)); --tw-text-opacity: 1; @@ -2769,6 +2793,10 @@ html { margin-inline-start: -1px; } +.join-item:focus { + isolation: isolate; +} + .link-primary { --tw-text-opacity: 1; color: var(--fallback-p,oklch(var(--p)/var(--tw-text-opacity))); @@ -3119,6 +3147,17 @@ html { outline-color: var(--fallback-bc,oklch(var(--bc)/0.2)); } +.select-primary { + --tw-border-opacity: 1; + border-color: var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity))); +} + +.select-primary:focus { + --tw-border-opacity: 1; + border-color: var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity))); + outline-color: var(--fallback-p,oklch(var(--p)/1)); +} + .select-disabled, .select:disabled, .select[disabled] { @@ -3321,6 +3360,73 @@ html { } } +[dir="rtl"] .toggle { + --handleoffsetcalculator: calc(var(--handleoffset) * 1); +} + +.toggle:focus-visible { + outline-style: solid; + outline-width: 2px; + outline-offset: 2px; + outline-color: var(--fallback-bc,oklch(var(--bc)/0.2)); +} + +.toggle:hover { + background-color: currentColor; +} + +.toggle:checked, + .toggle[checked="true"], + .toggle[aria-checked="true"] { + background-image: none; + --handleoffsetcalculator: var(--handleoffset); + --tw-text-opacity: 1; + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); +} + +[dir="rtl"] .toggle:checked, [dir="rtl"] .toggle[checked="true"], [dir="rtl"] .toggle[aria-checked="true"] { + --handleoffsetcalculator: calc(var(--handleoffset) * -1); +} + +.toggle:indeterminate { + --tw-text-opacity: 1; + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); + box-shadow: calc(var(--handleoffset) / 2) 0 0 2px var(--tglbg) inset, + calc(var(--handleoffset) / -2) 0 0 2px var(--tglbg) inset, + 0 0 0 2px var(--tglbg) inset; +} + +[dir="rtl"] .toggle:indeterminate { + box-shadow: calc(var(--handleoffset) / 2) 0 0 2px var(--tglbg) inset, + calc(var(--handleoffset) / -2) 0 0 2px var(--tglbg) inset, + 0 0 0 2px var(--tglbg) inset; +} + +.toggle-primary:focus-visible { + outline-color: var(--fallback-p,oklch(var(--p)/1)); +} + +.toggle-primary:checked, + .toggle-primary[checked="true"], + .toggle-primary[aria-checked="true"] { + border-color: var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity))); + --tw-border-opacity: 0.1; + --tw-bg-opacity: 1; + background-color: var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity))); + --tw-text-opacity: 1; + color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity))); +} + +.toggle:disabled { + cursor: not-allowed; + --tw-border-opacity: 1; + border-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity))); + background-color: transparent; + opacity: 0.3; + --togglehandleborder: 0 0 0 3px var(--fallback-bc,oklch(var(--bc)/1)) inset, + var(--handleoffsetcalculator) 0 0 3px var(--fallback-bc,oklch(var(--bc)/1)) inset; +} + .glass, .glass.btn-active { border: none; @@ -4697,6 +4803,11 @@ html { background-color: rgb(0 0 0 / var(--tw-bg-opacity)); } +.bg-error { + --tw-bg-opacity: 1; + background-color: var(--fallback-er,oklch(var(--er)/var(--tw-bg-opacity))); +} + .bg-gray-200 { --tw-bg-opacity: 1; background-color: rgb(229 231 235 / var(--tw-bg-opacity)); @@ -4707,6 +4818,11 @@ html { background-color: rgb(249 250 251 / var(--tw-bg-opacity)); } +.bg-gray-500 { + --tw-bg-opacity: 1; + background-color: rgb(107 114 128 / var(--tw-bg-opacity)); +} + .bg-purple-600 { --tw-bg-opacity: 1; background-color: rgb(147 51 234 / var(--tw-bg-opacity)); @@ -5029,6 +5145,11 @@ html { color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); } +.text-black { + --tw-text-opacity: 1; + color: rgb(0 0 0 / var(--tw-text-opacity)); +} + .text-error { --tw-text-opacity: 1; color: var(--fallback-er,oklch(var(--er)/var(--tw-text-opacity))); @@ -5255,6 +5376,17 @@ html { transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); } +.circle-tick { + display: inline-flex; + padding: 0.25rem; + margin-right: 0.75rem; + border-radius: 9999px; + width: 1.5rem; + height: 1.5rem; + align-items: center; + justify-content: center; +} + .modal-responsive { place-items: end; } @@ -6419,6 +6551,10 @@ button.btn.loading-htmx.htmx-request { width: 50%; } + .md\:w-1\/3 { + width: 33.333333%; + } + .md\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } diff --git a/frontend/templates/modals/invoices_to_destination.html b/frontend/templates/modals/invoices_to_destination.html index 81c3f3b8d..d76454773 100644 --- a/frontend/templates/modals/invoices_to_destination.html +++ b/frontend/templates/modals/invoices_to_destination.html @@ -1,53 +1,90 @@ {% component_block "modal" id="modal_invoices_to_destination" start_open="true" title="Update TO info" %} {% fill "content" %}
+ {% endfill %} {% endcomponent_block %} \ No newline at end of file diff --git a/frontend/templates/pages/clients/create/create.html b/frontend/templates/pages/clients/create/create.html index 7f2b5fa8b..13cb74013 100644 --- a/frontend/templates/pages/clients/create/create.html +++ b/frontend/templates/pages/clients/create/create.html @@ -6,6 +6,12 @@