772 lines
34 KiB
HTML
772 lines
34 KiB
HTML
{% extends 'base.html' %}
|
|
{% load i18n static %}
|
|
{% load custom_filters %}
|
|
{% 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..."
|
|
crossorigin="anonymous"
|
|
referrerpolicy="no-referrer" />
|
|
<style>
|
|
.pricing-card .card {
|
|
transition: all 0.3s ease-in-out;
|
|
}
|
|
|
|
.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 + .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);
|
|
}
|
|
|
|
.card.selected {
|
|
border-color: #0d6efd;
|
|
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.4);
|
|
}
|
|
|
|
.summary-box {
|
|
border: 1px solid #8d8f94;
|
|
border-radius: 0.5rem;
|
|
padding: 1.5rem;
|
|
}
|
|
|
|
.summary-box h5 {
|
|
padding-bottom: 0.5rem;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.summary-item {
|
|
margin-bottom: 0.75rem;
|
|
}
|
|
|
|
.form-label {
|
|
font-weight: 500;
|
|
}
|
|
#pricing_container{
|
|
max-width:60rem;
|
|
|
|
}
|
|
|
|
/* Validation styles */
|
|
.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;
|
|
}
|
|
|
|
.is-invalid ~ .invalid-feedback {
|
|
display: block;
|
|
}
|
|
</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>
|
|
<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>
|
|
<div class="card-body bg-light-subtle">
|
|
{% for pp in plan_list %}
|
|
<div class="col-md-12 mb-3">
|
|
<input type="radio"
|
|
class="btn-check"
|
|
name="selected_plan"
|
|
id="plan_{{ forloop.counter }}"
|
|
value="{{ pp.id }}"
|
|
data-name="{{ pp.plan.name }}"
|
|
data-price="{{ pp.price }}"
|
|
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 }} <span class="icon-saudi_riyal"></span><span class="fs-6 fw-normal">/ {{ pp.pricing.period }}</span> {% trans "days" %}
|
|
</h5>
|
|
<h5>{{ _("Include Haikal's") }}</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>
|
|
{{ line|capfirst }}
|
|
</li>
|
|
{% endfor %}
|
|
{% endif %}
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</label>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!--step2: Information-->
|
|
<div class="step d-none row justify-content-center mt-5 mb-3" 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">
|
|
<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"
|
|
required
|
|
placeholder="{{ _("First Name") }}"
|
|
value="{{ request.user.first_name }}">
|
|
</div>
|
|
<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"
|
|
required
|
|
placeholder="{{ _("Last Name") }}"
|
|
value="{{ request.user.last_name }}">
|
|
</div>
|
|
<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"
|
|
required
|
|
placeholder="email@example.com"
|
|
value="{{ request.user.email }}">
|
|
</div>
|
|
<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"
|
|
dir="ltr"
|
|
placeholder="{{ _("Phone Number") }}"
|
|
value="{{ request.dealer.phone_number.raw_input }}"
|
|
required>
|
|
</div>
|
|
<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") }}"
|
|
value="{{ request.dealer.get_local_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="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">
|
|
<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"
|
|
placeholder="{{ _("Cardholder Name") }}"
|
|
required>
|
|
<div class="invalid-feedback" id="card_name_feedback">
|
|
{{ _("Please enter the cardholder name") }}
|
|
</div>
|
|
</div>
|
|
<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"
|
|
placeholder="{{ _("Card Number") }}"
|
|
maxlength="19"
|
|
pattern="^\d{4}\s\d{4}\s\d{4}\s\d{4}$"
|
|
inputmode="numeric"
|
|
required
|
|
title="Enter a 16-digit card number">
|
|
<div class="invalid-feedback" id="card_number_feedback">
|
|
{{ _("Please enter a valid 16-digit card number") }}
|
|
</div>
|
|
</div>
|
|
<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") }}"
|
|
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)") }}
|
|
</div>
|
|
</div>
|
|
<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"
|
|
placeholder="{{ _("CVV") }}"
|
|
maxlength="3"
|
|
pattern="^\d{3}$"
|
|
inputmode="numeric"
|
|
required
|
|
title="Enter 3-digit CVV">
|
|
<div class="invalid-feedback" id="card_cvv_feedback">
|
|
{{ _("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="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>
|
|
<div class="summary-item">
|
|
<i class="fas fa-box me-2"></i><strong>{{ _("Plan") }}:</strong> <span id="summary_plan"></span>
|
|
</div>
|
|
<div class="summary-item">
|
|
<i class="fas fa-tag me-2"></i><strong>{{ _("Price") }}:</strong> <span id="summary_price"></span>
|
|
</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>
|
|
</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>
|
|
</div>
|
|
<div class="summary-item">
|
|
<i class="fas fa-envelope me-2"></i><strong>{{ _("Email") }}:</strong> <span id="summary_email"></span>
|
|
</div>
|
|
<div class="summary-item">
|
|
<i class="fas fa-building me-2"></i><strong>{{ _("Company") }}:</strong> <span id="summary_company"></span>
|
|
</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>
|
|
</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>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- Navigation -->
|
|
<div class="d-flex justify-content-between mt-4">
|
|
<button type="button"
|
|
class="btn btn-lg btn-phoenix-secondary"
|
|
id="prevBtn"
|
|
disabled>{{ _("Previous") }}</button>
|
|
<button type="button" class="btn btn-lg btn-phoenix-primary" id="nextBtn">{{ _("Next") }}</button>
|
|
<button type="submit"
|
|
class="btn btn-lg btn-phoenix-primary d-none"
|
|
id="submitBtn">{{ _("Confirm") }}</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
{% endblock content %}
|
|
{% block customJS %}
|
|
<script>
|
|
// wizard-form.js
|
|
document.addEventListener('DOMContentLoaded', initWizardForm);
|
|
document.addEventListener('htmx:afterSwap', initWizardForm);
|
|
|
|
function initWizardForm() {
|
|
const form = document.getElementById('wizardForm');
|
|
if (!form) return;
|
|
|
|
// Remove old event listeners to prevent duplicates
|
|
form.removeEventListener('submit', handleFormSubmit);
|
|
|
|
// Add new submit handler
|
|
form.addEventListener('submit', handleFormSubmit);
|
|
|
|
// Initialize radio button selections
|
|
initPlanSelection();
|
|
|
|
// Initialize wizard steps
|
|
initWizardSteps();
|
|
|
|
// Initialize card input formatting
|
|
initCardInputs();
|
|
|
|
// Initialize summary updates
|
|
initSummaryUpdates();
|
|
|
|
// Initialize validation
|
|
initValidation();
|
|
}
|
|
|
|
function handleFormSubmit(e) {
|
|
e.preventDefault();
|
|
|
|
// Validate all steps before submission
|
|
if (!validateAllSteps()) {
|
|
return;
|
|
}
|
|
|
|
if (typeof Swal !== 'undefined') {
|
|
Swal.fire({
|
|
title: 'Processing...',
|
|
html: 'Please wait while we submit your form',
|
|
allowOutsideClick: false,
|
|
didOpen: () => Swal.showLoading()
|
|
});
|
|
}
|
|
|
|
// Submit the form after a slight delay
|
|
setTimeout(() => {
|
|
const form = e.target;
|
|
if (form.reportValidity()) {
|
|
form.submit();
|
|
} else if (typeof Swal !== 'undefined') {
|
|
Swal.close();
|
|
}
|
|
}, 100);
|
|
}
|
|
|
|
function initPlanSelection() {
|
|
const radios = document.querySelectorAll('.btn-check');
|
|
radios.forEach(radio => {
|
|
// Remove old listeners
|
|
radio.removeEventListener('change', handlePlanChange);
|
|
|
|
// Add new listeners
|
|
radio.addEventListener('change', handlePlanChange);
|
|
|
|
// Initialize selected state
|
|
if (radio.checked) {
|
|
updatePlanSelection(radio.id);
|
|
}
|
|
});
|
|
}
|
|
|
|
function handlePlanChange(e) {
|
|
if (this.checked) {
|
|
updatePlanSelection(this.id);
|
|
}
|
|
}
|
|
|
|
function updatePlanSelection(radioId) {
|
|
document.querySelectorAll('.pricing-card .card').forEach(card => {
|
|
card.classList.remove('selected');
|
|
});
|
|
|
|
const selectedCard = document.querySelector(`label[for="${radioId}"] .card`);
|
|
if (selectedCard) {
|
|
selectedCard.classList.add('selected');
|
|
}
|
|
}
|
|
|
|
function initWizardSteps() {
|
|
let currentStep = 0;
|
|
const steps = document.querySelectorAll(".step");
|
|
const nextBtn = document.getElementById("nextBtn");
|
|
const prevBtn = document.getElementById("prevBtn");
|
|
const submitBtn = document.getElementById("submitBtn");
|
|
|
|
if (!steps.length || !nextBtn || !prevBtn || !submitBtn) return;
|
|
|
|
// Remove old listeners
|
|
nextBtn.removeEventListener("click", handleNext);
|
|
prevBtn.removeEventListener("click", handlePrev);
|
|
|
|
// Add new listeners
|
|
nextBtn.addEventListener("click", handleNext);
|
|
prevBtn.addEventListener("click", handlePrev);
|
|
|
|
function showStep(index) {
|
|
steps.forEach((step, i) => {
|
|
step.classList.toggle("d-none", i !== index);
|
|
});
|
|
|
|
prevBtn.disabled = index === 0;
|
|
nextBtn.classList.toggle("d-none", index === steps.length - 1);
|
|
submitBtn.classList.toggle("d-none", index !== steps.length - 1);
|
|
|
|
if (index === steps.length - 1) {
|
|
populateSummary();
|
|
}
|
|
}
|
|
|
|
function handleNext() {
|
|
// Validate current step before proceeding
|
|
if (!validateCurrentStep(currentStep)) {
|
|
return;
|
|
}
|
|
|
|
if (currentStep < steps.length - 1) {
|
|
currentStep++;
|
|
showStep(currentStep);
|
|
}
|
|
}
|
|
|
|
function handlePrev() {
|
|
if (currentStep > 0) {
|
|
currentStep--;
|
|
showStep(currentStep);
|
|
}
|
|
}
|
|
|
|
function populateSummary() {
|
|
const selectedPlan = document.querySelector('input[name="selected_plan"]:checked');
|
|
if (selectedPlan) {
|
|
document.getElementById("summary_plan").textContent = selectedPlan.dataset.name;
|
|
document.getElementById("summary_price").textContent = selectedPlan.dataset.price;
|
|
}
|
|
|
|
const firstName = document.getElementById("first_name")?.value || '';
|
|
const lastName = document.getElementById("last_name")?.value || '';
|
|
document.getElementById("summary_name").textContent = `${firstName} ${lastName}`.trim();
|
|
|
|
document.getElementById("summary_email").textContent = document.getElementById("email")?.value || '';
|
|
document.getElementById("summary_company").textContent = document.getElementById("company")?.value || '';
|
|
document.getElementById("summary_phone").textContent = document.getElementById("phone")?.value || '';
|
|
document.getElementById("summary_card_name").textContent = document.getElementById("card_name")?.value || '';
|
|
|
|
const cardNumber = document.getElementById("card_number")?.value || '';
|
|
document.getElementById("summary_card_number").textContent = maskCard(cardNumber);
|
|
|
|
document.getElementById("summary_card_expiry").textContent = document.getElementById("card_expiry")?.value || '';
|
|
}
|
|
|
|
function maskCard(cardNumber) {
|
|
const last4 = cardNumber.slice(-4);
|
|
return "**** **** **** " + last4;
|
|
}
|
|
|
|
// Initialize
|
|
showStep(currentStep);
|
|
}
|
|
|
|
function initCardInputs() {
|
|
const cardNumberInput = document.getElementById("card_number");
|
|
const expiryInput = document.getElementById("card_expiry");
|
|
|
|
if (cardNumberInput) {
|
|
cardNumberInput.removeEventListener("input", formatCardNumber);
|
|
cardNumberInput.addEventListener("input", formatCardNumber);
|
|
}
|
|
|
|
if (expiryInput) {
|
|
expiryInput.removeEventListener("input", formatExpiryDate);
|
|
expiryInput.addEventListener("input", formatExpiryDate);
|
|
}
|
|
|
|
function formatCardNumber(e) {
|
|
let val = this.value.replace(/\D/g, "").substring(0, 16);
|
|
this.value = val.replace(/(.{4})/g, "$1 ").trim();
|
|
|
|
// Validate as user types
|
|
validateCardNumber(this);
|
|
}
|
|
|
|
function formatExpiryDate(e) {
|
|
let val = this.value.replace(/\D/g, "").substring(0, 4);
|
|
if (val.length >= 3) {
|
|
val = val.substring(0, 2) + "/" + val.substring(2);
|
|
}
|
|
this.value = val;
|
|
|
|
// Validate as user types
|
|
validateExpiryDate(this);
|
|
}
|
|
}
|
|
|
|
function initSummaryUpdates() {
|
|
const planInputs = document.querySelectorAll("input[name='selected_plan']");
|
|
|
|
planInputs.forEach(input => {
|
|
input.removeEventListener("change", updatePricingSummary);
|
|
input.addEventListener("change", updatePricingSummary);
|
|
});
|
|
|
|
updatePricingSummary(); // Initial call
|
|
}
|
|
|
|
function initValidation() {
|
|
// Add input event listeners for validation
|
|
const cardNameInput = document.getElementById("card_name");
|
|
const cardNumberInput = document.getElementById("card_number");
|
|
const cardExpiryInput = document.getElementById("card_expiry");
|
|
const cardCvvInput = document.getElementById("card_cvv");
|
|
|
|
if (cardNameInput) {
|
|
cardNameInput.addEventListener("blur", function() {
|
|
validateCardName(this);
|
|
});
|
|
}
|
|
|
|
if (cardNumberInput) {
|
|
cardNumberInput.addEventListener("blur", function() {
|
|
validateCardNumber(this);
|
|
});
|
|
}
|
|
|
|
if (cardExpiryInput) {
|
|
cardExpiryInput.addEventListener("blur", function() {
|
|
validateExpiryDate(this);
|
|
});
|
|
}
|
|
|
|
if (cardCvvInput) {
|
|
cardCvvInput.addEventListener("blur", function() {
|
|
validateCvv(this);
|
|
});
|
|
}
|
|
}
|
|
|
|
function validateCurrentStep(stepIndex) {
|
|
switch(stepIndex) {
|
|
case 0: // Plan selection
|
|
return validatePlanSelection();
|
|
case 1: // User information
|
|
return validateUserInfo();
|
|
case 2: // Payment information
|
|
return validatePaymentInfo();
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
function validateAllSteps() {
|
|
return validatePlanSelection() &&
|
|
validateUserInfo() &&
|
|
validatePaymentInfo();
|
|
}
|
|
|
|
function validatePlanSelection() {
|
|
const selectedPlan = document.querySelector('input[name="selected_plan"]:checked');
|
|
if (!selectedPlan) {
|
|
if (typeof Swal !== 'undefined') {
|
|
Swal.fire({
|
|
icon: 'error',
|
|
title: 'Error',
|
|
text: 'Please select a plan to continue'
|
|
});
|
|
}
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function validateUserInfo() {
|
|
const firstName = document.getElementById("first_name");
|
|
const lastName = document.getElementById("last_name");
|
|
const email = document.getElementById("email");
|
|
const phone = document.getElementById("phone");
|
|
|
|
let isValid = true;
|
|
|
|
if (!firstName.value.trim()) {
|
|
markAsInvalid(firstName, "Please enter your first name");
|
|
isValid = false;
|
|
} else {
|
|
markAsValid(firstName);
|
|
}
|
|
|
|
if (!lastName.value.trim()) {
|
|
markAsInvalid(lastName, "Please enter your last name");
|
|
isValid = false;
|
|
} else {
|
|
markAsValid(lastName);
|
|
}
|
|
|
|
if (!email.value.trim() || !isValidEmail(email.value)) {
|
|
markAsInvalid(email, "Please enter a valid email address");
|
|
isValid = false;
|
|
} else {
|
|
markAsValid(email);
|
|
}
|
|
|
|
if (!phone.value.trim()) {
|
|
markAsInvalid(phone, "Please enter your phone number");
|
|
isValid = false;
|
|
} else {
|
|
markAsValid(phone);
|
|
}
|
|
|
|
return isValid;
|
|
}
|
|
|
|
function validatePaymentInfo() {
|
|
const cardName = document.getElementById("card_name");
|
|
const cardNumber = document.getElementById("card_number");
|
|
const cardExpiry = document.getElementById("card_expiry");
|
|
const cardCvv = document.getElementById("card_cvv");
|
|
|
|
let isValid = true;
|
|
|
|
if (!validateCardName(cardName)) {
|
|
isValid = false;
|
|
}
|
|
|
|
if (!validateCardNumber(cardNumber)) {
|
|
isValid = false;
|
|
}
|
|
|
|
if (!validateExpiryDate(cardExpiry)) {
|
|
isValid = false;
|
|
}
|
|
|
|
if (!validateCvv(cardCvv)) {
|
|
isValid = false;
|
|
}
|
|
|
|
return isValid;
|
|
}
|
|
|
|
function validateCardName(input) {
|
|
if (!input.value.trim()) {
|
|
markAsInvalid(input, "Please enter the cardholder name");
|
|
return false;
|
|
} else {
|
|
markAsValid(input);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
function validateCardNumber(input) {
|
|
// Remove spaces for validation
|
|
const cardNumber = input.value.replace(/\s/g, '');
|
|
|
|
if (cardNumber.length !== 16 || !/^\d+$/.test(cardNumber)) {
|
|
markAsInvalid(input, "Please enter a valid 16-digit card number");
|
|
return false;
|
|
} else {
|
|
markAsValid(input);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
function validateExpiryDate(input) {
|
|
const value = input.value;
|
|
const pattern = /^(0[1-9]|1[0-2])\/\d{2}$/;
|
|
|
|
if (!pattern.test(value)) {
|
|
markAsInvalid(input, "Please enter a valid expiry date (MM/YY)");
|
|
return false;
|
|
}
|
|
|
|
// Check if the card is expired
|
|
const [month, year] = value.split('/');
|
|
const expiryDate = new Date(2000 + parseInt(year), parseInt(month) - 1);
|
|
const currentDate = new Date();
|
|
|
|
if (expiryDate < currentDate) {
|
|
markAsInvalid(input, "This card has expired");
|
|
return false;
|
|
}
|
|
|
|
markAsValid(input);
|
|
return true;
|
|
}
|
|
|
|
function validateCvv(input) {
|
|
if (!input.value.trim() || input.value.length !== 3 || !/^\d+$/.test(input.value)) {
|
|
markAsInvalid(input, "Please enter a valid 3-digit CVV");
|
|
return false;
|
|
} else {
|
|
markAsValid(input);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
function isValidEmail(email) {
|
|
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
return re.test(email);
|
|
}
|
|
|
|
function markAsInvalid(input, message) {
|
|
input.classList.add("is-invalid");
|
|
|
|
// Update feedback message if available
|
|
const feedback = document.getElementById(input.id + "_feedback");
|
|
if (feedback) {
|
|
feedback.textContent = message;
|
|
}
|
|
}
|
|
|
|
function markAsValid(input) {
|
|
input.classList.remove("is-invalid");
|
|
}
|
|
</script>
|
|
{% endblock customJS %} |