ui change for plans list and admin management to only show the admin user

This commit is contained in:
Faheed 2025-09-09 18:23:39 +03:00
parent 2a292e546a
commit d8650f3b09
20 changed files with 531 additions and 349 deletions

View File

@ -5,119 +5,128 @@ from plans.models import Plan, Quota, PlanQuota, Pricing, PlanPricing
class Command(BaseCommand):
help = "Create basic subscription plans structure"
help = "Create basic subscription plans structure."
def add_arguments(self, parser):
parser.add_argument(
"--reset",
action="store_true",
help="Delete existing plans and quotas before creating new ones",
help="Delete existing plans and quotas before creating new ones.",
)
def handle(self, *args, **options):
# Plan.objects.all().delete()
# Quota.objects.all().delete()
# PlanQuota.objects.all().delete()
# Pricing.objects.all().delete()
# PlanPricing.objects.all().delete()
# UserPlan.objects.all().delete()
# Order.objects.all().delete()
# BillingInfo.objects.all().delete()
if options["reset"]:
self.stdout.write(self.style.WARNING("Resetting all plan-related data..."))
Plan.objects.all().delete()
Quota.objects.all().delete()
Pricing.objects.all().delete()
# Note: Deleting plans and quotas should cascade to related objects like PlanQuota and PlanPricing.
self.stdout.write(self.style.SUCCESS("Data reset complete."))
else:
self.stdout.write(self.style.NOTICE("Creating or updating default plans and quotas..."))
users_quota = Quota.objects.create(
name="Users", codename="Users", unit="number"
# Create or get quotas
users_quota, created_u = Quota.objects.get_or_create(
codename="Users",
defaults={"name": "Users", "unit": "number"}
)
cars_quota = Quota.objects.create(name="Cars", codename="Cars", unit="number")
# Create plans
basic_plan = Plan.objects.create(
name="Basic", description="basic plan", available=True, visible=True
if created_u:
self.stdout.write(self.style.SUCCESS('Created quota: "Users"'))
cars_quota, created_c = Quota.objects.get_or_create(
codename="Cars",
defaults={"name": "Cars", "unit": "number"}
)
pro_plan = Plan.objects.create(
name="Pro", description="Pro plan", available=True, visible=True
if created_c:
self.stdout.write(self.style.SUCCESS('Created quota: "Cars"'))
# Create or get plans
basic_plan, created_bp = Plan.objects.get_or_create(
name="Basic",
defaults={"description": "basic plan", "available": True, "visible": True}
)
enterprise_plan = Plan.objects.create(
if created_bp:
self.stdout.write(self.style.SUCCESS('Created plan: "Basic"'))
pro_plan, created_pp = Plan.objects.get_or_create(
name="Pro",
defaults={"description": "Pro plan", "available": True, "visible": True}
)
if created_pp:
self.stdout.write(self.style.SUCCESS('Created plan: "Pro"'))
enterprise_plan, created_ep = Plan.objects.get_or_create(
name="Enterprise",
description="Enterprise plan",
available=True,
visible=True,
defaults={"description": "Enterprise plan", "available": True, "visible": True}
)
if created_ep:
self.stdout.write(self.style.SUCCESS('Created plan: "Enterprise"'))
# Assign quotas to plans using get_or_create to prevent duplicates
PlanQuota.objects.get_or_create(
plan=basic_plan,
quota=users_quota,
defaults={"value": 10000000}
)
PlanQuota.objects.get_or_create(
plan=basic_plan,
quota=cars_quota,
defaults={"value": 10000000}
)
# Assign quotas to plans
PlanQuota.objects.create(plan=basic_plan, quota=users_quota, value=10000000)
PlanQuota.objects.create(plan=basic_plan, quota=cars_quota, value=10000000)
PlanQuota.objects.create(plan=pro_plan, quota=users_quota, value=10000000)
PlanQuota.objects.create(plan=pro_plan, quota=cars_quota, value=10000000)
PlanQuota.objects.create(plan=enterprise_plan, quota=users_quota, value=10000000)
PlanQuota.objects.create(plan=enterprise_plan, quota=cars_quota, value=10000000)
# PlanQuota.objects.create(plan=pro_plan, quota=project_quota, value=50)
# PlanQuota.objects.create(plan=pro_plan, quota=storage_quota, value=100)
# Define pricing
basic_pricing = Pricing.objects.create(name="3 Months", period=90)
pro_pricing = Pricing.objects.create(name="6 Months", period=180)
enterprise_pricing = Pricing.objects.create(name="1 Year", period=365)
PlanPricing.objects.create(
plan=basic_plan, pricing=basic_pricing, price=Decimal("2997.00")
# Pro plan quotas
PlanQuota.objects.get_or_create(
plan=pro_plan,
quota=users_quota,
defaults={"value": 10000000}
)
PlanPricing.objects.create(
plan=pro_plan, pricing=pro_pricing, price=Decimal("5395.00")
)
PlanPricing.objects.create(
plan=enterprise_plan, pricing=enterprise_pricing, price=Decimal("9590.00")
PlanQuota.objects.get_or_create(
plan=pro_plan,
quota=cars_quota,
defaults={"value": 10000000}
)
# # Create quotas
# project_quota = Quota.objects.create(name='projects', codename='projects', unit='projects')
# storage_quota = Quota.objects.create(name='storage', codename='storage', unit='GB')
# Enterprise plan quotas
PlanQuota.objects.get_or_create(
plan=enterprise_plan,
quota=users_quota,
defaults={"value": 10000000}
)
PlanQuota.objects.get_or_create(
plan=enterprise_plan,
quota=cars_quota,
defaults={"value": 10000000}
)
# # Create plans
# basic_plan = Plan.objects.create(name='Basic', description='Basic plan', available=True, visible=True)
# pro_plan = Plan.objects.create(name='Pro', description='Pro plan', available=True, visible=True)
# Create or get pricing
basic_pricing, created_bp_p = Pricing.objects.get_or_create(
name="3 Months",
defaults={"period": 90}
)
pro_pricing, created_pp_p = Pricing.objects.get_or_create(
name="6 Months",
defaults={"period": 180}
)
enterprise_pricing, created_ep_p = Pricing.objects.get_or_create(
name="1 Year",
defaults={"period": 365}
)
# # Assign quotas to plans
# PlanQuota.objects.create(plan=basic_plan, quota=project_quota, value=5)
# PlanQuota.objects.create(plan=basic_plan, quota=storage_quota, value=10)
# Assign pricing to plans
PlanPricing.objects.get_or_create(
plan=basic_plan,
pricing=basic_pricing,
defaults={"price": Decimal("2997.00")}
)
PlanPricing.objects.get_or_create(
plan=pro_plan,
pricing=pro_pricing,
defaults={"price": Decimal("5395.00")}
)
PlanPricing.objects.get_or_create(
plan=enterprise_plan,
pricing=enterprise_pricing,
defaults={"price": Decimal("9590.00")}
)
# PlanQuota.objects.create(plan=pro_plan, quota=project_quota, value=50)
# PlanQuota.objects.create(plan=pro_plan, quota=storage_quota, value=100)
# # Define pricing
# basic = Pricing.objects.create(name='Monthly', period=30)
# pro = Pricing.objects.create(name='Monthly', period=30)
# basic_pricing = PlanPricing.objects.create(plan=basic_plan, pricing=basic, price=Decimal('19.99'))
# pro_pricing = PlanPricing.objects.create(plan=pro_plan, pricing=pro, price=Decimal('29.99'))
# Create users
# user = User.objects.first()
# # Create user plans
# billing_info = BillingInfo.objects.create(
# user=user,
# tax_number='123456789',
# name='John Doe',
# street='123 Main St',
# zipcode='12345',
# city='Anytown',
# country='US',
# )
# order = Order.objects.create(
# user=user,
# plan=pro_plan,
# pricing=pro_pricing,
# amount=pro_pricing.price,
# currency="SAR",
# )
# UserPlan.objects.create(
# user=user,
# plan=pro_plan,
# expire=timezone.now() + timedelta(days=2),
# active=True,
# )
self.stdout.write(self.style.SUCCESS("Subscription plans structure successfully created or updated."))

View File

@ -1585,7 +1585,7 @@ def inventory_stats_view(request, dealer_slug):
for make_data in inventory.values()
],
}
return render(request, "inventory/inventory_stats.html", {"inventory": result})
return render(request, "inventory/inventory_stats.html", {"inventory": result,"empty_state_value":_("car")})
# @login_required
@ -2450,6 +2450,7 @@ class CustomerListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["query"] = self.request.GET.get("q", "")
context["empty_state_value"] = _("customers")
return context
@ -3553,7 +3554,10 @@ class UserListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"])
staff = models.Staff.objects.filter(dealer=dealer, active=True).all()
return apply_search_filters(staff, query)
def get_context_data(self, **kwargs):
context=super().get_context_data(**kwargs)
context['no_staff_message']=_("staff")
return context
class UserDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView):
"""
@ -3800,7 +3804,10 @@ class OrganizationListView(LoginRequiredMixin, PermissionRequiredMixin, ListView
organization = dealer.organizations.filter(active=True)
return apply_search_filters(organization, query)
def get_context_data(self, **kwargs):
context=super().get_context_data(**kwargs)
context["empty_state_value"]=_("organization")
return context
class OrganizationDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView):
"""
@ -5020,6 +5027,10 @@ def create_estimate(request, dealer_slug, slug=None):
],
"opportunity_id": slug if slug else None,
"customer_count": entity.get_customers().count(),
"no_items_message": _("Please add at least one car or complete the car info before creating a quotation."),
"no_items_button": _("Add car"),
"no_customers_message": _("Please add at least one customer before creating a quotation."),
"no_customers_button": _("Add Customer"),
}
return render(request, "sales/estimates/estimate_form.html", context)
@ -6168,6 +6179,10 @@ class LeadListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
if self.request.is_staff:
return qs.filter(staff=self.request.staff)
return models.Lead.objects.none()
def get_context_data(self, **kwargs):
context=super().get_context_data(**kwargs)
context['empty_state_value']=_("lead")
return context
class LeadDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView):
@ -6418,7 +6433,8 @@ def lead_tracking(request, dealer_slug):
"won": won,
"lose": lose,
"negotiation": negotiation,
"leads":leads
"leads":leads,
"empty_state_value": _("lead"),
}
return render(request, "crm/leads/lead_tracking.html", context)
@ -7471,6 +7487,7 @@ class OpportunityListView(LoginRequiredMixin, PermissionRequiredMixin, ListView)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["stage_choices"] = models.Stage.choices
context["empty_state_value"] = _("opportunity")
return context
def get_template_names(self):
@ -10720,6 +10737,7 @@ class PurchaseOrderListView(LoginRequiredMixin, PermissionRequiredMixin, ListVie
context = super().get_context_data(**kwargs)
context["entity_slug"] = dealer.entity.slug
context["vendors"] = vendors
context["empty_state_value"] = _("purchase order")
return context

View File

@ -21,10 +21,11 @@
</div>
</a>
<div class="text-center">
<h3 class="text-body-highlight">{% trans 'Account Created Successfully' %}</h3>
<h3 class="text-body-highlight">{% trans 'Registration Successful!' %}</h3>
<p class="text-body-tertiary fs-9">
{% blocktrans %}
Thank you for registering at Haikal. We will contact you soon.
Thank you for registering at Haikal. We've received your information and a member of our team will contact you shortly to confirm your account details.
{% endblocktrans %}
</p>
</div>

View File

@ -5,6 +5,7 @@
{% endblock %}
{% block content %}
{% if request.user.is_superuser %}
<div class="container py-5">
<header class="mb-5">
<h1 class="display-4 fw-bold">
@ -12,7 +13,7 @@
{% trans "Admin Dashboard" %}
</h1>
<p class="lead mt-3">
{% trans "Manage user accounts, review system logs, and control access permissions." %}
{% trans "Manage user accounts and review system logs." %}
</p>
</header>
@ -41,4 +42,5 @@
</div>
</div>
</div>
{% endif %}
{% endblock content %}

View File

@ -258,6 +258,6 @@
</div>
{% else %}
{% url 'lead_create' request.dealer.slug as create_lead_url %}
{% include "empty-illustration-page.html" with value="lead" url=create_lead_url %}
{% include "empty-illustration-page.html" with value=empty_state_value url=create_lead_url %}
{% endif %}
{% endblock %}

View File

@ -175,6 +175,6 @@
</div>
{% else %}
{% url 'lead_create' request.dealer.slug as create_lead_url %}
{% include "empty-illustration-page.html" with value="lead" url=create_lead_url %}
{% include "empty-illustration-page.html" with value=empty_state_value url=create_lead_url %}
{% endif %}
{% endblock %}

View File

@ -118,7 +118,7 @@
{% endif %}
{% else %}
{% url 'opportunity_create' request.dealer.slug as create_opportunity_url %}
{% include "empty-illustration-page.html" with value="opportunity" url=create_opportunity_url %}
{% include "empty-illustration-page.html" with value=empty_state_value url=create_opportunity_url %}
{% endif %}
{% block customJS %}
<script>

View File

@ -175,6 +175,6 @@
{% include 'modal/delete_modal.html' %}
{% else %}
{% url "customer_create" request.dealer.slug as create_customer_url %}
{% include "empty-illustration-page.html" with value="customer" url=create_customer_url %}
{% include "empty-illustration-page.html" with value=empty_state_value url=create_customer_url %}
{% endif %}
{% endblock %}

View File

@ -81,7 +81,7 @@
<div class="card-header "></div>
<h4 class="my-4">{% trans "Permissions" %}</h4>
<a class="btn btn-sm btn-phoenix-primary mt-2 mb-4"
href="{% url 'group_permission' request.dealer.slug group.id %}"><i class="fa-solid fa-unlock"></i>{% trans "Manage Permissions" %}</a>
href="{% url 'group_permission' request.dealer.slug group.id %}"><i class="fa-solid fa-unlock me-2"></i>{% trans "Manage Permissions" %}</a>
<table class="table table-hover table-responsive-sm fs-9 mb-0">
<thead>
<tr>

View File

@ -630,7 +630,7 @@
{% endif %}
</li>
<li class="nav-item">
{% if request.is_dealer %}
{% if request.user.is_superuser %}
<a class="nav-link px-3 d-block"
href="{% url 'management' request.dealer.slug %}"> <span class="me-2 text-body align-bottom" data-feather="shield"></span>{{ _("Admin Managemnet") }}</a>
{% endif %}

View File

@ -130,7 +130,7 @@
{% else %}
{% url "car_add" request.dealer.slug as create_car_url %}
{% include "empty-illustration-page.html" with value="car" url=create_car_url %}
{% include "empty-illustration-page.html" with value=empty_state_value url=create_car_url %}
{% endif %}
{% endblock %}
{% block customJS %}

View File

@ -191,6 +191,6 @@
</section>
{% else %}
{% url 'organization_create' request.dealer.slug as create_organization_url %}
{% include "empty-illustration-page.html" with value="organization" url=create_organization_url %}
{% include "empty-illustration-page.html" with value=empty_state_value url=create_organization_url %}
{% endif %}
{% endblock %}

View File

@ -2,38 +2,49 @@
{% load i18n static %}
{% block content %}
<main class="main">
<!-- Page Title -->
<div class="page-title light-background">
<div class="container d-lg-flex justify-content-between align-items-center">
<h1 class="mb-2 mb-lg-0">{% trans "Payment Failed" %}</h1>
<nav class="breadcrumbs">
<ol>
<li>
<section class="py-4">
<div class="container d-flex justify-content-between align-items-center">
<h1 class="h3 mb-0 text-body-emphasis">{% trans "Payment Failed" %}</h1>
<nav aria-label="breadcrumb">
<ol class="breadcrumb mb-0">
<li class="breadcrumb-item">
<a href="{% url 'home' %}">{% trans "Home" %}</a>
</li>
<li class="current">{% trans "Failed" %}</li>
<li class="breadcrumb-item active" aria-current="page">{% trans "Payment Failed" %}</li>
</ol>
</nav>
</div>
</div>
<!-- End Page Title -->
<!-- Failed Section -->
<section class="section">
<div class="container text-center" data-aos="fade-up">
<div class="py-5">
</section>
<section class="d-flex align-items-center justify-content-center py-5">
<div class="container text-center" style="max-width: 32rem;">
<div class="card p-5 shadow-sm border-0 rounded-4" data-aos="fade-up">
<div class="card-body p-0">
<div class="mb-4">
<i class="bi bi-x-circle-fill text-danger" style="font-size: 5rem;"></i>
<h2 class="mt-4">{% trans "Payment Failed" %}</h2>
</div>
<h2 class="mt-4 mb-3 text-body-emphasis fw-bold">{% trans "Payment Failed" %}</h2>
{% if message %}
<p class="lead">{{ message }}.</p>
<p class="lead text-body-secondary">{{ message }}.</p>
{% else %}
<p class="lead">{% trans "We couldn't process your payment. Please try again" %}.</p>
<p class="lead text-body-secondary">
{% trans "We couldn't process your payment. Please review your information and try again." %}
</p>
{% endif %}
<a href="{% url 'home' %}" class="btn btn-phoenix-primary mt-3">
<i class="bi bi-house-door"></i> {% trans "Back to Home" %}
<div class="d-grid gap-3 col-md-8 mx-auto mt-4">
<a href="{% url 'pricing_page' request.dealer.slug %}" class="btn btn-danger btn-lg rounded-pill">
{% trans "Go Back and Try Again" %}
</a>
<a href="{% url 'home' %}" class="btn btn-link text-body-secondary text-decoration-none">
{% trans "Back to Home" %}
</a>
</div>
</div>
</div>
</div>
</section>
<!-- /Failed Section -->
</main>
{% endblock content %}

View File

@ -1,40 +1,50 @@
{% extends "base.html" %}
{% load i18n static %}
{% block content %}
<main class="main">
<!-- Page Title -->
<div class="page-title light-background">
<div class="container d-lg-flex justify-content-between align-items-center">
<h1 class="mb-2 mb-lg-0">{% trans "Payment Successful" %}</h1>
<nav class="breadcrumbs">
<ol>
<li>
<a href="{% url 'home' %}">{{ _("Home") }}</a>
<section class="py-4">
<div class="container d-flex justify-content-between align-items-center">
<h1 class="h3 mb-0 text-body-emphasis">{% trans "Payment Successful" %}</h1>
<nav aria-label="breadcrumb">
<ol class="breadcrumb mb-0">
<li class="breadcrumb-item">
<a href="{% url 'home' %}">{% trans "Home" %}</a>
</li>
<li class="current">{{ _("Success") }}</li>
<li class="breadcrumb-item active" aria-current="page">{% trans "Success" %}</li>
</ol>
</nav>
</div>
</div>
<!-- End Page Title -->
<!-- Success Section -->
<section class="section">
<div class="container text-center" data-aos="fade-up">
<div class="py-5">
</section>
<section class="d-flex align-items-center justify-content-center py-5">
<div class="container text-center" style="max-width: 32rem;">
<div class="card p-5 shadow-sm border-0 rounded-4" data-aos="fade-up">
<div class="card-body p-0">
<div class="mb-4">
<i class="bi bi-check-circle-fill text-success" style="font-size: 5rem;"></i>
<h2 class="mt-4">{{ _("Thank You") }}!</h2>
<p class="lead">{{ _("Your payment was successful") }}. {{ _("Your order is being processed") }}.</p>
</div>
<h2 class="mt-4 mb-3 text-body-emphasis fw-bold">{{ _("Thank You") }}!</h2>
<p class="lead text-body-secondary">
{{ _("Your payment was successful") }}. {{ _("Your order is being processed") }}.
</p>
<div class="d-grid gap-3 col-md-8 mx-auto mt-4">
{% if invoice %}
<a href="{% url 'invoice_preview_html' invoice.pk %}"
class="btn btn-phoenix-primary mt-3"><i class="fas fa-eye"></i>
{{ _("View Invoice") }}</a>
class="btn btn-primary btn-lg rounded-pill">
<i class="fas fa-eye me-2"></i> {{ _("View Invoice") }}
</a>
{% endif %}
<a href="{% url 'home' %}" class="btn btn-phoenix-primary mt-3">
<i class="fas fa-home"></i> {{ _("Back to Home") }}
<a href="{% url 'home' %}" class="btn btn-link text-body-secondary text-decoration-none">
<i class="fas fa-home me-2"></i> {{ _("Back to Home") }}
</a>
</div>
</div>
</div>
</div>
</section>
<!-- /Success Section -->
</main>
{% endblock content %}

View File

@ -52,6 +52,6 @@
{% include "plans/pagination.html" %}
{% endblock %}
{% else %}
{% blocktrans %}You do not have any orders so far.{% endblocktrans %}
<p class="text-center">{% trans "You do not have any orders so far." %}<p>
{% endif %}
{% endblock %}

View File

@ -4,96 +4,222 @@
{% block title %}
{% trans 'Upgrade Plan' %}
{% endblock %}
{% block customCSS %}
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css"
integrity="sha512-X..."
integrity="sha512-X... (Your actual integrity hash)"
crossorigin="anonymous"
referrerpolicy="no-referrer" />
<style>
.pricing-card .card {
:root {
--primary-color: #0d6efd;
--primary-shadow: rgba(13, 110, 253, 0.25);
--border-color: #e9ecef;
--card-hover-shadow: rgba(0, 0, 0, 0.05);
--card-selected-border: #0d6efd;
--card-selected-shadow: rgba(13, 110, 253, 0.4);
}
body {
background-color: #f8f9fa;
}
.pricing-card-label {
transition: all 0.2s ease-in-out;
cursor: pointer;
}
.pricing-card-label .card {
border: 2px solid var(--border-color);
transition: all 0.3s ease-in-out;
box-shadow: 0 4px 12px var(--card-hover-shadow);
}
.pricing-card .card.selected {
border-color:rgb(20, 108, 241);
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.4);
.btn-check:checked + .pricing-card-label .card {
border-color: var(--card-selected-border) !important;
box-shadow: 0 0 0 0.25rem var(--card-selected-shadow) !important;
}
.btn-check:checked + .btn .card {
/* fallback if JS fails */
border-color:rgb(13, 91, 207);
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
.btn-check:checked + .pricing-card-label .card:hover {
transform: scale(1.02);
}
.card.selected {
border-color: #0d6efd;
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.4);
.pricing-card-label .card:hover {
box-shadow: 0 8px 20px var(--card-hover-shadow);
transform: scale(1.01);
}
.pricing-card-badge {
position: absolute;
top: 0;
right: 1.5rem;
transform: translateY(-50%);
padding: 0.25rem 0.75rem;
font-size: 0.8rem;
font-weight: bold;
color: white;
background-color: var(--primary-color);
border-radius: 1rem;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.progress-indicator {
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 2rem;
}
.progress-indicator .step-item {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
flex: 1;
color: #6c757d;
}
.progress-indicator .step-item .icon {
width: 3rem;
height: 3rem;
border-radius: 50%;
background-color: #e9ecef;
display: flex;
justify-content: center;
align-items: center;
font-size: 1.25rem;
color: #6c757d;
transition: all 0.3s;
border: 2px solid #e9ecef;
}
.progress-indicator .step-item.active .icon {
background-color: var(--primary-color);
color: white;
border-color: var(--primary-color);
}
.progress-indicator .step-item.completed .icon {
background-color: #28a745;
color: white;
border-color: #28a745;
}
.progress-indicator .step-item .step-text {
margin-top: 0.5rem;
font-size: 0.9rem;
font-weight: 500;
white-space: nowrap;
}
.progress-indicator .line {
height: 2px;
width: 100%;
background-color: #e9ecef;
margin: 0 -1.5rem;
position: relative;
z-index: -1;
}
.btn-phoenix-primary {
background-color: var(--primary-color);
color: white;
border-color: var(--primary-color);
transition: all 0.2s ease-in-out;
}
.btn-phoenix-primary:hover {
background-color: #0b5ed7;
border-color: #0a58ca;
}
.btn-phoenix-secondary {
background-color: #6c757d;
color: white;
border-color: #6c757d;
transition: all 0.2s ease-in-out;
}
.btn-phoenix-secondary:hover {
background-color: #5c636a;
border-color: #565e64;
}
.summary-box {
border: 1px solid #8d8f94;
border-radius: 0.5rem;
padding: 1.5rem;
background-color: #ffffff;
border: 1px solid var(--border-color);
border-radius: 0.75rem;
padding: 2rem;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
}
.summary-box h5 {
padding-bottom: 0.5rem;
margin-bottom: 1rem;
/* Validation styles for modern look */
.form-control:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 0.25rem var(--primary-shadow);
}
.summary-item {
margin-bottom: 0.75rem;
}
.form-label {
font-weight: 500;
}
#pricing_container{
max-width:60rem;
}
/* Validation styles */
.is-invalid {
.form-control.is-invalid {
border-color: #dc3545 !important;
padding-right: calc(1.5em + 0.75rem) !important;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e") !important;
background-repeat: no-repeat !important;
background-position: right calc(0.375em + 0.1875rem) center !important;
background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem) !important;
}
.invalid-feedback {
display: none;
width: 100%;
margin-top: 0.25rem;
font-size: 0.875em;
color: #dc3545;
font-size: 0.85em;
display: none;
margin-top: 0.25rem;
}
.is-invalid + .invalid-feedback,
.is-invalid ~ .invalid-feedback {
display: block;
}
/* Custom icons for lists */
.fa-ul .fa-li {
left: -1em;
}
</style>
{% endblock customCSS %}
{% block content %}
<div class="container py-5" id="pricing_container">
<h1 class="text-center mb-5 text-primary">{{ _("Choose Your Plan") }}</h1>
<h1 class="text-center mb-5 text-primary fw-bold">{{ _("Choose Your Plan") }}</h1>
<form method="POST"
action="{% url 'submit_plan' request.dealer.slug %}"
id="wizardForm">
{% csrf_token %}
<!--step1: Choose Plans-->
<div class="step row justify-content-center mt-5 mb-3" id="step1">
<div class="col-lg-8 col-md-10">
<div class="card shadow-sm border-0 rounded-3">
<div class="card-header bg-gray-200 py-3 border-0 rounded-top-3">
<h4 class="mb-0 fs-4 text-center text-white">1. {{ _("Select a Plan") }}</h4>
<div class="progress-indicator mb-5">
<div class="step-item active" id="progress-step-1">
<div class="icon"><i class="fas fa-dollar-sign"></i></div>
<span class="step-text">{{ _("Select Plan") }}</span>
</div>
<div class="card-body bg-light-subtle">
<div class="line"></div>
<div class="step-item" id="progress-step-2">
<div class="icon"><i class="fas fa-user-check"></i></div>
<span class="step-text">{{ _("Your Info") }}</span>
</div>
<div class="line"></div>
<div class="step-item" id="progress-step-3">
<div class="icon"><i class="fas fa-credit-card"></i></div>
<span class="step-text">{{ _("Payment") }}</span>
</div>
<div class="line"></div>
<div class="step-item" id="progress-step-4">
<div class="icon"><i class="fas fa-receipt"></i></div>
<span class="step-text">{{ _("Confirm") }}</span>
</div>
</div>
<div class="step row justify-content-center" id="step1">
<div class="col-lg-10 col-md-12">
<h4 class="text-center mb-4 text-secondary">{{ _("1. Select a Plan") }}</h4>
<div class="row g-4">
{% for pp in plan_list %}
<div class="col-md-12 mb-3">
<div class="col-md-4 mb-3 d-flex">
<input type="radio"
class="btn-check"
name="selected_plan"
@ -103,19 +229,23 @@
data-price="{{ pp.price_with_tax }}"
autocomplete="off"
{% if forloop.first %}checked{% endif %}>
<label class="btn w-100 p-0 pricing-card" for="plan_{{ forloop.counter }}">
<div class="card h-100 border border-2 rounded-4">
<div class="card-body p-4">
<h4 class="mb-3">{{ pp.plan.name|capfirst }}</h4>
<h5 class="mb-4">
{{pp.price_with_tax}} <span class="icon-saudi_riyal"></span><span class="fs-6 fw-normal">/ {{ pp.pricing.period }}</span> {% trans "days" %}
</h5>
<h5>{{ _("Include Haikal's") }}</h5>
<label class="btn w-100 p-0 pricing-card-label"
for="plan_{{ forloop.counter }}">
<div class="card h-100 rounded-4">
<div class="card-body p-4 position-relative">
{% if forloop.first %}
<div class="pricing-card-badge">{{ _("Most Popular") }}</div>
{% endif %}
<h4 class="mb-2 fw-bold text-primary">{{ pp.plan.name|capfirst }}</h4>
<h2 class="mb-4">
{{ pp.price_with_tax }}<span class="fs-5 fw-normal"> <span class="icon-saudi_riyal"></span> / {{ pp.pricing.period }} {{ _("days") }}</span>
</h2>
<h5>{{ _("Includes:") }}</h5>
<ul class="fa-ul ps-3">
{% if pp.plan.description %}
{% for line in pp.plan.description|splitlines %}
<li class="mb-2">
<span class="fa-li"><i class="fas fa-check text-primary"></i></span>
<span class="fa-li"><i class="fas fa-check-circle text-success"></i></span>
{{ line|capfirst }}
</li>
{% endfor %}
@ -129,105 +259,112 @@
</div>
</div>
</div>
</div>
<!--step2: Information-->
<div class="step d-none row justify-content-center mt-5 mb-3" id="step2">
<div class="step d-none row justify-content-center" id="step2">
<div class="col-lg-8 col-md-10">
<div class="card shadow-sm border-0 rounded-3">
<div class="card-header bg-gray-200 py-3 border-0 rounded-top-3">
<h3 class="mb-0 fs-4 text-center text-white">2. {{ _("Enter Your Information") }}</h3>
</div>
<div class="card-body bg-light-subtle">
<h4 class="text-center mb-4 text-secondary">{{ _("2. Enter Your Information") }}</h4>
<div class="card shadow-sm border-0 rounded-3 p-4">
<div class="mb-3">
<label class="form-label" for="first_name">{{ _("First Name") }}</label>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-user"></i></span>
<input type="text"
name="first_name"
id="first_name"
class="form-control form-control-sm"
class="form-control"
required
placeholder='{{ _("First Name") }}'
value="{{ request.dealer.first_name }}">
<div class="invalid-feedback">{{ _("Please enter your first name.") }}</div>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="last_name">{{ _("Last Name") }}</label>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-user"></i></span>
<input type="text"
name="last_name"
id="last_name"
class="form-control form-control-sm"
class="form-control"
required
placeholder='{{ _("Last Name") }}'
value="{{ request.dealer.last_name }}">
<div class="invalid-feedback">{{ _("Please enter your last name.") }}</div>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="email">{{ _("Email Address") }}</label>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-envelope"></i></span>
<input type="email"
name="email"
id="email"
class="form-control form-control-sm"
class="form-control"
required
placeholder="email@example.com"
value="{{ request.dealer.user.email }}">
<div class="invalid-feedback">{{ _("Please enter a valid email address.") }}</div>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="phone">{{ _("Phone Number") }}</label>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-phone"></i></span>
<input type="text"
name="phone"
id="phone"
class="form-control form-control-sm"
class="form-control"
dir="ltr"
maxlength="10"
pattern="^05\d{8}$"
inputmode="numeric"
placeholder='{{ _("Phone Number") }}'
placeholder='{{ _("05xxxxxxxx") }}'
value="{{ request.dealer.phone_number }}"
required>
<div class="invalid-feedback">{{ _("Please enter a valid 10-digit phone number.") }}</div>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="company">{{ _("Company") }}</label>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-building"></i></span>
<input type="text"
name="company"
id="company"
class="form-control form-control-sm"
placeholder='{{ _("Company") }}'
class="form-control"
placeholder='{{ _("Company Name") }}'
value="{{ request.dealer.name }}">
</div>
</div>
</div>
</div>
</div>
<!--step3: Payment-->
<div class="step d-none row justify-content-center mt-5 mb-3" id="step3">
<div class="step d-none row justify-content-center" id="step3">
<div class="col-lg-8 col-md-10">
<div class="card shadow-sm border-0 rounded-3">
<div class="card-header bg-gray-200 py-3 border-0 rounded-top-3">
<h3 class="mb-0 fs-4 text-center text-white">3. {{ _("Payment Information") }}</h3>
</div>
<div class="card-body bg-light-subtle">
<h4 class="text-center mb-4 text-secondary">{{ _("3. Payment Information") }}</h4>
<div class="card shadow-sm border-0 rounded-3 p-4">
<div class="mb-3">
<label class="form-label" for="card_name">{{ _("Cardholder Name") }}</label>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-user"></i></span>
<input type="text"
name="card_name"
id="card_name"
class="form-control form-control-sm"
class="form-control"
placeholder='{{ _("Cardholder Name") }}'
required>
<div class="invalid-feedback" id="card_name_feedback">
{{ _("Please enter the cardholder name") }}
{{ _("Please enter the cardholder name.") }}
</div>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="card_number">{{ _("Card Number") }}</label>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-credit-card"></i></span>
<input type="text"
name="card_number"
id="card_number"
class="form-control form-control-sm"
class="form-control"
placeholder='{{ _("Card Number") }}'
maxlength="19"
pattern="^\d{4}\s\d{4}\s\d{4}\s\d{4}$"
@ -235,33 +372,37 @@
required
title="Enter a 16-digit card number">
<div class="invalid-feedback" id="card_number_feedback">
{{ _("Please enter a valid 16-digit card number") }}
{{ _("Please enter a valid 16-digit card number.") }}
</div>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="card_expiry">{{ _("Expiry Date") }} (MM/YY)</label>
<div class="input-group">
<span class="input-group-text"><i class="far fa-calendar-alt"></i></span>
<input type="text"
name="card_expiry"
id="card_expiry"
class="form-control form-control-sm"
placeholder='{{ _("Expiry Date") }}'
class="form-control"
placeholder='{{ _("MM/YY") }}'
maxlength="5"
pattern="^(0[1-9]|1[0-2])\/\d{2}$"
inputmode="numeric"
required
title="Enter expiry in MM/YY format">
<div class="invalid-feedback" id="card_expiry_feedback">
{{ _("Please enter a valid expiry date (MM/YY)") }}
{{ _("Please enter a valid expiry date (MM/YY).") }}
</div>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="card_cvv">{{ _("CVV") }}</label>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-lock"></i></span>
<input type="text"
name="card_cvv"
id="card_cvv"
class="form-control form-control-sm"
class="form-control"
placeholder='{{ _("CVV") }}'
maxlength="3"
pattern="^\d{3}$"
@ -269,83 +410,73 @@
required
title="Enter 3-digit CVV">
<div class="invalid-feedback" id="card_cvv_feedback">
{{ _("Please enter a valid 3-digit CVV") }}
{{ _("Please enter a valid 3-digit CVV.") }}
</div>
</div>
</div>
</div>
</div>
</div>
<!--Step4 confirmation-->
<div class="step d-none row justify-content-center mt-5 mb-3" id="step4">
<div class="step d-none row justify-content-center" id="step4">
<div class="col-lg-8 col-md-10">
<div class="card shadow-sm border-0 rounded-3">
<div class="card-header bg-gray-200 py-3 border-0 rounded-top-3">
<h3 class="mb-0 fs-4 text-center text-white">4. {{ _("Confirm Your Information") }}</h3>
</div>
<div class="card-body bg-light-subtle">
<h5 class="text-center">
<i class="fas fa-file-invoice-dollar me-2"></i>{{ _("Order Summary") }}
</h5>
<h4 class="text-center mb-4 text-secondary">{{ _("4. Confirm Your Information") }}</h4>
<div class="summary-box">
<h5 class="text-center mb-4 fw-bold text-primary">{{ _("Order Summary") }}</h5>
<div class="summary-item">
<i class="fas fa-box me-2"></i><strong>{{ _("Plan") }}:</strong> <span id="summary_plan"></span>
<p class="mb-1"><i class="fas fa-box me-2"></i><strong>{{ _("Plan") }}:</strong> <span id="summary_plan"></span></p>
</div>
<div class="summary-item">
<i class="fas fa-tag me-2"></i><strong>{{ _("Price") }}:</strong> <span id="summary_price"></span>
<p class="mb-1"><i class="fas fa-tag me-2"></i><strong>{{ _("Price") }}:</strong> <span id="summary_price"></span></p>
</div>
<div class="summary-item">
<i class="fas fa-receipt me-2"></i><strong>{{ _("VAT") }} (15%):</strong> <span id="summary-tax">0.00</span> <span class="icon-saudi_riyal"></span>
<p class="mb-1"><i class="fas fa-receipt me-2"></i><strong>{{ _("VAT") }} (15%):</strong> <span id="summary-tax">0.00</span> <span class="icon-saudi_riyal"></span></p>
</div>
<hr class="my-3">
<div class="summary-item">
<p class="mb-0 fs-5 fw-bold"><i class="fas fa-hand-holding-usd me-2"></i>{{ _("Total") }}: <span id="summary-total">0.00</span> <span class="icon-saudi_riyal"></span></p>
</div>
<h5 class="mt-4 text-center mb-4 fw-bold text-primary">{{ _("User Information") }}</h5>
<div class="summary-item">
<p class="mb-1"><i class="fas fa-signature me-2"></i><strong>{{ _("Name") }}:</strong> <span id="summary_name"></span></p>
</div>
<div class="summary-item">
<i class="fas fa-hand-holding-usd me-2"></i><strong>{{ _("Total") }}:</strong> <span id="summary-total">0.00</span> <span class="icon-saudi_riyal"></span>
</div>
<hr>
<h5 class="mt-4 text-center">
<i class="fas fa-user me-2"></i>{{ _("User Information") }}
</h5>
<div class="summary-item">
<i class="fas fa-signature me-2"></i><strong>{{ _("Name") }}:</strong> <span id="summary_name"></span>
<p class="mb-1"><i class="fas fa-envelope me-2"></i><strong>{{ _("Email") }}:</strong> <span id="summary_email"></span></p>
</div>
<div class="summary-item">
<i class="fas fa-envelope me-2"></i><strong>{{ _("Email") }}:</strong> <span id="summary_email"></span>
<p class="mb-1"><i class="fas fa-building me-2"></i><strong>{{ _("Company") }}:</strong> <span id="summary_company"></span></p>
</div>
<div class="summary-item">
<i class="fas fa-building me-2"></i><strong>{{ _("Company") }}:</strong> <span id="summary_company"></span>
<p class="mb-1"><i class="fas fa-phone me-2"></i><strong>{{ _("Phone") }}:</strong> <span id="summary_phone"></span></p>
</div>
<h5 class="mt-4 text-center mb-4 fw-bold text-primary">{{ _("Payment") }}</h5>
<div class="summary-item">
<p class="mb-1"><i class="fas fa-user me-2"></i><strong>{{ _("Cardholder") }}:</strong> <span id="summary_card_name"></span></p>
</div>
<div class="summary-item">
<i class="fas fa-phone me-2"></i><strong>{{ _("Phone") }}:</strong> <span id="summary_phone"></span>
</div>
<hr>
<h5 class="mt-4 text-center">
<i class="fas fa-credit-card me-2"></i>{{ _("Payment") }}
</h5>
<div class="summary-item">
<i class="fas fa-user me-2"></i><strong>{{ _("Cardholder") }}:</strong> <span id="summary_card_name"></span>
<p class="mb-1"><i class="fas fa-credit-card me-2"></i><strong>{{ _("Card Number") }}:</strong> <span id="summary_card_number"></span></p>
</div>
<div class="summary-item">
<i class="fas fa-credit-card me-2"></i><strong>{{ _("Card Number") }}:</strong> <span id="summary_card_number"></span>
</div>
<div class="summary-item">
<i class="far fa-calendar-alt me-2"></i><strong>{{ _("Expiry") }}:</strong> <span id="summary_card_expiry"></span>
<p class="mb-1"><i class="far fa-calendar-alt me-2"></i><strong>{{ _("Expiry") }}:</strong> <span id="summary_card_expiry"></span></p>
</div>
</div>
</div>
</div>
</div>
<!-- Navigation -->
<div class="d-flex justify-content-between mt-4">
<div class="d-flex justify-content-between mt-5">
<button type="button"
class="btn btn-lg btn-phoenix-secondary"
class="btn btn-lg btn-phoenix-secondary px-5"
id="prevBtn"
disabled>{{ _("Previous") }}</button>
<button type="button" class="btn btn-lg btn-phoenix-primary" id="nextBtn">{{ _("Next") }}</button>
disabled><i class="fas fa-arrow-left me-2"></i>{{ _("Previous") }}</button>
<button type="button"
class="btn btn-lg btn-phoenix-primary px-5"
id="nextBtn">{{ _("Next") }}<i class="fas fa-arrow-right ms-2"></i></button>
<button type="submit"
class="btn btn-lg btn-phoenix-primary d-none"
class="btn btn-lg btn-phoenix-primary px-5 d-none"
id="submitBtn">{{ _("Confirm") }}</button>
</div>
</form>
</div>
{% endblock content %}
{% block customJS %}
<script>
// wizard-form.js

View File

@ -118,7 +118,7 @@
{% else %}
{% if vendors %}
{% url "purchase_order_create" request.dealer.slug request.dealer.entity.slug as create_purchase_url %}
{% include "empty-illustration-page.html" with value="purchase order" url=create_purchase_url %}
{% include "empty-illustration-page.html" with value=empty_state_value url=create_purchase_url %}
{% else %}
{% url "vendor_create" request.dealer.slug as vendor_create_url %}
{% include "message-illustration.html" with value1=_("You don't have a Vendor, Please add a Vendor before creating a Purchase Order.") value2=_("Create New Vendor") url=vendor_create_url %}

View File

@ -124,13 +124,13 @@
<div class="col">
{% if not items %}
{% url "car_add" request.dealer.slug as create_car_url %}
{% include "message-illustration.html" with value1="Please add at least one car or complete the car info before creating a quotation." value2="Add car" message_image="images/logos/no-content-new.jpg" url=create_car_url %}
{% include "message-illustration.html" with value1=no_items_message value2=no_items_button message_image="images/logos/no-content-new.jpg" url=create_car_url %}
{% endif %}
</div>
<div class="col">
{% if not customer_count %}
{% url "customer_create" request.dealer.slug as create_customer_url %}
{% include "message-illustration.html" with value1="Please add at least one customer before creating a quotation." value2="Add Customer" message_image="images/logos/no-content-new.jpg" url=create_customer_url %}
{% include "message-illustration.html" with value1=no_customers_message value2=no_customers_button message_image="images/logos/no-content-new.jpg" url=create_customer_url %}
{% endif %}
</div>
<div>

View File

@ -88,6 +88,6 @@
</div>
{% else %}
{% url 'estimate_create' request.dealer.slug as url %}
{% include "empty-illustration-page.html" with value="invoice" url=url %}
{% include "empty-illustration-page.html" with value=_("invoice") url=url %}
{% endif %}
{% endblock %}

View File

@ -90,7 +90,7 @@
{% else %}
{% if request.user.userplan %}
{% url "user_create" request.dealer.slug as create_staff_url %}
{% include "empty-illustration-page.html" with value="staff" url=create_staff_url %}
{% include "empty-illustration-page.html" with value=no_staff_message url=create_staff_url %}
{% else %}
{% url "pricing_page" request.dealer.slug as pricing_page_url %}
{% include "message-illustration.html" with value1=_("No active plan, Please create a subscription plan.") value2=_("Buy Plan") url=pricing_page_url %}