Frontend Components & JavaScript API Documentation
Table of Contents
- Overview
- JavaScript Dependencies
- UI Components
- DataTables Integration
- AJAX Patterns
- Form Handling
- File Upload Components
- Notification System
- Payment Integration
- Responsive Design
Overview
The frontend of the event management system is built using modern web technologies with a focus on user experience and performance. The system uses jQuery for DOM manipulation, Bootstrap for responsive design, and various specialized libraries for enhanced functionality.JavaScript Dependencies
Core Libraries
jQuery (v3.7.1)
Primary JavaScript library for DOM manipulation and AJAX requests.// Basic jQuery usage
$(document).ready(function() {
// DOM ready code
});
// AJAX requests
$.ajax({
url: '/api/eventos',
method: 'GET',
success: function(data) {
console.log(data);
}
});
Bootstrap (v5.3.2)
CSS framework for responsive design and UI components.<!-- Bootstrap components -->
<div class="container">
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-body">
<h5 class="card-title">Event Title</h5>
<p class="card-text">Event description</p>
</div>
</div>
</div>
</div>
</div>
Data Management Libraries
DataTables (v1.13.8)
Advanced table functionality with sorting, filtering, and pagination.// Initialize DataTable
$('#events-table').DataTable({
processing: true,
serverSide: true,
ajax: {
url: '/api/eventos',
data: function(d) {
d.search = $('#search-input').val();
d.category = $('#category-filter').val();
}
},
columns: [
{ data: 'name', title: 'Event Name' },
{ data: 'date_from_format', title: 'Start Date' },
{ data: 'participants', title: 'Participants' },
{
data: null,
title: 'Actions',
render: function(data, type, row) {
return `
<button class="btn btn-sm btn-primary edit-event" data-id="${row.id}">
Edit
</button>
<button class="btn btn-sm btn-danger delete-event" data-id="${row.id}">
Delete
</button>
`;
}
}
],
order: [[1, 'desc']],
pageLength: 25,
responsive: true
});
Tabulator (v5.5.2)
Advanced table library with additional features.// Initialize Tabulator
var table = new Tabulator("#events-table", {
ajaxURL: "/api/eventos",
layout: "fitColumns",
pagination: true,
paginationSize: 25,
columns: [
{title: "Name", field: "name", sorter: "string"},
{title: "Date", field: "date_from", sorter: "date"},
{title: "Participants", field: "participants", sorter: "number"},
{
title: "Actions",
formatter: function(cell, formatterParams) {
var row = cell.getRow();
var data = row.getData();
return `
<button class="btn btn-sm btn-primary" onclick="editEvent(${data.id})">
Edit
</button>
`;
}
}
]
});
UI Enhancement Libraries
Swiper (v11.2.6)
Touch slider for carousels and galleries.// Initialize Swiper
const swiper = new Swiper('.swiper-container', {
slidesPerView: 1,
spaceBetween: 30,
loop: true,
pagination: {
el: '.swiper-pagination',
clickable: true,
},
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
breakpoints: {
768: {
slidesPerView: 2,
},
1024: {
slidesPerView: 3,
}
}
});
Toastr (v2.1.4)
Notification system for user feedback.// Success notification
toastr.success('Event created successfully!', 'Success');
// Error notification
toastr.error('Failed to create event', 'Error');
// Warning notification
toastr.warning('Please check your input', 'Warning');
// Info notification
toastr.info('Processing your request...', 'Info');
// Configure toastr options
toastr.options = {
closeButton: true,
progressBar: true,
positionClass: "toast-top-right",
timeOut: 5000
};
Utility Libraries
Moment.js (v2.30.1)
Date and time manipulation library.// Format dates
moment().format('YYYY-MM-DD HH:mm:ss');
moment(event.date_from).format('MMMM Do YYYY');
// Date calculations
moment().add(7, 'days');
moment(event.date_from).diff(moment(), 'days');
// Timezone handling
moment.tz(event.date_from, event.timezone).format('YYYY-MM-DD HH:mm:ss');
Lodash (v4.17.19)
Utility library for common JavaScript operations.// Array operations
_.filter(events, { status: 'active' });
_.map(events, 'name');
_.groupBy(events, 'category');
// Object operations
_.pick(event, ['name', 'date_from', 'participants']);
_.omit(event, ['created_at', 'updated_at']);
UI Components
Modal Components
Event Creation Modal
<div class="modal fade" id="createEventModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Create New Event</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="createEventForm">
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Event Name</label>
<input type="text" class="form-control" name="name" required>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Category</label>
<select class="form-select" name="category" required>
<option value="">Select Category</option>
</select>
</div>
</div>
</div>
<!-- More form fields -->
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="saveEvent">Save Event</button>
</div>
</div>
</div>
</div>
Confirmation Modal
<div class="modal fade" id="confirmModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Confirm Action</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<p id="confirmMessage">Are you sure you want to perform this action?</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger" id="confirmAction">Confirm</button>
</div>
</div>
</div>
</div>
Form Components
Dynamic Form Builder
class FormBuilder {
constructor(container, fields) {
this.container = container;
this.fields = fields;
this.init();
}
init() {
this.fields.forEach(field => {
this.createField(field);
});
}
createField(field) {
const fieldElement = this.createFieldElement(field);
this.container.appendChild(fieldElement);
}
createFieldElement(field) {
const wrapper = document.createElement('div');
wrapper.className = 'mb-3';
const label = document.createElement('label');
label.className = 'form-label';
label.textContent = field.label;
if (field.required) {
label.innerHTML += ' <span class="text-danger">*</span>';
}
const input = this.createInput(field);
wrapper.appendChild(label);
wrapper.appendChild(input);
return wrapper;
}
createInput(field) {
switch (field.type) {
case 'text':
case 'email':
case 'number':
return this.createTextInput(field);
case 'select':
return this.createSelectInput(field);
case 'textarea':
return this.createTextareaInput(field);
case 'checkbox':
return this.createCheckboxInput(field);
case 'radio':
return this.createRadioInput(field);
default:
return this.createTextInput(field);
}
}
createTextInput(field) {
const input = document.createElement('input');
input.type = field.type || 'text';
input.className = 'form-control';
input.name = field.name;
input.required = field.required || false;
if (field.placeholder) {
input.placeholder = field.placeholder;
}
return input;
}
createSelectInput(field) {
const select = document.createElement('select');
select.className = 'form-select';
select.name = field.name;
select.required = field.required || false;
if (field.options) {
field.options.forEach(option => {
const optionElement = document.createElement('option');
optionElement.value = option.value;
optionElement.textContent = option.label;
select.appendChild(optionElement);
});
}
return select;
}
getFormData() {
const formData = new FormData();
const inputs = this.container.querySelectorAll('input, select, textarea');
inputs.forEach(input => {
if (input.type === 'checkbox') {
formData.append(input.name, input.checked);
} else {
formData.append(input.name, input.value);
}
});
return formData;
}
}
DataTables Integration
Server-Side Processing
// Advanced DataTable with server-side processing
$('#events-table').DataTable({
processing: true,
serverSide: true,
ajax: {
url: '/api/eventos',
type: 'GET',
data: function(d) {
// Add custom filters
d.search = $('#search-input').val();
d.category = $('#category-filter').val();
d.status = $('#status-filter').val();
d.date_from = $('#date-from-filter').val();
d.date_to = $('#date-to-filter').val();
},
dataSrc: function(json) {
return json.data || [];
}
},
columns: [
{
data: 'name',
render: function(data, type, row) {
if (type === 'display') {
return `<a href="/eventos/${row.id}" class="text-decoration-none">${data}</a>`;
}
return data;
}
},
{
data: 'date_from',
render: function(data, type, row) {
if (type === 'display') {
return moment(data).format('DD/MM/YYYY HH:mm');
}
return data;
}
},
{
data: 'participants',
className: 'text-center'
},
{
data: 'enrolled',
className: 'text-center',
render: function(data, type, row) {
const percentage = row.participants > 0 ?
Math.round((data / row.participants) * 100) : 0;
return `
<div class="progress" style="height: 20px;">
<div class="progress-bar" style="width: ${percentage}%">
${data}/${row.participants} (${percentage}%)
</div>
</div>
`;
}
},
{
data: 'status',
render: function(data, type, row) {
const statusClasses = {
'active': 'success',
'inactive': 'warning',
'draft': 'secondary'
};
return `<span class="badge bg-${statusClasses[data] || 'secondary'}">${data}</span>`;
}
},
{
data: null,
orderable: false,
render: function(data, type, row) {
return `
<div class="btn-group" role="group">
<button type="button" class="btn btn-sm btn-outline-primary"
onclick="editEvent(${row.id})">
<i class="fas fa-edit"></i>
</button>
<button type="button" class="btn btn-sm btn-outline-info"
onclick="viewEvent(${row.id})">
<i class="fas fa-eye"></i>
</button>
<button type="button" class="btn btn-sm btn-outline-danger"
onclick="deleteEvent(${row.id})">
<i class="fas fa-trash"></i>
</button>
</div>
`;
}
}
],
order: [[1, 'desc']],
pageLength: 25,
responsive: true,
dom: '<"row"<"col-sm-12 col-md-6"l><"col-sm-12 col-md-6"f>>' +
'<"row"<"col-sm-12"tr>>' +
'<"row"<"col-sm-12 col-md-5"i><"col-sm-12 col-md-7"p>>',
language: {
url: '/js/datatables-es.json'
}
});
Export Functionality
// Add export buttons to DataTable
$('#events-table').DataTable({
// ... other options
dom: 'Bfrtip',
buttons: [
{
extend: 'copy',
text: '<i class="fas fa-copy"></i> Copy',
className: 'btn btn-secondary btn-sm'
},
{
extend: 'csv',
text: '<i class="fas fa-file-csv"></i> CSV',
className: 'btn btn-secondary btn-sm'
},
{
extend: 'excel',
text: '<i class="fas fa-file-excel"></i> Excel',
className: 'btn btn-secondary btn-sm'
},
{
extend: 'pdf',
text: '<i class="fas fa-file-pdf"></i> PDF',
className: 'btn btn-secondary btn-sm'
},
{
extend: 'print',
text: '<i class="fas fa-print"></i> Print',
className: 'btn btn-secondary btn-sm'
}
]
});
AJAX Patterns
Standard AJAX Request Pattern
class ApiClient {
constructor(baseUrl = '/api') {
this.baseUrl = baseUrl;
this.setupAjaxDefaults();
}
setupAjaxDefaults() {
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
error: function(xhr, status, error) {
this.handleError(xhr, status, error);
}.bind(this)
});
}
handleError(xhr, status, error) {
let message = 'An error occurred';
if (xhr.responseJSON && xhr.responseJSON.message) {
message = xhr.responseJSON.message;
} else if (xhr.status === 422) {
message = 'Validation error. Please check your input.';
} else if (xhr.status === 401) {
message = 'Unauthorized. Please log in again.';
window.location.href = '/login';
} else if (xhr.status === 403) {
message = 'Access denied.';
} else if (xhr.status === 404) {
message = 'Resource not found.';
} else if (xhr.status === 500) {
message = 'Server error. Please try again later.';
}
toastr.error(message, 'Error');
}
get(url, params = {}) {
return $.ajax({
url: this.baseUrl + url,
method: 'GET',
data: params
});
}
post(url, data = {}) {
return $.ajax({
url: this.baseUrl + url,
method: 'POST',
data: data
});
}
put(url, data = {}) {
return $.ajax({
url: this.baseUrl + url,
method: 'PUT',
data: data
});
}
delete(url) {
return $.ajax({
url: this.baseUrl + url,
method: 'DELETE'
});
}
upload(url, formData) {
return $.ajax({
url: this.baseUrl + url,
method: 'POST',
data: formData,
processData: false,
contentType: false
});
}
}
// Usage
const api = new ApiClient();
// Get events
api.get('/eventos', { category: 1, status: 'active' })
.done(function(data) {
console.log('Events:', data);
});
// Create event
api.post('/eventos', {
name: 'New Event',
description: 'Event description',
date_from: '2024-06-15'
}).done(function(response) {
toastr.success('Event created successfully');
location.reload();
});
Promise-based AJAX Wrapper
class PromiseApiClient {
constructor(baseUrl = '/api') {
this.baseUrl = baseUrl;
}
request(method, url, data = null) {
return new Promise((resolve, reject) => {
$.ajax({
url: this.baseUrl + url,
method: method,
data: data,
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
success: function(response) {
resolve(response);
},
error: function(xhr, status, error) {
reject({ xhr, status, error });
}
});
});
}
get(url, params = {}) {
return this.request('GET', url, params);
}
post(url, data = {}) {
return this.request('POST', url, data);
}
put(url, data = {}) {
return this.request('PUT', url, data);
}
delete(url) {
return this.request('DELETE', url);
}
}
// Usage with async/await
const api = new PromiseApiClient();
async function loadEvents() {
try {
const events = await api.get('/eventos');
displayEvents(events);
} catch (error) {
toastr.error('Failed to load events');
console.error(error);
}
}
async function createEvent(eventData) {
try {
const response = await api.post('/eventos', eventData);
toastr.success('Event created successfully');
return response;
} catch (error) {
toastr.error('Failed to create event');
throw error;
}
}
Form Handling
Form Validation
class FormValidator {
constructor(formSelector) {
this.form = $(formSelector);
this.rules = {};
this.messages = {};
this.init();
}
init() {
this.setupValidation();
this.bindEvents();
}
setupValidation() {
this.form.validate({
rules: this.rules,
messages: this.messages,
errorElement: 'span',
errorClass: 'text-danger',
highlight: function(element) {
$(element).addClass('is-invalid');
},
unhighlight: function(element) {
$(element).removeClass('is-invalid');
},
errorPlacement: function(error, element) {
error.insertAfter(element);
}
});
}
bindEvents() {
this.form.on('submit', (e) => {
if (!this.form.valid()) {
e.preventDefault();
toastr.error('Please correct the errors in the form');
}
});
}
addRule(fieldName, rule, message) {
this.rules[fieldName] = rule;
this.messages[fieldName] = message;
}
validate() {
return this.form.valid();
}
reset() {
this.form[0].reset();
this.form.find('.is-invalid').removeClass('is-invalid');
this.form.find('.text-danger').remove();
}
}
// Usage
const eventForm = new FormValidator('#createEventForm');
eventForm.addRule('name', 'required', 'Event name is required');
eventForm.addRule('email', {
required: true,
email: true
}, 'Please enter a valid email address');
Dynamic Form Fields
class DynamicForm {
constructor(container) {
this.container = container;
this.fieldCounter = 0;
this.init();
}
init() {
this.bindEvents();
}
bindEvents() {
$(this.container).on('click', '.add-field', (e) => {
e.preventDefault();
this.addField();
});
$(this.container).on('click', '.remove-field', (e) => {
e.preventDefault();
$(e.target).closest('.field-group').remove();
});
}
addField() {
this.fieldCounter++;
const fieldHtml = this.createFieldHtml(this.fieldCounter);
$(this.container).find('.fields-container').append(fieldHtml);
}
createFieldHtml(index) {
return `
<div class="field-group border rounded p-3 mb-3">
<div class="row">
<div class="col-md-4">
<label class="form-label">Field Label</label>
<input type="text" class="form-control" name="fields[${index}][label]" required>
</div>
<div class="col-md-3">
<label class="form-label">Field Type</label>
<select class="form-select" name="fields[${index}][type]" required>
<option value="text">Text</option>
<option value="email">Email</option>
<option value="number">Number</option>
<option value="select">Select</option>
<option value="textarea">Textarea</option>
</select>
</div>
<div class="col-md-3">
<label class="form-label">Required</label>
<div class="form-check mt-2">
<input class="form-check-input" type="checkbox" name="fields[${index}][required]">
<label class="form-check-label">Required field</label>
</div>
</div>
<div class="col-md-2">
<button type="button" class="btn btn-danger remove-field mt-4">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
</div>
`;
}
getFormData() {
const formData = new FormData();
const fields = [];
$(this.container).find('.field-group').each(function() {
const field = {
label: $(this).find('input[name*="[label]"]').val(),
type: $(this).find('select[name*="[type]"]').val(),
required: $(this).find('input[name*="[required]"]').is(':checked')
};
fields.push(field);
});
formData.append('fields', JSON.stringify(fields));
return formData;
}
}
File Upload Components
Image Upload with Preview
class ImageUploader {
constructor(inputSelector, previewSelector) {
this.input = $(inputSelector);
this.preview = $(previewSelector);
this.maxSize = 5 * 1024 * 1024; // 5MB
this.allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
this.init();
}
init() {
this.bindEvents();
}
bindEvents() {
this.input.on('change', (e) => {
this.handleFileSelect(e.target.files[0]);
});
this.preview.on('click', '.remove-image', (e) => {
e.preventDefault();
this.removeImage();
});
}
handleFileSelect(file) {
if (!file) return;
// Validate file type
if (!this.allowedTypes.includes(file.type)) {
toastr.error('Please select a valid image file (JPEG, PNG, GIF)');
return;
}
// Validate file size
if (file.size > this.maxSize) {
toastr.error('File size must be less than 5MB');
return;
}
// Show preview
this.showPreview(file);
}
showPreview(file) {
const reader = new FileReader();
reader.onload = (e) => {
this.preview.html(`
<div class="position-relative d-inline-block">
<img src="${e.target.result}" class="img-thumbnail" style="max-width: 200px;">
<button type="button" class="btn btn-danger btn-sm position-absolute top-0 end-0 remove-image">
<i class="fas fa-times"></i>
</button>
</div>
`);
};
reader.readAsDataURL(file);
}
removeImage() {
this.input.val('');
this.preview.empty();
}
upload(url, additionalData = {}) {
const file = this.input[0].files[0];
if (!file) {
toastr.error('Please select a file to upload');
return Promise.reject('No file selected');
}
const formData = new FormData();
formData.append('image', file);
// Add additional data
Object.keys(additionalData).forEach(key => {
formData.append(key, additionalData[key]);
});
return $.ajax({
url: url,
method: 'POST',
data: formData,
processData: false,
contentType: false,
xhr: () => {
const xhr = new window.XMLHttpRequest();
xhr.upload.addEventListener('progress', (e) => {
if (e.lengthComputable) {
const percentComplete = (e.loaded / e.total) * 100;
this.updateProgress(percentComplete);
}
});
return xhr;
}
});
}
updateProgress(percent) {
// Update progress bar or show upload progress
console.log(`Upload progress: ${percent}%`);
}
}
// Usage
const imageUploader = new ImageUploader('#image-input', '#image-preview');
$('#upload-btn').on('click', () => {
imageUploader.upload('/api/upload-image', { product_id: 1 })
.done((response) => {
toastr.success('Image uploaded successfully');
})
.fail((error) => {
toastr.error('Failed to upload image');
});
});
Drag and Drop File Upload
class DragDropUploader {
constructor(dropZoneSelector) {
this.dropZone = $(dropZoneSelector);
this.init();
}
init() {
this.bindEvents();
}
bindEvents() {
this.dropZone.on('dragover', (e) => {
e.preventDefault();
this.dropZone.addClass('dragover');
});
this.dropZone.on('dragleave', (e) => {
e.preventDefault();
this.dropZone.removeClass('dragover');
});
this.dropZone.on('drop', (e) => {
e.preventDefault();
this.dropZone.removeClass('dragover');
const files = e.originalEvent.dataTransfer.files;
this.handleFiles(files);
});
this.dropZone.on('click', () => {
this.createFileInput();
});
}
handleFiles(files) {
Array.from(files).forEach(file => {
if (file.type.startsWith('image/')) {
this.uploadFile(file);
}
});
}
createFileInput() {
const input = $('<input type="file" multiple accept="image/*" style="display: none;">');
input.on('change', (e) => {
this.handleFiles(e.target.files);
});
input.click();
}
uploadFile(file) {
const formData = new FormData();
formData.append('file', file);
$.ajax({
url: '/api/upload-file',
method: 'POST',
data: formData,
processData: false,
contentType: false,
success: (response) => {
this.addFileToList(response);
},
error: (xhr) => {
toastr.error('Failed to upload file');
}
});
}
addFileToList(fileData) {
const fileHtml = `
<div class="uploaded-file">
<img src="${fileData.url}" class="img-thumbnail" style="max-width: 100px;">
<span class="file-name">${fileData.name}</span>
<button type="button" class="btn btn-sm btn-danger remove-file" data-id="${fileData.id}">
<i class="fas fa-trash"></i>
</button>
</div>
`;
this.dropZone.find('.uploaded-files').append(fileHtml);
}
}
Notification System
Custom Notification Manager
class NotificationManager {
constructor() {
this.notifications = [];
this.container = this.createContainer();
this.init();
}
createContainer() {
const container = $('<div id="notification-container"></div>');
container.css({
position: 'fixed',
top: '20px',
right: '20px',
zIndex: 9999,
maxWidth: '400px'
});
$('body').append(container);
return container;
}
init() {
// Configure toastr
toastr.options = {
closeButton: true,
progressBar: true,
positionClass: "toast-top-right",
timeOut: 5000,
extendedTimeOut: 1000,
preventDuplicates: true
};
}
show(message, type = 'info', options = {}) {
const defaultOptions = {
title: this.getTitle(type),
icon: this.getIcon(type)
};
const finalOptions = { ...defaultOptions, ...options };
switch (type) {
case 'success':
toastr.success(message, finalOptions.title);
break;
case 'error':
toastr.error(message, finalOptions.title);
break;
case 'warning':
toastr.warning(message, finalOptions.title);
break;
case 'info':
default:
toastr.info(message, finalOptions.title);
break;
}
}
getTitle(type) {
const titles = {
success: 'Success',
error: 'Error',
warning: 'Warning',
info: 'Information'
};
return titles[type] || 'Information';
}
getIcon(type) {
const icons = {
success: 'fas fa-check-circle',
error: 'fas fa-exclamation-circle',
warning: 'fas fa-exclamation-triangle',
info: 'fas fa-info-circle'
};
return icons[type] || 'fas fa-info-circle';
}
// Custom notification methods
showLoading(message = 'Loading...') {
const loadingHtml = `
<div class="alert alert-info d-flex align-items-center">
<div class="spinner-border spinner-border-sm me-2" role="status">
<span class="visually-hidden">Loading...</span>
</div>
${message}
</div>
`;
this.container.append(loadingHtml);
}
hideLoading() {
this.container.find('.alert-info').remove();
}
showConfirm(message, onConfirm, onCancel) {
const confirmHtml = `
<div class="alert alert-warning">
<p>${message}</p>
<div class="btn-group">
<button type="button" class="btn btn-sm btn-primary confirm-yes">Yes</button>
<button type="button" class="btn btn-sm btn-secondary confirm-no">No</button>
</div>
</div>
`;
const confirmElement = $(confirmHtml);
this.container.append(confirmElement);
confirmElement.find('.confirm-yes').on('click', () => {
confirmElement.remove();
if (onConfirm) onConfirm();
});
confirmElement.find('.confirm-no').on('click', () => {
confirmElement.remove();
if (onCancel) onCancel();
});
}
}
// Usage
const notifications = new NotificationManager();
notifications.show('Operation completed successfully', 'success');
notifications.show('Please check your input', 'warning');
notifications.show('An error occurred', 'error');
notifications.showConfirm(
'Are you sure you want to delete this item?',
() => {
// Handle confirmation
deleteItem();
},
() => {
// Handle cancellation
console.log('Operation cancelled');
}
);
Payment Integration
Payment Form Handler
class PaymentFormHandler {
constructor(formSelector) {
this.form = $(formSelector);
this.init();
}
init() {
this.bindEvents();
this.setupValidation();
}
bindEvents() {
this.form.on('submit', (e) => {
e.preventDefault();
this.processPayment();
});
// Real-time card validation
this.form.find('#card-number').on('input', (e) => {
this.validateCardNumber(e.target.value);
});
this.form.find('#card-cvv').on('input', (e) => {
this.validateCVV(e.target.value);
});
}
setupValidation() {
this.form.validate({
rules: {
'card-number': {
required: true,
creditcard: true
},
'card-expiry': {
required: true,
pattern: /^(0[1-9]|1[0-2])\/([0-9]{2})$/
},
'card-cvv': {
required: true,
minlength: 3,
maxlength: 4
}
},
messages: {
'card-number': {
required: 'Card number is required',
creditcard: 'Please enter a valid card number'
},
'card-expiry': {
required: 'Expiry date is required',
pattern: 'Please enter expiry date in MM/YY format'
},
'card-cvv': {
required: 'CVV is required',
minlength: 'CVV must be at least 3 digits',
maxlength: 'CVV cannot exceed 4 digits'
}
}
});
}
validateCardNumber(number) {
// Remove spaces and dashes
number = number.replace(/\s+/g, '').replace(/[^0-9]/gi, '');
// Luhn algorithm validation
let sum = 0;
let isEven = false;
for (let i = number.length - 1; i >= 0; i--) {
let digit = parseInt(number[i]);
if (isEven) {
digit *= 2;
if (digit > 9) {
digit -= 9;
}
}
sum += digit;
isEven = !isEven;
}
const isValid = sum % 10 === 0;
this.updateCardValidation(isValid);
return isValid;
}
validateCVV(cvv) {
const isValid = /^\d{3,4}$/.test(cvv);
this.updateCVVValidation(isValid);
return isValid;
}
updateCardValidation(isValid) {
const field = this.form.find('#card-number');
if (isValid) {
field.removeClass('is-invalid').addClass('is-valid');
} else {
field.removeClass('is-valid').addClass('is-invalid');
}
}
updateCVVValidation(isValid) {
const field = this.form.find('#card-cvv');
if (isValid) {
field.removeClass('is-invalid').addClass('is-valid');
} else {
field.removeClass('is-valid').addClass('is-invalid');
}
}
processPayment() {
if (!this.form.valid()) {
toastr.error('Please correct the errors in the form');
return;
}
const formData = this.form.serialize();
// Show loading state
this.form.find('button[type="submit"]').prop('disabled', true).html(
'<span class="spinner-border spinner-border-sm me-2"></span>Processing...'
);
$.ajax({
url: this.form.attr('action'),
method: 'POST',
data: formData,
success: (response) => {
if (response.success) {
toastr.success('Payment processed successfully');
window.location.href = response.redirect_url;
} else {
toastr.error(response.message || 'Payment failed');
}
},
error: (xhr) => {
toastr.error('Payment failed. Please try again.');
},
complete: () => {
// Reset button state
this.form.find('button[type="submit"]').prop('disabled', false).html('Pay Now');
}
});
}
}
Responsive Design
Responsive Table Handler
class ResponsiveTableHandler {
constructor(tableSelector) {
this.table = $(tableSelector);
this.init();
}
init() {
this.setupResponsive();
this.bindEvents();
}
setupResponsive() {
// Add responsive wrapper
this.table.wrap('<div class="table-responsive"></div>');
// Add responsive classes
this.table.addClass('table-responsive');
// Handle small screens
if (window.innerWidth < 768) {
this.convertToCards();
}
}
bindEvents() {
$(window).on('resize', () => {
this.handleResize();
});
}
handleResize() {
if (window.innerWidth < 768) {
this.convertToCards();
} else {
this.convertToTable();
}
}
convertToCards() {
if (this.table.hasClass('converted-to-cards')) return;
this.table.addClass('converted-to-cards');
const headers = this.table.find('thead th').map(function() {
return $(this).text();
}).get();
this.table.find('tbody tr').each((index, row) => {
const card = this.createCard($(row), headers);
this.table.after(card);
});
this.table.hide();
}
createCard(row, headers) {
const card = $('<div class="card mb-3"></div>');
const cardBody = $('<div class="card-body"></div>');
row.find('td').each((index, cell) => {
if (headers[index]) {
const field = $(`
<div class="row mb-2">
<div class="col-4"><strong>${headers[index]}:</strong></div>
<div class="col-8">${$(cell).html()}</div>
</div>
`);
cardBody.append(field);
}
});
card.append(cardBody);
return card;
}
convertToTable() {
if (!this.table.hasClass('converted-to-cards')) return;
this.table.removeClass('converted-to-cards');
this.table.siblings('.card').remove();
this.table.show();
}
}
// Usage
const tableHandler = new ResponsiveTableHandler('#events-table');
