add UserRegistration
This commit is contained in:
parent
21c7fedbba
commit
50c1d82e4c
@ -72,6 +72,7 @@ admin.site.register(models.UserActivityLog)
|
||||
admin.site.register(models.DealersMake)
|
||||
admin.site.register(models.ExtraInfo)
|
||||
admin.site.register(models.Ticket)
|
||||
admin.site.register(models.UserRegistration)
|
||||
|
||||
|
||||
@admin.register(models.Car)
|
||||
|
||||
@ -9,7 +9,7 @@ from plans.models import PlanPricing
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
from inventory.validators import SaudiPhoneNumberValidator
|
||||
from .models import CustomGroup, Status, Stage
|
||||
from .models import CustomGroup, Status, Stage, UserRegistration
|
||||
from .mixins import AddClassMixin
|
||||
from django_ledger.forms.invoice import (
|
||||
InvoiceModelCreateForm as InvoiceModelCreateFormBase,
|
||||
@ -2255,3 +2255,10 @@ class TicketResolutionForm(forms.ModelForm):
|
||||
super().__init__(*args, **kwargs)
|
||||
# Limit status choices to resolution options
|
||||
self.fields["status"].choices = [("resolved", "Resolved"), ("closed", "Closed")]
|
||||
|
||||
|
||||
|
||||
class CarDealershipRegistrationForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = UserRegistration
|
||||
fields = "__all__"
|
||||
@ -3858,3 +3858,42 @@ class CarImage(models.Model):
|
||||
self.id,
|
||||
task_name=f"generate_car_image_{self.car.vin}",
|
||||
)
|
||||
|
||||
|
||||
|
||||
class UserRegistration(models.Model):
|
||||
name = models.CharField(_("Name"), max_length=255)
|
||||
arabic_name = models.CharField(_("Arabic Name"), max_length=255)
|
||||
email = models.EmailField(_("email address"), unique=True)
|
||||
phone_number = models.CharField(
|
||||
max_length=255,
|
||||
verbose_name=_("Phone Number"),
|
||||
validators=[SaudiPhoneNumberValidator()],
|
||||
)
|
||||
crn = models.CharField(_("Commercial Registration Number"), max_length=10, unique=True)
|
||||
vrn = models.CharField(_("Vehicle Registration Number"), max_length=15, unique=True)
|
||||
address = models.TextField(_("Address"))
|
||||
password = models.CharField(_("Password"), max_length=255)
|
||||
is_created = models.BooleanField(default=False)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
REQUIRED_FIELDS = ["username", "arabic_name", "crn", "vrn", "address", "phone_number"]
|
||||
|
||||
def __str__(self):
|
||||
return self.email
|
||||
|
||||
def create_account(self):
|
||||
from django_q.tasks import async_task
|
||||
from .tasks import create_user_dealer
|
||||
|
||||
password = User.objects.make_random_password()
|
||||
|
||||
if self.is_created:
|
||||
return
|
||||
dealer = create_user_dealer(self.email,password,self.name,self.arabic_name,self.phone,self.crn,self.vrn,self.address)
|
||||
|
||||
if dealer:
|
||||
self.is_created = True
|
||||
self.password = password
|
||||
self.save()
|
||||
@ -72,12 +72,12 @@ User = get_user_model()
|
||||
|
||||
|
||||
# check with marwan
|
||||
@receiver(post_save, sender=models.Car)
|
||||
def create_dealers_make(sender, instance, created, **kwargs):
|
||||
if created:
|
||||
models.DealersMake.objects.get_or_create(
|
||||
dealer=instance.dealer, car_make=instance.id_car_make
|
||||
)
|
||||
# @receiver(post_save, sender=models.Car)
|
||||
# def create_dealers_make(sender, instance, created, **kwargs):
|
||||
# if created:
|
||||
# models.DealersMake.objects.get_or_create(
|
||||
# dealer=instance.dealer, car_make=instance.id_car_make
|
||||
# )
|
||||
|
||||
|
||||
@receiver(post_save, sender=models.Car)
|
||||
@ -325,6 +325,7 @@ def create_item_model(sender, instance, created, **kwargs):
|
||||
inventory.save()
|
||||
else:
|
||||
instance.item_model.default_amount = instance.marked_price
|
||||
instance.item_model.save()
|
||||
|
||||
# inventory = entity.create_item_inventory(
|
||||
# name=instance.vin,
|
||||
@ -1386,3 +1387,15 @@ def handle_car_image(sender, instance, created, **kwargs):
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error handling car image for {car.vin}: {e}")
|
||||
|
||||
|
||||
@receiver(post_save, sender=models.UserRegistration)
|
||||
def handle_user_registration(sender, instance, created, **kwargs):
|
||||
if instance.is_created:
|
||||
send_email(
|
||||
"Account Created",
|
||||
f"Your account has been created and you can login with your email and password: {instance.password}",
|
||||
settings.DEFAULT_FROM_EMAIL,
|
||||
[instance.email],
|
||||
fail_silently=False,
|
||||
)
|
||||
@ -1,3 +1,4 @@
|
||||
import time
|
||||
import base64
|
||||
import logging
|
||||
import requests
|
||||
@ -123,7 +124,7 @@ def retry_entity_creation(dealer_id, retry_count=0):
|
||||
Retry entity creation if initial attempt failed
|
||||
"""
|
||||
from .models import Dealer
|
||||
from yourapp.models import EntityModel
|
||||
from django_ledger.models import EntityModel
|
||||
|
||||
max_retries = 3
|
||||
|
||||
|
||||
@ -2,14 +2,16 @@ from inventory.utils import get_user_type
|
||||
from . import views
|
||||
from django.urls import path
|
||||
from django.urls import reverse_lazy
|
||||
from django.views.generic import RedirectView
|
||||
from django.views.generic import RedirectView,TemplateView
|
||||
from django_tables2.export.export import TableExport
|
||||
from django.conf.urls import handler403, handler400, handler404, handler500
|
||||
|
||||
urlpatterns = [
|
||||
# main URLs
|
||||
path("", views.WelcomeView, name="welcome"),
|
||||
path("signup/", views.dealer_signup, name="account_signup"),
|
||||
# path("signup/", views.dealer_signup, name="account_signup"),
|
||||
path('signup/', views.CarDealershipSignUpView.as_view(), name='account_signup'),
|
||||
path('success/', TemplateView.as_view(template_name='account/success.html'), name='registration_success'),
|
||||
path("", views.HomeView, name="home"),
|
||||
path('refund-policy/',views.refund_policy,name='refund_policy'),
|
||||
path("<slug:dealer_slug>/", views.HomeView, name="home"),
|
||||
|
||||
@ -6,7 +6,7 @@ import re
|
||||
class SaudiPhoneNumberValidator(RegexValidator):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(
|
||||
regex=r"^(\+9665|05)[0-9]{8}$",
|
||||
regex=r"^(\+9665|05|9665)[0-9]{8}$",
|
||||
message=_("Enter a valid Saudi phone number (05XXXXXXXX or +9665XXXXXXXX)"),
|
||||
)
|
||||
|
||||
|
||||
@ -5125,7 +5125,8 @@ def create_sale_order(request, dealer_slug, pk):
|
||||
f"KeyError: 'car_info' or 'status' key missing when attempting to update status to 'sold' for item.item_model PK: {getattr(item.item_model, 'pk', 'N/A')}."
|
||||
)
|
||||
pass
|
||||
|
||||
item.item_model.car.sold_date=timezone.now() # to be checked added by faheed
|
||||
item.item_model.car.save()# to be checked added byfaheed
|
||||
item.item_model.car.mark_as_sold()
|
||||
messages.success(request, "Sale Order created successfully")
|
||||
|
||||
@ -7539,17 +7540,17 @@ class NotificationListView(LoginRequiredMixin, ListView):
|
||||
|
||||
def get_queryset(self):
|
||||
return models.Notification.objects.filter(user=self.request.user)
|
||||
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
|
||||
|
||||
context = super().get_context_data(**kwargs)
|
||||
user_notifications = self.get_queryset()
|
||||
|
||||
|
||||
# Calculate the number of total, read and unread notifications
|
||||
context['total_count'] = user_notifications.count()
|
||||
context['read_count'] = user_notifications.filter(is_read=True).count()
|
||||
context['unread_count'] = user_notifications.filter(is_read=False).count()
|
||||
|
||||
|
||||
return context
|
||||
|
||||
|
||||
@ -11078,7 +11079,7 @@ def purchase_report_view(request, dealer_slug):
|
||||
bills = po.get_po_bill_queryset()
|
||||
vendors = set([bill.vendor.vendor_name for bill in bills])
|
||||
vendors_str = ", ".join(sorted(list(vendors))) if vendors else "N/A"
|
||||
|
||||
|
||||
data.append({
|
||||
"po_number": po.po_number,
|
||||
"po_created": po.created,
|
||||
@ -11194,7 +11195,7 @@ def car_sale_report_view(request, dealer_slug):
|
||||
cars_sold = cars_sold.filter(year=selected_year)
|
||||
if selected_stock_type:
|
||||
cars_sold = cars_sold.filter(stock_type=selected_stock_type)
|
||||
|
||||
|
||||
# Corrected: Apply date filters using the 'sold_date' field
|
||||
if start_date_str:
|
||||
try:
|
||||
@ -11202,7 +11203,7 @@ def car_sale_report_view(request, dealer_slug):
|
||||
cars_sold = cars_sold.filter(sold_date__gte=start_date)
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
|
||||
|
||||
if end_date_str:
|
||||
try:
|
||||
end_date = datetime.strptime(end_date_str, '%Y-%m-%d').date()
|
||||
@ -11224,7 +11225,7 @@ def car_sale_report_view(request, dealer_slug):
|
||||
total_vat_from_additonals = sum([car.get_additional_services()['services_vat'] for car in cars_sold])
|
||||
total_vat_collected = total_vat_on_cars + total_vat_from_additonals
|
||||
total_revenue_collected = total_revenue_from_cars + total_revenue_from_additonals
|
||||
|
||||
|
||||
total_discount = cars_sold.aggregate(total=Sum('discount_amount'))['total'] or 0
|
||||
|
||||
current_time = timezone.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
@ -11263,7 +11264,7 @@ def car_sale_report_view(request, dealer_slug):
|
||||
@login_required
|
||||
def get_filtered_choices(request, dealer_slug):
|
||||
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
|
||||
|
||||
|
||||
# Get all filter parameters from the request
|
||||
selected_make = request.GET.get('make')
|
||||
selected_model = request.GET.get('model')
|
||||
@ -11277,10 +11278,10 @@ def get_filtered_choices(request, dealer_slug):
|
||||
# Apply filters based on what is selected
|
||||
if selected_make:
|
||||
queryset = queryset.filter(id_car_make__name=selected_make)
|
||||
|
||||
|
||||
if selected_model:
|
||||
queryset = queryset.filter(id_car_model__name=selected_model)
|
||||
|
||||
|
||||
if selected_serie:
|
||||
queryset = queryset.filter(id_car_serie__name=selected_serie)
|
||||
|
||||
@ -11351,7 +11352,7 @@ def car_sale_report_csv_export(request, dealer_slug):
|
||||
cars_sold = cars_sold.filter(year=selected_year)
|
||||
if selected_stock_type:
|
||||
cars_sold = cars_sold.filter(stock_type=selected_stock_type)
|
||||
|
||||
|
||||
# Corrected: Apply date filters for CSV export
|
||||
if start_date_str:
|
||||
try:
|
||||
@ -11669,6 +11670,18 @@ class CharOfAccountModelActionView(CharOfAccountModelActionViewBase):
|
||||
|
||||
|
||||
|
||||
#for refund policy
|
||||
def refund_policy(request):
|
||||
return render(request,'haikal_policy/refund_policy.html')
|
||||
class CarDealershipSignUpView(CreateView):
|
||||
model = models.UserRegistration
|
||||
form_class = forms.CarDealershipRegistrationForm
|
||||
template_name = 'account/signup-wizard.html'
|
||||
success_url = reverse_lazy('registration_success')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['title'] = _('Car Dealership Registration')
|
||||
return context
|
||||
|
||||
def form_valid(self, form):
|
||||
response = super().form_valid(form)
|
||||
messages.success(self.request, _('Your request has been submitted. We will contact you soon.'))
|
||||
return response
|
||||
@ -58,6 +58,7 @@ django-widget-tweaks==1.5.0
|
||||
djangorestframework==3.16.0
|
||||
djhtml==3.0.8
|
||||
djlint==1.36.4
|
||||
dnspython==2.7.0
|
||||
docopt==0.6.2
|
||||
EditorConfig==0.17.1
|
||||
Faker==37.4.0
|
||||
@ -77,6 +78,8 @@ hyperlink==21.0.0
|
||||
icalendar==6.3.1
|
||||
idna==3.10
|
||||
incremental==24.7.2
|
||||
iron-core==1.2.1
|
||||
iron-mq==0.9
|
||||
jiter==0.10.0
|
||||
jsbeautifier==1.15.4
|
||||
json5==0.12.0
|
||||
@ -114,6 +117,7 @@ pycparser==2.22
|
||||
pydantic==2.11.7
|
||||
pydantic_core==2.33.2
|
||||
Pygments==2.19.2
|
||||
pymongo==4.14.1
|
||||
pyOpenSSL==25.1.0
|
||||
python-dateutil==2.9.0.post0
|
||||
python-dotenv==1.1.1
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
{% extends "welcome_base.html" %}
|
||||
{% load crispy_forms_filters %}
|
||||
{% load i18n static %}
|
||||
|
||||
{% block content %}
|
||||
<section class="main my-2">
|
||||
<div class="container" style="max-width:60rem;">
|
||||
@ -20,272 +21,27 @@
|
||||
</div>
|
||||
</a>
|
||||
<div class="text-center">
|
||||
<h3 class="text-body-highlight">{% trans 'Sign Up' %}</h3>
|
||||
<p class="text-body-tertiary fs-9">{% trans 'Create your account today' %}</p>
|
||||
<h3 class="text-body-highlight">{% trans 'Car Dealership Registration' %}</h3>
|
||||
<p class="text-body-tertiary fs-9">{% trans 'Create your dealership account today' %}</p>
|
||||
</div>
|
||||
<div data-signals="{ form1:{email:'',password:'',confirm_password:''}, form2:{name:'',arabic_name:'',phone_number:''}, form3:{crn:'',vrn:'',address:''}, form1_valid:true, form2_valid:true, form3_valid:true, email_valid:true, password_valid:true, phone_number_valid:true }"
|
||||
class="card theme-wizard"
|
||||
data-theme-wizard="data-theme-wizard">
|
||||
<div class="card-header pt-3 pb-2 ">
|
||||
<ul class="nav justify-content-between nav-wizard nav-wizard-success"
|
||||
role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<a class="nav-link active fw-semibold"
|
||||
href="#bootstrap-wizard-validation-tab1"
|
||||
data-bs-toggle="tab"
|
||||
data-wizard-step="1"
|
||||
aria-selected="true"
|
||||
role="tab">
|
||||
<div class="text-center d-inline-block">
|
||||
<span class="nav-item-circle-parent"><span class="nav-item-circle"><span class="fa fa-lock"></span></span></span><span class="d-none d-md-block mt-1 fs-9">{% trans 'Access' %}</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<a class="nav-link fw-semibold"
|
||||
href="#bootstrap-wizard-validation-tab2"
|
||||
data-bs-toggle="tab"
|
||||
data-wizard-step="2"
|
||||
aria-selected="false"
|
||||
tabindex="-1"
|
||||
role="tab">
|
||||
<div class="text-center d-inline-block">
|
||||
<span class="nav-item-circle-parent"><span class="nav-item-circle"><span class="fa fa-user"></span></span></span><span class="d-none d-md-block mt-1 fs-9">{% trans 'Account' %}</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<a class="nav-link fw-semibold"
|
||||
href="#bootstrap-wizard-validation-tab3"
|
||||
data-bs-toggle="tab"
|
||||
data-wizard-step="3"
|
||||
aria-selected="false"
|
||||
tabindex="-1"
|
||||
role="tab">
|
||||
<div class="text-center d-inline-block">
|
||||
<span class="nav-item-circle-parent"><span class="nav-item-circle">
|
||||
<svg class="fa fa-file-lines">
|
||||
</svg>
|
||||
</span></span><span class="d-none d-md-block mt-1 fs-9">{% trans 'Extra' %}</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<a class="nav-link fw-semibold"
|
||||
href="#bootstrap-wizard-validation-tab4"
|
||||
data-bs-toggle="tab"
|
||||
data-wizard-step="4"
|
||||
aria-selected="false"
|
||||
tabindex="-1"
|
||||
role="tab">
|
||||
<div class="text-center d-inline-block">
|
||||
<span class="nav-item-circle-parent"><span class="nav-item-circle"><span class="fa fa-check"></span></span></span><span class="d-none d-md-block mt-1 fs-9">{% trans 'Done' %}</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card-body pt-4 pb-0">
|
||||
<div class="tab-content" data-signals-current_form="1">
|
||||
<div class="tab-pane active"
|
||||
role="tabpanel"
|
||||
aria-labelledby="bootstrap-wizard-validation-tab1"
|
||||
id="bootstrap-wizard-validation-tab1">
|
||||
<form class="needs-validation"
|
||||
id="wizardValidationForm1"
|
||||
novalidate="novalidate"
|
||||
data-wizard-form="1"
|
||||
data-ref-f1>
|
||||
<div class="mb-3">
|
||||
<label for="email"
|
||||
data-class="{'text-danger':!$email_valid}"
|
||||
class="form-label">
|
||||
{% trans "Email" %}
|
||||
<span data-show="!$email_valid" class="text-danger">*</span>
|
||||
</label>
|
||||
<input data-on-input="$email_valid = validateEmail($form1.email)"
|
||||
data-on-blur="$email_valid = validateEmail($form1.email)"
|
||||
data-bind-form1.email
|
||||
data-class="{'is-invalid': !$email_valid , 'is-valid': ($email_valid && $form1.email)}"
|
||||
type="email"
|
||||
class="form-control"
|
||||
id="email"
|
||||
name="email"
|
||||
required>
|
||||
<div class="invalid-feedback" data-show="!$email_valid">{% trans "Please enter a valid email address" %}</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">{% trans "Password" %}</label>
|
||||
<input data-bind-form1.password
|
||||
type="password"
|
||||
data-on-input="$password_valid = validatePassword($form1.password,$form1.confirm_password)"
|
||||
data-on-blur="$password_valid = validatePassword($form1.password,$form1.confirm_password)"
|
||||
class="form-control"
|
||||
data-class="{'is-invalid':($form1.password.length && $form1.password.length < 8),'is-valid':$form1.password.length > 8 }"
|
||||
id="password"
|
||||
name="password"
|
||||
required>
|
||||
<div class="invalid-feedback" data-show="!$password_valid">
|
||||
{% trans "Password does not match. or length is less than 8 characters." %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="confirm_password" class="form-label">{% trans "Confirm Password" %}</label>
|
||||
<span class="text-danger" data-show="!$password_valid">*</span>
|
||||
<input data-bind-form1.confirm_password
|
||||
data-on-input="$password_valid = validatePassword($form1.password,$form1.confirm_password)"
|
||||
data-on-blur="$password_valid = validatePassword($form1.password,$form1.confirm_password)"
|
||||
type="password"
|
||||
class="form-control"
|
||||
data-class="{'is-invalid':!$password_valid,'is-valid':($password_valid&& $form1.confirm_password)}"
|
||||
id="confirm_password"
|
||||
name="confirm_password"
|
||||
required>
|
||||
<div class="invalid-feedback" data-show="!$password_valid">
|
||||
{% trans "Password does not match. or length is less than 8 characters." %}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="tab-pane"
|
||||
role="tabpanel"
|
||||
aria-labelledby="bootstrap-wizard-validation-tab2"
|
||||
id="bootstrap-wizard-validation-tab2">
|
||||
<form class="needs-validation"
|
||||
id="wizardValidationForm2"
|
||||
novalidate="novalidate"
|
||||
data-wizard-form="2"
|
||||
data-ref-f2>
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">{% trans "Name" %}</label>
|
||||
<input data-bind-form2.name
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="name"
|
||||
name="name"
|
||||
required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="arabic_name" class="form-label">{% trans "Arabic Name" %}</label>
|
||||
<input data-bind-form2.arabic_name
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="arabic_name"
|
||||
name="arabic_name"
|
||||
required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="phone_number" class="form-label">{% trans "Phone Number" %}</label>
|
||||
<span data-show="!$phone_number_valid" class="text-danger">*</span>
|
||||
<input data-bind-form2.phone_number
|
||||
type="tel"
|
||||
data-class="{'is-invalid':!$phone_number_valid}"
|
||||
class="form-control"
|
||||
id="phone_number"
|
||||
name="phone_number"
|
||||
required
|
||||
data-on-input="$phone_number_valid = validate_sa_phone_number($form2.phone_number)">
|
||||
<div class="invalid-feedback" data-show="!$phone_number_valid">{% trans "Please enter a valid phone number" %}</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="tab-pane"
|
||||
role="tabpanel"
|
||||
aria-labelledby="bootstrap-wizard-validation-tab3"
|
||||
id="bootstrap-wizard-validation-tab3">
|
||||
<form class="needs-validation"
|
||||
id="wizardValidationForm3"
|
||||
novalidate="novalidate"
|
||||
data-wizard-form="3"
|
||||
data-ref-f3>
|
||||
<div class="mb-3">
|
||||
<label for="crn" class="form-label">{% trans "CRN" %}</label>
|
||||
<input data-bind-form3.crn
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="crn"
|
||||
name="crn"
|
||||
required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="vrn" class="form-label">{% trans "VRN" %}</label>
|
||||
<input data-bind-form3.vrn
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="vrn"
|
||||
name="vrn"
|
||||
required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="address" class="form-label">{% trans "Address" %}</label>
|
||||
<textarea data-bind-form3.address
|
||||
class="form-control"
|
||||
id="address"
|
||||
name="address"
|
||||
required></textarea>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="tab-pane"
|
||||
role="tabpanel"
|
||||
aria-labelledby="bootstrap-wizard-validation-tab4"
|
||||
id="bootstrap-wizard-validation-tab4">
|
||||
<div class="row flex-center pb-8 pt-4 gx-3 gy-4">
|
||||
<div class="col-12 col-sm-auto">
|
||||
<div class="text-center text-sm-start">
|
||||
<img class="d-dark-none"
|
||||
src="{% static 'images/spot-illustrations/38.webp' %}"
|
||||
alt=""
|
||||
width="220">
|
||||
<img class="d-light-none"
|
||||
src="{% static 'images/spot-illustrations/dark_38.webp' %}"
|
||||
alt=""
|
||||
width="220">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-sm-auto">
|
||||
<div class="text-center text-sm-start">
|
||||
<h5 class="mb-3">{% trans 'You are all set!' %}</h5>
|
||||
<p class="text-body-emphasis fs-9">
|
||||
{% trans 'Now you can access your account' %}
|
||||
<br>
|
||||
{% trans 'anytime' %} {% trans 'anywhere' %}
|
||||
</p>
|
||||
<button data-on-click="sendFormData()"
|
||||
class="btn btn-primary px-6"
|
||||
id='submit_btn'>{% trans 'Submit' %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="post" action="{% url 'account_signup' %}" class="needs-validation">
|
||||
{% csrf_token %}
|
||||
<div class="card theme-wizard">
|
||||
<div class="card-body pt-4 pb-0">
|
||||
{{ form|crispy }}
|
||||
</div>
|
||||
</div>
|
||||
<div data-computed-form1_valid="validatePassword($form1.password,$form1.confirm_password) && validateEmail($form1.email)"
|
||||
class="card-footer border-top-0"
|
||||
data-wizard-footer="data-wizard-footer">
|
||||
<div class="d-flex pager wizard list-inline mb-0">
|
||||
<button class="d-none btn btn-link ps-0"
|
||||
type="button"
|
||||
data-wizard-prev-btn="data-wizard-prev-btn">{% trans 'Previous' %}</button>
|
||||
<div class="flex-1 text-end">
|
||||
<button data-attr-disabled="!$form1_valid"
|
||||
data-attr-disabled="!$phone_number_valid"
|
||||
class="btn btn-phoenix-primary px-6 px-sm-6 next"
|
||||
type="button"
|
||||
id="next_btn"
|
||||
data-wizard-next-btn="data-wizard-next-btn">{% trans 'Next' %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-primary" type="submit">{% trans 'Sign Up' %}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mx-auto mt-4 text-center">
|
||||
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#contentModal" data-url="{% url 'refund_policy' %}">
|
||||
Our Refund Policy
|
||||
Our Refund Policy
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="modal fade" id="contentModal" tabindex="-1" aria-labelledby="contentModalLabel" aria-hidden="true">
|
||||
@ -301,7 +57,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% include 'footer.html' %}
|
||||
<script src="{% static 'js/phoenix.js' %}"></script>
|
||||
{% endblock content %}
|
||||
@ -416,7 +172,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
const button = event.relatedTarget;
|
||||
// Extract the URL from the button's data-url attribute
|
||||
const url = button.getAttribute('data-url');
|
||||
|
||||
|
||||
// Select the modal body element
|
||||
const modalBody = contentModal.querySelector('.modal-body');
|
||||
|
||||
|
||||
35
templates/account/success.html
Normal file
35
templates/account/success.html
Normal file
@ -0,0 +1,35 @@
|
||||
{% extends "welcome_base.html" %}
|
||||
{% load crispy_forms_filters %}
|
||||
{% load i18n static %}
|
||||
|
||||
{% block content %}
|
||||
<section class="main my-2">
|
||||
<div class="container" style="max-width:60rem;">
|
||||
<div class="row form-container" id="form-container">
|
||||
<div class="col-12 ">
|
||||
<a class="d-flex flex-center text-decoration-none mb-4"
|
||||
href="{% url 'home' %}">
|
||||
<div class="d-flex align-items-center fw-bolder fs-3 d-inline-block">
|
||||
<img class="d-dark-none"
|
||||
src="{% static 'images/logos/logo-d.png' %}"
|
||||
alt="{% trans 'home' %}"
|
||||
width="58" />
|
||||
<img class="d-light-none"
|
||||
src="{% static 'images/logos/logo.png' %}"
|
||||
alt="{% trans 'home' %}"
|
||||
width="58" />
|
||||
</div>
|
||||
</a>
|
||||
<div class="text-center">
|
||||
<h3 class="text-body-highlight">{% trans 'Account Created Successfully' %}</h3>
|
||||
<p class="text-body-tertiary fs-9">
|
||||
{% blocktrans %}
|
||||
Thank you for registering at Haikal. We will contact you soon.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock content %}
|
||||
396
templates/registration/signup.html
Normal file
396
templates/registration/signup.html
Normal file
@ -0,0 +1,396 @@
|
||||
{% extends "welcome_base.html" %}
|
||||
{% load crispy_forms_filters %}
|
||||
{% load i18n static %}
|
||||
{% block content %}
|
||||
<section class="main my-2">
|
||||
<div class="container" style="max-width:60rem;">
|
||||
<div class="row form-container" id="form-container">
|
||||
<div class="col-12 ">
|
||||
<a class="d-flex flex-center text-decoration-none mb-4"
|
||||
href="{% url 'home' %}">
|
||||
<div class="d-flex align-items-center fw-bolder fs-3 d-inline-block">
|
||||
<img class="d-dark-none"
|
||||
src="{% static 'images/logos/logo-d.png' %}"
|
||||
alt="{% trans 'home' %}"
|
||||
width="58" />
|
||||
<img class="d-light-none"
|
||||
src="{% static 'images/logos/logo.png' %}"
|
||||
alt="{% trans 'home' %}"
|
||||
width="58" />
|
||||
</div>
|
||||
</a>
|
||||
<div class="text-center">
|
||||
<h3 class="text-body-highlight">{% trans 'Car Dealership Registration' %}</h3>
|
||||
<p class="text-body-tertiary fs-9">{% trans 'Create your dealership account today' %}</p>
|
||||
</div>
|
||||
<div data-signals="{ form1:{email:'',password:'',confirm_password:''}, form2:{name:'',arabic_name:'',phone_number:''}, form3:{crn:'',vrn:'',address:''}, form1_valid:true, form2_valid:true, form3_valid:true, email_valid:true, password_valid:true, phone_number_valid:true }"
|
||||
class="card theme-wizard"
|
||||
data-theme-wizard="data-theme-wizard">
|
||||
<div class="card-header pt-3 pb-2 ">
|
||||
<ul class="nav justify-content-between nav-wizard nav-wizard-success"
|
||||
role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<a class="nav-link active fw-semibold"
|
||||
href="#bootstrap-wizard-validation-tab1"
|
||||
data-bs-toggle="tab"
|
||||
data-wizard-step="1"
|
||||
aria-selected="true"
|
||||
role="tab">
|
||||
<div class="text-center d-inline-block">
|
||||
<span class="nav-item-circle-parent"><span class="nav-item-circle"><span class="fa fa-lock"></span></span></span><span class="d-none d-md-block mt-1 fs-9">{% trans 'Access' %}</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<a class="nav-link fw-semibold"
|
||||
href="#bootstrap-wizard-validation-tab2"
|
||||
data-bs-toggle="tab"
|
||||
data-wizard-step="2"
|
||||
aria-selected="false"
|
||||
tabindex="-1"
|
||||
role="tab">
|
||||
<div class="text-center d-inline-block">
|
||||
<span class="nav-item-circle-parent"><span class="nav-item-circle"><span class="fa fa-user"></span></span></span><span class="d-none d-md-block mt-1 fs-9">{% trans 'Account' %}</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<a class="nav-link fw-semibold"
|
||||
href="#bootstrap-wizard-validation-tab3"
|
||||
data-bs-toggle="tab"
|
||||
data-wizard-step="3"
|
||||
aria-selected="false"
|
||||
tabindex="-1"
|
||||
role="tab">
|
||||
<div class="text-center d-inline-block">
|
||||
<span class="nav-item-circle-parent"><span class="nav-item-circle">
|
||||
<svg class="fa fa-building">
|
||||
</svg>
|
||||
</span></span><span class="d-none d-md-block mt-1 fs-9">{% trans 'Dealership' %}</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<a class="nav-link fw-semibold"
|
||||
href="#bootstrap-wizard-validation-tab4"
|
||||
data-bs-toggle="tab"
|
||||
data-wizard-step="4"
|
||||
aria-selected="false"
|
||||
tabindex="-1"
|
||||
role="tab">
|
||||
<div class="text-center d-inline-block">
|
||||
<span class="nav-item-circle-parent"><span class="nav-item-circle"><span class="fa fa-check"></span></span></span><span class="d-none d-md-block mt-1 fs-9">{% trans 'Done' %}</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card-body pt-4 pb-0">
|
||||
<div class="tab-content" data-signals-current_form="1">
|
||||
<div class="tab-pane active"
|
||||
role="tabpanel"
|
||||
aria-labelledby="bootstrap-wizard-validation-tab1"
|
||||
id="bootstrap-wizard-validation-tab1">
|
||||
<form class="needs-validation"
|
||||
id="wizardValidationForm1"
|
||||
novalidate="novalidate"
|
||||
data-wizard-form="1"
|
||||
data-ref-f1>
|
||||
<div class="mb-3">
|
||||
<label for="email"
|
||||
data-class="{'text-danger':!$email_valid}"
|
||||
class="form-label">
|
||||
{% trans "Email" %}
|
||||
<span data-show="!$email_valid" class="text-danger">*</span>
|
||||
</label>
|
||||
<input data-on-input="$email_valid = validateEmail($form1.email)"
|
||||
data-on-blur="$email_valid = validateEmail($form1.email)"
|
||||
data-bind-form1.email
|
||||
data-class="{'is-invalid': !$email_valid , 'is-valid': ($email_valid && $form1.email)}"
|
||||
type="email"
|
||||
class="form-control"
|
||||
id="email"
|
||||
name="email"
|
||||
required>
|
||||
<div class="invalid-feedback" data-show="!$email_valid">{% trans "Please enter a valid email address" %}</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">{% trans "Password" %}</label>
|
||||
<input data-bind-form1.password
|
||||
type="password"
|
||||
data-on-input="$password_valid = validatePassword($form1.password,$form1.confirm_password)"
|
||||
data-on-blur="$password_valid = validatePassword($form1.password,$form1.confirm_password)"
|
||||
class="form-control"
|
||||
data-class="{'is-invalid':($form1.password.length && $form1.password.length < 8),'is-valid':$form1.password.length > 8 }"
|
||||
id="password"
|
||||
name="password1"
|
||||
required>
|
||||
<div class="invalid-feedback" data-show="!$password_valid">
|
||||
{% trans "Password does not match. or length is less than 8 characters." %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="confirm_password" class="form-label">{% trans "Confirm Password" %}</label>
|
||||
<span class="text-danger" data-show="!$password_valid">*</span>
|
||||
<input data-bind-form1.confirm_password
|
||||
data-on-input="$password_valid = validatePassword($form1.password,$form1.confirm_password)"
|
||||
data-on-blur="$password_valid = validatePassword($form1.password,$form1.confirm_password)"
|
||||
type="password"
|
||||
class="form-control"
|
||||
data-class="{'is-invalid':!$password_valid,'is-valid':($password_valid&& $form1.confirm_password)}"
|
||||
id="confirm_password"
|
||||
name="password2"
|
||||
required>
|
||||
<div class="invalid-feedback" data-show="!$password_valid">
|
||||
{% trans "Password does not match. or length is less than 8 characters." %}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="tab-pane"
|
||||
role="tabpanel"
|
||||
aria-labelledby="bootstrap-wizard-validation-tab2"
|
||||
id="bootstrap-wizard-validation-tab2">
|
||||
<form class="needs-validation"
|
||||
id="wizardValidationForm2"
|
||||
novalidate="novalidate"
|
||||
data-wizard-form="2"
|
||||
data-ref-f2>
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">{% trans "Dealership Name" %}</label>
|
||||
<input data-bind-form2.name
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="name"
|
||||
name="username"
|
||||
required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="arabic_name" class="form-label">{% trans "Arabic Name" %}</label>
|
||||
<input data-bind-form2.arabic_name
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="arabic_name"
|
||||
name="arabic_name"
|
||||
required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="phone_number" class="form-label">{% trans "Phone Number" %}</label>
|
||||
<span data-show="!$phone_number_valid" class="text-danger">*</span>
|
||||
<input data-bind-form2.phone_number
|
||||
type="tel"
|
||||
data-class="{'is-invalid':!$phone_number_valid}"
|
||||
class="form-control"
|
||||
id="phone_number"
|
||||
name="phone_number"
|
||||
required
|
||||
data-on-input="$phone_number_valid = validate_sa_phone_number($form2.phone_number)">
|
||||
<div class="invalid-feedback" data-show="!$phone_number_valid">{% trans "Please enter a valid Saudi phone number (e.g., 05XXXXXXXX)" %}</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="tab-pane"
|
||||
role="tabpanel"
|
||||
aria-labelledby="bootstrap-wizard-validation-tab3"
|
||||
id="bootstrap-wizard-validation-tab3">
|
||||
<form class="needs-validation"
|
||||
id="wizardValidationForm3"
|
||||
novalidate="novalidate"
|
||||
data-wizard-form="3"
|
||||
data-ref-f3>
|
||||
<div class="mb-3">
|
||||
<label for="crn" class="form-label">{% trans "Commercial Registration Number (CRN)" %}</label>
|
||||
<input data-bind-form3.crn
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="crn"
|
||||
name="crn"
|
||||
required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="vrn" class="form-label">{% trans "Vehicle Registration Number (VRN)" %}</label>
|
||||
<input data-bind-form3.vrn
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="vrn"
|
||||
name="vrn"
|
||||
required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="address" class="form-label">{% trans "Business Address" %}</label>
|
||||
<textarea data-bind-form3.address
|
||||
class="form-control"
|
||||
id="address"
|
||||
name="address"
|
||||
rows="4"
|
||||
required></textarea>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="tab-pane"
|
||||
role="tabpanel"
|
||||
aria-labelledby="bootstrap-wizard-validation-tab4"
|
||||
id="bootstrap-wizard-validation-tab4">
|
||||
<div class="row flex-center pb-8 pt-4 gx-3 gy-4">
|
||||
<div class="col-12 col-sm-auto">
|
||||
<div class="text-center text-sm-start">
|
||||
<img class="d-dark-none"
|
||||
src="{% static 'images/spot-illustrations/38.webp' %}"
|
||||
alt=""
|
||||
width="220">
|
||||
<img class="d-light-none"
|
||||
src="{% static 'images/spot-illustrations/dark_38.webp' %}"
|
||||
alt=""
|
||||
width="220">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-sm-auto">
|
||||
<div class="text-center text-sm-start">
|
||||
<h5 class="mb-3">{% trans 'You are all set!' %}</h5>
|
||||
<p class="text-body-emphasis fs-9">
|
||||
{% trans 'Now you can access your dealership account' %}
|
||||
<br>
|
||||
{% trans 'anytime' %} {% trans 'anywhere' %}
|
||||
</p>
|
||||
<button data-on-click="sendFormData()"
|
||||
class="btn btn-primary px-6"
|
||||
id='submit_btn'>{% trans 'Complete Registration' %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div data-computed-form1_valid="validatePassword($form1.password,$form1.confirm_password) && validateEmail($form1.email)"
|
||||
class="card-footer border-top-0"
|
||||
data-wizard-footer="data-wizard-footer">
|
||||
<div class="d-flex pager wizard list-inline mb-0">
|
||||
<button class="d-none btn btn-link ps-0"
|
||||
type="button"
|
||||
data-wizard-prev-btn="data-wizard-prev-btn">{% trans 'Previous' %}</button>
|
||||
<div class="flex-1 text-end">
|
||||
<button data-attr-disabled="!$form1_valid"
|
||||
data-attr-disabled="!$phone_number_valid"
|
||||
class="btn btn-phoenix-primary px-6 px-sm-6 next"
|
||||
type="button"
|
||||
id="next_btn"
|
||||
data-wizard-next-btn="data-wizard-next-btn">{% trans 'Next' %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% include 'footer.html' %}
|
||||
<script src="{% static 'js/phoenix.js' %}"></script>
|
||||
{% endblock content %}
|
||||
{% block customJS %}
|
||||
<script src="{% static 'js/main.js' %}"></script>
|
||||
<script src="{% static 'js/sweetalert2.all.min.js' %}"></script>
|
||||
<script type="module"
|
||||
src="https://cdn.jsdelivr.net/gh/starfederation/datastar@v1.0.0-beta.11/bundles/datastar.js"></script>
|
||||
<script>
|
||||
function validatePassword(password, confirmPassword) {
|
||||
return password === confirmPassword && password.length > 7 && password !== '';
|
||||
}
|
||||
function validateEmail(email) {
|
||||
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
||||
return emailRegex.test(email) && email !== '';
|
||||
}
|
||||
function validateform2(name,arabic_name,phone_number) {
|
||||
if (name === '' || arabic_name === '' || phone_number === '' || phone_number.length < 10 || !phone_number.startsWith('05')) {
|
||||
return false;
|
||||
}
|
||||
return true
|
||||
}
|
||||
function validate_sa_phone_number(phone_number) {
|
||||
const phone_numberRegex = /^05[0-9]{8}$/;
|
||||
return phone_numberRegex.test(phone_number) && phone_numberRegex !== '';
|
||||
}
|
||||
function getAllFormData() {
|
||||
const forms = document.querySelectorAll('.needs-validation');
|
||||
const formData = {};
|
||||
forms.forEach(form => {
|
||||
const fields = form.querySelectorAll('input,textarea,select');
|
||||
fields.forEach(field => {
|
||||
formData[field.name] = field.value;
|
||||
});
|
||||
});
|
||||
return formData;
|
||||
}
|
||||
|
||||
function showLoading() {
|
||||
Swal.fire({
|
||||
title: "{% trans 'Please Wait' %}",
|
||||
text: "{% trans 'Processing your registration' %}...",
|
||||
allowOutsideClick: false,
|
||||
didOpen: () => {
|
||||
Swal.showLoading();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function hideLoading() {
|
||||
Swal.close();
|
||||
}
|
||||
|
||||
function notify(tag,msg){
|
||||
Swal.fire({
|
||||
icon: tag,
|
||||
titleText: msg
|
||||
});
|
||||
}
|
||||
function getCookie(name) {
|
||||
let cookieValue = null;
|
||||
if (document.cookie && document.cookie !== "") {
|
||||
const cookies = document.cookie.split(";");
|
||||
for (let cookie of cookies) {
|
||||
cookie = cookie.trim();
|
||||
if (cookie.substring(0, name.length + 1) === name + "=") {
|
||||
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cookieValue;
|
||||
}
|
||||
async function sendFormData() {
|
||||
const formData = getAllFormData();
|
||||
const url = "{% url 'car_dealership_signup' %}";
|
||||
const csrftoken = getCookie('csrftoken');
|
||||
try {
|
||||
showLoading();
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRFToken': csrftoken,
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: new URLSearchParams(formData),
|
||||
});
|
||||
hideLoading();
|
||||
if (response.ok) {
|
||||
notify("success","{% trans 'Dealership account created successfully' %}");
|
||||
setTimeout(() => {
|
||||
window.location.href = "{% url 'home' %}";
|
||||
}, 1500);
|
||||
} else {
|
||||
const data = await response.json();
|
||||
if (data.errors) {
|
||||
let errorMsg = '';
|
||||
for (const [key, value] of Object.entries(data.errors)) {
|
||||
errorMsg += `${value.join(', ')}\n`;
|
||||
}
|
||||
notify("error", errorMsg);
|
||||
} else {
|
||||
notify("error", "{% trans 'An error occurred during registration' %}");
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
notify("error", "{% trans 'Network error. Please try again.' %}");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{% endblock customJS %}
|
||||
Loading…
x
Reference in New Issue
Block a user