new update

This commit is contained in:
Flatlogic Bot 2026-01-24 04:17:22 +00:00
parent 56126df7d4
commit 221476cb52
8 changed files with 161 additions and 40 deletions

View File

@ -158,4 +158,11 @@ class ShipperOfferForm(forms.Form):
delivery_date = forms.DateField(label=_('Requested Delivery Date'), widget=forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}))
amount = forms.DecimalField(label=_('Offer Amount'), max_digits=10, decimal_places=2, widget=forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01'}))
comments = forms.CharField(label=_('Comments'), required=False, widget=forms.Textarea(attrs={'class': 'form-control', 'rows': 2}))
comments = forms.CharField(label=_('Comments'), required=False, widget=forms.Textarea(attrs={'class': 'form-control', 'rows': 2}))
class RenewSubscriptionForm(forms.Form):
subscription_plan = forms.ChoiceField(
choices=[('MONTHLY', _('Monthly Plan')), ('ANNUAL', _('Annual Plan'))],
widget=forms.Select(attrs={'class': 'form-select'}),
label=_('Subscription Plan')
)

View File

@ -18,45 +18,63 @@
{% block content %}
<div class="container py-5">
<div class="row mb-4">
<div class="row mb-4 align-items-center">
<div class="col">
<h2 class="fw-bold">{% trans "Administrator Dashboard" %}</h2>
<p class="text-muted">{% trans "System overview and management" %}</p>
</div>
<div class="col-auto">
<div class="col-auto d-flex gap-2">
<a href="/admin/core/profile/" class="btn btn-primary">
<i class="fa-solid fa-users-gear me-2"></i>{% trans "Manage Subscriptions" %}
</a>
<a href="/admin/" class="btn btn-outline-primary">
<i class="fa-solid fa-gears me-2"></i>{% trans "Go to Django Admin" %}
</a>
</div>
</div>
<!-- Stats Overview -->
<div class="row g-4 mb-5">
<div class="col-md-3">
<div class="card border-0 shadow-sm text-center p-4">
<div class="display-5 text-primary mb-2"><i class="fa-solid fa-users"></i></div>
<h3 class="fw-bold">{{ total_users }}</h3>
<p class="text-muted mb-0">{% trans "Total Users" %}</p>
<div class="col-md-4 col-lg-2">
<div class="card border-0 shadow-sm text-center p-3 h-100">
<div class="h3 text-primary mb-1"><i class="fa-solid fa-users"></i></div>
<h4 class="fw-bold mb-0">{{ total_users }}</h4>
<p class="small text-muted mb-0">{% trans "Total Users" %}</p>
</div>
</div>
<div class="col-md-3">
<div class="card border-0 shadow-sm text-center p-4">
<div class="display-5 text-success mb-2"><i class="fa-solid fa-truck"></i></div>
<h3 class="fw-bold">{{ total_trucks }}</h3>
<p class="text-muted mb-0">{% trans "Registered Trucks" %}</p>
<div class="col-md-4 col-lg-2">
<div class="card border-0 shadow-sm text-center p-3 h-100">
<div class="h3 text-success mb-1"><i class="fa-solid fa-truck"></i></div>
<h4 class="fw-bold mb-0">{{ total_trucks }}</h4>
<p class="small text-muted mb-0">{% trans "Trucks" %}</p>
</div>
</div>
<div class="col-md-3">
<div class="card border-0 shadow-sm text-center p-4">
<div class="display-5 text-warning mb-2"><i class="fa-solid fa-box"></i></div>
<h3 class="fw-bold">{{ total_shipments }}</h3>
<p class="text-muted mb-0">{% trans "Total Shipments" %}</p>
<div class="col-md-4 col-lg-2">
<div class="card border-0 shadow-sm text-center p-3 h-100">
<div class="h3 text-warning mb-1"><i class="fa-solid fa-box"></i></div>
<h4 class="fw-bold mb-0">{{ total_shipments }}</h4>
<p class="small text-muted mb-0">{% trans "Shipments" %}</p>
</div>
</div>
<div class="col-md-3">
<div class="card border-0 shadow-sm text-center p-4">
<div class="display-5 text-info mb-2"><i class="fa-solid fa-hand-holding-dollar"></i></div>
<h3 class="fw-bold">{{ total_bids }}</h3>
<p class="text-muted mb-0">{% trans "Active Bids" %}</p>
<div class="col-md-4 col-lg-2">
<div class="card border-0 shadow-sm text-center p-3 h-100">
<div class="h3 text-info mb-1"><i class="fa-solid fa-hand-holding-dollar"></i></div>
<h4 class="fw-bold mb-0">{{ total_bids }}</h4>
<p class="small text-muted mb-0">{% trans "Bids" %}</p>
</div>
</div>
<div class="col-md-4 col-lg-2">
<div class="card border-0 shadow-sm text-center p-3 h-100 bg-success bg-opacity-10">
<div class="h3 text-success mb-1"><i class="fa-solid fa-check-circle"></i></div>
<h4 class="fw-bold mb-0">{{ active_subscriptions }}</h4>
<p class="small text-muted mb-0">{% trans "Active Subs" %}</p>
</div>
</div>
<div class="col-md-4 col-lg-2">
<div class="card border-0 shadow-sm text-center p-3 h-100 bg-danger bg-opacity-10">
<div class="h3 text-danger mb-1"><i class="fa-solid fa-times-circle"></i></div>
<h4 class="fw-bold mb-0">{{ expired_subscriptions }}</h4>
<p class="small text-muted mb-0">{% trans "Expired Subs" %}</p>
</div>
</div>
</div>
@ -391,4 +409,4 @@
border-bottom: none;
}
</style>
{% endblock %}
{% endblock %}

View File

@ -8,7 +8,7 @@
<div class="col-md-8 text-center">
<div class="card shadow-lg border-0 rounded-4 p-5">
<div class="mb-4">
<i class="bi bi-exclamination-triangle-fill text-warning" style="font-size: 4rem;"></i>
<i class="bi bi-exclamation-triangle-fill text-warning" style="font-size: 4rem;"></i>
</div>
<h2 class="fw-bold mb-3">{% trans "Subscription Expired" %}</h2>
<p class="lead text-muted mb-4">
@ -22,13 +22,36 @@
<li><strong>{% trans "Current Plan:" %}</strong> {{ profile.get_subscription_plan_display }}</li>
</ul>
</div>
<p class="mb-4">
{% trans "To continue using our services, please renew your subscription. You can contact our support team for renewal details." %}
</p>
<hr class="my-5">
<div class="renewal-section text-start">
<h4 class="fw-bold mb-4 text-center">{% trans "Renew Your Subscription" %}</h4>
<form action="{% url 'renew_subscription' %}" method="post">
{% csrf_token %}
<div class="mb-4">
<label class="form-label fw-bold">{{ form.subscription_plan.label }}</label>
{{ form.subscription_plan }}
</div>
<div id="fee-display" class="alert alert-info border-0 rounded-3 mb-4 text-center d-none">
<h5 class="mb-1">{% trans "Fee to Pay:" %}</h5>
<span id="fee-amount" class="display-6 fw-bold">0.00</span>
<span class="ms-1">{% trans "SAR" %}</span>
</div>
<div class="d-grid">
<button type="submit" class="btn btn-primary btn-lg rounded-pill py-3">
<i class="bi bi-credit-card me-2"></i> {% trans "Renew Now" %}
</button>
</div>
</form>
</div>
{% if app_settings.contact_phone or app_settings.contact_email %}
<div class="mb-5">
<h5 class="fw-bold">{% trans "Contact for Renewal" %}</h5>
<div class="mt-5 pt-4 border-top">
<h5 class="fw-bold small text-uppercase text-muted">{% trans "Need Help?" %}</h5>
{% if app_settings.contact_phone %}
<p class="mb-1"><i class="bi bi-whatsapp text-success me-2"></i> {{ app_settings.contact_phone }}</p>
{% endif %}
@ -38,11 +61,8 @@
</div>
{% endif %}
<div class="d-grid gap-2 d-md-flex justify-content-md-center">
<a href="{% url 'home' %}" class="btn btn-outline-secondary btn-lg px-4 rounded-pill">
{% trans "Back to Home" %}
</a>
<a href="{% url 'logout' %}" class="btn btn-danger btn-lg px-4 rounded-pill">
<div class="mt-4">
<a href="{% url 'logout' %}" class="btn btn-link text-danger">
{% trans "Logout" %}
</a>
</div>
@ -50,4 +70,29 @@
</div>
</div>
</div>
{% endblock %}
<script>
document.addEventListener('DOMContentLoaded', function() {
const fees = {{ fees_json|safe }};
const role = "{{ profile.role }}";
const planSelect = document.getElementById('id_subscription_plan');
const feeDisplay = document.getElementById('fee-display');
const feeAmount = document.getElementById('fee-amount');
function updateFee() {
const plan = planSelect.value;
if (plan && fees[role] && fees[role][plan]) {
feeAmount.textContent = parseFloat(fees[role][plan]).toFixed(2);
feeDisplay.classList.remove('d-none');
} else {
feeDisplay.classList.add('d-none');
}
}
if (planSelect) {
planSelect.addEventListener('change', updateFee);
updateFee(); // Initial call
}
});
</script>
{% endblock %}

View File

@ -23,4 +23,5 @@ urlpatterns = [
path("privacy-policy/", views.privacy_policy, name="privacy_policy"),
path("terms-of-service/", views.terms_of_service, name="terms_of_service"),
path("subscription-expired/", views.subscription_expired, name="subscription_expired"),
]
path("subscription-renew/", views.renew_subscription, name="renew_subscription"),
]

View File

@ -4,7 +4,7 @@ from django.contrib.auth.decorators import login_required
from django.contrib.auth import login, authenticate, logout
from django.utils import timezone
from .models import Profile, Truck, Shipment, Bid, Message, OTPCode, Country, City, AppSetting, Banner, HomeSection
from .forms import TruckForm, ShipmentForm, BidForm, UserRegistrationForm, OTPVerifyForm, ShipperOfferForm
from .forms import TruckForm, ShipmentForm, BidForm, UserRegistrationForm, OTPVerifyForm, ShipperOfferForm, RenewSubscriptionForm
from django.contrib import messages
from django.utils.translation import gettext as _
from django.db.models import Q
@ -208,6 +208,18 @@ def dashboard(request):
elif profile.role == 'ADMIN' or request.user.is_superuser:
pending_trucks = Truck.objects.filter(is_approved=False).order_by('-created_at')
approved_trucks = Truck.objects.filter(is_approved=True).order_by('-created_at')
# Subscription stats
today = timezone.now().date()
total_profiles = Profile.objects.exclude(role='ADMIN')
active_subscriptions = 0
expired_subscriptions = 0
for p in total_profiles:
if p.is_expired():
expired_subscriptions += 1
else:
active_subscriptions += 1
context = {
'total_users': User.objects.count(),
'total_trucks': Truck.objects.count(),
@ -215,6 +227,8 @@ def dashboard(request):
'total_bids': Bid.objects.count(),
'pending_trucks': pending_trucks,
'approved_trucks': approved_trucks,
'active_subscriptions': active_subscriptions,
'expired_subscriptions': expired_subscriptions,
}
return render(request, 'core/admin_dashboard.html', context)
else:
@ -429,10 +443,11 @@ def terms_of_service(request):
context = {
'article': {
'title': _('Terms of Service'),
'content': app_settings.terms_of_service if app_settings else _("Terms of service are coming soon.")
'content': app_settings.terms_of_service if app_settings else _("Terms of service are soon.")
}
}
return render(request, 'core/article_detail.html', context)
@login_required
def subscription_expired(request):
profile = request.user.profile
@ -440,8 +455,43 @@ def subscription_expired(request):
return redirect('dashboard')
app_settings = AppSetting.objects.first()
form = RenewSubscriptionForm()
# Simplified fees dictionary for JS
fees = {
'SHIPPER': {
'MONTHLY': str(app_settings.shipper_monthly_fee) if app_settings else "0.00",
'ANNUAL': str(app_settings.shipper_annual_fee) if app_settings else "0.00",
},
'TRUCK_OWNER': {
'MONTHLY': str(app_settings.truck_owner_monthly_fee) if app_settings else "0.00",
'ANNUAL': str(app_settings.truck_owner_annual_fee) if app_settings else "0.00",
}
}
return render(request, 'core/subscription_expired.html', {
'profile': profile,
'app_settings': app_settings
'app_settings': app_settings,
'form': form,
'fees_json': json.dumps(fees)
})
@login_required
def renew_subscription(request):
if request.method == 'POST':
form = RenewSubscriptionForm(request.POST)
if form.is_valid():
profile = request.user.profile
plan = form.cleaned_data['subscription_plan']
profile.subscription_plan = plan
profile.is_subscription_active = True
if plan == 'MONTHLY':
profile.subscription_expiry = timezone.now().date() + timedelta(days=30)
elif plan == 'ANNUAL':
profile.subscription_expiry = timezone.now().date() + timedelta(days=365)
profile.save()
messages.success(request, _("Subscription renewed successfully!"))
return redirect('dashboard')
return redirect('subscription_expired')