diff --git a/inventory/views.py b/inventory/views.py
index 173c6b00..2012916b 100644
--- a/inventory/views.py
+++ b/inventory/views.py
@@ -22,7 +22,7 @@ from django.conf import settings
from django.db import transaction
from django.db.models import Func
from django.contrib import messages
-from django.http import JsonResponse
+from django.http import JsonResponse, HttpResponseForbidden
from django.forms import HiddenInput, ValidationError
from django.shortcuts import HttpResponse
from django.db.models import Sum, F, Count
@@ -362,6 +362,8 @@ class ManagerDashboard(LoginRequiredMixin, TemplateView):
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return redirect("welcome")
+ if not getattr(request.user, 'dealer', False):
+ return HttpResponseForbidden("You are not authorized to view this dashboard.")
return super().dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
diff --git a/static/css/custom.css b/static/css/custom.css
index 5b485e9f..7a939487 100644
--- a/static/css/custom.css
+++ b/static/css/custom.css
@@ -27,4 +27,26 @@
.rtl .fa-chevron-right {
transform: scaleX(-1);
-}
\ No newline at end of file
+}
+
+.spinner-container {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 10px;
+ color: #555;
+ font-size: 14px;
+}
+
+.spinner {
+ width: 18px;
+ height: 18px;
+ border: 3px solid #ccc;
+ border-top-color: #333;
+ border-radius: 50%;
+ animation: spin 0.6s linear infinite;
+}
+
+@keyframes spin {
+ to { transform: rotate(360deg); }
+}
diff --git a/static/js/crm-dashboard.js b/static/js/crm-dashboard.js
index 5f7eceb5..58d4b86d 100644
--- a/static/js/crm-dashboard.js
+++ b/static/js/crm-dashboard.js
@@ -120,85 +120,7 @@
/* Echarts Total Sales */
/* -------------------------------------------------------------------------- */
- const contactsBySourceChartInit = () => {
- const { getColor, getData, toggleColor } = window.phoenix.utils;
- const chartElContainer = document.querySelector(
- '.echart-contact-by-source-container'
- );
- const chartEl = chartElContainer.querySelector('.echart-contact-by-source');
- const chartLabel = chartElContainer.querySelector('[data-label]');
- if (chartEl) {
- const userOptions = getData(chartEl, 'echarts');
- const chart = window.echarts.init(chartEl);
- const data = [
- { value: 80, name: 'Organic Search' },
- { value: 65, name: 'Paid Search' },
- { value: 40, name: 'Direct Traffic' },
- { value: 220, name: 'Social Media' },
- { value: 120, name: 'Referrals' },
- { value: 35, name: 'Others Campaigns' }
- ];
- const totalSource = data.reduce((acc, val) => val.value + acc, 0);
- if (chartLabel) {
- chartLabel.innerHTML = totalSource;
- }
- const getDefaultOptions = () => ({
- color: [
- getColor('primary'),
- getColor('success'),
- getColor('info'),
- getColor('info-light'),
- toggleColor(getColor('danger-lighter'), getColor('danger-darker')),
- toggleColor(getColor('warning-light'), getColor('warning-dark'))
- ],
- tooltip: {
- trigger: 'item',
- borderWidth: 0,
- position: (...params) => handleTooltipPosition(params),
- extraCssText: 'z-index: 1000'
- },
- responsive: true,
- maintainAspectRatio: false,
-
- series: [
- {
- name: 'Contacts by Source',
- type: 'pie',
- radius: ['55%', '90%'],
- startAngle: 90,
- avoidLabelOverlap: false,
- itemStyle: {
- borderColor: getColor('body-bg'),
- borderWidth: 3
- },
-
- label: {
- show: false
- },
- emphasis: {
- label: {
- show: false
- }
- },
- labelLine: {
- show: false
- },
- data
- }
- ],
- grid: {
- bottom: 0,
- top: 0,
- left: 0,
- right: 0,
- containLabel: false
- }
- });
-
- echartSetOption(chart, userOptions, getDefaultOptions);
- }
- };
// dayjs.extend(advancedFormat);
@@ -1338,7 +1260,6 @@
const { docReady } = window.phoenix.utils;
- docReady(contactsBySourceChartInit);
docReady(contactsCreatedChartInit);
docReady(newUsersChartsInit);
docReady(newLeadsChartsInit);
diff --git a/templates/base.html b/templates/base.html
index 9fb869bf..21f7ebb4 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -60,6 +60,7 @@
}
-->
{% block customCSS %}
+
{% endblock %}
diff --git a/templates/dashboards/manager.html b/templates/dashboards/manager.html
index 9f20bc6a..51f9651d 100644
--- a/templates/dashboards/manager.html
+++ b/templates/dashboards/manager.html
@@ -1,4 +1,3 @@
-{% extends 'base.html' %}
{% load i18n static custom_filters django_ledger%}
{% block content %}
diff --git a/templates/dashboards/sales.html b/templates/dashboards/sales.html
index e0c26d51..cf776614 100644
--- a/templates/dashboards/sales.html
+++ b/templates/dashboards/sales.html
@@ -1,4 +1,3 @@
-{% extends 'base.html' %}
{% load i18n static custom_filters django_ledger%}
{% block content %}
diff --git a/templates/header.html b/templates/header.html
index 4dfe8614..e17519ee 100644
--- a/templates/header.html
+++ b/templates/header.html
@@ -5,55 +5,8 @@
- -
-
-
-
+
+
-
Apps
diff --git a/templates/index.html b/templates/index.html
index cebc37e0..71ddead9 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -1,8 +1,19 @@
{% extends 'base.html' %}
-{% load i18n static %}
-
+{% load i18n static custom_filters django_ledger %}
{% block content %}
-
-
+{% if request.user.is_authenticated %}
+
+
+
+
Loading dashboard...
+
+
+{% endif %}
{% endblock %}
\ No newline at end of file
diff --git a/templates/inventory/car_detail.html b/templates/inventory/car_detail.html
index 0a86f5b9..eb20a04c 100644
--- a/templates/inventory/car_detail.html
+++ b/templates/inventory/car_detail.html
@@ -546,39 +546,64 @@
const specificationsContent = document.getElementById("specificationsContent");
showSpecificationButton.addEventListener("click", function () {
- specificationsContent.innerHTML = "";
- fetch(`${ajaxUrl}?action=get_specifications&trim_id={{ car.id_car_trim.id_car_trim }}`, {
- headers: {
- "X-Requested-With": "XMLHttpRequest",
- "X-CSRFToken": csrftoken,
- },
- })
- .then((response) => response.json())
- .then((data) => {
- if (data.length > 0) {
- data.forEach(function (parent) {
- const parentSpec = document.createElement("div");
- parentSpec.classList.add("mb-2");
- parentSpec.innerHTML = `${parent.parent_name}`;
+ specificationsContent.innerHTML = "";
+ fetch(`${ajaxUrl}?action=get_specifications&trim_id={{ car.id_car_trim.id_car_trim }}`, {
+ headers: {
+ "X-Requested-With": "XMLHttpRequest",
+ "X-CSRFToken": csrftoken,
+ },
+ })
+ .then((response) => response.json())
+ .then((data) => {
+ if (data.length > 0) {
+ data.forEach(function (parent) {
+ // Create a section container
+ const section = document.createElement("div");
+ section.classList.add("mb-4", "p-3", "border", "rounded");
- parent.specifications.forEach(function (specification) {
- const specificationDiv = document.createElement("div");
- specificationDiv.classList.add("ms-3");
- specificationDiv.innerHTML = `· ${specification.s_name}: ${specification.s_value} ${specification.s_unit}`;
- parentSpec.appendChild(specificationDiv);
- });
+ // Add section title
+ const sectionTitle = document.createElement("h4");
+ sectionTitle.classList.add("mb-3", "fw-bold");
+ sectionTitle.textContent = parent.parent_name;
+ section.appendChild(sectionTitle);
- specificationsContent.appendChild(parentSpec);
- });
- } else {
- specificationsContent.innerHTML = '{% trans "No specifications available." %}
';
- }
- })
- .catch((error) => {
- specificationsContent.innerHTML = '{% trans "Error loading specifications." %}
';
- console.error("Error fetching specifications:", error);
+ // Create a table for the specifications
+ const specsTable = document.createElement("div");
+ specsTable.classList.add("row");
+
+ parent.specifications.forEach(function (specification) {
+ // Create a row for each specification
+ const specRow = document.createElement("div");
+ specRow.classList.add("row", "mb-2");
+
+ // Left Column: Spec name
+ const specName = document.createElement("div");
+ specName.classList.add("col-6", "text-muted");
+ specName.textContent = specification.s_name;
+
+ // Right Column: Spec value + unit
+ const specValue = document.createElement("div");
+ specValue.classList.add("col-6", "text-end");
+ specValue.textContent = `${specification.s_value} ${specification.s_unit || ""}`;
+
+ specRow.appendChild(specName);
+ specRow.appendChild(specValue);
+
+ specsTable.appendChild(specRow);
+ });
+
+ section.appendChild(specsTable);
+ specificationsContent.appendChild(section);
});
+ } else {
+ specificationsContent.innerHTML = '{% trans "No specifications available." %}
';
+ }
+ })
+ .catch((error) => {
+ specificationsContent.innerHTML = '{% trans "Error loading specifications." %}
';
+ console.error("Error fetching specifications:", error);
});
+});
document.querySelectorAll(".reserve-btn").forEach((button) => {
button.addEventListener("click", async function () {
diff --git a/templates/inventory/car_list_view.html b/templates/inventory/car_list_view.html
index 4e0fe4ac..57889e3c 100644
--- a/templates/inventory/car_list_view.html
+++ b/templates/inventory/car_list_view.html
@@ -34,11 +34,11 @@
hx-on::before-request="on_before_request()"
hx-on::after-request="on_after_request()"
>
- - All({{stats.all}})
- - Available({{stats.available}})
- - Reserved({{stats.reserved}})
- - Transfer({{stats.transfer}})
- - Sold({{stats.sold}})
+ - {{ _("All") }}({{stats.all}})
+ - {{ _("Available") }}({{stats.available}})
+ - {{ _("Reserved") }}({{stats.reserved}})
+ - {{ _("Transfer") }}({{stats.transfer}})
+ - {{ _("Sold") }}({{stats.sold}})
@@ -151,13 +151,13 @@
{% if car.status == "available" %}
- {{car.status}}
+ {{ _("Available") }}
{% elif car.status == "reserved" %}
- {{car.status}}
+ {{ _("Reserved") }}
{% elif car.status == "sold" %}
- {{car.status}}
+ {{ _("Sold") }}
{% elif car.status == "transfer" %}
- {{car.status}}
+ {{ _("Transfer") }}
{% endif %}
|
|