haikal/templates/pricing_page.html
2025-09-24 11:07:31 +03:00

920 lines
38 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>
: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);
}
.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);
}
.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 + .pricing-card-label .card:hover {
transform: scale(1.02);
}
.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 var(--border-color);
border-radius: 0.75rem;
padding: 2rem;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
}
/* Validation styles for modern look */
.form-control:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 0.25rem var(--primary-shadow);
}
.form-control.is-invalid {
border-color: #dc3545 !important;
}
.invalid-feedback {
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 fw-bold">{{ _("Choose Your Plan") }}</h1>
<form method="POST"
action="{% url 'submit_plan' request.dealer.slug %}"
id="wizardForm">
{% csrf_token %}
<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="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-4 mb-3 d-flex">
<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_with_tax }}"
autocomplete="off"
{% if forloop.first %}checked{% endif %}>
<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">
{% comment %} {% if forloop.counter == 2 %}
<div class="pricing-card-badge">{{ _("Most Popular") }}</div>
{% endif %} {% endcomment %}
<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-circle text-success"></i></span>
{{ line|capfirst }}
</li>
{% endfor %}
{% endif %}
</ul>
</div>
</div>
</label>
</div>
{% endfor %}
</div>
</div>
</div>
<div class="step d-none row justify-content-center" id="step2">
<div class="col-lg-8 col-md-10">
<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"
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"
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"
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"
dir="ltr"
maxlength="10"
pattern="^(\+9665|05|9665)[0-9]{8}$"
inputmode="numeric"
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"
placeholder='{{ _("Company Name") }}'
value="{{ request.dealer.name }}">
</div>
</div>
</div>
</div>
</div>
<div class="step d-none row justify-content-center" id="step3">
<div class="col-lg-8 col-md-10">
<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"
placeholder='{{ _("Cardholder Name") }}'
required>
<div class="invalid-feedback" id="card_name_feedback">
{{ _("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"
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>
</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"
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).") }}
</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"
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>
<div class="step d-none row justify-content-center" id="step4">
<div class="col-lg-8 col-md-10">
<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">
<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">
<p class="mb-1"><i class="fas fa-tag me-2"></i><strong>{{ _("Price (excl. VAT)") }}:</strong> <span id="summary_price"></span> <span class="icon-saudi_riyal"></span></p>
</div>
<div class="summary-item">
<p class="mb-1"><i class="fas fa-receipt me-2"></i><strong>{{ _("VAT") }} (15%):</strong> <span id="summary-tax"></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">
<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">
<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">
<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">
<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">
<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 class="d-flex justify-content-between mt-5">
<button type="button"
class="btn btn-lg btn-phoenix-secondary px-5"
id="prevBtn"
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 px-5 d-none"
id="submitBtn">{{ _("Confirm") }}</button>
</div>
</form>
</div>
{% endblock content %}
{% block customJS %}
<script>
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 progressSteps = document.querySelectorAll(".progress-indicator .step-item");
const nextBtn = document.getElementById("nextBtn");
const prevBtn = document.getElementById("prevBtn");
const submitBtn = document.getElementById("submitBtn");
if (!steps.length || !nextBtn || !prevBtn || !submitBtn || !progressSteps.length) return;
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);
});
// Add this section to update the progress indicator
progressSteps.forEach((step, i) => {
step.classList.toggle("active", i === index);
step.classList.toggle("completed", 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() {
if (!validateCurrentStep(currentStep)) {
return;
}
if (currentStep < steps.length - 1) {
currentStep++;
showStep(currentStep);
}
}
function handlePrev() {
if (currentStep > 0) {
currentStep--;
showStep(currentStep);
}
}
function populateSummary() {
updatePricingSummary(); // Reuse logic for pricing
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 updatePricingSummary() {
const selectedPlan = document.querySelector('input[name="selected_plan"]:checked');
if (!selectedPlan) return;
const priceWithTax = parseFloat(selectedPlan.dataset.price);
const basePrice = priceWithTax / 1.15;
const vat = priceWithTax - basePrice;
document.getElementById("summary_plan").textContent = selectedPlan.dataset.name;
document.getElementById("summary_price").textContent = basePrice.toFixed(2);
document.getElementById("summary-tax").textContent = vat.toFixed(2);
document.getElementById("summary-total").textContent = priceWithTax.toFixed(2);
}
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 %}