adding reports
This commit is contained in:
parent
595ca7c1fe
commit
a094518411
Binary file not shown.
Binary file not shown.
@ -1,4 +1,4 @@
|
||||
{% extends "admin/index.html" %}
|
||||
{% extends "admin/base_site.html" %}
|
||||
{% load i18n static dashboard_stats %}
|
||||
|
||||
{% block extrastyle %}
|
||||
@ -243,8 +243,10 @@
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Standard Admin Index Content -->
|
||||
{{ block.super }}
|
||||
<!-- Standard Admin App List -->
|
||||
<div id="content-main">
|
||||
{% include "admin/app_list.html" with app_list=app_list show_changelinks=True %}
|
||||
</div>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js" integrity="sha512-d9xgZrVZpmmQlfonhQUvTR7lMPtO7NkZMkA0ABN3PHCbKA5nqylQ/yWlFAyY6hYgdF1Qh6nYiuADWwKB4C2WSw==" crossorigin="anonymous"></script>
|
||||
<script>
|
||||
@ -80,6 +80,9 @@
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav ms-auto align-items-center">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link px-3" href="{% url 'track' %}">{% trans "Track" %}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link px-3" href="{% url 'index' %}#how-it-works">{% trans "How it Works" %}</a>
|
||||
</li>
|
||||
|
||||
@ -17,30 +17,13 @@
|
||||
<div class="col-lg-5 offset-lg-1">
|
||||
<div class="glass-card">
|
||||
<h3 class="mb-4">{% trans "Track your Parcel" %}</h3>
|
||||
<form method="GET" action="{% url 'index' %}">
|
||||
<form method="GET" action="{% url 'track' %}">
|
||||
<div class="input-group mb-3">
|
||||
<input type="text" name="tracking_id" class="form-control tracking-input" placeholder="{% trans 'Enter Tracking ID (e.g. 5A2B...)' %}" value="{{ tracking_id|default:'' }}">
|
||||
<input type="text" name="tracking_number" class="form-control tracking-input" placeholder="{% trans 'Enter Tracking ID (e.g. 5A2B...)' %}" value="{{ tracking_number|default:'' }}">
|
||||
<button class="btn btn-masarx-primary px-4" type="submit">{% trans "Track" %}</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% if parcel %}
|
||||
<div class="mt-4 p-3 bg-white bg-opacity-10 rounded-3 text-start" dir="ltr">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<span class="text-uppercase small opacity-75">{% trans "Status" %}</span>
|
||||
<span class="parcel-status-badge status-{{ parcel.status }}">{{ parcel.get_status_display }}</span>
|
||||
</div>
|
||||
<h5 class="mb-1">{{ parcel.description|truncatechars:30 }}</h5>
|
||||
<p class="small mb-0 opacity-75">{% trans "From" %}: {{ parcel.pickup_address }}</p>
|
||||
<p class="small mb-0 opacity-75">{% trans "To" %}: {{ parcel.delivery_address }}</p>
|
||||
</div>
|
||||
{% elif error %}
|
||||
<div class="mt-3 text-danger small">
|
||||
<i class="bi bi-exclamation-circle me-1"></i> {{ error }}
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="small opacity-50 mt-3 mb-0">{% trans "Enter your 10-character tracking ID to see live updates." %}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ request.LANGUAGE_CODE }}" dir="{% if request.LANGUAGE_CODE == 'ar' %}rtl{% else %}ltr{% endif %}">
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{% trans "Tax Invoice" %} - {{ parcel.tracking_number }}</title>
|
||||
<title>Invoice {{ parcel.tracking_number }}</title>
|
||||
<style>
|
||||
@page {
|
||||
size: A4;
|
||||
@ -14,45 +14,59 @@
|
||||
font-family: sans-serif;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
line-height: 1.5;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.header {
|
||||
width: 100%;
|
||||
margin-bottom: 40px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 30px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 2px solid #eee;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
.logo {
|
||||
float: {% if request.LANGUAGE_CODE == 'ar' %}right{% else %}left{% endif %};
|
||||
width: 150px;
|
||||
.logo-section {
|
||||
width: 40%;
|
||||
}
|
||||
.company-info {
|
||||
float: {% if request.LANGUAGE_CODE == 'ar' %}left{% else %}right{% endif %};
|
||||
text-align: {% if request.LANGUAGE_CODE == 'ar' %}left{% else %}right{% endif %};
|
||||
width: 50%;
|
||||
text-align: right;
|
||||
font-size: 12px;
|
||||
}
|
||||
.title {
|
||||
clear: both;
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 30px;
|
||||
text-transform: uppercase;
|
||||
border-bottom: 2px solid #eee;
|
||||
padding-bottom: 10px;
|
||||
background-color: #f9f9f9;
|
||||
padding: 10px;
|
||||
border: 1px solid #eee;
|
||||
}
|
||||
.title span.ar { display: block; font-size: 18px; font-weight: normal; margin-top: 5px; }
|
||||
|
||||
.details-grid {
|
||||
display: table;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.details-col {
|
||||
display: table-cell;
|
||||
width: 50%;
|
||||
vertical-align: top;
|
||||
width: 48%;
|
||||
background: #fdfdfd;
|
||||
padding: 15px;
|
||||
border: 1px solid #eee;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.label {
|
||||
font-weight: bold;
|
||||
color: #666;
|
||||
margin-bottom: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
border-bottom: 1px solid #ddd;
|
||||
padding-bottom: 2px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.value {
|
||||
margin-bottom: 10px;
|
||||
@ -63,22 +77,23 @@
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
th, td {
|
||||
padding: 10px;
|
||||
padding: 12px;
|
||||
border-bottom: 1px solid #eee;
|
||||
text-align: left;
|
||||
}
|
||||
th {
|
||||
background-color: #f9f9f9;
|
||||
text-align: {% if request.LANGUAGE_CODE == 'ar' %}right{% else %}left{% endif %};
|
||||
background-color: #f5f5f5;
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
}
|
||||
.text-right {
|
||||
text-align: {% if request.LANGUAGE_CODE == 'ar' %}left{% else %}right{% endif %};
|
||||
}
|
||||
th span.ar { display: block; font-weight: normal; font-size: 11px; color: #555; }
|
||||
.text-right { text-align: right; }
|
||||
.total-row td {
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
border-top: 2px solid #333;
|
||||
border-bottom: none;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
.footer {
|
||||
margin-top: 50px;
|
||||
@ -98,14 +113,15 @@
|
||||
}
|
||||
.paid { color: #0f9d58; border: 1px solid #0f9d58; }
|
||||
.pending { color: #f4b400; border: 1px solid #f4b400; }
|
||||
.ar-text { direction: rtl; unicode-bidi: embed; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="header">
|
||||
<div class="logo">
|
||||
<div class="logo-section">
|
||||
{% if logo_base64 %}
|
||||
<img src="data:image/jpeg;base64,{{ logo_base64 }}" style="max-width: 100%; max-height: 80px;">
|
||||
<img src="data:image/jpeg;base64,{{ logo_base64 }}" style="max-width: 200px; max-height: 80px;">
|
||||
{% else %}
|
||||
<h2>{{ platform_profile.name }}</h2>
|
||||
{% endif %}
|
||||
@ -114,28 +130,41 @@
|
||||
<strong>{{ platform_profile.name }}</strong><br>
|
||||
{{ platform_profile.address|linebreaksbr }}<br>
|
||||
{% if platform_profile.vat_number %}
|
||||
{% trans "VAT Number" %}: {{ platform_profile.vat_number }}<br>
|
||||
VAT / الرقم الضريبي: {{ platform_profile.vat_number }}<br>
|
||||
{% endif %}
|
||||
{{ platform_profile.phone_number }}
|
||||
Tel: {{ platform_profile.phone_number }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="title">{% trans "Tax Invoice" %}</div>
|
||||
<div class="title">
|
||||
TAX INVOICE
|
||||
<span class="ar ar-text">فاتورة ضريبية</span>
|
||||
</div>
|
||||
|
||||
<div class="details-grid">
|
||||
<div class="details-col">
|
||||
<div class="label">{% trans "Invoice Details" %}</div>
|
||||
<div class="label">
|
||||
<span>INVOICE DETAILS</span>
|
||||
<span class="ar-text">تفاصيل الفاتورة</span>
|
||||
</div>
|
||||
<div class="value">
|
||||
{% trans "Invoice No" %}: #{{ parcel.id }}<br>
|
||||
{% trans "Date" %}: {{ parcel.created_at|date:"Y-m-d" }}<br>
|
||||
{% trans "Status" %}:
|
||||
<strong>No / رقم:</strong> #{{ parcel.id }}<br>
|
||||
<strong>Date / التاريخ:</strong> {{ parcel.created_at|date:"Y-m-d" }}<br>
|
||||
<strong>Status / الحالة:</strong>
|
||||
<span class="status-badge {% if parcel.payment_status == 'paid' %}paid{% else %}pending{% endif %}">
|
||||
{{ parcel.get_payment_status_display }}
|
||||
{% if parcel.payment_status == 'paid' %}
|
||||
PAID / مدفوع
|
||||
{% else %}
|
||||
PENDING / غير مدفوع
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="details-col text-right">
|
||||
<div class="label">{% trans "Bill To" %}</div>
|
||||
<div class="details-col">
|
||||
<div class="label">
|
||||
<span>BILL TO (Shipper)</span>
|
||||
<span class="ar-text">فاتورة إلى (الشاحن)</span>
|
||||
</div>
|
||||
<div class="value">
|
||||
<strong>{{ parcel.shipper.first_name }} {{ parcel.shipper.last_name }}</strong><br>
|
||||
{{ parcel.shipper.profile.address }}<br>
|
||||
@ -148,15 +177,30 @@
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Description" %}</th>
|
||||
<th>{% trans "Tracking Number" %}</th>
|
||||
<th>{% trans "Weight" %}</th>
|
||||
<th class="text-right">{% trans "Amount" %} (OMR)</th>
|
||||
<th width="50%">
|
||||
Description
|
||||
<span class="ar ar-text">الوصف</span>
|
||||
</th>
|
||||
<th>
|
||||
Tracking No
|
||||
<span class="ar ar-text">رقم التتبع</span>
|
||||
</th>
|
||||
<th>
|
||||
Weight
|
||||
<span class="ar ar-text">الوزن</span>
|
||||
</th>
|
||||
<th class="text-right">
|
||||
Amount (OMR)
|
||||
<span class="ar ar-text">المبلغ</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{% trans "Delivery Service" %} - {{ parcel.description|truncatechars:50 }}</td>
|
||||
<td>
|
||||
Delivery Service / خدمة توصيل<br>
|
||||
<small>{{ parcel.description|truncatechars:50 }}</small>
|
||||
</td>
|
||||
<td>{{ parcel.tracking_number }}</td>
|
||||
<td>{{ parcel.weight }} kg</td>
|
||||
<td class="text-right">{{ parcel.price }}</td>
|
||||
@ -164,18 +208,21 @@
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr class="total-row">
|
||||
<td colspan="3" class="text-right">{% trans "Total" %}</td>
|
||||
<td colspan="3" class="text-right">
|
||||
TOTAL / الإجمالي
|
||||
</td>
|
||||
<td class="text-right">{{ parcel.price }} OMR</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
||||
<div class="footer">
|
||||
<p>{% trans "Thank you for using" %} {{ platform_profile.name }}!</p>
|
||||
<p>Thank you for using {{ platform_profile.name }}!</p>
|
||||
<p class="ar-text">شكراً لاستخدامكم {{ platform_profile.name }}!</p>
|
||||
{% if platform_profile.registration_number %}
|
||||
<p>{% trans "CR No" %}: {{ platform_profile.registration_number }}</p>
|
||||
<p>CR No / السجل التجاري: {{ platform_profile.registration_number }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{% load i18n %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ LANGUAGE_CODE }}" dir="{{ LANGUAGE_BIDI|yesno:'rtl,ltr' }}">
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Label {{ parcel.tracking_number }}</title>
|
||||
@ -10,20 +10,21 @@
|
||||
margin: 0;
|
||||
}
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
font-family: sans-serif; /* WeasyPrint uses system fonts. Ensure Arabic font is available. */
|
||||
margin: 0;
|
||||
padding: 5mm;
|
||||
font-size: 12px;
|
||||
color: #000;
|
||||
}
|
||||
.container {
|
||||
border: 2px solid #000;
|
||||
height: 138mm; /* Adjusted slightly */
|
||||
height: 138mm;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.header {
|
||||
border-bottom: 2px solid #000;
|
||||
padding: 10px;
|
||||
padding: 5px;
|
||||
text-align: center;
|
||||
height: 20mm;
|
||||
display: flex;
|
||||
@ -36,16 +37,20 @@
|
||||
max-width: 80mm;
|
||||
}
|
||||
.big-code {
|
||||
font-size: 24px;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
padding: 8px;
|
||||
border-bottom: 2px solid #000;
|
||||
background-color: #f0f0f0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.address-box {
|
||||
border-bottom: 1px solid #000;
|
||||
padding: 10px;
|
||||
padding: 8px;
|
||||
flex-grow: 1;
|
||||
}
|
||||
.main-info {
|
||||
@ -53,13 +58,14 @@
|
||||
border-bottom: 2px solid #000;
|
||||
}
|
||||
.section {
|
||||
padding: 10px;
|
||||
padding: 8px;
|
||||
flex: 1;
|
||||
border-right: 1px solid #000;
|
||||
text-align: center;
|
||||
}
|
||||
.section:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
/* RTL support for border */
|
||||
html[dir="ltr"] .section-right { border-left: 2px solid #000; }
|
||||
html[dir="rtl"] .section-right { border-right: 2px solid #000; }
|
||||
|
||||
.qr-code {
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
@ -78,67 +84,107 @@
|
||||
.footer {
|
||||
padding: 5px;
|
||||
text-align: center;
|
||||
font-size: 10px;
|
||||
font-size: 9px;
|
||||
}
|
||||
h3 {
|
||||
margin: 0 0 5px 0;
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
color: #333;
|
||||
border-bottom: 1px dotted #ccc;
|
||||
padding-bottom: 2px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
h3 span.ar { font-weight: normal; }
|
||||
p { margin: 0 0 2px 0; line-height: 1.2; }
|
||||
.bold { font-weight: bold; font-size: 13px; }
|
||||
.dual-text {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 5px;
|
||||
}
|
||||
.ar-text {
|
||||
font-family: sans-serif;
|
||||
direction: rtl;
|
||||
}
|
||||
h3 { margin: 0 0 5px 0; font-size: 10px; text-transform: uppercase; color: #555; }
|
||||
p { margin: 0 0 2px 0; }
|
||||
.bold { font-weight: bold; font-size: 14px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<!-- Platform Header -->
|
||||
<div class="header">
|
||||
{% if logo_base64 %}
|
||||
<img src="data:image/jpeg;base64,{{ logo_base64 }}" alt="MasarX">
|
||||
<img src="data:image/jpeg;base64,{{ logo_base64 }}" alt="Logo">
|
||||
{% else %}
|
||||
<h1>{{ platform_profile.name|default:"MASARX" }}</h1>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Destination City Code -->
|
||||
<div class="big-code">
|
||||
{{ parcel.delivery_city.name|upper }}
|
||||
<span>{{ parcel.delivery_city.name_en|upper }}</span>
|
||||
<span class="ar-text">/ {{ parcel.delivery_city.name_ar }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Receiver Info -->
|
||||
<div class="address-box">
|
||||
<h3>{% trans "TO (Receiver)" %}</h3>
|
||||
<h3>
|
||||
<span>TO (Receiver)</span>
|
||||
<span class="ar-text">إلى (المستلم)</span>
|
||||
</h3>
|
||||
<p class="bold">{{ parcel.receiver_name }}</p>
|
||||
<p>{{ parcel.delivery_address }}</p>
|
||||
<p>{{ parcel.delivery_city.name }}, {{ parcel.delivery_governate.name }}</p>
|
||||
<p>{{ parcel.delivery_country.name }}</p>
|
||||
<p>{% trans "Tel" %}: {{ parcel.receiver_phone }}</p>
|
||||
<p>
|
||||
{{ parcel.delivery_city.name_en }} / <span class="ar-text">{{ parcel.delivery_city.name_ar }}</span>
|
||||
</p>
|
||||
<p>
|
||||
{{ parcel.delivery_governate.name_en }} / <span class="ar-text">{{ parcel.delivery_governate.name_ar }}</span> - {{ parcel.delivery_country.name_en }}
|
||||
</p>
|
||||
<p><strong>Tel / هاتف:</strong> {{ parcel.receiver_phone }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Sender Info -->
|
||||
<div class="address-box">
|
||||
<h3>{% trans "FROM (Sender)" %}</h3>
|
||||
<h3>
|
||||
<span>FROM (Sender)</span>
|
||||
<span class="ar-text">من (المرسل)</span>
|
||||
</h3>
|
||||
<p class="bold">{{ parcel.shipper.get_full_name|default:parcel.shipper.username }}</p>
|
||||
<p>{{ parcel.pickup_address }}</p>
|
||||
<p>{{ parcel.pickup_city.name }}, {{ parcel.pickup_governate.name }}</p>
|
||||
<p>{% trans "Tel" %}: {{ parcel.shipper.profile.phone_number }}</p>
|
||||
<p>
|
||||
{{ parcel.pickup_city.name_en }} / <span class="ar-text">{{ parcel.pickup_city.name_ar }}</span>
|
||||
</p>
|
||||
<p><strong>Tel / هاتف:</strong> {{ parcel.shipper.profile.phone_number }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Info Row: Date, Weight, Price -->
|
||||
<div class="main-info">
|
||||
<div class="section">
|
||||
<h3>{% trans "Date" %}</h3>
|
||||
<h3>Date / التاريخ</h3>
|
||||
<p class="bold">{{ parcel.created_at|date:"Y-m-d" }}</p>
|
||||
</div>
|
||||
<div class="section section-right">
|
||||
<h3>{% trans "Weight" %}</h3>
|
||||
<div class="section">
|
||||
<h3>Weight / الوزن</h3>
|
||||
<p class="bold">{{ parcel.weight }} KG</p>
|
||||
</div>
|
||||
<div class="section section-right">
|
||||
<h3>{% trans "Price" %}</h3>
|
||||
<div class="section">
|
||||
<h3>COD / الدفع</h3>
|
||||
<p class="bold">{{ parcel.price }} OMR</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tracking & QR -->
|
||||
<div class="qr-code">
|
||||
<img src="data:image/png;base64,{{ qr_code }}" alt="QR Code">
|
||||
<div class="tracking-number">{{ parcel.tracking_number }}</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="footer">
|
||||
{% trans "Thank you for shipping with us." %}
|
||||
Thank you for shipping with us.<br>
|
||||
<span class="ar-text">شكراً لاستخدامكم خدماتنا.</span>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
110
core/templates/core/track.html
Normal file
110
core/templates/core/track.html
Normal file
@ -0,0 +1,110 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load static i18n %}
|
||||
|
||||
{% block title %}{% trans "Track Shipment" %} - MasarX{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container py-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-8">
|
||||
<div class="card shadow-sm border-0 rounded-4">
|
||||
<div class="card-body p-4 p-md-5">
|
||||
<h2 class="text-center mb-4">{% trans "Track Your Shipment" %}</h2>
|
||||
|
||||
<!-- Search Form -->
|
||||
<form method="GET" action="{% url 'track' %}" class="mb-5">
|
||||
<div class="input-group input-group-lg">
|
||||
<input type="text" name="tracking_number" class="form-control border-end-0" placeholder="{% trans 'Enter Tracking ID (e.g. TRK123456789)' %}" value="{{ tracking_number|default:'' }}" required>
|
||||
<button class="btn btn-primary px-4" type="submit">
|
||||
<i class="bi bi-search me-2"></i>{% trans "Track" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% if error %}
|
||||
<div class="alert alert-danger rounded-3" role="alert">
|
||||
<i class="bi bi-exclamation-triangle-fill me-2"></i> {{ error }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if parcel %}
|
||||
<!-- Status Timeline -->
|
||||
<div class="mb-5">
|
||||
<h5 class="mb-4 text-muted">{% trans "Shipment Status" %}</h5>
|
||||
<div class="position-relative m-4">
|
||||
<div class="progress" style="height: 4px;">
|
||||
<div class="progress-bar {% if parcel.status == 'delivered' %}bg-success{% else %}bg-primary{% endif %}" role="progressbar" style="width:
|
||||
{% if parcel.status == 'pending' %}10%
|
||||
{% elif parcel.status == 'picked_up' %}50%
|
||||
{% elif parcel.status == 'in_transit' %}75%
|
||||
{% elif parcel.status == 'delivered' %}100%
|
||||
{% else %}0%{% endif %}
|
||||
" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
|
||||
<!-- Steps -->
|
||||
<div class="position-absolute top-0 start-0 translate-middle btn btn-sm rounded-pill {% if parcel.status != 'cancelled' %}btn-primary{% else %}btn-secondary{% endif %}" style="width: 2rem; height:2rem;">1</div>
|
||||
<div class="position-absolute top-0 start-50 translate-middle btn btn-sm rounded-pill {% if parcel.status in 'picked_up,in_transit,delivered' %}btn-primary{% else %}btn-secondary{% endif %}" style="width: 2rem; height:2rem;">2</div>
|
||||
<div class="position-absolute top-0 start-100 translate-middle btn btn-sm rounded-pill {% if parcel.status == 'delivered' %}btn-success{% else %}btn-secondary{% endif %}" style="width: 2rem; height:2rem;">3</div>
|
||||
|
||||
<!-- Labels -->
|
||||
<div class="d-flex justify-content-between mt-2">
|
||||
<small class="fw-bold">{% trans "Order Placed" %}</small>
|
||||
<small class="fw-bold">{% trans "On the Way" %}</small>
|
||||
<small class="fw-bold">{% trans "Delivered" %}</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Parcel Details Card -->
|
||||
<div class="bg-light p-4 rounded-3 border">
|
||||
<div class="row g-4">
|
||||
<div class="col-md-6">
|
||||
<h6 class="text-uppercase text-muted small mb-2">{% trans "Tracking Number" %}</h6>
|
||||
<p class="fw-bold fs-5 mb-0">{{ parcel.tracking_number }}</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h6 class="text-uppercase text-muted small mb-2">{% trans "Current Status" %}</h6>
|
||||
<span class="badge {% if parcel.status == 'delivered' %}bg-success{% elif parcel.status == 'cancelled' %}bg-danger{% else %}bg-primary{% endif %} fs-6">
|
||||
{{ parcel.get_status_display }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-12"><hr class="my-1"></div>
|
||||
|
||||
<!-- From / To (Public View: Only City/Governate) -->
|
||||
<div class="col-md-6">
|
||||
<h6 class="text-uppercase text-muted small mb-2">{% trans "From" %}</h6>
|
||||
<p class="mb-0 fw-medium">{{ parcel.pickup_city.name }}</p>
|
||||
<small class="text-muted">{{ parcel.pickup_governate.name }}</small>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h6 class="text-uppercase text-muted small mb-2">{% trans "To" %}</h6>
|
||||
<p class="mb-0 fw-medium">{{ parcel.delivery_city.name }}</p>
|
||||
<small class="text-muted">{{ parcel.delivery_governate.name }}</small>
|
||||
</div>
|
||||
|
||||
<div class="col-12"><hr class="my-1"></div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<h6 class="text-uppercase text-muted small mb-2">{% trans "Last Updated" %}</h6>
|
||||
<p class="mb-0">{{ parcel.updated_at|date:"d M Y, h:i A" }}</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h6 class="text-uppercase text-muted small mb-2">{% trans "Description" %}</h6>
|
||||
<p class="mb-0">{{ parcel.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<!-- Empty State / Instructions -->
|
||||
<div class="text-center text-muted py-5">
|
||||
<i class="bi bi-box-seam display-1 opacity-25"></i>
|
||||
<p class="mt-3">{% trans "Enter your tracking number above to see shipment details." %}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Binary file not shown.
@ -12,7 +12,7 @@ register = template.Library()
|
||||
def get_dashboard_stats():
|
||||
# User Stats
|
||||
total_users = User.objects.count()
|
||||
drivers_count = Profile.objects.filter(role='driver').count()
|
||||
drivers_count = Profile.objects.filter(role='car_owner').count()
|
||||
shippers_count = Profile.objects.filter(role='shipper').count()
|
||||
|
||||
# Parcel Stats
|
||||
@ -21,6 +21,9 @@ def get_dashboard_stats():
|
||||
pending_parcels = Parcel.objects.filter(status='pending').count()
|
||||
|
||||
# Financials
|
||||
# Summing price of delivered parcels (assuming delivered = revenue realized)
|
||||
# If payments are enabled, we might want to check payment_status='paid' as well.
|
||||
# For now, sticking to delivered as a safe proxy for completed business.
|
||||
total_revenue = Parcel.objects.filter(status='delivered').aggregate(Sum('price'))['price__sum'] or 0
|
||||
|
||||
# Recent Activity
|
||||
@ -32,6 +35,7 @@ def get_dashboard_stats():
|
||||
|
||||
daily_counts = []
|
||||
for date_str in dates:
|
||||
# We'll use created_at__date lookup
|
||||
count = Parcel.objects.filter(created_at__date=date_str).count()
|
||||
daily_counts.append(count)
|
||||
|
||||
|
||||
@ -34,6 +34,7 @@ urlpatterns = [
|
||||
path('dashboard/', views.dashboard, name='dashboard'),
|
||||
path('scan-qr/', views.scan_qr_view, name='scan_qr'),
|
||||
path('shipment-request/', views.shipment_request, name='shipment_request'),
|
||||
path('track/', views.track_parcel, name='track'),
|
||||
path('accept-parcel/<int:parcel_id>/', views.accept_parcel, name='accept_parcel'),
|
||||
path('update-status/<int:parcel_id>/', views.update_status, name='update_status'),
|
||||
path('rate-driver/<int:parcel_id>/', views.rate_driver, name='rate_driver'),
|
||||
|
||||
@ -35,14 +35,10 @@ from io import BytesIO
|
||||
import base64
|
||||
|
||||
def index(request):
|
||||
# If tracking_id is present, redirect to the new track view
|
||||
tracking_id = request.GET.get('tracking_id')
|
||||
parcel = None
|
||||
error = None
|
||||
if tracking_id:
|
||||
try:
|
||||
parcel = Parcel.objects.get(tracking_number=tracking_id)
|
||||
except Parcel.DoesNotExist:
|
||||
error = _("Parcel not found.")
|
||||
return redirect(f"{reverse('track')}?tracking_number={tracking_id}")
|
||||
|
||||
testimonials = Testimonial.objects.filter(is_active=True)
|
||||
|
||||
@ -58,14 +54,28 @@ def index(request):
|
||||
).order_by('-shipment_count')[:5]
|
||||
|
||||
return render(request, 'core/index.html', {
|
||||
'parcel': parcel,
|
||||
'error': error,
|
||||
'tracking_id': tracking_id,
|
||||
'testimonials': testimonials,
|
||||
'top_drivers': top_drivers,
|
||||
'top_shippers': top_shippers
|
||||
})
|
||||
|
||||
def track_parcel(request):
|
||||
tracking_number = request.GET.get('tracking_number')
|
||||
parcel = None
|
||||
error = None
|
||||
|
||||
if tracking_number:
|
||||
try:
|
||||
parcel = Parcel.objects.get(tracking_number__iexact=tracking_number.strip())
|
||||
except Parcel.DoesNotExist:
|
||||
error = _("Parcel not found with this Tracking ID.")
|
||||
|
||||
return render(request, 'core/track.html', {
|
||||
'parcel': parcel,
|
||||
'error': error,
|
||||
'tracking_number': tracking_number
|
||||
})
|
||||
|
||||
def register(request):
|
||||
if request.method == 'POST':
|
||||
form = UserRegistrationForm(request.POST)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user