add loading to payment checkout

This commit is contained in:
ismail 2025-05-20 13:13:40 +03:00
parent de3f6cf558
commit 7d8ea79cc9
10 changed files with 103 additions and 31 deletions

View File

@ -186,6 +186,17 @@ class CustomerForm(forms.ModelForm):
"address",
'image',
]
widgets = {
'title': forms.Select(attrs={'class': 'form-control form-control-sm'}),
'first_name': forms.TextInput(attrs={'class': 'form-control form-control-sm'}),
'last_name': forms.TextInput(attrs={'class': 'form-control form-control-sm'}),
'email': forms.EmailInput(attrs={'class': 'form-control form-control-sm'}),
'phone_number': forms.TextInput(attrs={'class': 'form-control form-control-sm'}),
'national_id': forms.TextInput(attrs={'class': 'form-control form-control-sm'}),
'dob': DateInput(attrs={'class': 'form-control form-control-sm', 'type': 'date'}),
'address': forms.Textarea(attrs={'class': 'form-control form-control-sm'}),
'image': forms.FileInput(attrs={'class': 'form-control form-control-sm'}),
}
# class CustomerForm(forms.Form):
# """
# Represents a form for collecting customer information.
@ -1025,6 +1036,7 @@ class LeadForm(forms.ModelForm):
"hx-swap": "outerHTML",
"hx-on::before-request": "document.querySelector('#id_id_car_model').setAttribute('disabled', true)",
"hx-on::after-request": "document.querySelector('#id_id_car_model').removeAttribute('disabled')",
"hx-indicator": "#spinner",
}
),
required=True,

View File

@ -2091,7 +2091,7 @@ class CustomerUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView
@login_required
def delete_customer(request, pk):
def delete_customer(request, slug):
"""
Deletes a customer from the system and deactivates the corresponding user account.
@ -2107,7 +2107,7 @@ def delete_customer(request, pk):
:return: A redirect response to the customer list page.
:rtype: HttpResponseRedirect
"""
customer = get_object_or_404(models.Customer, pk=pk)
customer = get_object_or_404(models.Customer, slug=slug)
customer.deactivate_account()
messages.success(request, _("Customer deactivated successfully"))
return redirect("customer_list")
@ -3760,7 +3760,7 @@ def create_sale_order(request, pk):
models.Car.objects.get(vin=item.item_model.name).mark_as_sold(request)
messages.success(request, "Sale Order created successfully")
return redirect("estimate_detail", pk=pk)
return redirect("estimate_detail", pk=estimate.pk)
form = forms.SaleOrderForm()
form.fields["estimate"].queryset = EstimateModel.objects.filter(pk=pk)
@ -4246,7 +4246,6 @@ def invoice_create(request, pk):
form = forms.InvoiceModelCreateForm(
entity_slug=entity.slug, user_model=entity.admin
)
form.initial.update(
{
"customer": estimate.customer,
@ -4255,6 +4254,7 @@ def invoice_create(request, pk):
"unearned_account": dealer.settings.invoice_unearned_account,
}
)
print(dir(form.fields["customer"]))
context = {
"form": form,
@ -4675,11 +4675,15 @@ def lead_create(request):
form = forms.LeadForm()
form.filter_qs(dealer=dealer)
make = request.GET.get("id_car_make", None)
if make:
if make := request.GET.get("id_car_make", None):
form.fields["id_car_model"].queryset = models.CarModel.objects.filter(
id_car_make=int(make)
)
else:
if first_make := form.fields["id_car_make"].queryset.first():
form.fields["id_car_model"].queryset = models.CarModel.objects.filter(
id_car_make=first_make.id_car_make
)
return render(request, "crm/leads/lead_form.html", {"form": form})
def lead_tracking(request):
@ -4769,7 +4773,7 @@ class LeadUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
@login_required
@permission_required("inventory.delete_lead", raise_exception=True)
def LeadDeleteView(request, pk):
def LeadDeleteView(request, slug):
"""
Handles the deletion of a Lead along with its associated customer and potentially
a related user account in the system. Ensures proper permissions and login
@ -4780,7 +4784,7 @@ def LeadDeleteView(request, pk):
:param pk: The primary key identifier of the Lead to be deleted.
:return: An HTTP redirect response to the lead list page.
"""
lead = get_object_or_404(models.Lead, pk=pk)
lead = get_object_or_404(models.Lead, slug=slug)
try:
User.objects.get(email=lead.customer.email).delete()
lead.customer.delete()
@ -5193,11 +5197,11 @@ class OpportunityCreateView(CreateView,SuccessMessageMixin, LoginRequiredMixin):
dealer = get_user_type(self.request)
return context
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
dealer = get_user_type(self.request)
kwargs["car"].queryset = models.Car.objects.filter(dealer=dealer,)
return kwargs
# def get_form_kwargs(self):
# kwargs = super().get_form_kwargs()
# dealer = get_user_type(self.request)
# kwargs["car"].queryset = models.Car.objects.filter(dealer=dealer,)
# return kwargs
# def get_initial(self):
# initial = super().get_initial()

View File

@ -1,11 +1,26 @@
{% extends 'base.html' %}
{% load i18n static crispy_forms_filters %}
{% block customcss %}
<style>
.htmx-indicator{
opacity:0;
transition: opacity 500ms ease-in;
}
.htmx-request .htmx-indicator{
opacity:1;
}
.htmx-request.htmx-indicator{
opacity:1;
}
</style>
{% endblock customcss %}
{% block content %}
<div class="container-fluid">
<h1>{% if object %}{{ _("Update") }}{% else %}{{ _("Create") }}{% endif %}</h1>
<div class="row mb-3">
<div class="col-sm-6 col-md-8">
<div class="col-sm-6 col-md-8">
<form class="form" method="post">
{% csrf_token %}
{{ form|crispy }}
@ -21,4 +36,23 @@
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// First, create the spinner div (or use the existing one)
const spinner = document.createElement('div');
spinner.id = 'spinner';
spinner.className = 'htmx-indicator spinner-border inline-spinner';
spinner.setAttribute('role', 'status');
spinner.innerHTML = '<span class="visually-hidden">Loading...</span>';
// Find the form field you want to place it next to
// Replace 'id_your_field_name' with the actual ID of your form field
const targetField = document.getElementById('div_id_id_car_model');
if (targetField) {
// Insert the spinner right after the target field
targetField.parentNode.insertBefore(spinner, targetField.nextSibling);
}
});
</script>
{% endblock %}

View File

@ -132,9 +132,7 @@
{{ _("Action") }}
</th>
<th class="text-end white-space-nowrap align-middle" scope="col"></th>
</tr>
</thead>
<tbody class="list" id="lead-tables-body">
</tr>image
{% for lead in leads %}
<!-- Delete Modal -->
<div class="modal fade" id="deleteModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="deleteModalLabel" aria-hidden="true">
@ -159,7 +157,7 @@
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
<td class="name align-middle white-space-nowrap ps-0">
<div class="d-flex align-items-center">
<div><a class="fs-8 fw-bold" href="{% url 'lead_detail' lead.slug %}">{{lead.full_name}}</a>
<div><a class="fs-8 fw-bold" href="{% url 'lead_detail' lead.slug %}">{{lead.full_name|capfirst}}</a>
<div class="d-flex align-items-center">
<p class="mb-0 text-body-highlight fw-semibold fs-9 me-2"></p>
{% if lead.status == "new" %}

View File

@ -41,7 +41,11 @@
<div class="card-body d-flex flex-column justify-content-between pb-3">
<div class="row align-items-center g-5 mb-3 text-center text-sm-start">
<div class="col-12 col-sm-auto mb-sm-2">
<div class="avatar avatar-5xl"><img class="rounded-circle" src="{{ customer.image.url }}" alt="" /></div>
<div class="avatar avatar-5xl">
{% if customer.image %}
<img class="rounded-circle" src="{{ customer.image.url }}" alt="" />
{% endif %}
</div>
</div>
<div class="col-12 col-sm-auto flex-1">
<h3>{{ customer.full_name }}</h3>

View File

@ -20,7 +20,7 @@
{% if not car.ready %}
<div class="alert alert-outline-warning d-flex align-items-center" role="alert">
<i class="fa-solid fa-circle-info fs-6"></i>
<p class="mb-0 flex-1">{{ _("This car information is not complete , please add colors and finances before making it ready for sale .") }}</p>
<p class="mb-0 flex-1">{{ _("This car information is not complete , please add colors and finances before making it ready for sale .") }}<a class="ms-3 text-body-primary fs-9" href="{% url 'add_color' car.slug %}"> {{ _("Add Color") }} </a></p>
<button class="btn-close" type="button" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endif %}

View File

@ -10,7 +10,7 @@
<h1 class="mb-2 mb-lg-0">{% trans "Payment Failed" %}</h1>
<nav class="breadcrumbs">
<ol>
<li><a href="">{% trans "Home"%}</a></li>
<li><a href="{% url 'home' %}">{% trans "Home"%}</a></li>
<li class="current">{% trans "Failed"%}</li>
</ol>
</nav>
@ -22,13 +22,13 @@
<div class="container text-center" data-aos="fade-up">
<div class="py-5">
<i class="bi bi-x-circle-fill text-danger" style="font-size: 5rem;"></i>
<h2 class="mt-4">{% trans "Payment Failed"%}</h2>
<h2 class="mt-4">{% trans "Payment Failed"%}</h2>
{% if message %}
<p class="lead">{{message}}.</p>
{% else %}
<p class="lead">{% trans "We couldn't process your payment. Please try again"%}.</p>
{% endif %}
<a href="" class="btn btn-primary mt-3">
<a href="{% url 'home' %}" class="btn btn-primary mt-3">
<i class="bi bi-house-door"></i> {% trans "Back to Home"%}
</a>
</div>

View File

@ -29,7 +29,7 @@
<i class="bi bi-house-door"></i> View Invoice
</a>
{% endif %}
<a href="" class="btn btn-primary mt-3">
<a href="{% url 'home' %}" class="btn btn-primary mt-3">
<i class="bi bi-house-door"></i> Back to Home
</a>
</div>

View File

@ -52,7 +52,6 @@
<h1 class="text-center mb-5">{{ _("Choose Your Plan")}}</h1>
<form method="POST" action="{% url 'submit_plan' %}" id="wizardForm">
{% csrf_token %}
<!-- Step 1: Plan Selection -->
<div class="step" id="step1">
<h4 class="mb-4">1. {{ _("Select a Plan")}}</h4>
@ -208,6 +207,27 @@
{% block customJS %}
<script>
document.addEventListener("DOMContentLoaded", function () {
const form = document.getElementById('wizardForm');
form.addEventListener('submit', function(e) {
e.preventDefault(); // Prevent default form submission
// Show loading alert
Swal.fire({
title: 'Processing...',
html: 'Please wait while we submit your form',
allowOutsideClick: false,
didOpen: () => {
Swal.showLoading();
}
});
// Submit the form after a slight delay to ensure Swal is shown
setTimeout(() => {
form.submit();
}, 100);
});
const radios = document.querySelectorAll('.btn-check');
radios.forEach(radio => {
radio.addEventListener('change', function () {

View File

@ -17,14 +17,14 @@
{% if not items %}
<div class="alert alert-outline-warning d-flex align-items-center" role="alert">
<i class="fa-solid fa-circle-info fs-6"></i>
<p class="mb-0 flex-1">{{ _("Please add at least one car before creating a quotation.") }}</p>
<p class="mb-0 flex-1">{{ _("Please add at least one car before creating a quotation.") }}<a class="ms-3 text-body-primary fs-9" href="{% url 'car_add' %}"> {{ _("Add Car") }} </a></p>
<button class="btn-close" type="button" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endif %}
{% if not customer_count %}
<div class="alert alert-outline-warning d-flex align-items-center" role="alert">
<i class="fa-solid fa-circle-info fs-6"></i>&nbsp;&nbsp;
<p class="mb-0 flex-1"> {{ _("Please add at least one customer before creating a quotation.") }}</p>
<p class="mb-0 flex-1"> {{ _("Please add at least one customer before creating a quotation.") }}<a class="ms-3 text-body-primary fs-9" href="{% url 'customer_create' %}"> {{ _("Add Customer") }} </a></p>
<button class="btn-close" type="button" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endif %}
@ -57,7 +57,7 @@
<div class="col-12">
<button id="addMoreBtn" class="btn btn-sm btn-primary"><i class="fa-solid fa-plus me-2"></i> {{ _("Add More")}}</button>
</div>
</div>
</div>
@ -67,7 +67,7 @@
<a href="{% url 'estimate_list' %}" class="btn btn-danger"><i class="fa-solid fa-ban me-1"></i> {% trans "Cancel" %}</a>
</div> {% endcomment %}
<div class="d-flex justify-content-center">
<button class="btn btn-sm btn-success me-2" type="submit"><i class="fa-solid fa-floppy-disk me-1"></i>
<!--<i class="bi bi-save"></i> -->
{{ _("Save") }}
@ -75,9 +75,9 @@
<a href="{{request.META.HTTP_REFERER}}" class="btn btn-sm btn-danger"><i class="fa-solid fa-ban me-1"></i>{% trans "Cancel" %}</a>
</div>
</form>
</div>
{% endblock content %}