new update
This commit is contained in:
parent
56126df7d4
commit
221476cb52
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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')
|
||||
)
|
||||
|
||||
@ -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 %}
|
||||
@ -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 %}
|
||||
@ -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"),
|
||||
]
|
||||
@ -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')
|
||||
Loading…
x
Reference in New Issue
Block a user