This commit is contained in:
Marwan Alwali 2025-01-02 19:17:06 +03:00
parent 4664029c95
commit 7b3688cb89
33 changed files with 560 additions and 464 deletions

BIN
.DS_Store vendored

Binary file not shown.

BIN
db.sqlite

Binary file not shown.

View File

@ -254,6 +254,7 @@ wmi_manufacturer_mapping = {
"LND": "JMEV", "LND": "JMEV",
"LNP": "NAC MG", "LNP": "NAC MG",
"LNY": "Yuejin", "LNY": "Yuejin",
"LMX": "Dongfeng Forthing",
"LPA": "Changan", "LPA": "Changan",
"LPE": "BYD", "LPE": "BYD",
"LPS": "Polestar", "LPS": "Polestar",
@ -346,9 +347,7 @@ wmi_manufacturer_mapping = {
"MCG": "Atul", "MCG": "Atul",
"MC1": "Force", "MC1": "Force",
"MC2": "Eicher", "MC2": "Eicher",
"MDE": "Kinetic Engineering Limited",
"MDH": "Nissan", "MDH": "Nissan",
"MDT": "Kerala Limited",
"MD6": "TVS", "MD6": "TVS",
"MD9": "Shuttles", "MD9": "Shuttles",
"MEC": "Daimler", "MEC": "Daimler",
@ -358,8 +357,6 @@ wmi_manufacturer_mapping = {
"MET": "Piaggio", "MET": "Piaggio",
"MEX": "Škoda", "MEX": "Škoda",
"ME1": "Yamaha", "ME1": "Yamaha",
"ME3": "Royal Enfield",
"MYH": "Ather Energy",
"MZB": "Kia", "MZB": "Kia",
"MZZ": "Citroën", "MZZ": "Citroën",
"MZ7": "MG", "MZ7": "MG",
@ -1501,7 +1498,7 @@ def decode_vds(manufacturer, vds):
return "Unknown Model" return "Unknown Model"
def decode_vin(vin): def decode_vin_haikalna(vin):
if len(vin) != 17: if len(vin) != 17:
raise ValueError("Invalid VIN length. VIN must be 17 characters.") raise ValueError("Invalid VIN length. VIN must be 17 characters.")
@ -1518,21 +1515,22 @@ def decode_vin(vin):
manufacturer = wmi_manufacturer_mapping.get(wmi, vds) manufacturer = wmi_manufacturer_mapping.get(wmi, vds)
year_code = vis[0] year_code = vis[0]
year = vin_years(year_code) year = vin_years(year_code)[0]
model = decode_vds(manufacturer, vds) model = decode_vds(manufacturer, vds)
return { data = {
'VIN': vin, 'maker': manufacturer,
'Manufacturer': manufacturer, 'model': model,
'Year': year, 'modelYear': year,
'Model': model
} }
return data
# JTDKB3FU4G3507653 # JTDKB3FU4G3507653
# VR3USHNLWRJ521303 # VR3USHNLWRJ521303
# KNARH81E8P5194005 # KNARH81E8P5194005
# Example usage # Example usage
vin_number = 'VYFED9HP0SJ519559' # vin_number = 'LMXA14AF7PZ356070'
decoded_vin = decode_vin(vin_number) # decoded_vin = decode_vin_haikalna(vin_number)
print(decoded_vin) # print(decoded_vin)

View File

@ -1,18 +0,0 @@
# Generated by Django 5.1.4 on 2024-12-31 21:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0021_opportunitylog'),
]
operations = [
migrations.AlterField(
model_name='customer',
name='is_lead',
field=models.BooleanField(default=False, verbose_name='Is Lead'),
),
]

View File

@ -0,0 +1,34 @@
# Generated by Django 5.1.4 on 2025-01-01 16:25
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('django_ledger', '0017_alter_accountmodel_unique_together_and_more'),
('inventory', '0021_opportunitylog'),
]
operations = [
migrations.RemoveField(
model_name='opportunitylog',
name='user',
),
migrations.AddField(
model_name='opportunitylog',
name='staff',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='inventory.staff', verbose_name='Staff'),
),
migrations.AlterField(
model_name='carfinance',
name='additional_services',
field=models.ManyToManyField(blank=True, related_name='additional_finances', to='django_ledger.itemmodel'),
),
migrations.AlterField(
model_name='customer',
name='is_lead',
field=models.BooleanField(default=False, verbose_name='Is Lead'),
),
]

View File

@ -1,5 +1,6 @@
# Generated by Django 4.2.17 on 2025-01-01 12:43 # Generated by Django 5.1.4 on 2025-01-01 16:40
import django.db.models.deletion
from django.db import migrations, models from django.db import migrations, models
@ -7,7 +8,7 @@ class Migration(migrations.Migration):
dependencies = [ dependencies = [
('django_ledger', '0017_alter_accountmodel_unique_together_and_more'), ('django_ledger', '0017_alter_accountmodel_unique_together_and_more'),
('inventory', '0022_rename_log_oportunitylog'), ('inventory', '0022_remove_opportunitylog_user_opportunitylog_staff_and_more'),
] ]
operations = [ operations = [
@ -17,7 +18,8 @@ class Migration(migrations.Migration):
), ),
migrations.AddField( migrations.AddField(
model_name='carfinance', model_name='carfinance',
name='services', name='additional_services',
field=models.ManyToManyField(blank=True, related_name='services', to='django_ledger.itemmodel'), field=models.ForeignKey(blank=True, default=1, on_delete=django.db.models.deletion.CASCADE, related_name='additional_finances', to='django_ledger.itemmodel'),
preserve_default=False,
), ),
] ]

View File

@ -1,23 +0,0 @@
# Generated by Django 5.1.4 on 2025-01-01 01:24
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0022_alter_customer_is_lead'),
]
operations = [
migrations.RemoveField(
model_name='opportunitylog',
name='user',
),
migrations.AddField(
model_name='opportunitylog',
name='staff',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='inventory.staff', verbose_name='Staff'),
),
]

View File

@ -1,4 +1,4 @@
# Generated by Django 4.2.17 on 2025-01-01 12:46 # Generated by Django 5.1.4 on 2025-01-01 16:51
from django.db import migrations, models from django.db import migrations, models
@ -13,7 +13,7 @@ class Migration(migrations.Migration):
operations = [ operations = [
migrations.RemoveField( migrations.RemoveField(
model_name='carfinance', model_name='carfinance',
name='services', name='additional_services',
), ),
migrations.AddField( migrations.AddField(
model_name='carfinance', model_name='carfinance',

View File

@ -154,7 +154,6 @@ class CarSpecificationValue(models.Model):
verbose_name = "Specification Value" verbose_name = "Specification Value"
# Car Model
class CarStatusChoices(models.TextChoices): class CarStatusChoices(models.TextChoices):
AVAILABLE = "available", _("Available") AVAILABLE = "available", _("Available")
SOLD = "sold", _("Sold") SOLD = "sold", _("Sold")
@ -184,6 +183,7 @@ class AdditionalServices(models.Model, LocalizedNameMixin):
def __str__(self): def __str__(self):
return self.name + " - " + str(self.price) return self.name + " - " + str(self.price)
class Car(models.Model): class Car(models.Model):
vin = models.CharField(max_length=17, unique=True, verbose_name=_("VIN")) vin = models.CharField(max_length=17, unique=True, verbose_name=_("VIN"))
dealer = models.ForeignKey( dealer = models.ForeignKey(
@ -308,6 +308,7 @@ class CarReservation(models.Model):
verbose_name = _("Car Reservation") verbose_name = _("Car Reservation")
verbose_name_plural = _("Car Reservations") verbose_name_plural = _("Car Reservations")
# Car Finance Model # Car Finance Model
class CarFinance(models.Model): class CarFinance(models.Model):
additional_services = models.ManyToManyField(ItemModel, related_name="additional_finances",blank=True) additional_services = models.ManyToManyField(ItemModel, related_name="additional_finances",blank=True)
@ -389,7 +390,6 @@ class InteriorColors(models.Model, LocalizedNameMixin):
return f"{self.name} ({self.rgb})" return f"{self.name} ({self.rgb})"
# Colors Model
class CarColors(models.Model): class CarColors(models.Model):
car = models.ForeignKey("Car", on_delete=models.CASCADE, related_name="colors") car = models.ForeignKey("Car", on_delete=models.CASCADE, related_name="colors")
exterior = models.ForeignKey( exterior = models.ForeignKey(
@ -408,7 +408,6 @@ class CarColors(models.Model):
return f"{self.car} ({self.exterior.name}) ({self.interior.name})" return f"{self.car} ({self.exterior.name}) ({self.interior.name})"
# Custom Card Model
class CustomCard(models.Model): class CustomCard(models.Model):
car = models.OneToOneField(Car, on_delete=models.CASCADE, related_name='custom_cards', verbose_name=_("Car")) car = models.OneToOneField(Car, on_delete=models.CASCADE, related_name='custom_cards', verbose_name=_("Car"))
custom_number = models.CharField(max_length=255, verbose_name=_("Custom Number")) custom_number = models.CharField(max_length=255, verbose_name=_("Custom Number"))
@ -471,7 +470,7 @@ class CarLocation(models.Model):
""" """
return self.owner == self.showroom return self.owner == self.showroom
# Car Registration Model
class CarRegistration(models.Model): class CarRegistration(models.Model):
car = models.ForeignKey( car = models.ForeignKey(
Car, Car,
@ -501,7 +500,7 @@ class TimestampedModel(models.Model):
class Meta: class Meta:
abstract = True abstract = True
#subscription
class Subscription(models.Model): class Subscription(models.Model):
plan = models.CharField(max_length=255) # e.g. "basic", "premium" plan = models.CharField(max_length=255) # e.g. "basic", "premium"
start_date = models.DateField() start_date = models.DateField()
@ -529,7 +528,6 @@ class SubscriptionPlan(models.Model):
return f"{self.name} - {self.price}" return f"{self.name} - {self.price}"
# Dealer Model
class Dealer(models.Model, LocalizedNameMixin): class Dealer(models.Model, LocalizedNameMixin):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="dealer") user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="dealer")
crn = models.CharField(max_length=10, crn = models.CharField(max_length=10,
@ -608,18 +606,21 @@ class Dealer(models.Model, LocalizedNameMixin):
# return self.parent_dealer if self.parent_dealer else self # return self.parent_dealer if self.parent_dealer else self
class StaffTypes(models.TextChoices):
class STAFF_TYPES(models.TextChoices):
MANAGER = "manager", _("Manager") MANAGER = "manager", _("Manager")
INVENTORY = "inventory", _("Inventory") INVENTORY = "inventory", _("Inventory")
ACCOUNTANT = "accountant", _("Accountant") ACCOUNTANT = "accountant", _("Accountant")
SALES = "sales", _("Sales") SALES = "sales", _("Sales")
##############################
# Additional staff types for later
# COORDINATOR = "coordinator", _("Coordinator") # COORDINATOR = "coordinator", _("Coordinator")
# RECEPTIONIST = "receptionist", _("Receptionist") # RECEPTIONIST = "receptionist", _("Receptionist")
# AGENT = "agent", _("Agent") # AGENT = "agent", _("Agent")
# TECHNICIAN = "technician", _("Technician") # TECHNICIAN = "technician", _("Technician")
# DRIVER = "driver", _("Driver") # DRIVER = "driver", _("Driver")
##############################
class Staff(models.Model, LocalizedNameMixin): class Staff(models.Model, LocalizedNameMixin):
@ -628,7 +629,7 @@ class Staff(models.Model, LocalizedNameMixin):
name = models.CharField(max_length=255, verbose_name=_("Name")) name = models.CharField(max_length=255, verbose_name=_("Name"))
arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name")) arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name"))
phone_number = PhoneNumberField(region="SA", verbose_name=_("Phone Number")) phone_number = PhoneNumberField(region="SA", verbose_name=_("Phone Number"))
staff_type = models.CharField(choices=STAFF_TYPES.choices, max_length=255, verbose_name=_("Staff Type")) staff_type = models.CharField(choices=StaffTypes.choices, max_length=255, verbose_name=_("Staff Type"))
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created At")) created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created At"))
updated_at = models.DateTimeField(auto_now=True, verbose_name=_("Updated At")) updated_at = models.DateTimeField(auto_now=True, verbose_name=_("Updated At"))
@ -641,7 +642,6 @@ class Staff(models.Model, LocalizedNameMixin):
return f"{self.name} - {self.get_staff_type_display()}" return f"{self.name} - {self.get_staff_type_display()}"
# Vendor Model
class Vendor(models.Model, LocalizedNameMixin): class Vendor(models.Model, LocalizedNameMixin):
dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name="vendors") dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name="vendors")
crn = models.CharField( crn = models.CharField(
@ -671,7 +671,6 @@ class Vendor(models.Model, LocalizedNameMixin):
return self.name return self.name
# Customer Model
class Customer(models.Model): class Customer(models.Model):
dealer = models.ForeignKey(Dealer, dealer = models.ForeignKey(Dealer,
on_delete=models.CASCADE, on_delete=models.CASCADE,
@ -932,6 +931,7 @@ class SaleQuotation(models.Model):
last_quotation_number = 0 last_quotation_number = 0
return itertools.count(last_quotation_number + 1) return itertools.count(last_quotation_number + 1)
class SaleQuotationCar(models.Model): class SaleQuotationCar(models.Model):
quotation = models.ForeignKey( quotation = models.ForeignKey(
SaleQuotation, SaleQuotation,

View File

@ -12,6 +12,7 @@ from pyvin import VIN
from django.conf import settings from django.conf import settings
from openai import OpenAI from openai import OpenAI
from .models import Car,CarMake,CarModel from .models import Car,CarMake,CarModel
from inventory.haikalna import decode_vin_haikalna
def get_make(item): def get_make(item):
@ -37,6 +38,8 @@ def decodevin(vin):
return result return result
elif result:=elm(vin): elif result:=elm(vin):
return result return result
elif result:=decode_vin_haikalna(vin):
return result
else: else:
return None return None

View File

@ -30,3 +30,4 @@ def attr(field, args):
else: else:
attrs[definition.strip()] = True attrs[definition.strip()] = True
return field.as_widget(attrs=attrs) return field.as_widget(attrs=attrs)

View File

@ -27,7 +27,7 @@ urlpatterns = [
path('login/code/', allauth_views.RequestLoginCodeView.as_view(template_name='account/request_login_code.html')), path('login/code/', allauth_views.RequestLoginCodeView.as_view(template_name='account/request_login_code.html')),
#Dashboards #Dashboards
path('dashboards/accounting/', views.AccountingDashboard.as_view(), name='accounting'), path('dashboards/accounting/', views.AccountingDashboard.as_view(), name='accounting'),
path('test/', views.TestView.as_view(), name='test'),
# Dealer URLs # Dealer URLs
path('dealers/<int:pk>/', views.DealerDetailView.as_view(), name='dealer_detail'), path('dealers/<int:pk>/', views.DealerDetailView.as_view(), name='dealer_detail'),
path('dealers/<int:pk>/update/', views.DealerUpdateView.as_view(), name='dealer_update'), path('dealers/<int:pk>/update/', views.DealerUpdateView.as_view(), name='dealer_update'),

View File

@ -58,3 +58,11 @@ def send_email(from_, to_, subject, message):
from_email = from_ from_email = from_
recipient_list = [to_] recipient_list = [to_]
send_mail(subject, message, from_email, recipient_list) send_mail(subject, message, from_email, recipient_list)
def get_user_type(request):
if hasattr(request.user, 'dealer'):
dealer = request.user.dealer
elif hasattr(request.user, 'staff'):
dealer = request.user.staff.dealer
return dealer

View File

@ -66,7 +66,7 @@ from . import models, forms
from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.auth.mixins import PermissionRequiredMixin
from django.contrib.messages.views import SuccessMessageMixin from django.contrib.messages.views import SuccessMessageMixin
from django.contrib.auth.models import Group from django.contrib.auth.models import Group
from .utils import get_calculations, send_email from .utils import get_calculations, send_email, get_user_type
from django.contrib.auth.models import User from django.contrib.auth.models import User
from allauth.account import views from allauth.account import views
from django.db.models import Count, F, Value from django.db.models import Count, F, Value
@ -166,6 +166,10 @@ class HomeView(TemplateView):
template_name = "index.html" template_name = "index.html"
class TestView(TemplateView):
template_name = "test.html"
class AccountingDashboard(LoginRequiredMixin, TemplateView): class AccountingDashboard(LoginRequiredMixin, TemplateView):
template_name = "dashboards/accounting.html" template_name = "dashboards/accounting.html"
@ -374,8 +378,9 @@ class CarInventory(LoginRequiredMixin, ListView):
model_id = self.kwargs["model_id"] model_id = self.kwargs["model_id"]
trim_id = self.kwargs["trim_id"] trim_id = self.kwargs["trim_id"]
dealer = get_user_type(self.request)
cars = models.Car.objects.filter( cars = models.Car.objects.filter(
dealer=self.request.user.dealer, dealer=dealer,
id_car_make=make_id, id_car_make=make_id,
id_car_model=model_id, id_car_model=model_id,
id_car_trim=trim_id, id_car_trim=trim_id,
@ -415,7 +420,7 @@ class CarColorCreate(LoginRequiredMixin, CreateView):
@login_required @login_required
def inventory_stats_view(request): def inventory_stats_view(request):
dealer = request.user.dealer dealer = get_user_type(request)
# Annotate total cars by make, model, and trim # Annotate total cars by make, model, and trim
cars = ( cars = (
@ -692,7 +697,6 @@ class DealerUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
class CustomerListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): class CustomerListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
model = models.Customer model = models.Customer
home_label = _("customers")
context_object_name = "customers" context_object_name = "customers"
paginate_by = 10 paginate_by = 10
template_name = "customers/customer_list.html" template_name = "customers/customer_list.html"
@ -700,17 +704,15 @@ class CustomerListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
def get_queryset(self): def get_queryset(self):
query = self.request.GET.get("q") query = self.request.GET.get("q")
if self.request.user.is_staff: dealer = get_user_type(self.request)
dealer = self.request.user.staff.dealer
customers = models.Customer.objects.filter( customers = models.Customer.objects.filter(dealer=dealer)
dealer=dealer,
)
if query: if query:
customers = customers.filter( customers = customers.filter(
Q(national_id__icontains=query) Q(national_id__icontains=query) |
| Q(first_name__icontains=query) Q(first_name__icontains=query) |
| Q(last_name__icontains=query) Q(last_name__icontains=query)
) )
return customers return customers
@ -879,7 +881,7 @@ class QuotationDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailVie
@login_required @login_required
def generate_invoice(request, pk): def generate_invoice(request, pk):
quotation = get_object_or_404(models.SaleQuotation, pk=pk) quotation = get_object_or_404(models.SaleQuotation, pk=pk)
dealer = request.user.dealer dealer = get_user_type(request)
entity = dealer.entity entity = dealer.entity
if not quotation.is_approved: if not quotation.is_approved:
messages.error( messages.error(
@ -1731,7 +1733,8 @@ class EstimateListView(LoginRequiredMixin, ListView):
# @csrf_exempt # @csrf_exempt
@login_required @login_required
def create_estimate(request): def create_estimate(request):
entity = request.entity dealer = get_user_type(request)
entity = dealer.entity
if request.method == "POST": if request.method == "POST":
try: try:
data = json.loads(request.body) data = json.loads(request.body)
@ -1932,7 +1935,8 @@ class InvoiceListView(LoginRequiredMixin, ListView):
context_object_name = "invoices" context_object_name = "invoices"
def get_queryset(self): def get_queryset(self):
entity = self.request.user.dealer.entity dealer = get_user_type(self.request)
entity = dealer.entity
return entity.get_invoices() return entity.get_invoices()
@ -2137,7 +2141,9 @@ def PaymentCreateView(request, pk=None):
def PaymentListView(request): def PaymentListView(request):
entity = request.user.dealer.entity dealer = get_user_type(request)
entity = dealer.entity
journals = JournalEntryModel.objects.filter(ledger__entity=entity).all() journals = JournalEntryModel.objects.filter(ledger__entity=entity).all()
return render(request, "sales/payments/payment_list.html", {"journals": journals}) return render(request, "sales/payments/payment_list.html", {"journals": journals})

BIN
static/.DS_Store vendored

Binary file not shown.

View File

@ -0,0 +1,9 @@
.color-div {
width: 22px;
height: 22px;
border-radius: 50%;
border: 1px solid #ccc;
text-align: center;
vertical-align: middle;
line-height: 22px;
}

Binary file not shown.

Binary file not shown.

View File

@ -21,7 +21,7 @@ const getDataTableInit = () => {
button.classList[disabled ? 'add' : 'remove']('disabled'); button.classList[disabled ? 'add' : 'remove']('disabled');
}; };
// Selectors // Selectors
const table = document.getElementById('opportunityTable'); const table = document.getElementById('inventoryTable');
if (table) { if (table) {
const options = { const options = {

View File

@ -31,7 +31,7 @@
<link href="{% static 'vendors/simplebar/simplebar.min.css' %}" rel="stylesheet"> <link href="{% static 'vendors/simplebar/simplebar.min.css' %}" rel="stylesheet">
<link href="{% static 'css/sweetalert2.min.css' %}" rel="stylesheet"> <link href="{% static 'css/sweetalert2.min.css' %}" rel="stylesheet">
<link rel="stylesheet" href="https://unicons.iconscout.com/release/v4.0.8/css/line.css"> <link rel="stylesheet" href="https://unicons.iconscout.com/release/v4.0.8/css/line.css">
<link href="{% static 'css/custom.css' %}" rel="stylesheet">
{% if LANGUAGE_CODE == 'en' %} {% if LANGUAGE_CODE == 'en' %}
<link href="{% static 'css/theme.min.css' %}" type="text/css" rel="stylesheet" id="style-default"> <link href="{% static 'css/theme.min.css' %}" type="text/css" rel="stylesheet" id="style-default">
<link href="{% static 'css/user.min.css' %}" type="text/css" rel="stylesheet" id="user-style-default"> <link href="{% static 'css/user.min.css' %}" type="text/css" rel="stylesheet" id="user-style-default">
@ -39,6 +39,7 @@
<link href="{% static 'css/theme-rtl.min.css' %}" type="text/css" rel="stylesheet" id="style-rtl"> <link href="{% static 'css/theme-rtl.min.css' %}" type="text/css" rel="stylesheet" id="style-rtl">
<link href="{% static 'css/user-rtl.min.css' %}" type="text/css" rel="stylesheet" id="user-style-rtl"> <link href="{% static 'css/user-rtl.min.css' %}" type="text/css" rel="stylesheet" id="user-style-rtl">
{% endif %} {% endif %}
{% block extra_css %}{% endblock extra_css %} {% block extra_css %}{% endblock extra_css %}
</head> </head>
<body> <body>

View File

@ -1,9 +1,9 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% load static %} {% load i18n static custom_filters %}
{% load i18n %}
{% load custom_filters %}
{% block title %}{{ _("Car Details") }}{% endblock %} {% block title %}{{ _("Car Details") }}{% endblock %}
{% block content %} {% block content %}
<style> <style>
.color-circle { .color-circle {
width: 32px; width: 32px;
@ -13,82 +13,16 @@
border-color: #fff; border-color: #fff;
} }
</style> </style>
<div class="container">
<div class="pb-5">
<!-- Custom Card Modal -->
<div class="modal fade" id="customCardModal" tabindex="-1" aria-labelledby="customCardModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header bg-primary">
<h5 class="modal-title text-light" id="customCardModalLabel">{% trans 'Custom Card' %}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<!-- Content will be loaded here via AJAX -->
</div>
</div>
</div>
</div>
<!-- Reservation Modal -->
<div class="modal fade" id="reserveModal" tabindex="-1" aria-labelledby="reserveModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header bg-primary">
<h5 class="modal-title text-light" id="reserveModalLabel">{% trans 'Car Reservation' %}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
{% trans 'Are you sure you want to reserve this car?' %}
</div>
<div class="modal-footer">
<button type="button"
class="btn btn-sm btn-phoenix-danger"
data-bs-dismiss="modal">
{% trans 'No' %}
</button>
<form method="POST" action="{% url 'reserve_car' car.id %}" class="d-inline">
{% csrf_token %}
<button type="submit" class="btn btn-phoenix-success btn-sm">{% trans "Yes" %}</button>
</form>
</div>
</div>
</div>
</div>
<!-- Specification Modal -->
<div class="modal fade" id="specificationsModal" tabindex="-1" aria-labelledby="specificationsModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="specificationsModalLabel">{% trans 'specifications'|upper %}</h5>
<button class="btn btn-close p-1" type="button" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div id="specificationsContent"></div>
</div>
<div class="modal-footer">
<button class="btn btn-phoenix-primary" type="button" data-bs-dismiss="modal">{% trans 'Close' %}</button>
</div>
</div>
</div>
</div>
<!-- Specification Modal -->
<!-- Main Container --> <!-- Main Container -->
<div class="container-fluid">
<div class="row gx-6"> <div class="row g-3 justify-content-between">
<div class="col-lg-12 col-xl-6"> <div class="col-lg-12 col-xl-6">
<div class="container-small-fluid">
<div class="card rounded shadow d-flex align-content-center"> <div class="card rounded shadow d-flex align-content-center">
<p class="card-header rounded-top fw-bold">{% trans 'Car Details' %}</p> <p class="card-header rounded-top fw-bold">{% trans 'Car Details' %}</p>
<div class="card-body"> <div class="card-body">
<div class="table-responsive"> <div class="table-responsive scrollbar mb-3">
<table class="table fs-9 mb-0"> <table class="table table-sm fs-9 mb-0 overflow-hidden">
<tr> <tr>
<th>{% trans "VIN" %}</th> <th>{% trans "VIN" %}</th>
<td>{{ car.vin }}</td> <td>{{ car.vin }}</td>
@ -142,11 +76,7 @@
<tr> <tr>
<th>{% trans 'specifications'|capfirst %}</th> <th>{% trans 'specifications'|capfirst %}</th>
<td> <td>
<button type="button" <button type="button" class="btn btn-phoenix-primary btn-sm" id="specification-btn" data-bs-toggle="modal" data-bs-target="#specificationsModal">
class="btn btn-phoenix-primary btn-sm"
id="specification-btn"
data-bs-toggle="modal"
data-bs-target="#specificationsModal">
{% trans 'view'|capfirst %} {% trans 'view'|capfirst %}
</button> </button>
</td> </td>
@ -164,10 +94,7 @@
<tr> <tr>
<th>{% trans "Custom Card" %}</th> <th>{% trans "Custom Card" %}</th>
<td> <td>
<button type="button" <button type="button" class="btn btn-sm btn-phoenix-success" data-bs-toggle="modal" data-bs-target="#customCardModal">
class="btn btn-sm btn-phoenix-success"
data-bs-toggle="modal"
data-bs-target="#customCardModal">
{% trans 'Add' %} {% trans 'Add' %}
</button> </button>
</td> </td>
@ -176,19 +103,12 @@
<tr> <tr>
<th>{% trans 'Location'|capfirst %}</th> <th>{% trans 'Location'|capfirst %}</th>
<td> <td>
{% if car.location %} {% if car.location %} {% if car.location.is_owner_showroom %} {% trans 'Our Showroom' %} {% else %} {{ car.location.showroom.get_local_name }} {% endif %}
{% if car.location.is_owner_showroom %} <a href="{% url 'transfer' car.location.pk %}" class="btn btn-phoenix-danger btn-sm">
{% trans 'Our Showroom' %}
{% else %}
{{ car.location.showroom.get_local_name }}
{% endif %}
<a href="{% url 'transfer' car.location.pk %}"
class="btn btn-phoenix-danger btn-sm">
{% trans "transfer"|capfirst %} {% trans "transfer"|capfirst %}
</a> </a>
{% else %} {% trans "No location available." %} {% else %} {% trans "No location available." %}
<a href="{% url 'add_car_location' car.pk %}" <a href="{% url 'add_car_location' car.pk %}" class="btn btn-phoenix-success btn-sm ms-2">
class="btn btn-phoenix-success btn-sm ms-2">
{% trans "Add" %} {% trans "Add" %}
</a> </a>
</td> </td>
@ -201,17 +121,15 @@
</div> </div>
</div> </div>
</div> </div>
</div>
</div> </div>
<div class="col-lg-6 col-xl-6"> <div class="col-lg-6 col-xl-6">
<div class="card rounded shadow"> <div class="card rounded shadow d-flex align-content-center">
<p class="card-header rounded-top fw-bold">{% trans 'Financial Details' %}</p> <p class="card-header rounded-top fw-bold">{% trans 'Financial Details' %}</p>
<div class="card-body"> <div class="card-body">
<div class="table-responsive scrollbar mb-3">
<table class="table table-sm fs-9 mb-0 overflow-hidden">
{% if car.finances %} {% if car.finances %}
<div class="table-responsive">
<table class="table fs-9 mb-0">
{% if perms.inventory.view_carfinance %} {% if perms.inventory.view_carfinance %}
<tr> <tr>
<th>{% trans "Cost Price"|capfirst %}</th> <th>{% trans "Cost Price"|capfirst %}</th>
@ -249,16 +167,13 @@
<tr> <tr>
<td colspan="2"> <td colspan="2">
{% if perms.inventory.change_carfinance %} {% if perms.inventory.change_carfinance %}
<a href="{% url 'car_finance_update' car.finances.pk %}" <a href="{% url 'car_finance_update' car.finances.pk %}" class="btn btn-phoenix-warning btn-sm mb-3">
class="btn btn-phoenix-warning btn-sm mb-3">
{% trans "Edit" %} {% trans "Edit" %}
</a> </a>
{% endif %} {% endif %}
{% else %} {% else %}
<p>{% trans "No finance details available." %}</p> <p>{% trans "No finance details available." %}</p>
<a href="{% url 'car_finance_create' car.pk %}" <a href="{% url 'car_finance_create' car.pk %}" class="btn btn-phoenix-success btn-sm mb-3">
class="btn btn-phoenix-success btn-sm mb-3">
{% trans "Add" %} {% trans "Add" %}
</a> </a>
</td> </td>
@ -268,12 +183,11 @@
</div> </div>
</div> </div>
</div> </div>
<div class="card rounded shadow mt-3"> <div class="card rounded shadow d-flex align-content-center mt-3">
<p class="card-header rounded-top fw-bold">{% trans 'Colors Details' %}</p> <p class="card-header rounded-top fw-bold">{% trans 'Colors Details' %}</p>
<div class="card-body"> <div class="card-body">
<div class="table-responsive"> <div class="table-responsive scrollbar mb-3">
<table class="table fs-9 mb-0"> <table class="table table-sm fs-9 mb-0 overflow-hidden">
<tbody class="align-middle">
{% if car.colors.exists %} {% if car.colors.exists %}
{% for color in car.colors.all %} {% for color in car.colors.all %}
<tr> <tr>
@ -282,9 +196,7 @@
<span>{{ color.exterior.get_local_name }}</span> <span>{{ color.exterior.get_local_name }}</span>
</td> </td>
<td class="align-middle"> <td class="align-middle">
<div class="text-end color-circle" <div class="text-end color-circle" style="background-color: rgb({{ color.exterior.rgb }});"></div>
style="background-color: rgb({{ color.exterior.rgb }});">
</div>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -293,13 +205,10 @@
<span>{{ color.interior.get_local_name }}</span> <span>{{ color.interior.get_local_name }}</span>
</td> </td>
<td class="align-middle"> <td class="align-middle">
<div class="text-end color-circle" <div class="text-end color-circle" style="background-color: rgb({{ color.interior.rgb }});"></div>
style="background-color: rgb({{ color.interior.rgb }});">
</div>
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %} {% else %}
{% else %}
<tr> <tr>
<td colspan="2"> <td colspan="2">
{% trans "No colors available for this car." %} {% trans "No colors available for this car." %}
@ -313,18 +222,16 @@
</td> </td>
</tr> </tr>
{% endif %} {% endif %}
</tbody>
</table> </table>
</div> </div>
</div> </div>
</div> </div>
<div class="card rounded shadow d-flex align-content-center mt-3">
<div class="card rounded shadow mt-3">
<p class="card-header rounded-top fw-bold">{% trans 'Reservations Details' %}</p> <p class="card-header rounded-top fw-bold">{% trans 'Reservations Details' %}</p>
<div class="card-body"> <div class="card-body">
<div class="table-responsive"> <div class="table-responsive scrollbar mb-3">
<table class="table table-sm fs-9 mb-0 overflow-hidden">
{% if car.is_reserved %} {% if car.is_reserved %}
<table class="table fs-9 mb-0">
<thead> <thead>
<tr> <tr>
<th>{% trans "Reserved By" %}</th> <th>{% trans "Reserved By" %}</th>
@ -341,50 +248,92 @@
{% if reservation.is_active %} {% if reservation.is_active %}
<form method="post" action="{% url 'reservations' reservation.id %}"> <form method="post" action="{% url 'reservations' reservation.id %}">
{% csrf_token %} {% csrf_token %}
<button type="submit" <button type="submit" name="action" value="renew" class="btn btn-sm btn-phoenix-success">
name="action"
value="renew"
class="btn btn-sm btn-phoenix-success">
{% trans "Renew" %} {% trans "Renew" %}
</button> </button>
<button type="submit" <button type="submit" name="action" value="cancel" class="btn btn-sm btn-phoenix-secondary">
name="action"
value="cancel"
class="btn btn-sm btn-phoenix-secondary">
{% trans "Cancel" %} {% trans "Cancel" %}
</button> </button>
</form> </form>
{% else %} {% else %}
<span class="badge bg-danger" <span class="badge bg-danger" style="width: 120px;">
style="width: 120px;">
{% trans "Expired" %} {% trans "Expired" %}
</span> </span>
{% endif %} {% endif %}
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %} {% else %}
{% else %}
<tr> <tr>
<td> <td>
<button type="button" <button type="button" class="btn btn-sm btn-phoenix-success" data-bs-toggle="modal" data-bs-target="#reserveModal">
class="btn btn-sm btn-phoenix-success"
data-bs-toggle="modal"
data-bs-target="#reserveModal">
{% trans 'Reserve' %} {% trans 'Reserve' %}
</button> </button>
{% endif %}
</td> </td>
</tr> </tr>
</tbody> </tbody>
{% endif %}
</table> </table>
<div class="table-responsive">
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<!-- Custom Card Modal -->
<div class="modal fade" id="customCardModal" tabindex="-1" aria-labelledby="customCardModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header bg-primary">
<h5 class="modal-title text-light" id="customCardModalLabel">{% trans 'Custom Card' %}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<!-- Content will be loaded here via AJAX -->
</div>
</div>
</div>
</div>
<!-- Reservation Modal -->
<div class="modal fade" id="reserveModal" tabindex="-1" aria-labelledby="reserveModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header bg-primary">
<h5 class="modal-title text-light" id="reserveModalLabel">{% trans 'Car Reservation' %}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
{% trans 'Are you sure you want to reserve this car?' %}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-sm btn-phoenix-danger" data-bs-dismiss="modal">
{% trans 'No' %}
</button>
<form method="POST" action="{% url 'reserve_car' car.id %}" class="d-inline">
{% csrf_token %}
<button type="submit" class="btn btn-phoenix-success btn-sm">{% trans "Yes" %}</button>
</form>
</div>
</div>
</div>
</div>
<!-- Specification Modal -->
<div class="modal fade" id="specificationsModal" tabindex="-1" aria-labelledby="specificationsModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="specificationsModalLabel">{% trans 'specifications'|upper %}</h5>
<button class="btn btn-close p-1" type="button" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div id="specificationsContent"></div>
</div>
<div class="modal-footer">
<button class="btn btn-phoenix-primary" type="button" data-bs-dismiss="modal">{% trans 'Close' %}</button>
</div>
</div>
</div>
</div>
<script> <script>
document.addEventListener("DOMContentLoaded", function () { document.addEventListener("DOMContentLoaded", function () {
@ -403,7 +352,7 @@
return cookieValue; return cookieValue;
} }
const csrfToken = getCookie("csrftoken");
const ajaxUrl = "{% url 'ajax_handler' %}"; const ajaxUrl = "{% url 'ajax_handler' %}";
const modal = document.getElementById("customCardModal"); const modal = document.getElementById("customCardModal");
@ -436,7 +385,7 @@
fetch(`${ajaxUrl}?action=get_specifications&trim_id={{ car.id_car_trim.id_car_trim }}`, { fetch(`${ajaxUrl}?action=get_specifications&trim_id={{ car.id_car_trim.id_car_trim }}`, {
headers: { headers: {
"X-Requested-With": "XMLHttpRequest", "X-Requested-With": "XMLHttpRequest",
"X-CSRFToken": {% csrf_token %}, "X-CSRFToken": csrfToken,
}, },
}) })
.then((response) => response.json()) .then((response) => response.json())
@ -472,7 +421,7 @@
const response = await fetch(`{% url 'reserve_car' car.pk %}`, { const response = await fetch(`{% url 'reserve_car' car.pk %}`, {
method: "POST", method: "POST",
headers: { headers: {
"X-CSRFToken": {% csrf_token %}, "X-CSRFToken": csrfToken,
"X-Requested-With": "XMLHttpRequest", "X-Requested-With": "XMLHttpRequest",
}, },
}); });

View File

@ -1,135 +1,135 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% load i18n static %} {% load i18n static %}
{% block title %} {% block title %}
{% trans 'inventory'|capfirst %} {% trans 'inventory'|capfirst %}
{% endblock %} {% endblock %}
{% block inventory %} {% block inventory %}
<a class="nav-link active fw-bold">{% trans "inventory" %}<span class="visually-hidden">(current)</span></a> <a class="nav-link active fw-bold">{% trans "inventory" %}<span class="visually-hidden">(current)</span></a>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<style>
.color-div {
width: 22px;
height: 22px;
border-radius: 50%;
border: 1px solid #ccc;
text-align: center;
vertical-align: middle;
line-height: 22px;
}
</style>
<div class="container"> <div class="container">
<div class="row g-3 justify-content-between mb-4"> <div class="row g-3 justify-content-between">
<div class="col-sm-12"> <div class="col-sm-12">
<!---->
<div class="card border h-100 w-100 overflow-hidden"> <div class="card border h-100 w-100 overflow-hidden">
<div class="bg-holder d-block bg-card" style="background-image:url({% static 'images/spot-illustrations/32.png' %});background-position: top right;"> <div class="bg-holder d-block bg-card" style="background-image:url({% static 'images/spot-illustrations/32.png' %});background-position: top right;">
</div> </div>
<!--/.bg-holder-->
<div class="d-dark-none"> <div class="d-dark-none">
<div class="bg-holder d-none d-sm-block d-xl-none d-xxl-block bg-card" style="background-image:url({% static 'images/spot-illustrations/dark_21.png' %});background-position: bottom right; background-size: auto;"> <div class="bg-holder d-none d-sm-block d-xl-none d-xxl-block bg-card" style="background-image:url({% static 'images/spot-illustrations/dark_21.png' %});background-position: bottom right; background-size: auto;">
</div> </div>
<!--/.bg-holder-->
</div> </div>
<div class="d-light-none"> <div class="d-light-none">
<div class="bg-holder d-none d-sm-block d-xl-none d-xxl-block bg-card" style="background-image:url({% static 'images/spot-illustrations/21.png' %});background-position: bottom right; background-size: auto;"> <div class="bg-holder d-none d-sm-block d-xl-none d-xxl-block bg-card" style="background-image:url({% static 'images/spot-illustrations/21.png' %});background-position: bottom right; background-size: auto;">
</div> </div>
<!--/.bg-holder-->
</div> </div>
<div class="card-body px-5 position-relative"> <div class="card-body px-lg-5 position-relative">
<div class="badge badge-phoenix fs-10 badge-phoenix-warning mb-2">{{ _("Year") }}<span class="ms-1 fw-bold">{{ cars.first.year }}</span></div> <br><br><h3 class="mb-2">{{ cars.first.id_car_make.get_local_name }}<span class="ms-2 text-body-tertiary fw-semibold">{{ cars.first.id_car_model.get_local_name }}</span></h3>
<h3 class="mb-2">{{ cars.first.id_car_make.get_local_name }}<span class="ms-2 text-body-tertiary fw-semibold">{{ cars.first.id_car_model.get_local_name }}</span></h3>
</div>
<div class="card-footer border-0 py-0 px-5 z-1">
<p class="text-body-tertiary fw-semibold">{{ cars.first.id_car_serie.name }}, <span class="fs-10">{{ cars.first.id_car_trim.name }}</span></p> <p class="text-body-tertiary fw-semibold">{{ cars.first.id_car_serie.name }}, <span class="fs-10">{{ cars.first.id_car_trim.name }}</span></p>
</div> </div>
</div> </div>
<!---->
</div> </div>
</div> </div>
<div class="row g-3 justify-content-between mb-4"> <div class="row g-3 justify-content-between mt-4">
<div class="col-sm-12"> <div class="col-sm-12">
<div class="table-responsive scrollbar mx-n1 px-1"> <div class="table-list" id="inventoryTable">
<table class="table fs-9 mb-0 leads-table border-top border-translucent"> <div class="table-responsive scrollbar mb-3">
<thead> <table class="table table-sm fs-9 mb-0 overflow-hidden">
<thead class="text-body">
<tr> <tr>
<th>{% trans "VIN" %}</th> <th class="pe-1 align-middle white-space-nowrap text-center" data-sort="stock">{% trans 'Stock' %}</th>
<th>{% trans "Year" %}</th> <th class="pe-1 align-middle white-space-nowrap text-start" data-sort="vin" style="min-width: 4.5rem;">{% trans "VIN" %}</th>
<th>{% trans 'Exterior Color' %}</th> <th class="pe-1 align-middle white-space-nowrap text-center" data-sort="date">{% trans "Year"|upper %}</th>
<th>{% trans 'Interior Color' %}</th> <th class="pe-1 align-middle white-space-nowrap text-center" data-sort="extColor" style="min-width: 8.5rem">{% trans 'Exterior Color'|upper %}</th>
<th>{% trans "Showroom Location" %}</th> <th class="pe-1 align-middle white-space-nowrap text-center" data-sort="intColor" style="min-width: 8.5rem">{% trans 'Interior Color'|upper %}</th>
<th>{% trans "Actions" %}</th> <th class="pe-1 align-middle white-space-nowrap text-center" data-sort="location" style="min-width: 12.5rem;">{% trans "Showroom Location"|upper %}</th>
<th class="pe-1 align-middle white-space-nowrap text-center" data-sort="status">{% trans 'Status'|upper %}</th>
<th class="pe-1 align-middle white-space-nowrap text-center" data-sort="age" style="min-width: 7rem">{% trans 'Age'|upper %}</th>
<th class="no-sort align-middle white-space-nowrap text-center"></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for car in cars %} {% for car in cars %}
<tr class="{% if car.is_reserved %}table-danger{% endif %}"> <tr>
<td>{{ car.vin }}</td> <td class="align-middle white-space-nowrap text-center fw-bold text-body-tertiary">
<td>{{ car.year }}</td> {% if car.stock_type == "new" %}
<span class="badge badge-phoenix fs-10 badge-phoenix-success"><span class="badge-label">{{_("New")}}</span><span class="ms-1" data-feather="plus" style="height:13px;width:13px;"></span></span>
{% elif car.status == "used" %}
<span class="badge badge-phoenix fs-10 badge-phoenix-info"><span class="badge-label">{{_("Used")}}</span><span class="ms-1" data-feather="plus" style="height:13px;width:13px;"></span></span>
{% endif %}
</td>
<td class="align-middle white-space-nowrap text-start fw-bold">{{ car.vin }}</td>
<td class="align-middle white-space-nowrap text-center fw-bold">{{ car.year }}</td>
{% if car.colors.exists %} {% if car.colors.exists %}
<td> <td class="align-middle white-space-nowrap text-body fs-9 text-start">
<div class="d-flex flex-column align-items-center"> <div class="d-flex flex-column align-items-center">
<span class="color-div" <span class="color-div" style="background-color: rgb({{ car.colors.first.exterior.rgb }});" title="{{ car.colors.first.exterior.get_local_name }}"></span><span>{{ car.colors.first.exterior.get_local_name }}</span>
style="background-color: rgb({{ car.colors.first.exterior.rgb }});"
title="{{ car.colors.first.exterior.get_local_name }}">
</span>
</div> </div>
</td> </td>
<td> <td class="align-middle white-space-nowrap text-body fs-9 text-start">
<div class="d-flex flex-column align-items-center"> <div class="d-flex flex-column align-items-center">
<span class="color-div" <span class="color-div" style="background-color: rgb({{ car.colors.first.interior.rgb }});" title="{{ car.colors.first.interior.get_local_name }}"></span><span>{{ car.colors.first.interior.get_local_name }}</span>
style="background-color: rgb({{ car.colors.first.interior.rgb }});"
title="{{ car.colors.first.interior.get_local_name }}">
</span>
</div> </div>
</td> </td>
{% else %} {% else %}
<td><span class="color-div">{% trans 'No Color' %}</span></td> <td class="align-middle white-space-nowrap text-body fs-9 text-center"><span class="color-div">{% trans 'No Color' %}</span></td>
<td><span class="color-div">{% trans 'No Color' %}</span></td> <td class="align-middle white-space-nowrap text-body fs-9 text-center"><span class="color-div">{% trans 'No Color' %}</span></td>
{% endif %} {% endif %}
<td class="align-middle white-space-nowrap text-body fs-9 text-center">
{% if car.location.is_owner_showroom %} {% if car.location.is_owner_showroom %}
<td> {% trans 'Our Showroom' %}</td> {% trans 'Our Showroom' %}
{% else %} {% else %}
<td>{{ car.location.showroom.get_local_name }}</td> {{ car.location.showroom.get_local_name }}
{% endif %} {% endif %}
<td> </td>
<a href="{% url 'car_detail' car.pk %}" class="btn btn-success"> <td class="status align-middle white-space-nowrap text-center fw-bold text-body-tertiary">
{% trans "view"|capfirst %} {% if car.status == "available" %}
</a> <span class="badge badge-phoenix fs-10 badge-phoenix-success"><span class="badge-label">{{_("Available")}}</span><span class="ms-1" data-feather="check" style="height:13px;width:13px;"></span></span>
{% elif car.status == "sold" %}
<span class="badge badge-phoenix fs-10 badge-phoenix-primary"><span class="badge-label">{{_("Sold")}}</span><span class="ms-1" data-feather="package" style="height:13px;width:13px;"></span></span>
{% elif car.status == "hold" %}
<span class="badge badge-phoenix fs-10 badge-phoenix-warning"><span class="badge-label">{{ _("Hold") }}</span><span class="ms-1" data-feather="alert-octagon" style="height:13px;width:13px;"></span></span>
{% elif car.status == "reserved" %}
<span class="badge badge-phoenix fs-10 badge-phoenix-danger"><span class="badge-label">{{ _("Reserved") }}</span><span class="ms-1" data-feather="info" style="height:13px;width:13px;"></span></span>
{% elif car.status == "damaged" %}
<span class="badge badge-phoenix fs-10 badge-phoenix-secondary"><span class="badge-label">{{ _("Damaged") }}</span><span class="ms-1" data-feather="x" style="height:13px;width:13px;"></span></span>
{% endif %}
</td>
<td class="date align-middle white-space-nowrap text-body-tertiary fs-9 ps-4 text-start">
<span class="fw-light">{{ car.receiving_date|timesince }}</span>
</td>
<td class="align-middle white-space-nowrap text-end pe-0 ps-4">
<div class="btn-reveal-trigger position-static">
<button class="btn btn-sm dropdown-toggle dropdown-caret-none transition-none btn-reveal fs-10" type="button" data-bs-toggle="dropdown" data-boundary="window" aria-haspopup="true" aria-expanded="false" data-bs-reference="parent"><span class="fas fa-ellipsis-h fs-10"></span></button>
<div class="dropdown-menu dropdown-menu-end py-2"><a class="dropdown-item" href="{% url 'car_detail' car.pk %}">{% trans "view"|capfirst %}</a>
<div class="dropdown-divider"></div><a class="dropdown-item text-danger" href="#!">Remove</a>
</div>
</div>
</td> </td>
</tr> </tr>
{% empty %} {% empty %}
<tr> <tr>
<td colspan="6">{% trans "No cars available." %}</td> <td colspan="7" class="d-flex flex-column align-items-center">
<p class="text-muted">{% trans "No cars available." %}</p>
<a href="{% url 'add_car' %}" class="btn btn-primary">{% trans "Add a Car" %}</a>
</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
</div>
</div>
<!-- Pagination --> <!-- Pagination -->
{% if is_paginated %} {% if is_paginated %}
<nav aria-label="Page navigation"> <nav aria-label="Page navigation">
<ul class="pagination pagination-sm justify-content-center"> <ul class="pagination pagination-sm justify-content-center">
{% if page_obj.has_previous %} {% if page_obj.has_previous %}
<li class="page-item"> <li class="page-item">
<a class="page-link" href="?page=1" aria-label="First"> <a class="page-link" href="?page=1" aria-label="First">&laquo;&laquo;</a>
&laquo;&laquo;
</a>
</li> </li>
<li class="page-item"> <li class="page-item">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}" aria-label="Previous"> <a class="page-link" href="?page={{ page_obj.previous_page_number }}" aria-label="Previous">&laquo;</a>
&laquo;
</a>
</li>
{% else %}
<li class="page-item disabled">
<span class="page-link">&laquo;&laquo;</span>
</li>
<li class="page-item disabled">
<span class="page-link">&laquo;</span>
</li> </li>
{% endif %} {% endif %}
{% for num in page_obj.paginator.page_range %} {% for num in page_obj.paginator.page_range %}
@ -145,21 +145,10 @@
{% endfor %} {% endfor %}
{% if page_obj.has_next %} {% if page_obj.has_next %}
<li class="page-item"> <li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}" aria-label="Next"> <a class="page-link" href="?page={{ page_obj.next_page_number }}" aria-label="Next">&raquo;</a>
&raquo;
</a>
</li> </li>
<li class="page-item"> <li class="page-item">
<a class="page-link" href="?page={{ page_obj.paginator.num_pages }}" aria-label="Last"> <a class="page-link" href="?page={{ page_obj.paginator.num_pages }}" aria-label="Last">&raquo;&raquo;</a>
&raquo;&raquo;
</a>
</li>
{% else %}
<li class="page-item disabled">
<span class="page-link">&raquo;</span>
</li>
<li class="page-item disabled">
<span class="page-link">&raquo;&raquo;</span>
</li> </li>
{% endif %} {% endif %}
</ul> </ul>
@ -168,5 +157,4 @@
</div> </div>
</div> </div>
</div> </div>
</div>
{% endblock %} {% endblock %}

View File

@ -0,0 +1 @@
{% load static %}

137
templates/test.html Normal file
View File

@ -0,0 +1,137 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Car Inventory</title>
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome for Icons -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css" rel="stylesheet">
<!-- Custom CSS -->
<style>
.color-circle {
width: 22px;
height: 22px;
border-radius: 50%;
border: 1px solid #ccc;
display: inline-block;
}
.table-hover tbody tr:hover {
background-color: #f8f9fa;
}
.badge {
font-size: 0.9rem;
}
.table-responsive {
overflow-x: auto;
}
.table thead th {
background-color: #007bff;
color: white;
}
.table tbody td {
vertical-align: middle;
}
</style>
</head>
<body>
<div class="container my-5">
<h1 class="text-center mb-4">Car Inventory</h1>
<!-- Search and Filter Section -->
<div class="row mb-4 g-3">
<div class="col-md-6">
<div class="input-group">
<span class="input-group-text"><i class="fas fa-search"></i></span>
<input type="text" class="form-control" placeholder="Search by VIN, Make, or Model">
</div>
</div>
<div class="col-md-3">
<select class="form-select">
<option>All Years</option>
<option>2023</option>
<option>2022</option>
<option>2021</option>
</select>
</div>
<div class="col-md-3">
<button class="btn btn-primary w-100"><i class="fas fa-filter"></i> Filter</button>
</div>
</div>
<!-- Car Listing Table -->
<div class="table-responsive">
<table class="table table-hover table-bordered">
<thead>
<tr>
<th>#</th>
<th>Image</th>
<th>Make & Model</th>
<th>Year</th>
<th>VIN</th>
<th>Exterior Color</th>
<th>Interior Color</th>
<th>Location</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<!-- Example Row -->
<tr>
<td>1</td>
<td><img src="https://via.placeholder.com/100" alt="Car Image" class="img-thumbnail" style="width: 100px;"></td>
<td>Toyota Camry</td>
<td>2023</td>
<td>1HGCM82633A123456</td>
<td><span class="color-circle" style="background-color: #0000ff;"></span> Blue</td>
<td><span class="color-circle" style="background-color: #ffffff;"></span> White</td>
<td>Main Showroom</td>
<td><span class="badge bg-success">Available</span></td>
<td>
<a href="#" class="btn btn-sm btn-outline-primary"><i class="fas fa-eye"></i> View</a>
<a href="#" class="btn btn-sm btn-outline-warning"><i class="fas fa-edit"></i> Edit</a>
</td>
</tr>
<!-- Add more rows here -->
<tr>
<td>2</td>
<td><img src="https://via.placeholder.com/100" alt="Car Image" class="img-thumbnail" style="width: 100px;"></td>
<td>Honda Accord</td>
<td>2022</td>
<td>1HGCM82633A654321</td>
<td><span class="color-circle" style="background-color: #ff0000;"></span> Red</td>
<td><span class="color-circle" style="background-color: #000000;"></span> Black</td>
<td>Downtown Showroom</td>
<td><span class="badge bg-warning">Reserved</span></td>
<td>
<a href="#" class="btn btn-sm btn-outline-primary"><i class="fas fa-eye"></i> View</a>
<a href="#" class="btn btn-sm btn-outline-warning"><i class="fas fa-edit"></i> Edit</a>
</td>
</tr>
</tbody>
</table>
</div>
<!-- Pagination -->
<nav aria-label="Page navigation" class="mt-4">
<ul class="pagination justify-content-center">
<li class="page-item disabled">
<a class="page-link" href="#" tabindex="-1" aria-disabled="true"><i class="fas fa-chevron-left"></i></a>
</li>
<li class="page-item active"><a class="page-link" href="#">1</a></li>
<li class="page-item"><a class="page-link" href="#">2</a></li>
<li class="page-item"><a class="page-link" href="#">3</a></li>
<li class="page-item">
<a class="page-link" href="#"><i class="fas fa-chevron-right"></i></a>
</li>
</ul>
</nav>
</div>
<!-- Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>