Merge branch 'main' into frontend
4
.gitignore
vendored
@ -24,6 +24,10 @@ dev_venv
|
|||||||
*.bak
|
*.bak
|
||||||
play.sh
|
play.sh
|
||||||
git-sync.sh
|
git-sync.sh
|
||||||
|
deploy*
|
||||||
|
git-sync.sh
|
||||||
|
update.sh
|
||||||
|
rollback.sh
|
||||||
# If you are using PyCharm #
|
# If you are using PyCharm #
|
||||||
# User-specific stuff
|
# User-specific stuff
|
||||||
.idea/**/workspace.xml
|
.idea/**/workspace.xml
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from . import models
|
from . import models
|
||||||
from django_ledger import models as ledger_models
|
from django_ledger import models as ledger_models
|
||||||
|
from django.contrib import messages
|
||||||
# from django_pdf_actions.actions import export_to_pdf_landscape, export_to_pdf_portrait
|
# from django_pdf_actions.actions import export_to_pdf_landscape, export_to_pdf_portrait
|
||||||
# from appointment import models as appointment_models
|
# from appointment import models as appointment_models
|
||||||
from import_export.admin import ExportMixin
|
from import_export.admin import ExportMixin
|
||||||
@ -72,6 +72,7 @@ admin.site.register(models.UserActivityLog)
|
|||||||
admin.site.register(models.DealersMake)
|
admin.site.register(models.DealersMake)
|
||||||
admin.site.register(models.ExtraInfo)
|
admin.site.register(models.ExtraInfo)
|
||||||
admin.site.register(models.Ticket)
|
admin.site.register(models.Ticket)
|
||||||
|
# admin.site.register(models.UserRegistration)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(models.Car)
|
@admin.register(models.Car)
|
||||||
@ -174,3 +175,93 @@ class CarOptionAdmin(admin.ModelAdmin):
|
|||||||
# class ItemTransactionModelAdmin(admin.ModelAdmin):
|
# class ItemTransactionModelAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
# actions = [export_to_pdf_landscape, export_to_pdf_portrait]
|
# actions = [export_to_pdf_landscape, export_to_pdf_portrait]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(models.UserRegistration)
|
||||||
|
class UserRegistrationAdmin(admin.ModelAdmin):
|
||||||
|
# Fields to display in the list view
|
||||||
|
list_display = [
|
||||||
|
'name',
|
||||||
|
'arabic_name',
|
||||||
|
'email',
|
||||||
|
'crn',
|
||||||
|
'vrn',
|
||||||
|
'phone_number',
|
||||||
|
'is_created',
|
||||||
|
'created_at',
|
||||||
|
]
|
||||||
|
|
||||||
|
# Filters in the right sidebar
|
||||||
|
list_filter = [
|
||||||
|
'is_created',
|
||||||
|
'created_at',
|
||||||
|
]
|
||||||
|
|
||||||
|
# Searchable fields
|
||||||
|
search_fields = [
|
||||||
|
'name', 'arabic_name', 'email', 'crn', 'vrn', 'phone_number'
|
||||||
|
]
|
||||||
|
|
||||||
|
# Read-only fields in detail view
|
||||||
|
readonly_fields = [
|
||||||
|
'created_at', 'updated_at', 'is_created', 'password'
|
||||||
|
]
|
||||||
|
|
||||||
|
# Organize form layout
|
||||||
|
fieldsets = [
|
||||||
|
('Account Information', {
|
||||||
|
'fields': ('name', 'arabic_name', 'email', 'phone_number')
|
||||||
|
}),
|
||||||
|
('Business Details', {
|
||||||
|
'fields': ('crn', 'vrn', 'address')
|
||||||
|
}),
|
||||||
|
('Status', {
|
||||||
|
'fields': ('is_created', 'password', 'created_at', 'updated_at'),
|
||||||
|
'classes': ('collapse',)
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Custom action to create accounts
|
||||||
|
actions = ['create_dealer_accounts']
|
||||||
|
|
||||||
|
@admin.action(description='Create dealer account(s) for selected registrations')
|
||||||
|
def create_dealer_accounts(self, request, queryset):
|
||||||
|
created_count = 0
|
||||||
|
already_created_count = 0
|
||||||
|
failed_count = 0
|
||||||
|
|
||||||
|
for registration in queryset:
|
||||||
|
try:
|
||||||
|
if not registration.is_created:
|
||||||
|
registration.create_account() # Your existing method
|
||||||
|
created_count += 1
|
||||||
|
else:
|
||||||
|
already_created_count += 1
|
||||||
|
except Exception as e:
|
||||||
|
self.message_user(
|
||||||
|
request,
|
||||||
|
f"Error creating account for {registration.name}: {str(e)}",
|
||||||
|
level=messages.ERROR
|
||||||
|
)
|
||||||
|
failed_count += 1
|
||||||
|
|
||||||
|
# Show summary message
|
||||||
|
if created_count > 0:
|
||||||
|
self.message_user(
|
||||||
|
request,
|
||||||
|
f"Successfully created {created_count} account(s).",
|
||||||
|
level=messages.SUCCESS
|
||||||
|
)
|
||||||
|
if already_created_count > 0:
|
||||||
|
self.message_user(
|
||||||
|
request,
|
||||||
|
f"{already_created_count} registration(s) were already created.",
|
||||||
|
level=messages.INFO
|
||||||
|
)
|
||||||
|
if failed_count > 0:
|
||||||
|
self.message_user(
|
||||||
|
request,
|
||||||
|
f"Failed to create {failed_count} account(s). Check logs.",
|
||||||
|
level=messages.ERROR
|
||||||
|
)
|
||||||
@ -57,6 +57,7 @@ from .models import (
|
|||||||
Tasks,
|
Tasks,
|
||||||
Recall,
|
Recall,
|
||||||
Ticket,
|
Ticket,
|
||||||
|
UserRegistration
|
||||||
)
|
)
|
||||||
from django_ledger import models as ledger_models
|
from django_ledger import models as ledger_models
|
||||||
from django.forms import (
|
from django.forms import (
|
||||||
@ -363,6 +364,7 @@ class CarForm(
|
|||||||
"receiving_date",
|
"receiving_date",
|
||||||
"vendor",
|
"vendor",
|
||||||
]
|
]
|
||||||
|
required_fields = ["vin","id_car_make", "id_car_model", "id_car_serie", "id_car_trim", "vendor"]
|
||||||
widgets = {
|
widgets = {
|
||||||
"id_car_make": forms.Select(attrs={"class": "form-select form-select-sm"}),
|
"id_car_make": forms.Select(attrs={"class": "form-select form-select-sm"}),
|
||||||
"receiving_date": forms.DateTimeInput(attrs={"type": "datetime-local"}),
|
"receiving_date": forms.DateTimeInput(attrs={"type": "datetime-local"}),
|
||||||
@ -1259,19 +1261,8 @@ class OpportunityForm(forms.ModelForm):
|
|||||||
label=_("Expected Closing Date"), widget=forms.DateInput(attrs={"type": "date"})
|
label=_("Expected Closing Date"), widget=forms.DateInput(attrs={"type": "date"})
|
||||||
)
|
)
|
||||||
|
|
||||||
probability = forms.IntegerField(
|
car = forms.ModelChoiceField(
|
||||||
label=_("Probability (%)"),
|
queryset=Car.objects.all(), label=_("Car"), required=True
|
||||||
widget=forms.NumberInput(
|
|
||||||
attrs={
|
|
||||||
"type": "range",
|
|
||||||
"min": "0",
|
|
||||||
"max": "100",
|
|
||||||
"step": "1",
|
|
||||||
"class": "form-range",
|
|
||||||
"oninput": "this.nextElementSibling.value = this.value",
|
|
||||||
}
|
|
||||||
),
|
|
||||||
initial=50, # Default value
|
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -1280,23 +1271,17 @@ class OpportunityForm(forms.ModelForm):
|
|||||||
"lead",
|
"lead",
|
||||||
"car",
|
"car",
|
||||||
"stage",
|
"stage",
|
||||||
"probability",
|
|
||||||
"amount",
|
|
||||||
"expected_revenue",
|
|
||||||
"expected_close_date",
|
"expected_close_date",
|
||||||
]
|
]
|
||||||
widgets = {
|
|
||||||
"expected_revenue": forms.NumberInput(attrs={"readonly": "readonly"}),
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
# def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
# super().__init__(*args, **kwargs)
|
||||||
# Add a visible number input to display the current value
|
# # Add a visible number input to display the current value
|
||||||
self.fields["probability"].widget.attrs["class"] = (
|
# self.fields["probability"].widget.attrs["class"] = (
|
||||||
"d-none" # Hide the default input
|
# "d-none" # Hide the default input
|
||||||
)
|
# )
|
||||||
if self.instance and self.instance.pk:
|
# if self.instance and self.instance.pk:
|
||||||
self.fields["probability"].initial = self.instance.probability
|
# self.fields["probability"].initial = self.instance.probability
|
||||||
|
|
||||||
|
|
||||||
class OpportunityStageForm(forms.ModelForm):
|
class OpportunityStageForm(forms.ModelForm):
|
||||||
@ -2255,3 +2240,12 @@ class TicketResolutionForm(forms.ModelForm):
|
|||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
# Limit status choices to resolution options
|
# Limit status choices to resolution options
|
||||||
self.fields["status"].choices = [("resolved", "Resolved"), ("closed", "Closed")]
|
self.fields["status"].choices = [("resolved", "Resolved"), ("closed", "Closed")]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class CarDealershipRegistrationForm(forms.ModelForm):
|
||||||
|
# Add additional fields for the registration form
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = UserRegistration
|
||||||
|
fields = ("name","arabic_name", "email","phone_number", "crn", "vrn", "address")
|
||||||
@ -49,7 +49,11 @@ from plans.models import UserPlan
|
|||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from imagekit.models import ImageSpecField
|
from imagekit.models import ImageSpecField
|
||||||
from imagekit.processors import ResizeToFill
|
from imagekit.processors import ResizeToFill
|
||||||
|
from encrypted_model_fields.fields import (
|
||||||
|
EncryptedCharField,
|
||||||
|
EncryptedDateField,
|
||||||
|
EncryptedEmailField,
|
||||||
|
)
|
||||||
# from plans.models import AbstractPlan
|
# from plans.models import AbstractPlan
|
||||||
# from simple_history.models import HistoricalRecords
|
# from simple_history.models import HistoricalRecords
|
||||||
from plans.models import Invoice
|
from plans.models import Invoice
|
||||||
@ -1339,7 +1343,7 @@ class Dealer(models.Model, LocalizedNameMixin):
|
|||||||
)
|
)
|
||||||
arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name"))
|
arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name"))
|
||||||
name = models.CharField(max_length=255, verbose_name=_("English Name"))
|
name = models.CharField(max_length=255, verbose_name=_("English Name"))
|
||||||
phone_number = models.CharField(
|
phone_number = EncryptedCharField(
|
||||||
max_length=255,
|
max_length=255,
|
||||||
verbose_name=_("Phone Number"),
|
verbose_name=_("Phone Number"),
|
||||||
validators=[SaudiPhoneNumberValidator()],
|
validators=[SaudiPhoneNumberValidator()],
|
||||||
@ -1707,18 +1711,18 @@ class Customer(models.Model):
|
|||||||
max_length=1,
|
max_length=1,
|
||||||
verbose_name=_("Gender"),
|
verbose_name=_("Gender"),
|
||||||
)
|
)
|
||||||
dob = models.DateField(verbose_name=_("Date of Birth"), null=True, blank=True)
|
dob = EncryptedDateField(verbose_name=_("Date of Birth"), null=True, blank=True)
|
||||||
email = models.EmailField(verbose_name=_("Email"))
|
email = EncryptedEmailField(verbose_name=_("Email"))
|
||||||
national_id = models.CharField(
|
national_id = EncryptedCharField(
|
||||||
max_length=10, unique=True, verbose_name=_("National ID"), null=True, blank=True
|
max_length=10, unique=True, verbose_name=_("National ID"), null=True, blank=True
|
||||||
)
|
)
|
||||||
|
|
||||||
phone_number = models.CharField(
|
phone_number = EncryptedCharField(
|
||||||
max_length=255,
|
max_length=255,
|
||||||
verbose_name=_("Phone Number"),
|
verbose_name=_("Phone Number"),
|
||||||
validators=[SaudiPhoneNumberValidator()],
|
validators=[SaudiPhoneNumberValidator()],
|
||||||
)
|
)
|
||||||
address = models.CharField(
|
address = EncryptedCharField(
|
||||||
max_length=200, blank=True, null=True, verbose_name=_("Address")
|
max_length=200, blank=True, null=True, verbose_name=_("Address")
|
||||||
)
|
)
|
||||||
active = models.BooleanField(default=True, verbose_name=_("Active"))
|
active = models.BooleanField(default=True, verbose_name=_("Active"))
|
||||||
@ -2294,22 +2298,48 @@ class Schedule(models.Model):
|
|||||||
related_name="schedules",
|
related_name="schedules",
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
|
verbose_name=_("Customer"),
|
||||||
)
|
)
|
||||||
scheduled_by = models.ForeignKey(User, on_delete=models.CASCADE)
|
scheduled_by = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
purpose = models.CharField(max_length=200, choices=PURPOSE_CHOICES)
|
purpose = models.CharField(
|
||||||
scheduled_at = models.DateTimeField()
|
max_length=200,
|
||||||
start_time = models.TimeField(verbose_name=_("Start Time"), null=True, blank=True)
|
choices=PURPOSE_CHOICES,
|
||||||
end_time = models.TimeField(verbose_name=_("End Time"), null=True, blank=True)
|
verbose_name=_("Purpose"),
|
||||||
|
help_text=_("What is the purpose of this schedule?"),
|
||||||
|
)
|
||||||
|
scheduled_at = models.DateTimeField(verbose_name=_("Scheduled Date"))
|
||||||
|
start_time = models.TimeField(
|
||||||
|
verbose_name=_("Start Time"), null=True, blank=True, help_text=_("HH:MM")
|
||||||
|
)
|
||||||
|
end_time = models.TimeField(
|
||||||
|
verbose_name=_("End Time"), null=True, blank=True, help_text=_("HH:MM")
|
||||||
|
)
|
||||||
scheduled_type = models.CharField(
|
scheduled_type = models.CharField(
|
||||||
max_length=200, choices=ScheduledType, default="Call"
|
max_length=200,
|
||||||
|
choices=ScheduledType,
|
||||||
|
default="Call",
|
||||||
|
verbose_name=_("Scheduled Type"),
|
||||||
|
help_text=_("What type of schedule is this?"),
|
||||||
)
|
)
|
||||||
completed = models.BooleanField(default=False, verbose_name=_("Completed"))
|
completed = models.BooleanField(
|
||||||
notes = models.TextField(blank=True, null=True)
|
default=False,
|
||||||
|
verbose_name=_("Completed"),
|
||||||
|
help_text=_("Has this schedule been completed?"),
|
||||||
|
)
|
||||||
|
notes = models.TextField(blank=True, null=True, verbose_name=_("Notes"))
|
||||||
status = models.CharField(
|
status = models.CharField(
|
||||||
max_length=200, choices=ScheduleStatusChoices, default="Scheduled"
|
max_length=200,
|
||||||
|
choices=ScheduleStatusChoices,
|
||||||
|
default="Scheduled",
|
||||||
|
verbose_name=_("Status"),
|
||||||
|
help_text=_("What is the status of this schedule?"),
|
||||||
|
)
|
||||||
|
created_at = models.DateTimeField(
|
||||||
|
auto_now_add=True, verbose_name=_("Created Date"), help_text=_("When was this schedule created?")
|
||||||
|
)
|
||||||
|
updated_at = models.DateTimeField(
|
||||||
|
auto_now=True, verbose_name=_("Updated Date"), help_text=_("When was this schedule last updated?")
|
||||||
)
|
)
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
|
||||||
updated_at = models.DateTimeField(auto_now=True)
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"Scheduled {self.purpose} on {self.scheduled_at}"
|
return f"Scheduled {self.purpose} on {self.scheduled_at}"
|
||||||
@ -2414,10 +2444,11 @@ class Opportunity(models.Model):
|
|||||||
"Lead",
|
"Lead",
|
||||||
related_name="opportunity",
|
related_name="opportunity",
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
|
verbose_name=_("Lead"),
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
)
|
)
|
||||||
probability = models.PositiveIntegerField(validators=[validate_probability])
|
probability = models.PositiveIntegerField(validators=[validate_probability],null=True, blank=True)
|
||||||
amount = models.DecimalField(
|
amount = models.DecimalField(
|
||||||
max_digits=10,
|
max_digits=10,
|
||||||
decimal_places=2,
|
decimal_places=2,
|
||||||
@ -3858,3 +3889,63 @@ class CarImage(models.Model):
|
|||||||
self.id,
|
self.id,
|
||||||
task_name=f"generate_car_image_{self.car.vin}",
|
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,null=True,blank=True)
|
||||||
|
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 .tasks import create_user_dealer
|
||||||
|
|
||||||
|
if self.is_created or User.objects.filter(email=self.email).exists():
|
||||||
|
logger.info(f"Account already created or exists: {self.email}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
password = make_random_password()
|
||||||
|
|
||||||
|
try:
|
||||||
|
logger.info(f"Creating user account {self.email}")
|
||||||
|
dealer = create_user_dealer(
|
||||||
|
email=self.email,
|
||||||
|
password=password,
|
||||||
|
name=self.name,
|
||||||
|
arabic_name=self.arabic_name,
|
||||||
|
phone=self.phone_number,
|
||||||
|
crn=self.crn,
|
||||||
|
vrn=self.vrn,
|
||||||
|
address=self.address
|
||||||
|
)
|
||||||
|
|
||||||
|
if dealer:
|
||||||
|
self.is_created = True
|
||||||
|
self.password = password
|
||||||
|
self.save()
|
||||||
|
logger.info(f"User account created successfully: {self.email}")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
logger.error(f"Failed to create dealer account: {self.email}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error creating account for {self.email}: {e}")
|
||||||
|
return False
|
||||||
@ -72,12 +72,12 @@ User = get_user_model()
|
|||||||
|
|
||||||
|
|
||||||
# check with marwan
|
# check with marwan
|
||||||
@receiver(post_save, sender=models.Car)
|
# @receiver(post_save, sender=models.Car)
|
||||||
def create_dealers_make(sender, instance, created, **kwargs):
|
# def create_dealers_make(sender, instance, created, **kwargs):
|
||||||
if created:
|
# if created:
|
||||||
models.DealersMake.objects.get_or_create(
|
# models.DealersMake.objects.get_or_create(
|
||||||
dealer=instance.dealer, car_make=instance.id_car_make
|
# dealer=instance.dealer, car_make=instance.id_car_make
|
||||||
)
|
# )
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=models.Car)
|
@receiver(post_save, sender=models.Car)
|
||||||
@ -310,21 +310,24 @@ def create_item_model(sender, instance, created, **kwargs):
|
|||||||
"""
|
"""
|
||||||
entity = instance.dealer.entity
|
entity = instance.dealer.entity
|
||||||
|
|
||||||
if created:
|
coa = entity.get_default_coa()
|
||||||
coa = entity.get_default_coa()
|
uom = entity.get_uom_all().filter(name="Unit").first()
|
||||||
uom = entity.get_uom_all().get(name="Unit")
|
if not uom:
|
||||||
|
uom = entity.create_uom(name="Unit", unit_abbr="unit")
|
||||||
|
|
||||||
if not instance.item_model:
|
if not instance.item_model:
|
||||||
inventory = entity.create_item_product(
|
inventory = entity.create_item_product(
|
||||||
name=instance.vin,
|
name=instance.vin,
|
||||||
item_type=ItemModel.ITEM_TYPE_MATERIAL,
|
item_type=ItemModel.ITEM_TYPE_MATERIAL,
|
||||||
uom_model=uom,
|
uom_model=uom,
|
||||||
coa_model=coa,
|
coa_model=coa,
|
||||||
)
|
)
|
||||||
instance.item_model = inventory
|
instance.item_model = inventory
|
||||||
inventory.save()
|
instance.save()
|
||||||
else:
|
|
||||||
|
if instance.marked_price:
|
||||||
instance.item_model.default_amount = instance.marked_price
|
instance.item_model.default_amount = instance.marked_price
|
||||||
|
instance.item_model.save()
|
||||||
|
|
||||||
# inventory = entity.create_item_inventory(
|
# inventory = entity.create_item_inventory(
|
||||||
# name=instance.vin,
|
# name=instance.vin,
|
||||||
@ -1386,3 +1389,39 @@ def handle_car_image(sender, instance, created, **kwargs):
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error handling car image for {car.vin}: {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 created:
|
||||||
|
send_email(
|
||||||
|
settings.DEFAULT_FROM_EMAIL,
|
||||||
|
instance.email,
|
||||||
|
"Account Registration",
|
||||||
|
"""
|
||||||
|
Thank you for registering with us. We will contact you shortly to complete your application.
|
||||||
|
شكرا لمراسلتنا. سوف نتصل بك قريبا لاستكمال طلبك.
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
if instance.is_created:
|
||||||
|
logger.info(f"User account created: {instance.email}, sending email")
|
||||||
|
send_email(
|
||||||
|
settings.DEFAULT_FROM_EMAIL,
|
||||||
|
instance.email,
|
||||||
|
"Account Created",
|
||||||
|
f"""
|
||||||
|
Dear {instance.name},
|
||||||
|
|
||||||
|
Your account has been created and you can login with your email {instance.email} and password: {instance.password}.
|
||||||
|
Please login to the website to complete your profile and start using our services.
|
||||||
|
|
||||||
|
Thank you for choosing us.
|
||||||
|
|
||||||
|
عزيزي {instance.name},
|
||||||
|
|
||||||
|
لقد تم إنشاء حسابك والآن يمكنك تسجيل الدخول باستخدام بريدك الإلكتروني {instance.email} وكلمة المرور: {instance.password}.
|
||||||
|
يرجى تسجيل الدخول إلى الموقع لاستكمال الملف الشخصي والبدء في استخدام خدماتنا.
|
||||||
|
|
||||||
|
شكرا لاختيارك لنا.
|
||||||
|
""")
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import time
|
||||||
import base64
|
import base64
|
||||||
import logging
|
import logging
|
||||||
import requests
|
import requests
|
||||||
@ -123,7 +124,7 @@ def retry_entity_creation(dealer_id, retry_count=0):
|
|||||||
Retry entity creation if initial attempt failed
|
Retry entity creation if initial attempt failed
|
||||||
"""
|
"""
|
||||||
from .models import Dealer
|
from .models import Dealer
|
||||||
from yourapp.models import EntityModel
|
from django_ledger.models import EntityModel
|
||||||
|
|
||||||
max_retries = 3
|
max_retries = 3
|
||||||
|
|
||||||
|
|||||||
@ -2,16 +2,18 @@ from inventory.utils import get_user_type
|
|||||||
from . import views
|
from . import views
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
from django.urls import reverse_lazy
|
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_tables2.export.export import TableExport
|
||||||
from django.conf.urls import handler403, handler400, handler404, handler500
|
from django.conf.urls import handler403, handler400, handler404, handler500
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# main URLs
|
# main URLs
|
||||||
path("", views.WelcomeView, name="welcome"),
|
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("", views.HomeView, name="home"),
|
||||||
path('refund-policy/',views.refund_policy,name='refund_policy'),
|
# path('refund-policy/',views.refund_policy,name='refund_policy'),
|
||||||
path("<slug:dealer_slug>/", views.HomeView, name="home"),
|
path("<slug:dealer_slug>/", views.HomeView, name="home"),
|
||||||
# Tasks
|
# Tasks
|
||||||
path("legal/", views.terms_and_privacy, name="terms_and_privacy"),
|
path("legal/", views.terms_and_privacy, name="terms_and_privacy"),
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import re
|
|||||||
class SaudiPhoneNumberValidator(RegexValidator):
|
class SaudiPhoneNumberValidator(RegexValidator):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(
|
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)"),
|
message=_("Enter a valid Saudi phone number (05XXXXXXXX or +9665XXXXXXXX)"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -1440,13 +1440,13 @@ class CarListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
|
|||||||
qs = super().get_queryset()
|
qs = super().get_queryset()
|
||||||
qs = qs.filter(dealer=dealer)
|
qs = qs.filter(dealer=dealer)
|
||||||
# status = self.request.GET.get("status")
|
# status = self.request.GET.get("status")
|
||||||
|
# status = self.request.GET.get("status")
|
||||||
search = self.request.GET.get("search")
|
search = self.request.GET.get("search")
|
||||||
make = self.request.GET.get("make", None)
|
make = self.request.GET.get("make", None)
|
||||||
model = self.request.GET.get("model", None)
|
model = self.request.GET.get("model", None)
|
||||||
year = self.request.GET.get("year", None)
|
year = self.request.GET.get("year", None)
|
||||||
car_status = self.request.GET.get("car_status", None)
|
car_status = self.request.GET.get("car_status", None)
|
||||||
print("ALLLLLLLLL:::",make,model)
|
print(car_status)
|
||||||
|
|
||||||
if car_status:
|
if car_status:
|
||||||
qs = qs.filter(status=car_status)
|
qs = qs.filter(status=car_status)
|
||||||
if search:
|
if search:
|
||||||
@ -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')}."
|
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
|
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()
|
item.item_model.car.mark_as_sold()
|
||||||
messages.success(request, "Sale Order created successfully")
|
messages.success(request, "Sale Order created successfully")
|
||||||
|
|
||||||
@ -6330,9 +6331,14 @@ def lead_create(request, dealer_slug):
|
|||||||
form.filter_qs(dealer=dealer)
|
form.filter_qs(dealer=dealer)
|
||||||
|
|
||||||
if make := request.GET.get("id_car_make", None):
|
if make := request.GET.get("id_car_make", None):
|
||||||
form.fields["id_car_model"].queryset = models.CarModel.objects.filter(
|
qs = models.CarModel.objects.filter(
|
||||||
id_car_make=int(make)
|
id_car_make=int(make)
|
||||||
)
|
)
|
||||||
|
form.fields["id_car_model"].queryset = qs
|
||||||
|
form.fields["id_car_model"].choices = [
|
||||||
|
(obj.id_car_model, obj.get_local_name()) for obj in qs
|
||||||
|
]
|
||||||
|
|
||||||
else:
|
else:
|
||||||
dealer_make_list = models.DealersMake.objects.filter(dealer=dealer).values_list(
|
dealer_make_list = models.DealersMake.objects.filter(dealer=dealer).values_list(
|
||||||
"car_make", flat=True
|
"car_make", flat=True
|
||||||
@ -6366,7 +6372,11 @@ def lead_create(request, dealer_slug):
|
|||||||
]
|
]
|
||||||
|
|
||||||
if first_make := qs.first():
|
if first_make := qs.first():
|
||||||
form.fields["id_car_model"].queryset = first_make.carmodel_set.all()
|
qs = first_make.carmodel_set.all()
|
||||||
|
form.fields["id_car_model"].queryset = qs
|
||||||
|
form.fields["id_car_model"].choices = [
|
||||||
|
(obj.id_car_model, obj.get_local_name()) for obj in qs
|
||||||
|
]
|
||||||
|
|
||||||
return render(request, "crm/leads/lead_form.html", {"form": form})
|
return render(request, "crm/leads/lead_form.html", {"form": form})
|
||||||
|
|
||||||
@ -7539,17 +7549,17 @@ class NotificationListView(LoginRequiredMixin, ListView):
|
|||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return models.Notification.objects.filter(user=self.request.user)
|
return models.Notification.objects.filter(user=self.request.user)
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
|
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
user_notifications = self.get_queryset()
|
user_notifications = self.get_queryset()
|
||||||
|
|
||||||
# Calculate the number of total, read and unread notifications
|
# Calculate the number of total, read and unread notifications
|
||||||
context['total_count'] = user_notifications.count()
|
context['total_count'] = user_notifications.count()
|
||||||
context['read_count'] = user_notifications.filter(is_read=True).count()
|
context['read_count'] = user_notifications.filter(is_read=True).count()
|
||||||
context['unread_count'] = user_notifications.filter(is_read=False).count()
|
context['unread_count'] = user_notifications.filter(is_read=False).count()
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
@ -11078,7 +11088,7 @@ def purchase_report_view(request, dealer_slug):
|
|||||||
bills = po.get_po_bill_queryset()
|
bills = po.get_po_bill_queryset()
|
||||||
vendors = set([bill.vendor.vendor_name for bill in bills])
|
vendors = set([bill.vendor.vendor_name for bill in bills])
|
||||||
vendors_str = ", ".join(sorted(list(vendors))) if vendors else "N/A"
|
vendors_str = ", ".join(sorted(list(vendors))) if vendors else "N/A"
|
||||||
|
|
||||||
data.append({
|
data.append({
|
||||||
"po_number": po.po_number,
|
"po_number": po.po_number,
|
||||||
"po_created": po.created,
|
"po_created": po.created,
|
||||||
@ -11194,7 +11204,7 @@ def car_sale_report_view(request, dealer_slug):
|
|||||||
cars_sold = cars_sold.filter(year=selected_year)
|
cars_sold = cars_sold.filter(year=selected_year)
|
||||||
if selected_stock_type:
|
if selected_stock_type:
|
||||||
cars_sold = cars_sold.filter(stock_type=selected_stock_type)
|
cars_sold = cars_sold.filter(stock_type=selected_stock_type)
|
||||||
|
|
||||||
# Corrected: Apply date filters using the 'sold_date' field
|
# Corrected: Apply date filters using the 'sold_date' field
|
||||||
if start_date_str:
|
if start_date_str:
|
||||||
try:
|
try:
|
||||||
@ -11202,7 +11212,7 @@ def car_sale_report_view(request, dealer_slug):
|
|||||||
cars_sold = cars_sold.filter(sold_date__gte=start_date)
|
cars_sold = cars_sold.filter(sold_date__gte=start_date)
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if end_date_str:
|
if end_date_str:
|
||||||
try:
|
try:
|
||||||
end_date = datetime.strptime(end_date_str, '%Y-%m-%d').date()
|
end_date = datetime.strptime(end_date_str, '%Y-%m-%d').date()
|
||||||
@ -11224,7 +11234,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_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_vat_collected = total_vat_on_cars + total_vat_from_additonals
|
||||||
total_revenue_collected = total_revenue_from_cars + total_revenue_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
|
total_discount = cars_sold.aggregate(total=Sum('discount_amount'))['total'] or 0
|
||||||
|
|
||||||
current_time = timezone.now().strftime("%Y-%m-%d %H:%M:%S")
|
current_time = timezone.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
@ -11263,7 +11273,7 @@ def car_sale_report_view(request, dealer_slug):
|
|||||||
@login_required
|
@login_required
|
||||||
def get_filtered_choices(request, dealer_slug):
|
def get_filtered_choices(request, dealer_slug):
|
||||||
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
|
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
|
||||||
|
|
||||||
# Get all filter parameters from the request
|
# Get all filter parameters from the request
|
||||||
selected_make = request.GET.get('make')
|
selected_make = request.GET.get('make')
|
||||||
selected_model = request.GET.get('model')
|
selected_model = request.GET.get('model')
|
||||||
@ -11277,10 +11287,10 @@ def get_filtered_choices(request, dealer_slug):
|
|||||||
# Apply filters based on what is selected
|
# Apply filters based on what is selected
|
||||||
if selected_make:
|
if selected_make:
|
||||||
queryset = queryset.filter(id_car_make__name=selected_make)
|
queryset = queryset.filter(id_car_make__name=selected_make)
|
||||||
|
|
||||||
if selected_model:
|
if selected_model:
|
||||||
queryset = queryset.filter(id_car_model__name=selected_model)
|
queryset = queryset.filter(id_car_model__name=selected_model)
|
||||||
|
|
||||||
if selected_serie:
|
if selected_serie:
|
||||||
queryset = queryset.filter(id_car_serie__name=selected_serie)
|
queryset = queryset.filter(id_car_serie__name=selected_serie)
|
||||||
|
|
||||||
@ -11351,7 +11361,7 @@ def car_sale_report_csv_export(request, dealer_slug):
|
|||||||
cars_sold = cars_sold.filter(year=selected_year)
|
cars_sold = cars_sold.filter(year=selected_year)
|
||||||
if selected_stock_type:
|
if selected_stock_type:
|
||||||
cars_sold = cars_sold.filter(stock_type=selected_stock_type)
|
cars_sold = cars_sold.filter(stock_type=selected_stock_type)
|
||||||
|
|
||||||
# Corrected: Apply date filters for CSV export
|
# Corrected: Apply date filters for CSV export
|
||||||
if start_date_str:
|
if start_date_str:
|
||||||
try:
|
try:
|
||||||
@ -11669,6 +11679,18 @@ class CharOfAccountModelActionView(CharOfAccountModelActionViewBase):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
#for refund policy
|
class CarDealershipSignUpView(CreateView):
|
||||||
def refund_policy(request):
|
model = models.UserRegistration
|
||||||
return render(request,'haikal_policy/refund_policy.html')
|
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
|
||||||
512
requirements.txt
@ -1,350 +1,162 @@
|
|||||||
aiohappyeyeballs
|
annotated-types==0.7.0
|
||||||
aiohttp
|
anyio==4.9.0
|
||||||
aiohttp-retry
|
arrow==1.3.0
|
||||||
aiosignal
|
asgiref==3.9.1
|
||||||
alabaster
|
attrs==25.3.0
|
||||||
albucore
|
autobahn==24.4.2
|
||||||
albumentations
|
Automat==25.4.16
|
||||||
annotated-types
|
Babel==2.15.0
|
||||||
anthropic
|
beautifulsoup4==4.13.4
|
||||||
anyio
|
blessed==1.21.0
|
||||||
arabic-reshaper
|
cattrs==25.1.1
|
||||||
argcomplete
|
certifi==2025.7.9
|
||||||
arrow
|
cffi==1.17.1
|
||||||
asgiref
|
channels==4.2.2
|
||||||
astor
|
charset-normalizer==3.4.2
|
||||||
astroid
|
click==8.2.1
|
||||||
attrs
|
colorama==0.4.6
|
||||||
autopep8
|
constantly==23.10.4
|
||||||
Babel
|
crispy-bootstrap5==2025.6
|
||||||
beautifulsoup4
|
cryptography==45.0.5
|
||||||
bleach
|
cssbeautifier==1.15.4
|
||||||
blessed
|
daphne==4.2.1
|
||||||
blinker
|
defusedxml==0.7.1
|
||||||
boto3
|
diff-match-patch==20241021
|
||||||
botocore
|
distro==1.9.0
|
||||||
Brotli
|
Django==5.2.4
|
||||||
cachetools
|
django-allauth==65.10.0
|
||||||
cattrs
|
django-appconf==1.1.0
|
||||||
certifi
|
django-appointment==3.8.0
|
||||||
cffi
|
django-background-tasks==1.2.8
|
||||||
chardet
|
django-bootstrap5==25.1
|
||||||
charset-normalizer
|
django-ckeditor==6.7.3
|
||||||
click
|
django-cors-headers==4.7.0
|
||||||
cohere
|
django-countries==7.6.1
|
||||||
colorama
|
django-crispy-forms==2.4
|
||||||
commonmark
|
django-debug-toolbar==5.2.0
|
||||||
contourpy
|
django-easy-audit==1.3.7
|
||||||
crispy-bootstrap5
|
django-encrypted-model-fields==0.6.5
|
||||||
cryptography
|
django-extensions==4.1
|
||||||
cssselect2
|
django-filter==25.1
|
||||||
ctranslate2
|
django-imagekit==5.0.0
|
||||||
cycler
|
django-import-export==4.3.8
|
||||||
Cython
|
django-js-asset==3.1.2
|
||||||
dataclasses-json
|
django-ledger==0.7.6.1
|
||||||
decorator
|
django-manager-utils==3.1.5
|
||||||
defusedxml
|
django-next-url-mixin==0.4.0
|
||||||
desert
|
django-ordered-model==3.7.4
|
||||||
diff-match-patch
|
django-phonenumber-field==8.0.0
|
||||||
dill
|
django-picklefield==3.3
|
||||||
distro
|
django-plans==2.0.0
|
||||||
dj-rest-auth
|
django-prometheus==2.4.1
|
||||||
Django
|
django-q2==1.8.0
|
||||||
django-allauth
|
django-query-builder==3.2.0
|
||||||
django-appointment
|
django-schema-graph==3.1.0
|
||||||
django-autoslug
|
django-sequences==3.0
|
||||||
django-background-tasks
|
django-tables2==2.7.5
|
||||||
django-bootstrap5
|
django-treebeard==4.7.1
|
||||||
django-ckeditor
|
django-widget-tweaks==1.5.0
|
||||||
django-classy-tags
|
djangorestframework==3.16.0
|
||||||
django-cors-headers
|
djhtml==3.0.8
|
||||||
django-countries
|
djlint==1.36.4
|
||||||
django-crispy-forms
|
dnspython==2.7.0
|
||||||
django-debug-toolbar
|
docopt==0.6.2
|
||||||
django-easy-audit
|
EditorConfig==0.17.1
|
||||||
django-extensions
|
Faker==37.4.0
|
||||||
django-filter
|
fleming==0.7.0
|
||||||
django-formtools
|
fonttools==4.58.5
|
||||||
django-import-export
|
fpdf==1.7.2
|
||||||
django-js-asset
|
fpdf2==2.8.3
|
||||||
django-ledger
|
greenlet==3.2.3
|
||||||
django-model-utils
|
gunicorn==23.0.0
|
||||||
django-money
|
h11==0.16.0
|
||||||
django-next-url-mixin
|
h2==4.2.0
|
||||||
django-nine
|
hpack==4.1.0
|
||||||
django-nonefield
|
httpcore==1.0.9
|
||||||
django-ordered-model
|
httpx==0.28.1
|
||||||
django-pdf-actions
|
hyperframe==6.1.0
|
||||||
django-phonenumber-field
|
hyperlink==21.0.0
|
||||||
django-picklefield
|
icalendar==6.3.1
|
||||||
django-plans
|
idna==3.10
|
||||||
django-prometheus
|
incremental==24.7.2
|
||||||
django-q2
|
iron-core==1.2.1
|
||||||
django-schema-graph
|
iron-mq==0.9
|
||||||
django-sekizai
|
jiter==0.10.0
|
||||||
django-sequences
|
jsbeautifier==1.15.4
|
||||||
django-silk
|
json5==0.12.0
|
||||||
django-simple-history
|
jsonpatch==1.33
|
||||||
django-sms
|
jsonpointer==3.0.0
|
||||||
django-sslserver-v2
|
jwt==1.4.0
|
||||||
django-tables2
|
langchain==0.3.26
|
||||||
django-treebeard
|
langchain-core==0.3.68
|
||||||
django-view-breadcrumbs
|
langchain-ollama==0.3.4
|
||||||
django-widget-tweaks
|
langchain-text-splitters==0.3.8
|
||||||
djangocms-admin-style
|
langsmith==0.4.4
|
||||||
djangorestframework
|
luhnchecker==0.0.12
|
||||||
djangorestframework_simplejwt
|
Markdown==3.8.2
|
||||||
djangoviz
|
markdown-it-py==3.0.0
|
||||||
djhtml
|
mdurl==0.1.2
|
||||||
docopt
|
num2words==0.5.14
|
||||||
docutils
|
numpy==2.3.1
|
||||||
easy-thumbnails
|
ofxtools==0.9.5
|
||||||
emoji
|
ollama==0.5.1
|
||||||
et_xmlfile
|
openai==1.93.3
|
||||||
eval_type_backport
|
opencv-python==4.11.0.86
|
||||||
executing
|
orjson==3.10.18
|
||||||
Faker
|
packaging==24.2
|
||||||
fasta2a
|
pandas==2.3.1
|
||||||
fastavro
|
pathspec==0.12.1
|
||||||
filelock
|
phonenumbers==8.13.42
|
||||||
fire
|
pilkit==3.0
|
||||||
fonttools
|
pillow==10.4.0
|
||||||
fpdf
|
priority==1.3.0
|
||||||
fpdf2
|
prometheus_client==0.22.1
|
||||||
frozenlist
|
psycopg2-binary==2.9.10
|
||||||
fsspec
|
pyasn1==0.6.1
|
||||||
google-auth
|
pyasn1_modules==0.4.2
|
||||||
google-genai
|
pycparser==2.22
|
||||||
googleapis-common-protos
|
pydantic==2.11.7
|
||||||
gprof2dot
|
pydantic_core==2.33.2
|
||||||
graphqlclient
|
Pygments==2.19.2
|
||||||
greenlet
|
pymongo==4.14.1
|
||||||
griffe
|
pyOpenSSL==25.1.0
|
||||||
groq
|
python-dateutil==2.9.0.post0
|
||||||
h11
|
python-dotenv==1.1.1
|
||||||
h2
|
python-slugify==8.0.4
|
||||||
hf-xet
|
python-stdnum==2.1
|
||||||
hpack
|
pytz==2025.2
|
||||||
hstspreload
|
pyvin==0.0.2
|
||||||
httpcore
|
PyYAML==6.0.2
|
||||||
httpx
|
pyzbar==0.1.9
|
||||||
httpx-sse
|
redis==6.2.0
|
||||||
huggingface-hub
|
regex==2024.11.6
|
||||||
hyperframe
|
requests==2.32.4
|
||||||
icalendar
|
requests-toolbelt==1.0.0
|
||||||
idna
|
rich==14.0.0
|
||||||
imageio
|
ruff==0.12.2
|
||||||
imagesize
|
service-identity==24.2.0
|
||||||
imgaug
|
setuptools==80.9.0
|
||||||
importlib_metadata
|
six==1.17.0
|
||||||
iso4217
|
sniffio==1.3.1
|
||||||
isodate
|
soupsieve==2.7
|
||||||
isort
|
SQLAlchemy==2.0.41
|
||||||
itsdangerous
|
sqlparse==0.5.3
|
||||||
Jinja2
|
suds==1.2.0
|
||||||
jiter
|
swapper==1.3.0
|
||||||
jmespath
|
tablib==3.8.0
|
||||||
joblib
|
tenacity==9.1.2
|
||||||
jsonpatch
|
text-unidecode==1.3
|
||||||
jsonpointer
|
tqdm==4.67.1
|
||||||
jwt
|
Twisted==25.5.0
|
||||||
kiwisolver
|
txaio==25.6.1
|
||||||
langchain
|
types-python-dateutil==2.9.0.20250708
|
||||||
langchain-community
|
typing-inspection==0.4.1
|
||||||
langchain-core
|
typing_extensions==4.14.1
|
||||||
langchain-ollama
|
tzdata==2025.2
|
||||||
langchain-text-splitters
|
urllib3==2.5.0
|
||||||
langsmith
|
uvicorn==0.35.0
|
||||||
lazy_loader
|
uvicorn-worker==0.3.0
|
||||||
ledger
|
wcwidth==0.2.13
|
||||||
libretranslatepy
|
whitenoise==6.9.0
|
||||||
lmdb
|
zope.interface==7.2
|
||||||
logfire
|
zstandard==0.23.0
|
||||||
logfire-api
|
|
||||||
luhnchecker
|
|
||||||
lxml
|
|
||||||
Markdown
|
|
||||||
markdown-it-py
|
|
||||||
MarkupSafe
|
|
||||||
marshmallow
|
|
||||||
matplotlib
|
|
||||||
mccabe
|
|
||||||
mcp
|
|
||||||
mdurl
|
|
||||||
mistralai
|
|
||||||
MouseInfo
|
|
||||||
mpmath
|
|
||||||
multidict
|
|
||||||
mypy_extensions
|
|
||||||
networkx
|
|
||||||
newrelic
|
|
||||||
nltk
|
|
||||||
num2words
|
|
||||||
numpy
|
|
||||||
oauthlib
|
|
||||||
ofxtools
|
|
||||||
ollama
|
|
||||||
openai
|
|
||||||
opencv-contrib-python
|
|
||||||
opencv-python
|
|
||||||
opencv-python-headless
|
|
||||||
openpyxl
|
|
||||||
opentelemetry-api
|
|
||||||
opentelemetry-exporter-otlp-proto-common
|
|
||||||
opentelemetry-exporter-otlp-proto-http
|
|
||||||
opentelemetry-instrumentation
|
|
||||||
opentelemetry-proto
|
|
||||||
opentelemetry-sdk
|
|
||||||
opentelemetry-semantic-conventions
|
|
||||||
opt_einsum
|
|
||||||
orjson
|
|
||||||
outcome
|
|
||||||
packaging
|
|
||||||
pandas
|
|
||||||
pango
|
|
||||||
pdfkit
|
|
||||||
phonenumbers
|
|
||||||
pillow
|
|
||||||
platformdirs
|
|
||||||
prometheus_client
|
|
||||||
prompt_toolkit
|
|
||||||
propcache
|
|
||||||
protobuf
|
|
||||||
psycopg
|
|
||||||
psycopg-binary
|
|
||||||
psycopg-c
|
|
||||||
psycopg2-binary
|
|
||||||
py-moneyed
|
|
||||||
pyasn1
|
|
||||||
pyasn1_modules
|
|
||||||
PyAutoGUI
|
|
||||||
pyclipper
|
|
||||||
pycodestyle
|
|
||||||
pycparser
|
|
||||||
pydantic
|
|
||||||
pydantic-ai
|
|
||||||
pydantic-ai-slim
|
|
||||||
pydantic-evals
|
|
||||||
pydantic-graph
|
|
||||||
pydantic-settings
|
|
||||||
pydantic_core
|
|
||||||
pydotplus
|
|
||||||
pydyf
|
|
||||||
PyGetWindow
|
|
||||||
Pygments
|
|
||||||
PyJWT
|
|
||||||
pylint
|
|
||||||
PyMsgBox
|
|
||||||
PyMySQL
|
|
||||||
pyobjc-core
|
|
||||||
pyobjc-framework-Cocoa
|
|
||||||
pyobjc-framework-Quartz
|
|
||||||
pyparsing
|
|
||||||
pypdf
|
|
||||||
pyperclip
|
|
||||||
pyphen
|
|
||||||
pypng
|
|
||||||
PyRect
|
|
||||||
PyScreeze
|
|
||||||
pyserial
|
|
||||||
PySocks
|
|
||||||
python-bidi
|
|
||||||
python-dateutil
|
|
||||||
python-docx
|
|
||||||
python-dotenv
|
|
||||||
python-multipart
|
|
||||||
python-openid
|
|
||||||
python-slugify
|
|
||||||
python-stdnum
|
|
||||||
python3-saml
|
|
||||||
pytweening
|
|
||||||
pytz
|
|
||||||
pyvin
|
|
||||||
pywa
|
|
||||||
pywhat
|
|
||||||
pywhatkit
|
|
||||||
PyYAML
|
|
||||||
pyzbar
|
|
||||||
qrcode
|
|
||||||
RapidFuzz
|
|
||||||
redis
|
|
||||||
regex
|
|
||||||
reportlab
|
|
||||||
requests
|
|
||||||
requests-oauthlib
|
|
||||||
requests-toolbelt
|
|
||||||
rfc3986
|
|
||||||
rich
|
|
||||||
rsa
|
|
||||||
rubicon-objc
|
|
||||||
s3transfer
|
|
||||||
sacremoses
|
|
||||||
safetensors
|
|
||||||
scikit-image
|
|
||||||
scikit-learn
|
|
||||||
scipy
|
|
||||||
selenium
|
|
||||||
sentence-transformers
|
|
||||||
sentencepiece
|
|
||||||
shapely
|
|
||||||
simsimd
|
|
||||||
six
|
|
||||||
slugify
|
|
||||||
sniffio
|
|
||||||
snowballstemmer
|
|
||||||
sortedcontainers
|
|
||||||
soupsieve
|
|
||||||
SQLAlchemy
|
|
||||||
sqlparse
|
|
||||||
sse-starlette
|
|
||||||
stanza
|
|
||||||
starlette
|
|
||||||
stringzilla
|
|
||||||
suds
|
|
||||||
swapper
|
|
||||||
sympy
|
|
||||||
tablib
|
|
||||||
tenacity
|
|
||||||
termcolor
|
|
||||||
text-unidecode
|
|
||||||
threadpoolctl
|
|
||||||
tifffile
|
|
||||||
tinycss2
|
|
||||||
tinyhtml5
|
|
||||||
tokenizers
|
|
||||||
tomli
|
|
||||||
tomlkit
|
|
||||||
torch
|
|
||||||
tqdm
|
|
||||||
transformers
|
|
||||||
trio
|
|
||||||
trio-websocket
|
|
||||||
twilio
|
|
||||||
types-python-dateutil
|
|
||||||
types-requests
|
|
||||||
typing-inspect
|
|
||||||
typing-inspection
|
|
||||||
typing_extensions
|
|
||||||
tzdata
|
|
||||||
Unidecode
|
|
||||||
upgrade-requirements
|
|
||||||
urllib3
|
|
||||||
uvicorn
|
|
||||||
vin
|
|
||||||
vininfo
|
|
||||||
vishap
|
|
||||||
vpic-api
|
|
||||||
wcwidth
|
|
||||||
weasyprint
|
|
||||||
webencodings
|
|
||||||
websocket-client
|
|
||||||
websockets
|
|
||||||
Werkzeug
|
|
||||||
wikipedia
|
|
||||||
wrapt
|
|
||||||
wsproto
|
|
||||||
xmlsec
|
|
||||||
yarl
|
|
||||||
zipp
|
|
||||||
zopfli
|
|
||||||
zstandard
|
|
||||||
|
|||||||
161
requirements_prod.txt
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
annotated-types==0.7.0
|
||||||
|
anyio==4.9.0
|
||||||
|
arrow==1.3.0
|
||||||
|
asgiref==3.9.1
|
||||||
|
attrs==25.3.0
|
||||||
|
autobahn==24.4.2
|
||||||
|
Automat==25.4.16
|
||||||
|
Babel==2.15.0
|
||||||
|
beautifulsoup4==4.13.4
|
||||||
|
blessed==1.21.0
|
||||||
|
cattrs==25.1.1
|
||||||
|
certifi==2025.7.9
|
||||||
|
cffi==1.17.1
|
||||||
|
channels==4.2.2
|
||||||
|
charset-normalizer==3.4.2
|
||||||
|
click==8.2.1
|
||||||
|
colorama==0.4.6
|
||||||
|
constantly==23.10.4
|
||||||
|
crispy-bootstrap5==2025.6
|
||||||
|
cryptography==45.0.5
|
||||||
|
cssbeautifier==1.15.4
|
||||||
|
daphne==4.2.1
|
||||||
|
defusedxml==0.7.1
|
||||||
|
diff-match-patch==20241021
|
||||||
|
distro==1.9.0
|
||||||
|
Django==5.2.4
|
||||||
|
django-allauth==65.10.0
|
||||||
|
django-appconf==1.1.0
|
||||||
|
django-appointment==3.8.0
|
||||||
|
django-background-tasks==1.2.8
|
||||||
|
django-bootstrap5==25.1
|
||||||
|
django-ckeditor==6.7.3
|
||||||
|
django-cors-headers==4.7.0
|
||||||
|
django-countries==7.6.1
|
||||||
|
django-crispy-forms==2.4
|
||||||
|
django-debug-toolbar==5.2.0
|
||||||
|
django-easy-audit==1.3.7
|
||||||
|
django-extensions==4.1
|
||||||
|
django-filter==25.1
|
||||||
|
django-imagekit==5.0.0
|
||||||
|
django-import-export==4.3.8
|
||||||
|
django-js-asset==3.1.2
|
||||||
|
django-ledger==0.7.6.1
|
||||||
|
django-manager-utils==3.1.5
|
||||||
|
django-next-url-mixin==0.4.0
|
||||||
|
django-ordered-model==3.7.4
|
||||||
|
django-phonenumber-field==8.0.0
|
||||||
|
django-picklefield==3.3
|
||||||
|
django-plans==2.0.0
|
||||||
|
django-prometheus==2.4.1
|
||||||
|
django-q2==1.8.0
|
||||||
|
django-query-builder==3.2.0
|
||||||
|
django-schema-graph==3.1.0
|
||||||
|
django-sequences==3.0
|
||||||
|
django-tables2==2.7.5
|
||||||
|
django-treebeard==4.7.1
|
||||||
|
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
|
||||||
|
fleming==0.7.0
|
||||||
|
fonttools==4.58.5
|
||||||
|
fpdf==1.7.2
|
||||||
|
fpdf2==2.8.3
|
||||||
|
greenlet==3.2.3
|
||||||
|
gunicorn==23.0.0
|
||||||
|
h11==0.16.0
|
||||||
|
h2==4.2.0
|
||||||
|
hpack==4.1.0
|
||||||
|
httpcore==1.0.9
|
||||||
|
httpx==0.28.1
|
||||||
|
hyperframe==6.1.0
|
||||||
|
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
|
||||||
|
jsonpatch==1.33
|
||||||
|
jsonpointer==3.0.0
|
||||||
|
jwt==1.4.0
|
||||||
|
langchain==0.3.26
|
||||||
|
langchain-core==0.3.68
|
||||||
|
langchain-ollama==0.3.4
|
||||||
|
langchain-text-splitters==0.3.8
|
||||||
|
langsmith==0.4.4
|
||||||
|
luhnchecker==0.0.12
|
||||||
|
Markdown==3.8.2
|
||||||
|
markdown-it-py==3.0.0
|
||||||
|
mdurl==0.1.2
|
||||||
|
num2words==0.5.14
|
||||||
|
numpy==2.3.1
|
||||||
|
ofxtools==0.9.5
|
||||||
|
ollama==0.5.1
|
||||||
|
openai==1.93.3
|
||||||
|
opencv-python==4.11.0.86
|
||||||
|
orjson==3.10.18
|
||||||
|
packaging==24.2
|
||||||
|
pandas==2.3.1
|
||||||
|
pathspec==0.12.1
|
||||||
|
phonenumbers==8.13.42
|
||||||
|
pilkit==3.0
|
||||||
|
pillow==10.4.0
|
||||||
|
priority==1.3.0
|
||||||
|
prometheus_client==0.22.1
|
||||||
|
psycopg2-binary==2.9.10
|
||||||
|
pyasn1==0.6.1
|
||||||
|
pyasn1_modules==0.4.2
|
||||||
|
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
|
||||||
|
python-slugify==8.0.4
|
||||||
|
python-stdnum==2.1
|
||||||
|
pytz==2025.2
|
||||||
|
pyvin==0.0.2
|
||||||
|
PyYAML==6.0.2
|
||||||
|
pyzbar==0.1.9
|
||||||
|
redis==6.2.0
|
||||||
|
regex==2024.11.6
|
||||||
|
requests==2.32.4
|
||||||
|
requests-toolbelt==1.0.0
|
||||||
|
rich==14.0.0
|
||||||
|
ruff==0.12.2
|
||||||
|
service-identity==24.2.0
|
||||||
|
setuptools==80.9.0
|
||||||
|
six==1.17.0
|
||||||
|
sniffio==1.3.1
|
||||||
|
soupsieve==2.7
|
||||||
|
SQLAlchemy==2.0.41
|
||||||
|
sqlparse==0.5.3
|
||||||
|
suds==1.2.0
|
||||||
|
swapper==1.3.0
|
||||||
|
tablib==3.8.0
|
||||||
|
tenacity==9.1.2
|
||||||
|
text-unidecode==1.3
|
||||||
|
tqdm==4.67.1
|
||||||
|
Twisted==25.5.0
|
||||||
|
txaio==25.6.1
|
||||||
|
types-python-dateutil==2.9.0.20250708
|
||||||
|
typing-inspection==0.4.1
|
||||||
|
typing_extensions==4.14.1
|
||||||
|
tzdata==2025.2
|
||||||
|
urllib3==2.5.0
|
||||||
|
uvicorn==0.35.0
|
||||||
|
uvicorn-worker==0.3.0
|
||||||
|
wcwidth==0.2.13
|
||||||
|
whitenoise==6.9.0
|
||||||
|
zope.interface==7.2
|
||||||
|
zstandard==0.23.0
|
||||||
|
After Width: | Height: | Size: 390 KiB |
|
After Width: | Height: | Size: 368 KiB |
|
After Width: | Height: | Size: 415 KiB |
|
After Width: | Height: | Size: 390 KiB |
|
After Width: | Height: | Size: 415 KiB |
|
After Width: | Height: | Size: 420 KiB |
|
After Width: | Height: | Size: 429 KiB |
|
After Width: | Height: | Size: 493 KiB |
|
After Width: | Height: | Size: 412 KiB |
|
After Width: | Height: | Size: 470 KiB |
|
After Width: | Height: | Size: 418 KiB |
|
After Width: | Height: | Size: 364 KiB |
|
After Width: | Height: | Size: 388 KiB |
|
After Width: | Height: | Size: 406 KiB |
|
After Width: | Height: | Size: 380 KiB |
|
After Width: | Height: | Size: 423 KiB |
|
After Width: | Height: | Size: 353 KiB |
|
After Width: | Height: | Size: 393 KiB |
|
After Width: | Height: | Size: 450 KiB |
|
After Width: | Height: | Size: 374 KiB |
|
After Width: | Height: | Size: 4.4 MiB |
|
After Width: | Height: | Size: 4.4 MiB |
@ -77,7 +77,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
{% include 'footer.html' %}
|
|
||||||
{% if LOGIN_BY_CODE_ENABLED or PASSKEY_LOGIN_ENABLED %}
|
{% if LOGIN_BY_CODE_ENABLED or PASSKEY_LOGIN_ENABLED %}
|
||||||
<hr>
|
<hr>
|
||||||
{% element button_group vertical=True %}
|
{% element button_group vertical=True %}
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
{% extends "welcome_base.html" %}
|
{% extends "welcome_base.html" %}
|
||||||
{% load crispy_forms_filters %}
|
{% load crispy_forms_filters %}
|
||||||
{% load i18n static %}
|
{% load i18n static %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<section class="main my-2">
|
<section class="main my-4">
|
||||||
<div class="container" style="max-width:60rem;">
|
<div class="container" style="max-width:60rem;">
|
||||||
<div class="row form-container" id="form-container">
|
<div class="row form-container" id="form-container">
|
||||||
<div class="col-12 ">
|
<div class="col-12 ">
|
||||||
@ -20,292 +21,49 @@
|
|||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<h3 class="text-body-highlight">{% trans 'Sign Up' %}</h3>
|
<h3 class="text-body-highlight">{% trans 'Car Dealership Registration' %}</h3>
|
||||||
<p class="text-body-tertiary fs-9">{% trans 'Create your account today' %}</p>
|
<p class="text-body-tertiary fs-9">{% trans 'Create your dealership account today' %}</p>
|
||||||
</div>
|
</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"
|
<form method="post" action="{% url 'account_signup' %}" class="needs-validation">
|
||||||
data-theme-wizard="data-theme-wizard">
|
{% csrf_token %}
|
||||||
<div class="card-header pt-3 pb-2 ">
|
<div class="card theme-wizard">
|
||||||
<ul class="nav justify-content-between nav-wizard nav-wizard-success"
|
<div class="card-body pt-4 pb-0">
|
||||||
role="tablist">
|
{{ form|crispy }}
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
|
<button class="btn btn-primary" type="submit">{% trans 'Sign Up' %}</button>
|
||||||
</div>
|
</div>
|
||||||
<div data-computed-form1_valid="validatePassword($form1.password,$form1.confirm_password) && validateEmail($form1.email)"
|
</form>
|
||||||
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>
|
</div>
|
||||||
<div class="mx-auto mt-4 text-center">
|
<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' %}">
|
<button type="button" class="btn btn-lg btn-primary" data-bs-toggle="modal" data-bs-target="#exampleModal">
|
||||||
Our Refund Policy
|
{% trans "Our Refund Policy" %}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<div class="modal fade" id="contentModal" tabindex="-1" aria-labelledby="contentModalLabel" aria-hidden="true">
|
<div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-xl modal-dialog-centered">
|
<div class="modal-dialog modal-xl">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title" id="contentModalLabel">Refund Policy</h5>
|
<h5 class="modal-title" id="exampleModalLabel">{% trans "Our Refund Policy" %}</h5>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p>Loading...</p>
|
{% include 'haikal_policy/refund_policy.html' %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% include 'footer.html' %}
|
|
||||||
<script src="{% static 'js/phoenix.js' %}"></script>
|
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|
||||||
{% block customJS %}
|
{% block customJS %}
|
||||||
|
<script src="{% static 'js/phoenix.js' %}"></script>
|
||||||
<script src="{% static 'js/main.js' %}"></script>
|
<script src="{% static 'js/main.js' %}"></script>
|
||||||
<script src="{% static 'js/sweetalert2.all.min.js' %}"></script>
|
<script src="{% static 'js/sweetalert2.all.min.js' %}"></script>
|
||||||
<script type="module"
|
<script type="module"
|
||||||
@ -406,37 +164,5 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//modal for policy
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
const contentModal = document.getElementById('contentModal');
|
|
||||||
if (contentModal) {
|
|
||||||
contentModal.addEventListener('show.bs.modal', function(event) {
|
|
||||||
// Get the button that triggered the modal
|
|
||||||
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');
|
|
||||||
|
|
||||||
// Use the Fetch API to load content
|
|
||||||
fetch(url)
|
|
||||||
.then(response => {
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error('Network response was not ok');
|
|
||||||
}
|
|
||||||
return response.text();
|
|
||||||
})
|
|
||||||
.then(html => {
|
|
||||||
// Insert the HTML content into the modal body
|
|
||||||
modalBody.innerHTML = html;
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
modalBody.innerHTML = `<p class="text-danger">Failed to load content: ${error.message}</p>`;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
{% endblock customJS %}
|
{% endblock customJS %}
|
||||||
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 %}
|
||||||
@ -282,7 +282,7 @@
|
|||||||
type="button"
|
type="button"
|
||||||
data-bs-toggle="modal"
|
data-bs-toggle="modal"
|
||||||
data-bs-target="#exampleModal">
|
data-bs-target="#exampleModal">
|
||||||
<i class="fa-solid fa-user-plus me-2"></i> Reassign Lead
|
<i class="fa-solid fa-user-plus me-2"></i> {%trans "Reassign Lead"%}
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<button class="btn btn-phoenix-primary btn-sm"
|
<button class="btn btn-phoenix-primary btn-sm"
|
||||||
@ -301,7 +301,7 @@
|
|||||||
method="post">
|
method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title" id="exampleModalLabel">Reassign Lead To Another Employee</h5>
|
<h5 class="modal-title" id="exampleModalLabel">{%trans "Reassign Lead To Another Employee"%}</h5>
|
||||||
<button class="btn btn-close p-1"
|
<button class="btn btn-close p-1"
|
||||||
type="button"
|
type="button"
|
||||||
data-bs-dismiss="modal"
|
data-bs-dismiss="modal"
|
||||||
@ -581,25 +581,25 @@
|
|||||||
scope="col"
|
scope="col"
|
||||||
data-sort="subject"
|
data-sort="subject"
|
||||||
style="width:31%;
|
style="width:31%;
|
||||||
min-width:350px">Subject</th>
|
min-width:350px">{{ _("Subject") }}</th>
|
||||||
<th class="sort align-middle pe-3 text-uppercase"
|
<th class="sort align-middle pe-3 text-uppercase"
|
||||||
scope="col"
|
scope="col"
|
||||||
data-sort="sent"
|
data-sort="sent"
|
||||||
style="width:15%;
|
style="width:15%;
|
||||||
min-width:130px">Sent by</th>
|
min-width:130px">{{ _("Sent by") }}</th>
|
||||||
<th class="sort align-middle text-start text-uppercase"
|
<th class="sort align-middle text-start text-uppercase"
|
||||||
scope="col"
|
scope="col"
|
||||||
data-sort="date"
|
data-sort="date"
|
||||||
style="min-width:165px">Date</th>
|
style="min-width:165px">{{ _("Date") }}</th>
|
||||||
<th class="sort align-middle pe-0 text-uppercase"
|
<th class="sort align-middle pe-0 text-uppercase"
|
||||||
scope="col"
|
scope="col"
|
||||||
style="width:15%;
|
style="width:15%;
|
||||||
min-width:100px">Action</th>
|
min-width:100px">{{ _("Action") }}</th>
|
||||||
<th class="sort align-middle text-end text-uppercase"
|
<th class="sort align-middle text-end text-uppercase"
|
||||||
scope="col"
|
scope="col"
|
||||||
data-sort="status"
|
data-sort="status"
|
||||||
style="width:15%;
|
style="width:15%;
|
||||||
min-width:100px">Status</th>
|
min-width:100px">{{ _("Status") }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="list" id="all-email-table-body">
|
<tbody class="list" id="all-email-table-body">
|
||||||
@ -619,10 +619,10 @@
|
|||||||
<td class="sent align-middle white-space-nowrap text-start fw-bold text-body-tertiary py-2">{{ email.from_email }}</td>
|
<td class="sent align-middle white-space-nowrap text-start fw-bold text-body-tertiary py-2">{{ email.from_email }}</td>
|
||||||
<td class="date align-middle white-space-nowrap text-body py-2">{{ email.created|naturalday }}</td>
|
<td class="date align-middle white-space-nowrap text-body py-2">{{ email.created|naturalday }}</td>
|
||||||
<td class="align-middle white-space-nowrap ps-3">
|
<td class="align-middle white-space-nowrap ps-3">
|
||||||
<a class="text-body" href=""><span class="fa-solid fa-phone text-primary me-2"></span>Call</a>
|
<a class="text-body" href=""><span class="fa-solid fa-phone text-primary me-2"></span>{%trans "Call"%</a>
|
||||||
</td>
|
</td>
|
||||||
<td class="status align-middle fw-semibold text-end py-2">
|
<td class="status align-middle fw-semibold text-end py-2">
|
||||||
<span class="badge badge-phoenix fs-10 badge-phoenix-success">sent</span>
|
<span class="badge badge-phoenix fs-10 badge-phoenix-success">{%trans "sent"%}</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@ -670,25 +670,25 @@
|
|||||||
scope="col"
|
scope="col"
|
||||||
data-sort="subject"
|
data-sort="subject"
|
||||||
style="width:31%;
|
style="width:31%;
|
||||||
min-width:350px">Subject</th>
|
min-width:350px">{% trans "Subject" %}</th>
|
||||||
<th class="sort align-middle pe-3 text-uppercase"
|
<th class="sort align-middle pe-3 text-uppercase"
|
||||||
scope="col"
|
scope="col"
|
||||||
data-sort="sent"
|
data-sort="sent"
|
||||||
style="width:15%;
|
style="width:15%;
|
||||||
min-width:130px">Sent by</th>
|
min-width:130px">{% trans "Sent by" %}</th>
|
||||||
<th class="sort align-middle text-start text-uppercase"
|
<th class="sort align-middle text-start text-uppercase"
|
||||||
scope="col"
|
scope="col"
|
||||||
data-sort="date"
|
data-sort="date"
|
||||||
style="min-width:165px">Date</th>
|
style="min-width:165px">{% trans "Date" %}</th>
|
||||||
<th class="sort align-middle pe-0 text-uppercase"
|
<th class="sort align-middle pe-0 text-uppercase"
|
||||||
scope="col"
|
scope="col"
|
||||||
style="width:15%;
|
style="width:15%;
|
||||||
min-width:100px">Action</th>
|
min-width:100px">{% trans "Action" %}</th>
|
||||||
<th class="sort align-middle text-end text-uppercase"
|
<th class="sort align-middle text-end text-uppercase"
|
||||||
scope="col"
|
scope="col"
|
||||||
data-sort="status"
|
data-sort="status"
|
||||||
style="width:15%;
|
style="width:15%;
|
||||||
min-width:100px">Status</th>
|
min-width:100px">{% trans "Status" %}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="list" id="drafts-email-table-body">
|
<tbody class="list" id="drafts-email-table-body">
|
||||||
@ -777,20 +777,20 @@
|
|||||||
scope="col"
|
scope="col"
|
||||||
data-sort="subject"
|
data-sort="subject"
|
||||||
style="width:31%;
|
style="width:31%;
|
||||||
min-width:100px">Name</th>
|
min-width:100px">{%trans "Name"%}</th>
|
||||||
<th class="sort align-middle pe-3 text"
|
<th class="sort align-middle pe-3 text"
|
||||||
scope="col"
|
scope="col"
|
||||||
data-sort="sent"
|
data-sort="sent"
|
||||||
style="width:15%;
|
style="width:15%;
|
||||||
min-width:400px">Note</th>
|
min-width:400px">{%trans "Note"%}</th>
|
||||||
<th class="sort align-middle text-start"
|
<th class="sort align-middle text-start"
|
||||||
scope="col"
|
scope="col"
|
||||||
data-sort="date"
|
data-sort="date"
|
||||||
style="min-width:100px">Due Date</th>
|
style="min-width:100px">{%trans "Due Date"%}</th>
|
||||||
<th class="sort align-middle text-start"
|
<th class="sort align-middle text-start"
|
||||||
scope="col"
|
scope="col"
|
||||||
data-sort="date"
|
data-sort="date"
|
||||||
style="min-width:100px">Completed</th>
|
style="min-width:100px">{%trans "Completed"%}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="list taskTable" id="all-tasks-table-body">
|
<tbody class="list taskTable" id="all-tasks-table-body">
|
||||||
@ -802,9 +802,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row align-items-center justify-content-between py-2 pe-0 fs-9 mt-3">
|
<div class="row align-items-center justify-content-between py-2 pe-0 fs-9 mt-3">
|
||||||
<div class="col-auto d-flex">
|
<div class="col-auto d-flex">
|
||||||
<a class="nav-link px-3 d-block"
|
|
||||||
href="{% url 'appointment:get_user_appointments' %}"> <span class="me-2 text-body align-bottom" data-feather="calendar"></span>{{ _("View in Calendar") }}
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -686,7 +686,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tab-pane fade"
|
<div class="tab-pane fade"
|
||||||
id="tab-emails"
|
id="tab-emails"
|
||||||
role="tabpanel"
|
role="tabpanel"
|
||||||
@ -694,7 +694,7 @@
|
|||||||
<h2 class="mb-4">Emails</h2>
|
<h2 class="mb-4">Emails</h2>
|
||||||
{% if perms.inventory.change_opportunity %}
|
{% if perms.inventory.change_opportunity %}
|
||||||
<div class="d-flex justify-content-end">
|
<div class="d-flex justify-content-end">
|
||||||
|
|
||||||
{% if opportunity.lead %}
|
{% if opportunity.lead %}
|
||||||
<button class="btn btn-phoenix-primary btn-sm"
|
<button class="btn btn-phoenix-primary btn-sm"
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@ -62,7 +62,7 @@
|
|||||||
{% if form.stage.errors %}<div class="invalid-feedback d-block">{{ form.stage.errors }}</div>{% endif %}
|
{% if form.stage.errors %}<div class="invalid-feedback d-block">{{ form.stage.errors }}</div>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<!-- Amount Field -->
|
<!-- Amount Field -->
|
||||||
<div class="mb-4">
|
{% comment %} <div class="mb-4">
|
||||||
<label class="form-label" for="{{ form.amount.id_for_label }}">
|
<label class="form-label" for="{{ form.amount.id_for_label }}">
|
||||||
{{ form.amount.label }}
|
{{ form.amount.label }}
|
||||||
<span class="text-danger">*</span>
|
<span class="text-danger">*</span>
|
||||||
@ -72,9 +72,9 @@
|
|||||||
{{ form.amount|add_class:"form-control" }}
|
{{ form.amount|add_class:"form-control" }}
|
||||||
</div>
|
</div>
|
||||||
{% if form.amount.errors %}<div class="invalid-feedback d-block">{{ form.amount.errors }}</div>{% endif %}
|
{% if form.amount.errors %}<div class="invalid-feedback d-block">{{ form.amount.errors }}</div>{% endif %}
|
||||||
</div>
|
</div> {% endcomment %}
|
||||||
<!-- Probability Field -->
|
<!-- Probability Field -->
|
||||||
<div class="mb-4">
|
{% comment %} <div class="mb-4">
|
||||||
<label class="form-label" for="{{ form.probability.id_for_label }}">
|
<label class="form-label" for="{{ form.probability.id_for_label }}">
|
||||||
{{ form.probability.label }}
|
{{ form.probability.label }}
|
||||||
<span class="text-danger">*</span>
|
<span class="text-danger">*</span>
|
||||||
@ -106,10 +106,10 @@
|
|||||||
{% if form.expected_revenue.errors %}
|
{% if form.expected_revenue.errors %}
|
||||||
<div class="invalid-feedback d-block">{{ form.expected_revenue.errors }}</div>
|
<div class="invalid-feedback d-block">{{ form.expected_revenue.errors }}</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div> {% endcomment %}
|
||||||
<!-- Closing Date -->
|
<!-- Closing Date -->
|
||||||
<div class="mb-5">
|
<div class="mb-5">
|
||||||
<label class="form-label" for="{{ form.closing_date.id_for_label }}">{{ form.closing_date.label }}</label>
|
<label class="form-label" for="{{ form.expected_close_date.id_for_label }}">{{ form.expected_close_date.label }}</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
{{ form.expected_close_date|add_class:"form-control" }}
|
{{ form.expected_close_date|add_class:"form-control" }}
|
||||||
<span class="input-group-text"><span class="far fa-calendar"></span></span>
|
<span class="input-group-text"><span class="far fa-calendar"></span></span>
|
||||||
|
|||||||
@ -75,7 +75,7 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container mt-4">
|
<div class="container mt-4">
|
||||||
<h2>
|
<h2>
|
||||||
Upload Cars CSV <i class="fa-solid fa-file-csv text-primary"></i>
|
{% trans 'Upload Cars CSV' %} <i class="fa-solid fa-file-csv text-primary"></i>
|
||||||
</h2>
|
</h2>
|
||||||
<div class="d-flex justify-content-end">
|
<div class="d-flex justify-content-end">
|
||||||
<a href="{% static 'sample/cars_sample.csv' %}"
|
<a href="{% static 'sample/cars_sample.csv' %}"
|
||||||
|
|||||||
@ -1,16 +1,4 @@
|
|||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>{% trans "Haikal Refund Policy" %}</title>
|
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&display=swap" rel="stylesheet">
|
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div class="policy-container m-4">
|
<div class="policy-container m-4">
|
||||||
<div class="policy-header">
|
<div class="policy-header">
|
||||||
@ -80,7 +68,3 @@
|
|||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@ -488,30 +488,15 @@
|
|||||||
{% if request.user.is_authenticated %}
|
{% if request.user.is_authenticated %}
|
||||||
<div class="navbar-logo">
|
<div class="navbar-logo">
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
{% with name_to_display=request.user.staff.first_name|default:request.dealer.name %}
|
{% with name_to_display=request.user.first_name|default:request.dealer.name %}
|
||||||
<h6 class="text-gray-600 ms-2 d-none d-sm-block fs-8"
|
<h6 class="text-gray-600 ms-2 d-none d-sm-block fs-8"
|
||||||
data-bs-toggle="tooltip"
|
data-bs-toggle="tooltip"
|
||||||
data-bs-placement="bottom"
|
data-bs-placement="bottom"
|
||||||
title="{% trans 'Logged in as ' %}{{ request.user.username }}">
|
title="{% trans 'Logged in as ' %}{{ request.user.username }}">
|
||||||
{% trans 'Hello, ' %}{{ name_to_display }}
|
{% trans 'Hello, ' %}{{ name_to_display }}
|
||||||
</h6>
|
</h6>
|
||||||
|
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="navbar-logo ">
|
|
||||||
|
|
||||||
<div class="d-flex align-items-center">
|
|
||||||
|
|
||||||
<small class="text-gray-600 ms-2 d-none d-sm-block fs-9"
|
|
||||||
data-bs-toggle="tooltip"
|
|
||||||
data-bs-placement="bottom"
|
|
||||||
>
|
|
||||||
{% now "l, F j, Y g:i A" %}
|
|
||||||
</small>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<ul class="navbar-nav navbar-nav-icons flex-row gap-2" hx-boost="false">
|
<ul class="navbar-nav navbar-nav-icons flex-row gap-2" hx-boost="false">
|
||||||
@ -665,19 +650,19 @@
|
|||||||
</hr>
|
</hr>
|
||||||
<div class="px-3">
|
<div class="px-3">
|
||||||
<a class="btn btn-sm btn-phoenix-danger d-flex flex-center w-100"
|
<a class="btn btn-sm btn-phoenix-danger d-flex flex-center w-100"
|
||||||
href="{% url 'account_logout' %}"> <i class="fa-solid fa-right-from-bracket me-2"></i></span>{% trans 'Sign Out' %}</a>
|
href="{% url 'account_logout' %}"> <span class="fas fa-power-off me-2"></span>{% trans 'Sign Out' %}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="my-2 text-center fw-bold fs-10 text-body-quaternary">
|
<div class="my-2 text-center fw-bold fs-10 text-body-quaternary">
|
||||||
<a class="text-body-quaternary me-1" href="">{% trans 'Privacy policy' %}</a>•<a class="text-body-quaternary mx-1" href="">{% trans 'Terms' %}</a>•<a class="text-body-quaternary ms-1" href="">{% trans "Cookies" %}</a>
|
<a class="text-body-quaternary me-1" href="">{% trans 'Privacy policy' %}</a>•<a class="text-body-quaternary mx-1" href="">{% trans 'Terms' %}</a>•<a class="text-body-quaternary ms-1" href="">Cookies</a>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="px-3">
|
<div class="px-3">
|
||||||
<a class="btn btn-phoenix-succes d-flex flex-center w-100"
|
<a class="btn btn-phoenix-succes d-flex flex-center w-100"
|
||||||
href="{% url 'account_login' %}"> <i class="fa-solid fa-right-to-bracket me-2"></i>{% trans 'Sign In' %}</a>
|
href="{% url 'account_login' %}"> <span class="me-2" data-feather="log-in"></span>{% trans 'Sign In' %}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="px-3">
|
<div class="px-3">
|
||||||
<a class="btn btn-phoenix-primary d-flex flex-center w-100"
|
<a class="btn btn-phoenix-primary d-flex flex-center w-100"
|
||||||
href="{% url 'account_signup' %}"> <i class="fa-solid fa-user-plus"></i>{% trans 'Sign Up' %}</a>
|
href="{% url 'account_signup' %}"> <span class="me-2" data-feather="user-plus"></span>{% trans 'Sign Up' %}</a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -194,11 +194,12 @@
|
|||||||
{% if perms.inventory.add_carregistration %}
|
{% if perms.inventory.add_carregistration %}
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="btn btn-sm btn-phoenix-success"
|
class="btn btn-sm btn-phoenix-success"
|
||||||
data-bs-toggle="modal"
|
|
||||||
data-bs-target="#mainModal"
|
|
||||||
hx-get="{% url 'add_registration' request.dealer.slug car.slug %}"
|
hx-get="{% url 'add_registration' request.dealer.slug car.slug %}"
|
||||||
hx-target=".main-modal-body"
|
hx-target=".main-modal-body"
|
||||||
hx-swap="innerHTML">{% trans 'Add' %}</button>
|
hx-swap="innerHTML"
|
||||||
|
data-bs-toggle="modal"
|
||||||
|
data-bs-target="#mainModal"
|
||||||
|
>{% trans 'Add' %}</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -214,10 +215,10 @@
|
|||||||
{{ car.location.showroom.get_local_name }}
|
{{ car.location.showroom.get_local_name }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.inventory.add_cartransfer %}
|
{% if perms.inventory.add_cartransfer %}
|
||||||
<a href="{% url 'update_car_location' car.slug car.location.pk %}"
|
{% comment %} <a href="{% url 'update_car_location' car.slug car.location.pk %}"
|
||||||
class="btn btn-phoenix-danger btn-sm">
|
class="btn btn-phoenix-danger btn-sm">
|
||||||
{% trans "transfer"|capfirst %}
|
{% trans "transfer"|capfirst %}
|
||||||
</a>
|
</a> {% endcomment %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% trans "No location available." %}
|
{% trans "No location available." %}
|
||||||
|
|||||||
@ -25,7 +25,7 @@
|
|||||||
</style>
|
</style>
|
||||||
{% endblock customCSS %}
|
{% endblock customCSS %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% if cars or request.GET.q %}
|
|
||||||
<div class="container-fluid" id="projectSummary">
|
<div class="container-fluid" id="projectSummary">
|
||||||
<div class="row g-3 justify-content-between align-items-end mb-4">
|
<div class="row g-3 justify-content-between align-items-end mb-4">
|
||||||
<div class="col-12 col-sm-auto">
|
<div class="col-12 col-sm-auto">
|
||||||
@ -105,7 +105,7 @@
|
|||||||
<input class="form-control search-input search"
|
<input class="form-control search-input search"
|
||||||
name="search"
|
name="search"
|
||||||
type="search"
|
type="search"
|
||||||
placeholder="Search"
|
placeholder="{% trans 'Search' %}"
|
||||||
aria-label="Search"
|
aria-label="Search"
|
||||||
hx-get="{% url 'car_list' request.dealer.slug %}"
|
hx-get="{% url 'car_list' request.dealer.slug %}"
|
||||||
hx-trigger="keyup changed delay:500ms"
|
hx-trigger="keyup changed delay:500ms"
|
||||||
@ -169,17 +169,17 @@
|
|||||||
hx-get="{% url 'car_list' request.dealer.slug %}"
|
hx-get="{% url 'car_list' request.dealer.slug %}"
|
||||||
hx-include=".make,.model,.year,.car_status"
|
hx-include=".make,.model,.year,.car_status"
|
||||||
hx-indicator=".htmx-indicator"
|
hx-indicator=".htmx-indicator"
|
||||||
hx-target=".table-responsive"
|
hx-target=".table-responsive1"
|
||||||
hx-select=".table-responsive"
|
hx-select=".table-responsive1"
|
||||||
hx-swap="outerHTML show:window:top"
|
hx-swap="outerHTML show:window:top"
|
||||||
class="btn btn-sm btn-phoenix-primary ms-1"
|
class="btn btn-sm btn-phoenix-primary ms-1"
|
||||||
hx-on::before-request="filter_before_request()"
|
hx-on::before-request="filter_before_request()"
|
||||||
hx-on::after-request="filter_after_request()">{{ _("Search") }}</button>
|
hx-on::after-request="filter_after_request()">{{ _("Search") }}</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="table-responsive scrollbar">
|
<div class="table-responsive1 scrollbar">
|
||||||
<div class="d-flex flex-wrap align-items-center justify-content-between py-3 pe-0 fs-9">
|
<div class="d-flex flex-wrap align-items-center justify-content-between py-3 pe-0 fs-9">
|
||||||
<div class="d-flex"
|
<div class="d-flex"
|
||||||
hx-boost="true"
|
hx-boost="false"
|
||||||
hx-push-url="false"
|
hx-push-url="false"
|
||||||
hx-include=".make,.model,.year,.car_status"
|
hx-include=".make,.model,.year,.car_status"
|
||||||
hx-target=".table-responsive"
|
hx-target=".table-responsive"
|
||||||
@ -301,10 +301,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
|
||||||
{% url "car_add" request.dealer.slug as create_car_url %}
|
|
||||||
{% include "empty-illustration-page.html" with value="car" url=create_car_url %}
|
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block customJS%}
|
{% block customJS%}
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
@ -169,7 +169,7 @@
|
|||||||
class="form-control form-control-sm"
|
class="form-control form-control-sm"
|
||||||
required
|
required
|
||||||
placeholder="email@example.com"
|
placeholder="email@example.com"
|
||||||
value="{{ request.dealer.email }}">
|
value="{{ request.dealer.user.email }}">
|
||||||
</div>
|
</div>
|
||||||
<label class="form-label" for="phone">{{ _("Phone Number") }}</label>
|
<label class="form-label" for="phone">{{ _("Phone Number") }}</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
|
|||||||
@ -1,5 +1,15 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
|
||||||
<div class="form-group" id="form-{{ name }}">
|
<div class="form-group" id="form-{{ name }}">
|
||||||
<label for="{{ name }}">{{ name|capfirst }}</label>
|
{% if name == "make" %}
|
||||||
|
<label for="{{ name }}">{% trans "Make" %}</label>
|
||||||
|
{% elif name == "model" %}
|
||||||
|
<label for="{{ name }}">{% trans "Model" %}</label>
|
||||||
|
{% elif name == "serie" %}
|
||||||
|
<label for="{{ name }}">{% trans "Serie" %}</label>
|
||||||
|
{% elif name == "trim" %}
|
||||||
|
<label for="{{ name }}">{% trans "Trim" %}</label>
|
||||||
|
{% endif %}
|
||||||
<select class="form-control"
|
<select class="form-control"
|
||||||
name="{{ name }}"
|
name="{{ name }}"
|
||||||
id="{{ name }}"
|
id="{{ name }}"
|
||||||
|
|||||||
@ -31,7 +31,7 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr class="bg-body-highlight">
|
<tr class="bg-body-highlight">
|
||||||
<th scope="col">{% trans "Name" %}</th>
|
<th scope="col">{% trans "Name" %}</th>
|
||||||
<th scope="col">{% trans "Quatnity" %}</th>
|
<th scope="col">{% trans "Quantity" %}</th>
|
||||||
<th scope="col">{% trans "Unit Cost" %}</th>
|
<th scope="col">{% trans "Unit Cost" %}</th>
|
||||||
<th scope="col">{% trans "Is Data Uploaded ?" %}</th>
|
<th scope="col">{% trans "Is Data Uploaded ?" %}</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
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 %}
|
||||||
@ -10,7 +10,7 @@
|
|||||||
</style>
|
</style>
|
||||||
{% endblock extraCSS %}
|
{% endblock extraCSS %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row">
|
<div class="row d-flex justify-content-center">
|
||||||
<div class="booking-hero-header d-flex align-items-center">
|
<div class="booking-hero-header d-flex align-items-center">
|
||||||
<div class="bg-holder overlay bg-opacity-75"
|
<div class="bg-holder overlay bg-opacity-75"
|
||||||
style="background-image:url({% static 'video/image-haikal-02.png' %})">
|
style="background-image:url({% static 'video/image-haikal-02.png' %})">
|
||||||
@ -36,11 +36,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<section class="pt-6 pt-md-10 pb-10" id="feature">
|
<section class="pt-6 pt-md-10 pb-10" id="feature">
|
||||||
<div class="container-medium">
|
<div class="bg-holder d-none d-xl-block"
|
||||||
<div class="bg-holder d-none d-xl-block"
|
style="background-image:url({% static 'images/bg/bg-left-27.png' %});
|
||||||
style="background-image:url({% static 'images/bg/bg-left-27.png' %});
|
background-size:auto;
|
||||||
background-size:auto;
|
background-position:left"></div>
|
||||||
background-position:left"></div>
|
|
||||||
<div class="bg-holder d-none d-xl-block"
|
<div class="bg-holder d-none d-xl-block"
|
||||||
style="background-image:url({% static 'images/bg/bg-right-27.png' %});
|
style="background-image:url({% static 'images/bg/bg-right-27.png' %});
|
||||||
background-size:auto;
|
background-size:auto;
|
||||||
@ -128,9 +127,7 @@
|
|||||||
<h2 class="mb-7">{{ _("Pricing") }}</h2>
|
<h2 class="mb-7">{{ _("Pricing") }}</h2>
|
||||||
<div class="row g-3 mb-7 mb-lg-11">
|
<div class="row g-3 mb-7 mb-lg-11">
|
||||||
{% for plan in plan_list %}
|
{% for plan in plan_list %}
|
||||||
|
|
||||||
<div class="col-lg-4" onclick="window.location='{% url "account_signup" %}';">
|
<div class="col-lg-4" onclick="window.location='{% url "account_signup" %}';">
|
||||||
|
|
||||||
<input type="radio"
|
<input type="radio"
|
||||||
class="btn-check"
|
class="btn-check"
|
||||||
name="selected_plan"
|
name="selected_plan"
|
||||||
@ -144,7 +141,7 @@
|
|||||||
<h3 class="h6 mb-3">
|
<h3 class="h6 mb-3">
|
||||||
{{ plan.planpricing_set.first.price }}
|
{{ plan.planpricing_set.first.price }}
|
||||||
<span class="icon-saudi_riyal"></span>
|
<span class="icon-saudi_riyal"></span>
|
||||||
<span class="fs-8 fw-normal">/ {{ _("Per month") }}</span>
|
<span class="fs-8 fw-normal">/{{plan.planpricing_set.first.pricing.period}} {{ _("month") }}</span>
|
||||||
</h3>
|
</h3>
|
||||||
<h5 class="mb-3 h6">{{ _("Included") }}</h5>
|
<h5 class="mb-3 h6">{{ _("Included") }}</h5>
|
||||||
<ul class="fa-ul ps-3 m-0">
|
<ul class="fa-ul ps-3 m-0">
|
||||||
@ -158,9 +155,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -238,8 +233,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="pt-lg-0 pt-xl-8">
|
|
||||||
{% include 'footer.html' %}
|
|
||||||
</section>
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@ -43,9 +43,7 @@
|
|||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@300;400;600;700;800;900&display=swap"
|
<link href="https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@300;400;600;700;800;900&display=swap"
|
||||||
rel="stylesheet">
|
rel="stylesheet">
|
||||||
{% comment %} <link href="{% static 'vendors/simplebar/simplebar.min.css' %}" rel="stylesheet"> {% endcomment %}
|
|
||||||
{% comment %} <link href="{% static 'css/sweetalert2.min.css' %}" rel="stylesheet"> {% endcomment %}
|
|
||||||
{% comment %} <link rel="stylesheet" href="https://unicons.iconscout.com/release/v4.0.8/css/line.css"> {% endcomment %}
|
|
||||||
{% if LANGUAGE_CODE == 'ar' %}
|
{% if LANGUAGE_CODE == 'ar' %}
|
||||||
<link href="{% static 'css/theme-rtl.min.css' %}"
|
<link href="{% static 'css/theme-rtl.min.css' %}"
|
||||||
type="text/css"
|
type="text/css"
|
||||||
@ -67,23 +65,22 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<script src="{% static 'vendors/simplebar/simplebar.min.js' %}"></script>
|
<script src="{% static 'vendors/simplebar/simplebar.min.js' %}"></script>
|
||||||
<script src="{% static 'js/config.js' %}"></script>
|
<script src="{% static 'js/config.js' %}"></script>
|
||||||
{% comment %} <script src="{% static 'js/sweetalert2.all.min.js' %}"></script> {% endcomment %}
|
|
||||||
<link href="{% static 'css/custom.css' %}" rel="stylesheet">
|
<link href="{% static 'css/custom.css' %}" rel="stylesheet">
|
||||||
|
|
||||||
|
{% block extraCSS %}
|
||||||
|
{% endblock extraCSS %}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="d-flex flex-column min-vh-100">
|
||||||
{% include 'welcome_header.html' %}
|
{% include 'welcome_header.html' %}
|
||||||
{% block content %}
|
<main class="flex-grow-1">
|
||||||
{% endblock content %}
|
{% block content %}
|
||||||
{% comment %}
|
{% endblock content %}
|
||||||
<script src="{% static 'vendors/anchorjs/anchor.min.js' %}"></script>
|
</main>
|
||||||
<script src="{% static 'vendors/is/is.min.js' %}"></script>
|
|
||||||
<script src="{% static 'vendors/list.js/list.min.js' %}"></script>
|
<div class="site-footer mt-auto">
|
||||||
<script src="{% static 'vendors/dayjs/dayjs.min.js' %}"></script>
|
{% include 'welcome_footer.html' %}
|
||||||
<script src="{% static 'vendors/echarts/echarts.min.js' %}"></script>
|
</div>
|
||||||
<script src="{% static 'js/travel-agency-dashboard.js' %}"></script>
|
|
||||||
<script src="{% static 'vendors/mapbox-gl/mapbox-gl.js' %}"></script>
|
|
||||||
<script src="https://unpkg.com/@turf/turf@6/turf.min.js"></script>
|
|
||||||
<script src="{% static 'vendors/swiper/swiper-bundle.min.js' %}"></script> {% endcomment %}
|
|
||||||
<script src="{% static 'vendors/popper/popper.min.js' %}"></script>
|
<script src="{% static 'vendors/popper/popper.min.js' %}"></script>
|
||||||
<script src="{% static 'vendors/bootstrap/bootstrap.min.js' %}"></script>
|
<script src="{% static 'vendors/bootstrap/bootstrap.min.js' %}"></script>
|
||||||
<script src="{% static 'vendors/fontawesome/all.min.js' %}"></script>
|
<script src="{% static 'vendors/fontawesome/all.min.js' %}"></script>
|
||||||
@ -91,23 +88,8 @@
|
|||||||
<script src="{% static 'vendors/feather-icons/feather.min.js' %}"></script>
|
<script src="{% static 'vendors/feather-icons/feather.min.js' %}"></script>
|
||||||
<script src="{% static 'vendors/typed.js/typed.umd.js' %}"></script>
|
<script src="{% static 'vendors/typed.js/typed.umd.js' %}"></script>
|
||||||
<script src="{% static 'js/phoenix.js' %}"></script>
|
<script src="{% static 'js/phoenix.js' %}"></script>
|
||||||
{% comment %} <script src="{% static 'vendors/popper/popper.min.js' %}"></script>
|
|
||||||
<script src="{% static 'vendors/bootstrap/bootstrap.min.js' %}"></script>
|
|
||||||
<script src="{% static 'vendors/anchorjs/anchor.min.js' %}"></script>
|
|
||||||
<script src="{% static 'vendors/is/is.min.js' %}"></script>
|
|
||||||
<script src="{% static 'vendors/fontawesome/all.min.js' %}"></script>
|
|
||||||
<script src="{% static 'vendors/lodash/lodash.min.js' %}"></script>
|
|
||||||
<script src="{% static 'vendors/list.js/list.min.js' %}"></script>
|
|
||||||
<script src="{% static 'vendors/feather-icons/feather.min.js' %}"></script>
|
|
||||||
<script src="{% static 'vendors/dayjs/dayjs.min.js' %}"></script>
|
|
||||||
<script src="{% static 'js/phoenix.js' %}"></script>
|
|
||||||
<script src="{% static 'vendors/echarts/echarts.min.js' %}"></script>
|
|
||||||
<script src="{% static 'js/travel-agency-dashboard.js' %}"></script>
|
|
||||||
<script src="{% static 'vendors/mapbox-gl/mapbox-gl.js' %}"></script>
|
|
||||||
<script src="https://unpkg.com/@turf/turf@6/turf.min.js"></script>
|
|
||||||
<script src="{% static 'vendors/swiper/swiper-bundle.min.js' %}"></script>
|
|
||||||
<script src="{% static 'vendors/typed.js/typed.umd.js' %}"></script> {% endcomment %}
|
|
||||||
{% block customJS %}
|
{% block customJS %}
|
||||||
{% endblock customJS %}
|
{% endblock customJS %}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@ -1,5 +1,5 @@
|
|||||||
<section class="footer pb-6 pb-md-11 pt-15">
|
<section class="footer pb-6 pb-md-11 pt-5">
|
||||||
<div class="container-medium">
|
<div class="container-medium mt-3">
|
||||||
<div class="row gy-3 justify-content-between">
|
<div class="row gy-3 justify-content-between">
|
||||||
<div class="row g-0 justify-content-between align-items-center h-100">
|
<div class="row g-0 justify-content-between align-items-center h-100">
|
||||||
<div class="col-12 col-sm-auto text-center">
|
<div class="col-12 col-sm-auto text-center">
|
||||||
|
|||||||