Sales section updated #80

Merged
ismail merged 14 commits from frontend into main 2025-06-25 14:18:55 +03:00
26 changed files with 351 additions and 339 deletions
Showing only changes of commit 39ed2620cd - Show all commits

BIN
.DS_Store vendored

Binary file not shown.

Binary file not shown.

BIN
static/.DS_Store vendored

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 878 KiB

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 864 KiB

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 873 KiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 875 KiB

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,35 +1,37 @@
{% extends "base.html" %}
{% load i18n custom_filters humanize %}
{% load static i18n custom_filters humanize %}
{%block title%} {%trans 'Stocks'%} {%endblock%}
{% block customCSS %}
<style>
.htmx-indicator{
opacity:0;
<style>
.htmx-indicator {
opacity: 0;
transition: opacity 500ms ease-in;
}
.htmx-request .htmx-indicator{
opacity:1;
.htmx-request .htmx-indicator {
opacity: 1;
}
.htmx-request.htmx-indicator{
opacity:1;
.htmx-request.htmx-indicator {
opacity: 1;
}
.on-before-request{
.on-before-request {
opacity: 0.5;
pointer-events: none;
}
.transition {
transition: all ease-in 1s ;
transition: all ease-in 1s;
}
</style>
</style>
{% endblock customCSS %}
{% block content %}
<div class="mb-9">
<div class="mb-9">
<div id="projectSummary">
<div class="row g-3 justify-content-between align-items-end mb-4">
<div class="col-12 col-sm-auto">
<ul class="nav nav-links mx-n2"
<ul
class="nav nav-links mx-n2"
hx-boost="true"
hx-push-url='false'
hx-push-url="false"
hx-target=".table-responsive"
hx-select=".table-responsive"
hx-swap="innerHTML show:window:top"
@ -37,28 +39,22 @@
hx-on::before-request="on_before_request()"
hx-on::after-request="on_after_request()">
<li class="nav-item">
<a class="nav-link px-2 py-1 active"
aria-current="page"
href="{% url 'car_list' %}"><span>{{ _("All") }}</span><span class="text-body-tertiary fw-semibold">({{ stats.all }})</span></a>
<a class="nav-link px-2 py-1 active" aria-current="page" href="{% url 'car_list' %}"><span>{{ _("All") }}</span><span class="text-body-tertiary fw-semibold">({{ stats.all }})</span></a>
</li>
<li class="nav-item">
<a class="nav-link px-2 py-1"
href="{% url 'car_list' %}?status=available"><span>{{ _("Available") }}</span><span class="text-body-tertiary fw-semibold">({{ stats.available }})</span></a>
<a class="nav-link px-2 py-1" href="{% url 'car_list' %}?status=available"><span>{{ _("Available") }}</span><span class="text-body-tertiary fw-semibold">({{ stats.available }})</span></a>
</li>
<li class="nav-item">
<a class="nav-link px-2 py-1"
href="{% url 'car_list' %}?status=reserved"><span>{{ _("Reserved") }}</span><span class="text-body-tertiary fw-semibold">({{ stats.reserved }})</span></a>
<a class="nav-link px-2 py-1" href="{% url 'car_list' %}?status=reserved"><span>{{ _("Reserved") }}</span><span class="text-body-tertiary fw-semibold">({{ stats.reserved }})</span></a>
</li>
<li class="nav-item">
<a class="nav-link px-2 py-1"
href="{% url 'car_list' %}?status=transfer"><span>{{ _("Transfer") }}</span><span class="text-body-tertiary fw-semibold">({{ stats.transfer }})</span></a>
<a class="nav-link px-2 py-1" href="{% url 'car_list' %}?status=transfer"><span>{{ _("Transfer") }}</span><span class="text-body-tertiary fw-semibold">({{ stats.transfer }})</span></a>
</li>
<li class="nav-item">
<a class="nav-link px-2 py-1" href="{% url 'car_list' %}?status=sold"><span>{{ _("Sold") }}</span><span class="text-body-tertiary fw-semibold">({{ stats.sold }})</span></a>
</li>
<li class="nav-item">
<button hx-on:click="toggle_filter()"
class="btn btn-sm btn-phoenix-primary px-2 py-1">
<button hx-on:click="toggle_filter()" class="btn btn-sm btn-phoenix-primary px-2 py-1">
<span><span class="fa fa-filter me-1"></span>{{ _("Filter") }}</span><span class="fas fa-caret-down fs-9 ms-1 filter-icon"></span>
</button>
</li>
@ -71,19 +67,21 @@
</div>
<div class="search-box me-3">
<form class="position-relative">
<input class="form-control search-input search"
name='search'
<input
class="form-control search-input search"
name="search"
type="search"
placeholder="Search"
aria-label="Search"
hx-get="{% url 'car_list' %}"
hx-trigger='keyup changed delay:500ms'
hx-target='.table-responsive'
hx-select='.table-responsive'
hx-trigger="keyup changed delay:500ms"
hx-target=".table-responsive"
hx-select=".table-responsive"
hx-swap="innerHTML show:window:top"
hx-indicator=".htmx-indicator"
hx-on::before-request="on_before_request()"
hx-on::after-request="on_after_request()" />
hx-on::after-request="on_after_request()"
/>
<span class="fas fa-search search-box-icon"></span>
</form>
</div>
@ -91,10 +89,11 @@
</div>
</div>
<div class="d-flex align-items-center d-none filter">
<select hx-get="{% url 'car_list' %}"
<select
hx-get="{% url 'car_list' %}"
name="make"
hx-target='.model-select'
hx-select='.model-select'
hx-target=".model-select"
hx-select=".model-select"
hx-swap="outerHTML show:window:top"
hx-indicator=".htmx-indicator"
class="form-select form-control-sm me-1 make"
@ -102,13 +101,16 @@
hx-on::before-request="filter_before_request()"
hx-on::after-request="filter_after_request()">
<option selected="" value="" disabled>{{ _("Make") }}</option>
{% for m in make %}<option value="{{ m.pk }}">{{ m.get_local_name|default:m.name }}</option>{% endfor %}
{% for m in make %}
<option value="{{ m.pk }}">{{ m.get_local_name|default:m.name }}</option>
{% endfor %}
</select>
<select hx-get="{% url 'car_list' %}"
<select
hx-get="{% url 'car_list' %}"
hx-include=".make"
name="model"
hx-target='.year'
hx-select='.year'
hx-target=".year"
hx-select=".year"
hx-swap="outerHTML show:window:top"
hx-indicator=".htmx-indicator"
class="form-select form-control-sm me-1 model-select"
@ -116,50 +118,52 @@
hx-on::before-request="filter_before_request()"
hx-on::after-request="filter_after_request()">
<option selected="" value="" disabled>{{ _("Model") }}</option>
{% for m in model %}<option value="{{ m.pk }}">{{ m.get_local_name|default:m.name }}</option>{% endfor %}
{% for m in model %}
<option value="{{ m.pk }}">{{ m.get_local_name|default:m.name }}</option>
{% endfor %}
</select>
<select class="form-select form-control-sm me-1 year"
name="year"
aria-label="Default select example">
<select class="form-select form-control-sm me-1 year" name="year" aria-label="Default select example">
<option selected="" value="" disabled>{{ _("Year") }}</option>
{% for y in year %}<option value="{{ y.0 }}">{{ y.0 }}</option>{% endfor %}
{% for y in year %}
<option value="{{ y.0 }}">{{ y.0 }}</option>
{% endfor %}
</select>
<select class="form-select form-control-sm me-1 car_status"
name="car_status"
aria-label="Default select example">
<select class="form-select form-control-sm me-1 car_status" name="car_status" aria-label="Default select example">
<option selected="" value="">{{ _("All") }}</option>
<option value="available">{{ _("Available") }}</option>
<option value="reserved">{{ _("Reserved") }}</option>
<option value="sold">{{ _("Sold") }}</option>
<option value="transfer">{{ _("Transfer") }}</option>
</select>
<button id="search"
<button
id="search"
hx-get="{% url 'car_list' %}"
hx-include=".make,.model,.year,.car_status"
hx-indicator=".htmx-indicator"
hx-target='.table-responsive'
hx-select='.table-responsive'
hx-target=".table-responsive"
hx-select=".table-responsive"
hx-swap="outerHTML show:window:top"
class="btn btn-sm btn-phoenix-primary ms-1"
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 class="row">
<form hx-boost='true' action="{% url 'bulk_update_car_price' %}" method="post"
hx-include=".car-checkbox"
class="update-price-form d-flex flex-row align-items-center ms-auto w-25 d-none" style="float:right;">
<form hx-boost="true" action="{% url 'bulk_update_car_price' %}" method="post" hx-include=".car-checkbox" class="update-price-form d-flex flex-row align-items-center ms-auto w-25 d-none" style="float: right;">
{% csrf_token %}
<div class="form-floating me-2">
<input class="form-control" type="number" placeholder='{{ _("Search") }}' name="price" aria-label="Price" id="price">
<input class="form-control" type="number" placeholder='{{ _("Search") }}' name="price" aria-label="Price" id="price" />
<label for="price">{{ _("Price") }}</label>
</div>
<button class="btn btn-outline-primary" type="submit">{{ _("Search") }}</button>
</form>
<div class="table-responsive scrollbar transition">
<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-push-url='false'
hx-push-url="false"
hx-include=".make,.model,.year,.car_status"
hx-target=".table-responsive"
hx-select=".table-responsive"
@ -167,91 +171,90 @@
hx-indicator=".htmx-indicator"
hx-on::before-request="on_before_request()"
hx-on::after-request="on_after_request()"></div>
<table class="table align-items-center table-flush">
<thead class="text-body">
<tr class="bg-body-highlight">
<th class="sort white-space-nowrap align-middle" scope="col">
<div class="w-100 list table-responsive" >
<div class="form-check">
<input class="form-check-input" type="checkbox" id="select-all" />
<input class="form-check-input ms-4" type="checkbox" id="select-all" /> <span class="ms-1 text-body-tertiary">{{ _("Select All") }}</span>
</div>
</th>
<th class="sort white-space-nowrap align-middle" scope="col">{{ _("VIN") }}</th>
<th class="sort white-space-nowrap align-middle" scope="col">{{ _("Make") }}</th>
<th class="sort white-space-nowrap align-middle" scope="col">{{ _("Model") }}</th>
<th class="sort white-space-nowrap align-middle" scope="col">{{ _("Year") }}</th>
<th class="sort white-space-nowrap align-middle" scope="col">{{ _("Trim") }}</th>
<th class="sort white-space-nowrap align-middle" scope="col">{{ _("Color") }}</th>
<th class="sort white-space-nowrap align-middle" scope="col">{{ _("Date Received") }}</th>
<th class="sort white-space-nowrap align-middle" scope="col">{{ _("Status") }}</th>
<th class="sort white-space-nowrap align-middle" scope="col">{{ _("Inventory Ready") }}</th>
<th class="sort white-space-nowrap align-middle" scope="col"></th>
</tr>
</thead>
<tbody class="list" id="project-list-table-body">
{% for car in cars %}
<tr class="position-static">
<td class="align-middle white-space-nowrap">
<div class="card border mb-3 py-0 px-0" id="project-list-table-body">
<div class="card-body">
<div class="row align-items-center">
<div class="col-auto">
<div class="form-check">
<input class="form-check-input car-checkbox" type="checkbox" name="car" value="{{ car.pk }}" id="car-{{car.pk}}">
</div>
</td>
<td class="align-middle white-space-nowrap ps-1">
<a class="fw-bold" href="{% url 'car_detail' car.slug %}">{{ car.vin }}</a>
</td>
<td class="align-middle white-space-nowrap">
{% if car.id_car_make %}
<p class="text-body mb-0">{{ car.id_car_make.get_local_name|default:car.id_car_make.name }}</p>
{% endif %}
</td>
<td class="align-middle white-space-nowrap">
{% if car.id_car_model %}
<p class="text-body mb-0">{{ car.id_car_model.get_local_name|default:car.id_car_model.name }}</p>
{% endif %}
</td>
<td class="align-middle white-space-nowrap">
<p class="text-body mb-0">{{ car.year }}</p>
</td>
<td class="align-middle white-space-nowrap">
<p class="fw-bold text-body mb-0">{{ car.id_car_trim }}</p>
</td>
<td class="align-middle white-space-nowrap">
<div class="d-flex flex-row align-items-center">
<div class="d-flex flex-column align-items-center">
<span class="color-div"
style="background: linear-gradient(90deg, rgba({{ car.colors.exterior.rgb }},1) 10%, rgba({{ car.colors.exterior.rgb }},0.10) 100%)"
title="{{ car.colors.exterior.get_local_name }}"></span><span>{{ car.colors.exterior.get_local_name }}</span>
</div>
<div class="d-flex flex-column align-items-center">
<span class="color-div"
style="background: linear-gradient(90deg, rgba({{ car.colors.interior.rgb }},1) 10%, rgba({{ car.colors.interior.rgb }},0.10) 100%)"
title="{{ car.colors.interior.get_local_name }}"></span><span>{{ car.colors.interior.get_local_name }}</span>
<input class="form-check-input car-checkbox" type="checkbox" name="car" value="{{ car.pk }}" id="car-{{car.pk}}" />
</div>
</div>
</td>
<td class="align-middle white-space-nowrap">
<p class="fw-bold text-body mb-0">{{ car.receiving_date|naturalday|capfirst }}</p>
</td>
<td class="align-middle white-space-nowrap statuses">
<!-- Vehicle Image/Icon -->
<div class="col-auto">
<div class="avatar avatar-3xl">
<img class="rounded-soft shadow shadow-lg" src="{% static 'images/cars/' %}{{ car.vin }}.png" alt="{{ car.vin }}" />
</div>
</div>
<!-- Vehicle Details -->
<div class="col">
<div class="row">
<!-- Make/Model/Specs -->
<div class="col-md-4">
<h5 class="text-body-emphasis fw-bold">{{ car.year }}</h5>
<p class="text-body-emphasis fw-bold">
<a href="{% url 'car_detail' car.slug %}" class="text-decoration-none text-body-emphasis">
{{ car.id_car_make.get_local_name }}
</a>
<small>{{ car.id_car_model.get_local_name }}</small>
</p>
<small class="text-body-secondary" dir="ltr">
{{ car.id_car_trim }}
</small>
</div>
<!-- Color and Date -->
<div class="col-md-3">
<p class="text-body mb-1">
{{ car.colors.exterior.get_local_name }}
</p>
<small class="text-body-secondary">
{{ car.receiving_date|naturalday|capfirst }}
</small>
</div>
<!-- Status Badge -->
<div class="col-md-3">
{% if car.status == "available" %}
<span class="badge badge-phoenix fs-11 badge-phoenix-success">{{ _("Available") }}</span>
<span class="badge badge-phoenix fs-10 badge-phoenix-success text-uppercase px-3 py-2">{{ _("Available") }}</span>
{% elif car.status == "reserved" %}
<span class="badge badge-phoenix fs-11 badge-phoenix-danger">{{ _("Reserved") }}</span>
<span class="badge badge-phoenix fs-10 badge-phoenix-warning text-uppercase px-3 py-2">{{ _("Reserved") }}</span>
{% elif car.status == "sold" %}
<span class="badge badge-phoenix fs-11 badge-phoenix-info">{{ _("Sold") }}</span>
<span class="badge badge-phoenix fs-10 badge-phoenix-danger text-uppercase px-3 py-2">{{ _("Sold") }}</span>
{% elif car.status == "transfer" %}
<span class="badge badge-phoenix fs-11 badge-phoenix-warning">{{ _("Transfer") }}</span>
<span class="badge badge-phoenix fs-10 badge-phoenix-info text-uppercase px-3 py-2">{{ _("Transfer") }}</span>
{% endif %}
</td>
<td class="align-middle product white-space-nowrap">
{% if not car.ready %}
<span class="text-danger"> {{ _("NO") }} </span>
{%else%}
<span class="text-success"> {{ _("YES") }} </span>
{%endif%}
</td>
<td class="align-middle text-end white-space-nowrap pe-0 action">
</div>
<!-- Ready Status -->
<div class="col-md-2">
<div class="d-flex align-items-center">
<span class="fs-10 fw-light me-2">{{ _("Inventory Ready") }}</span>
{% if car.ready %}
<span class="badge bg-success rounded-circle p-1 me-2">
<span class="visually-hidden">Ready</span>
</span>
<span class="text-success fw-bold">{{ _("Yes") }}</span>
{% else %}
<span class="badge bg-danger rounded-circle p-1 me-2">
<span class="visually-hidden">Not Ready</span>
</span>
<span class="text-danger fw-bold">{{ _("No") }}</span>
{% endif %}
</div>
</div>
</div>
</div>
<!-- Action Menu -->
<div class="col-auto">
<div class="btn-reveal-trigger position-static">
<button class="btn btn-sm dropdown-toggle dropdown-caret-none transition-none btn-reveal fs-10"
<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"
@ -261,89 +264,98 @@
<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.slug %}">{{ _("View") }}</a>
<a class="dropdown-item" href="#!">{{ _("Export") }}</a>
<a class="dropdown-item" href="{% url 'car_detail' car.slug %}"> <span class="fas fa-eye me-2"></span>{{ _("View") }} </a>
<a class="dropdown-item" href="{% url 'car_update' car.slug %}"> <span class="fas fa-edit me-2"></span>{{ _("Edit") }} </a>
<div class="dropdown-divider"></div>
<a class="dropdown-item text-danger" href="{% url 'car_delete' car.slug %}"> <span class="fas fa-trash me-2"></span>{{ _("Remove") }} </a>
</div>
</div>
</div>
</div>
</div>
</div>
{% empty %}
<div class="text-center py-5">
<div class="text-body-secondary">
<span class="fas fa-car fa-4x mb-3 opacity-50"></span>
<h4 class="text-body-secondary">{{ _("No vehicles found") }}</h4>
<p class="text-body-tertiary">{{ _("Try adjusting your search criteria or filters") }}</p>
</div>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="d-flex justify-content-end mt-3">
<div class="d-flex">
{% if is_paginated %}
{% include 'partials/pagination.html' %}
{% endif %}
{% if is_paginated %} {% include 'partials/pagination.html' %} {% endif %}
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block customJS %}
{% endblock %} {% block customJS %}
<script>
links = document.querySelectorAll('.nav-link')
links.forEach(link => {
link.addEventListener('click', () => {
links.forEach(link => {
link.classList.remove('active')
})
link.classList.add('active')
})
})
links = document.querySelectorAll(".nav-link");
links.forEach((link) => {
link.addEventListener("click", () => {
links.forEach((link) => {
link.classList.remove("active");
});
link.classList.add("active");
});
});
function on_before_request() {
document.querySelector('.table').classList.toggle('on-before-request')
document.querySelector('.model-select').classList.add('on-after-request')
document.querySelector(".table").classList.toggle("on-before-request");
document.querySelector(".model-select").classList.add("on-after-request");
}
function on_after_request() {
document.querySelector('.table').classList.remove('on-before-request')
document.querySelector('.model-select').classList.remove('on-after-request')
document.querySelector(".table").classList.remove("on-before-request");
document.querySelector(".model-select").classList.remove("on-after-request");
}
function toggle_filter(){
document.querySelector('.filter').classList.toggle('d-none')
document.querySelector('.filter-icon').classList.toggle("fa-caret-down");
document.querySelector('.filter-icon').classList.toggle("fa-caret-up");
function toggle_filter() {
document.querySelector(".filter").classList.toggle("d-none");
document.querySelector(".filter-icon").classList.toggle("fa-caret-down");
document.querySelector(".filter-icon").classList.toggle("fa-caret-up");
}
function filter_before_request(){
document.querySelector('.model-select').setAttribute('disabled', true)
document.querySelector('.year').setAttribute('disabled', true)
document.querySelector('.car_status').setAttribute('disabled', true)
function filter_before_request() {
document.querySelector(".model-select").setAttribute("disabled", true);
document.querySelector(".year").setAttribute("disabled", true);
document.querySelector(".car_status").setAttribute("disabled", true);
}
function filter_after_request(){
document.querySelector('.model-select').removeAttribute('disabled')
document.querySelector('.year').removeAttribute('disabled')
document.querySelector('.car_status').removeAttribute('disabled')
function filter_after_request() {
document.querySelector(".model-select").removeAttribute("disabled");
document.querySelector(".year").removeAttribute("disabled");
document.querySelector(".car_status").removeAttribute("disabled");
}
document.getElementById('select-all').addEventListener('change', function() {
document.getElementById("select-all").addEventListener("change", function () {
const checkboxes = document.querySelectorAll('#project-list-table-body input[type="checkbox"]');
if (this.checked) {
checkboxes.forEach(checkbox => checkbox.checked = true);
checkboxes.forEach((checkbox) => (checkbox.checked = true));
} else {
checkboxes.forEach(checkbox => checkbox.checked = false);
checkboxes.forEach((checkbox) => (checkbox.checked = false));
}
updateFormVisibility();
});
const cbox = document.querySelectorAll('.car-checkbox');
cbox.forEach(checkbox => {
checkbox.addEventListener('change', function() {
const cbox = document.querySelectorAll(".car-checkbox");
cbox.forEach((checkbox) => {
checkbox.addEventListener("change", function () {
updateFormVisibility();
});
});
function updateFormVisibility() {
const form = document.querySelector('.update-price-form');
const checkedCount = document.querySelectorAll('.car-checkbox:checked').length;
const form = document.querySelector(".update-price-form");
const checkedCount = document.querySelectorAll(".car-checkbox:checked").length;
const submitButton = form.querySelector('button[type="submit"]');
if (checkedCount > 0) {
form.classList.remove('d-none');
form.classList.remove("d-none");
submitButton.textContent = `Update Cost Price (${checkedCount})`;
} else {
form.classList.add('d-none');
form.classList.add("d-none");
}
}
</script>
{% endblock customJS %}
{% endblock customJS %}
</div>