Laravel 11, Vuexy Admin 10.3, by admin@koneko.mx

This commit is contained in:
2025-01-25 04:23:40 -06:00
parent c3045b43b1
commit 64d505910f
1283 changed files with 140198 additions and 0 deletions

View File

@ -0,0 +1,247 @@
export default class FormCustomListener {
constructor(config = {}) {
const defaultConfig = {
formSelector: '.form-custom-listener', // Selector para formularios
buttonSelectors: [], // Selectores específicos para botones
callbacks: [], // Callbacks correspondientes a los botones específicos
allowedInputTags: ['INPUT', 'SELECT', 'TEXTAREA'], // Tags permitidos para cambios
validationConfig: null, // Nueva propiedad para la configuración de validación
dispatchOnSubmit: null // Callback Livewire para disparar al enviar el formulario
};
this.config = { ...defaultConfig, ...config };
// Aseguramos que los métodos que dependen de `this` estén vinculados al contexto correcto
this.defaultButtonHandler = this.defaultButtonHandler.bind(this);
this.formValidationInstance = null;
this.initForms();
}
/**
* Inicializa los formularios encontrados en el DOM.
*/
initForms() {
const forms = document.querySelectorAll(this.config.formSelector);
if (forms.length === 0) {
console.error(`No se encontraron formularios con el selector ${this.config.formSelector}.`);
return;
}
forms.forEach(form => {
if (form.dataset.initialized === 'true') {
console.warn(`Formulario ya inicializado: ${form}`);
return;
}
this.initFormEvents(form);
// Si se pasó configuración de validación, inicialízala
if (this.config.validationConfig) {
this.initializeValidation(form);
}
form.dataset.initialized = 'true'; // Marcar formulario como inicializado
});
}
/**
* Configura los eventos para un formulario individual.
* @param {HTMLElement} form - El formulario que será manejado.
*/
initFormEvents(form) {
const buttons = this.getButtons(form);
buttons.forEach(({ button, callback }, index) => {
if (button) {
button.addEventListener('click', () => {
this.handleButtonClick(index, form, buttons, callback);
});
}
});
form.addEventListener('input', event =>
this.handleInputChange(
event,
form,
buttons.map(b => b.button)
)
);
}
/**
* Obtiene los botones y sus callbacks según la configuración.
* @param {HTMLElement} form - El formulario del cual obtener botones.
* @returns {Array} Array de objetos con { button, callback }.
*/
getButtons(form) {
const buttons = [];
this.config.buttonSelectors.forEach((selector, index) => {
const buttonList = Array.from(form.querySelectorAll(selector));
const callback = this.config.callbacks[index];
buttonList.forEach(button => {
buttons.push({ button, callback });
});
});
return buttons;
}
/**
* Maneja los cambios en los campos de entrada.
* @param {Event} event - El evento del cambio.
* @param {HTMLElement} form - El formulario actual.
* @param {HTMLElement[]} buttons - Array de botones en el formulario.
*/
handleInputChange(event, form, buttons) {
const target = event.target;
if (['INPUT', 'SELECT', 'TEXTAREA'].includes(target.tagName)) {
this.toggleButtonsState(buttons, true);
}
}
/**
* Maneja el clic en un botón específico.
* @param {number} index - Índice del botón.
* @param {HTMLElement} form - El formulario actual.
* @param {Array} buttons - Array de objetos { button, callback }.
* @param {function|null} callback - Callback definido para el botón.
*/
handleButtonClick(index, form, buttons, callback) {
if (typeof callback === 'function') {
callback(
form,
buttons[index].button,
buttons.map(b => b.button)
);
} else {
this.defaultButtonHandler(
form,
buttons[index].button,
buttons.map(b => b.button)
);
}
}
/**
* Maneja la acción cuando el formulario es válido.
* Este método puede ser sobreescrito para personalizar el comportamiento.
*/
handleFormValid(form) {
console.log('Formulario válido');
// Ejecutar callback opcional (si lo proporcionaste)
if (typeof this.config.handleValidForm === 'function') {
this.config.handleValidForm(form);
} else if (this.config.dispatchOnSubmit) {
this.handleValidForm(form);
} else {
form.submit();
}
}
/**
* Método que maneja la acción cuando el formulario es válido.
* Al ser un método de la clase, no necesitamos usar bind.
*/
handleValidForm(form) {
const saveButton = form.querySelector('#save_website_button');
const allButtons = Array.from(form.querySelectorAll('.btn'));
this.toggleButtonsState(allButtons, false); // Deshabilitar todos los botones
this.toggleFormFields(form, false); // Deshabilitar todos los campos del formulario
this.setButtonLoadingState(saveButton, true); // Poner en estado de carga al botón anfitrión
// Enviar la solicitud de Livewire correspondiente al enviar el formulario
Livewire.dispatch(this.config.dispatchOnSubmit);
}
/**
* Manejador por defecto para los botones.
* @param {HTMLElement} form - El formulario actual.
* @param {HTMLElement} hostButton - El botón anfitrión que disparó el evento.
* @param {HTMLElement[]} allButtons - Todos los botones relevantes del formulario.
*/
defaultButtonHandler(form, hostButton, allButtons) {
this.toggleButtonsState(allButtons, false); // Deshabilitar todos los botones
this.toggleFormFields(form, false); // Deshabilitar todos los campos del formulario
this.setButtonLoadingState(hostButton, true); // Poner en estado de carga al botón anfitrión
}
/**
* Deshabilita o habilita los campos del formulario.
* @param {HTMLElement} form - El formulario actual.
* @param {boolean} isEnabled - Si los campos deben habilitarse.
*/
toggleFormFields(form, isEnabled) {
form.querySelectorAll('input, select, textarea').forEach(field => {
field.disabled = !isEnabled;
});
}
/**
* Habilita o deshabilita los botones.
* @param {HTMLElement[]} buttons - Array de botones.
* @param {boolean} isEnabled - Si los botones deben habilitarse.
*/
toggleButtonsState(buttons, isEnabled) {
buttons.forEach(button => {
if (button) button.disabled = !isEnabled;
});
}
/**
* Cambia el estado de carga de un botón.
* @param {HTMLElement} button - Botón que se manejará.
* @param {boolean} isLoading - Si el botón está en estado de carga.
*/
setButtonLoadingState(button, isLoading) {
if (!button) return;
const loadingText = button.getAttribute('data-loading-text');
if (loadingText && isLoading) {
button.setAttribute('data-original-text', button.innerHTML);
button.innerHTML = loadingText;
button.disabled = true;
} else if (!isLoading) {
button.innerHTML = button.getAttribute('data-original-text') || button.innerHTML;
button.disabled = false;
}
}
/**
* Inicializa la validación del formulario con la configuración proporcionada.
* @param {HTMLElement} form - El formulario que va a ser validado.
*/
initializeValidation(form) {
if (this.config.validationConfig) {
this.formValidationInstance = FormValidation.formValidation(form, this.config.validationConfig).on(
'core.form.valid',
() => this.handleFormValid(form)
);
}
}
reloadValidation() {
const form = document.querySelector(this.config.formSelector);
// Verificar si el formulario existe y si la validación está inicializada
if (form && this.formValidationInstance) {
try {
// En lugar de destruir la validación, simplemente reiniciamos la validación.
this.formValidationInstance.resetForm(); // Resetear el formulario, limpiando los errores
// Reinicializar la validación con la configuración actual
this.initializeValidation(form);
} catch (error) {
console.error('Error al reiniciar la validación:', error);
}
} else {
console.warn('Formulario no encontrado o instancia de validación no disponible.');
}
}
}

View File

@ -0,0 +1,190 @@
export default class LivewireNotification {
constructor(config = {}) {
const defaultConfig = {
notificationTimeout: 6000, // Tiempo predeterminado para las notificaciones
onNotificationShown: null, // Callback al mostrar una notificación
onNotificationRemoved: null, // Callback al eliminar una notificación
onNotificationClosed: null // Callback al cerrar una notificación mediante botón
};
this.config = { ...defaultConfig, ...config };
this.initLivewireNotification();
}
/**
* Inicializa la escucha de notificaciones desde Livewire.
*/
initLivewireNotification() {
// Mostrar notificación almacenada después de la recarga
const storedNotification = localStorage.getItem('pendingNotification');
if (storedNotification) {
const event = JSON.parse(storedNotification);
this.showStoredNotification(event);
localStorage.removeItem('pendingNotification'); // Limpiar después de mostrar
}
// Escuchar nuevas notificaciones desde Livewire
Livewire.on('notification', event => {
if (event.deferReload) {
// Guardar la notificación en localStorage para mostrar después de la recarga
localStorage.setItem('pendingNotification', JSON.stringify(event));
window.location.reload();
} else {
// Mostrar la notificación inmediatamente
this.showNotification(event);
}
});
}
/**
* Método para emitir notificaciones desde JavaScript.
* @param {Object} options - Opciones de la notificación.
* @param {Function} callback - Callback opcional que se ejecutará después de mostrar la notificación.
* @param {number} customTimeout - Timeout personalizado (opcional).
*/
emitNotification(options, callback, customTimeout) {
const event = {
target: options.target || 'body',
message: options.message || 'Notificación',
type: options.type || 'info',
deferReload: options.deferReload || false,
notificationTimeout: customTimeout || options.notificationTimeout || this.config.notificationTimeout // Usar el timeout personalizado o el predeterminado
};
// Mostrar la notificación
this.showNotification(event);
// Ejecutar callback si está definido
if (typeof callback === 'function') {
callback(event);
}
}
/**
* Muestra una notificación almacenada.
* @param {Object} event - Datos del evento de notificación.
*/
showStoredNotification(event) {
this.showNotification(event);
}
/**
* Muestra una notificación.
* @param {Object} event - Datos del evento de notificación.
*/
showNotification(event) {
const targetElement = document.querySelector(event.target);
if (!targetElement) {
console.error(`Target ${event.target} no encontrado. Mostrando en el contenedor global.`);
this.showInGlobalContainer(event);
return;
}
// Crear un contenedor para notificaciones si no existe
if (!targetElement.querySelector('.notification-container')) {
const container = document.createElement('div');
container.className = 'notification-container';
targetElement.appendChild(container);
}
const notificationContainer = targetElement.querySelector('.notification-container');
const notificationElement = this.renderNotification(notificationContainer, event);
// Callback opcional al mostrar la notificación
if (typeof this.config.onNotificationShown === 'function') {
this.config.onNotificationShown(notificationElement, event);
}
// Configurar el timeout para eliminar la notificación
this.setNotificationTimeout(notificationElement, event);
// Configurar el evento para el botón de cierre
this.setupCloseButton(notificationElement, event);
}
/**
* Renderiza una notificación en el contenedor global (body).
* @param {Object} event - Datos del evento de notificación.
*/
showInGlobalContainer(event) {
const globalContainer = document.body;
if (!globalContainer.querySelector('.notification-container')) {
const container = document.createElement('div');
container.className = 'notification-container';
globalContainer.appendChild(container);
}
const notificationContainer = globalContainer.querySelector('.notification-container');
const notificationElement = this.renderNotification(notificationContainer, event);
if (typeof this.config.onNotificationShown === 'function') {
this.config.onNotificationShown(notificationElement, event);
}
this.setNotificationTimeout(notificationElement, event);
this.setupCloseButton(notificationElement, event);
}
/**
* Renderiza una notificación en el contenedor.
* @param {HTMLElement} container - Contenedor de notificaciones.
* @param {Object} event - Evento de notificación con tipo y mensaje.
* @returns {HTMLElement} - Elemento de la notificación recién creada.
*/
renderNotification(container, event) {
const notificationElement = document.createElement('div');
notificationElement.className = `alert alert-${event.type} alert-dismissible fade show`;
notificationElement.role = 'alert';
notificationElement.innerHTML = `${event.message} <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>`;
container.appendChild(notificationElement);
return notificationElement;
}
/**
* Configura un timeout para limpiar una notificación específica.
* @param {HTMLElement} notificationElement - Elemento de la notificación.
* @param {Object} event - Evento asociado a la notificación.
*/
setNotificationTimeout(notificationElement, event) {
const timeout = event.notificationTimeout || this.config.notificationTimeout;
setTimeout(() => {
if (notificationElement && notificationElement.parentElement) {
notificationElement.remove();
// Callback opcional al eliminar la notificación
if (typeof this.config.onNotificationRemoved === 'function') {
this.config.onNotificationRemoved(notificationElement, event);
}
}
}, timeout);
}
/**
* Configura el cierre manual de una notificación mediante el botón "Cerrar".
* @param {HTMLElement} notificationElement - Elemento de la notificación.
* @param {Object} event - Evento asociado a la notificación.
*/
setupCloseButton(notificationElement, event) {
const closeButton = notificationElement.querySelector('.btn-close');
if (closeButton) {
closeButton.addEventListener('click', () => {
notificationElement.remove();
// Callback opcional al cerrar la notificación manualmente
if (typeof this.config.onNotificationClosed === 'function') {
this.config.onNotificationClosed(notificationElement, event);
}
});
}
}
}

View File

@ -0,0 +1,220 @@
export default class SenderResponseForm {
constructor(config = {}) {
const defaultConfig = {
formSenderResponseId: 'mail-sender-response-settings-card',
replyToMethodId: 'reply_to_method',
saveSenderResponseButtonId: 'save_sender_response_button',
cancelButtonId: 'cancel_sender_response_button'
};
this.config = { ...defaultConfig, ...config };
this.formSenderResponse = null;
this.smtpReplyToMethod = null;
this.saveButton = null;
this.cancelButton = null;
this.init(); // Inicializa el formulario
}
// Método para inicializar el formulario
init() {
try {
// Obtener elementos esenciales
this.formSenderResponse = document.getElementById(this.config.formSenderResponseId);
this.smtpReplyToMethod = document.getElementById(this.config.replyToMethodId);
this.saveButton = document.getElementById(this.config.saveSenderResponseButtonId);
this.cancelButton = document.getElementById(this.config.cancelButtonId);
// Asignar eventos
this.formSenderResponse.addEventListener('input', event => this.handleInput(event));
this.smtpReplyToMethod.addEventListener('change', () => this.handleToggleReplyToMethod());
this.cancelButton.addEventListener('click', () => this.handleCancel());
// Inicializar validación del formulario
this.initializeFormValidation(this.formSenderResponse, this.senderResponsValidateConfig, () => {
this.handleFormValid();
});
// Disparar el evento 'change' en el método de respuesta
setTimeout(() => this.smtpReplyToMethod.dispatchEvent(new Event('change')), 0);
} catch (error) {
console.error('Error al inicializar el formulario de respuesta:', error);
}
}
/**
* Método de recarga parcial
* Este método restablece la validación y los eventos sin destruir la instancia.
*/
reload() {
try {
// Vuelve a inicializar la validación del formulario
this.initializeFormValidation(this.formSenderResponse, this.senderResponsValidateConfig, () => {
this.handleFormValid();
});
// Vuelve a agregar los eventos (si es necesario, depende de tu lógica)
this.smtpReplyToMethod.dispatchEvent(new Event('change'));
} catch (error) {
console.error('Error al recargar el formulario:', error);
}
}
/**
* Maneja el evento de entrada en el formulario.
* @param {Event} event - Evento de entrada.
*/
handleInput(event) {
const target = event.target;
if (['INPUT', 'SELECT', 'TEXTAREA'].includes(target.tagName)) {
this.toggleButtonsState(this.formSenderResponse, true); // Habilitar botones
}
}
/**
* Muestra u oculta el campo de correo personalizado según el método de respuesta seleccionado.
* @param {HTMLElement} smtpReplyToMethod - Elemento select del método de respuesta.
*/
handleToggleReplyToMethod() {
const emailCustomDiv = document.querySelector('.email-custom-div');
if (emailCustomDiv) {
emailCustomDiv.style.display = Number(this.smtpReplyToMethod.value) === 3 ? 'block' : 'none';
}
}
/**
* Cancels the sender response form submission.
* This method disables the form buttons, disables all form fields,
* and sets the cancel button to a loading state.
*
* @returns {void}
*/
handleCancel() {
this.disableFormFields(this.formSenderResponse);
this.toggleButtonsState(this.formSenderResponse, false);
this.setButtonLoadingState(this.cancelButton, true);
}
/**
* Inicializa la validación del formulario.
* @param {HTMLElement} form - El formulario.
* @param {Object} config - Configuración de validación.
* @param {Function} onValidCallback - Callback cuando el formulario es válido.
*/
initializeFormValidation(form, config, onValidCallback) {
return FormValidation.formValidation(form, config).on('core.form.valid', onValidCallback);
}
/**
* Maneja la acción cuando el formulario es válido.
*/
handleFormValid() {
this.disableFormFields(this.formSenderResponse);
this.toggleButtonsState(this.formSenderResponse, false);
this.setButtonLoadingState(this.saveButton, true);
Livewire.dispatch('saveMailSenderResponseSettings');
}
/**
* Deshabilita todos los campos del formulario.
* @param {HTMLElement} form - El formulario.
*/
disableFormFields(form) {
form.querySelectorAll('input, select, textarea').forEach(field => {
field.disabled = true;
});
}
/**
* Habilita o deshabilita los botones dentro del formulario.
* @param {HTMLElement} form - El formulario.
* @param {boolean} state - Estado de habilitación.
*/
toggleButtonsState(form, state) {
form.querySelectorAll('button').forEach(button => {
button.disabled = !state;
});
}
/**
* Configura el estado de carga de un botón.
* @param {HTMLElement} button - El botón.
* @param {boolean} isLoading - Si el botón está en estado de carga.
*/
setButtonLoadingState(button, isLoading) {
const loadingText = button.getAttribute('data-loading-text');
if (loadingText && isLoading) {
button.innerHTML = loadingText;
} else {
button.innerHTML = button.getAttribute('data-original-text') || button.innerHTML;
}
}
senderResponsValidateConfig = {
fields: {
from_address: {
validators: {
notEmpty: {
message: 'El correo electrónico de salida es obligatorio.'
},
emailAddress: {
message: 'Por favor, introduce un correo electrónico válido.'
}
}
},
from_name: {
validators: {
notEmpty: {
message: 'El nombre del remitente es obligatorio.'
}
}
},
reply_to_method: {
validators: {
notEmpty: {
message: 'El método de respuesta es obligatorio.'
}
}
},
reply_to_email: {
validators: {
callback: {
message: 'El correo electrónico de respuesta es obligatorio.',
callback: function (input) {
if (Number(document.getElementById('reply_to_method').value) === 3) {
return input.value.trim() !== '' && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(input.value);
}
return true;
}
}
}
},
reply_to_name: {
validators: {
callback: {
message: 'El nombre de respuesta es obligatorio.',
callback: function (input) {
if (Number(document.getElementById('reply_to_method').value) === 3) {
return input.value.trim() !== '';
}
return true;
}
}
}
}
},
plugins: {
trigger: new FormValidation.plugins.Trigger(),
bootstrap5: new FormValidation.plugins.Bootstrap5({
eleValidClass: '',
rowSelector: '.fv-row'
}),
submitButton: new FormValidation.plugins.SubmitButton(),
autoFocus: new FormValidation.plugins.AutoFocus()
}
};
}

View File

@ -0,0 +1,239 @@
export default class SmtpSettingsForm {
constructor(config = {}) {
const defaultConfig = {
formSmtpSettingsSelector: '#mail-smtp-settings-card',
changeSmtpSettingsId: 'change_smtp_settings',
testSmtpConnectionButtonId: 'test_smtp_connection_button',
saveSmtpConnectionButtonId: 'save_smtp_connection_button',
cancelSmtpConnectionButtonId: 'cancel_smtp_connection_button'
};
this.config = { ...defaultConfig, ...config };
this.formSmtpSettings = null;
this.changeSmtpSettingsCheckbox = null;
this.testButton = null;
this.saveButton = null;
this.cancelButton = null;
this.validator = null;
this.init();
}
/**
* Inicializa el formulario de configuración SMTP.
*/
init() {
try {
// Obtener elementos esenciales
this.formSmtpSettings = document.querySelector(this.config.formSmtpSettingsSelector);
this.notificationArea = this.formSmtpSettings.querySelector(this.config.notificationAreaSelector);
this.changeSmtpSettingsCheckbox = document.getElementById(this.config.changeSmtpSettingsId);
this.testButton = document.getElementById(this.config.testSmtpConnectionButtonId);
this.saveButton = document.getElementById(this.config.saveSmtpConnectionButtonId);
this.cancelButton = document.getElementById(this.config.cancelSmtpConnectionButtonId);
// Asignar eventos
this.changeSmtpSettingsCheckbox.addEventListener('change', event => this.handleCheckboxChange(event));
this.testButton.addEventListener('click', event => this.handleTestConnection(event));
this.cancelButton.addEventListener('click', () => this.handleCancel());
// Inicializar validación del formulario
this.validator = FormValidation.formValidation(this.formSmtpSettings, this.smtpSettingsFormValidateConfig);
} catch (error) {
console.error('Error al inicializar el formulario SMTP:', error);
}
}
/**
* Método de recarga parcial
* Este método restablece la validación y los eventos sin destruir la instancia.
*/
reload() {
try {
// Inicializar validación del formulario
this.validator = FormValidation.formValidation(this.formSmtpSettings, this.smtpSettingsFormValidateConfig);
} catch (error) {
console.error('Error al recargar el formulario:', error);
}
}
/**
* Maneja el cambio en la casilla "Cambiar configuración SMTP".
* @param {Event} event - Evento de cambio.
*/
handleCheckboxChange(event) {
const isEnabled = event.target.checked;
this.toggleFieldsState(['host', 'port', 'encryption', 'username', 'password'], isEnabled);
this.testButton.disabled = false;
this.saveButton.disabled = true;
this.cancelButton.disabled = false;
if (!isEnabled) {
Livewire.dispatch('loadSettings');
}
}
/**
* Maneja el clic en el botón "Probar conexión".
* @param {Event} event - Evento de clic.
*/
handleTestConnection(event) {
event.preventDefault();
this.validator.resetForm();
this.validator.validate().then(status => {
if (status === 'Valid') {
this.disableFormFields(this.formSmtpSettings);
this.toggleButtonsState(this.formSmtpSettings, false);
this.setButtonLoadingState(this.testButton, true);
Livewire.dispatch('testSmtpConnection');
}
});
}
/**
* Maneja la cancelación de cambios en la configuración SMTP.
*/
handleCancel() {
this.disableFormFields(this.formSmtpSettings);
this.toggleButtonsState(this.formSmtpSettings, false);
this.setButtonLoadingState(this.cancelButton, true);
Livewire.dispatch('loadSettings');
}
/**
* Habilita o deshabilita los campos del formulario.
* @param {Array} fields - IDs de los campos a actualizar.
* @param {boolean} isEnabled - Estado de habilitación.
*/
toggleFieldsState(fields, isEnabled) {
fields.forEach(id => {
const field = document.getElementById(id);
if (field) field.disabled = !isEnabled;
});
}
/**
* Deshabilita todos los campos del formulario.
* @param {HTMLElement} form - El formulario.
*/
disableFormFields(form) {
form.querySelectorAll('input, select, textarea').forEach(field => {
field.disabled = true;
});
}
/**
* Habilita o deshabilita los botones dentro del formulario.
* @param {HTMLElement} form - El formulario.
* @param {boolean} state - Estado de habilitación.
*/
toggleButtonsState(form, state) {
form.querySelectorAll('button').forEach(button => {
button.disabled = !state;
});
}
/**
* Configura el estado de carga de un botón.
* @param {HTMLElement} button - El botón.
* @param {boolean} isLoading - Si el botón está en estado de carga.
*/
setButtonLoadingState(button, isLoading) {
const loadingText = button.getAttribute('data-loading-text');
if (loadingText && isLoading) {
button.innerHTML = loadingText;
} else {
button.innerHTML = button.getAttribute('data-original-text') || button.innerHTML;
}
}
smtpSettingsFormValidateConfig = {
fields: {
host: {
validators: {
callback: {
message: 'El servidor SMTP es obligatorio.',
callback: function (input) {
// Ejecutar la validación solo si 'change_smtp_settings' está marcado
if (document.getElementById('change_smtp_settings').checked) {
return input.value.trim() !== '';
}
return true; // No validar si no está marcado
}
}
}
},
port: {
validators: {
callback: {
message: 'El puerto SMTP es obligatorio.',
callback: function (input) {
if (document.getElementById('change_smtp_settings').checked) {
return (
input.value.trim() !== '' &&
/^\d+$/.test(input.value) &&
input.value >= 1 &&
input.value <= 65535
);
}
return true;
}
}
}
},
encryption: {
validators: {
callback: {
message: 'La encriptación es obligatoria.',
callback: function (input) {
if (document.getElementById('change_smtp_settings').checked) {
return input.value.trim() !== '';
}
return true;
}
}
}
},
username: {
validators: {
callback: {
message: 'El usuario SMTP es obligatorio.',
callback: function (input) {
if (document.getElementById('change_smtp_settings').checked) {
return input.value.trim().length >= 6;
}
return true;
}
}
}
},
password: {
validators: {
callback: {
message: 'Por favor, introduzca su contraseña.',
callback: function (input) {
if (document.getElementById('change_smtp_settings').checked) {
return input.value.trim().length >= 5;
}
return true;
}
}
}
}
},
plugins: {
trigger: new FormValidation.plugins.Trigger(),
bootstrap5: new FormValidation.plugins.Bootstrap5({ rowSelector: '.fv-row' }),
submitButton: new FormValidation.plugins.SubmitButton(),
autoFocus: new FormValidation.plugins.AutoFocus()
}
};
}

View File

@ -0,0 +1,252 @@
export default class WebsiteLegalSettingsForm {
constructor(config = {}) {
const defaultConfig = {
contentId: 'website-legal-settings-card',
forms: {
'legal_terminos_y_condiciones-nav': {
enabledCheckboxId: 'legal_terminos_y_condiciones-enabled',
quillSelector: '#legal_terminos_y_condiciones-content',
quillPlaceholder: 'Ingrese los términos y condiciones'
},
'legal_aviso_de_privacidad-nav': {
enabledCheckboxId: 'legal_aviso_de_privacidad-enabled',
quillSelector: '#legal_aviso_de_privacidad-content',
quillPlaceholder: 'Ingrese los aviso de privacidad'
},
'legal_politica_de_devoluciones-nav': {
enabledCheckboxId: 'legal_politica_de_devoluciones-enabled',
quillSelector: '#legal_politica_de_devoluciones-content',
quillPlaceholder: 'Ingrese los política de devoluciones y reembolsos'
},
'legal_politica_de_envios-nav': {
enabledCheckboxId: 'legal_politica_de_envios-enabled',
quillSelector: '#legal_politica_de_envios-content',
quillPlaceholder: 'Ingrese los política de envíos'
},
'legal_politica_de_cookies-nav': {
enabledCheckboxId: 'legal_politica_de_cookies-enabled',
quillSelector: '#legal_politica_de_cookies-content',
quillPlaceholder: 'Ingrese los política de cookies'
},
'legal_autorizaciones_y_licencias-nav': {
enabledCheckboxId: 'legal_autorizaciones_y_licencias-enabled',
quillSelector: '#legal_autorizaciones_y_licencias-content',
quillPlaceholder: 'Ingrese los autorizaciones y licencias'
},
'legal_informacion_comercial-nav': {
enabledCheckboxId: 'legal_informacion_comercial-enabled',
quillSelector: '#legal_informacion_comercial-content',
quillPlaceholder: 'Ingrese los información comercial'
},
'legal_consentimiento_para_el_login_de_terceros-nav': {
enabledCheckboxId: 'legal_consentimiento_para_el_login_de_terceros-enabled',
quillSelector: '#legal_consentimiento_para_el_login_de_terceros-content',
quillPlaceholder: 'Ingrese los consentimiento para el login de terceros'
},
'legal_leyendas_de_responsabilidad-nav': {
enabledCheckboxId: 'legal_leyendas_de_responsabilidad-enabled',
quillSelector: '#legal_leyendas_de_responsabilidad-content',
quillPlaceholder: 'Ingrese los leyendas de responsabilidad'
}
},
saveButtonId: 'save-button',
cancelButtonId: 'cancel-button',
notificationAreaSelector: '.notification-container'
};
this.config = { ...defaultConfig, ...config };
this.content = null;
this.saveButton = null;
this.cancelButton = null;
this.notificationArea = null;
this.currentFormKey = null;
this.quillInstances = {}; // Almacenar instancias de Quill
this.editState = {};
this.init();
}
init() {
this.content = document.getElementById(this.config.contentId);
this.saveButton = document.getElementById(this.config.saveButtonId);
this.cancelButton = document.getElementById(this.config.cancelButtonId);
this.notificationArea = this.content.querySelector(this.config.notificationAreaSelector);
// Puntero para rastrear si un formulario ha sido editado
this.editState = Object.keys(this.config.forms).reduce((state, key) => {
state[key] = false; // Inicializar todos los formularios como no editados
return state;
}, {});
this.switchToForm(Object.keys(this.config.forms)[0]); // Cargar el primer formulario
this.saveButton.addEventListener('click', () => this.handleSave());
this.cancelButton.addEventListener('click', () => this.handleCancel());
}
reload() {
const currentFormKey = this.currentFormKey;
if (currentFormKey) {
const quillInstance = this.quillInstances[currentFormKey];
const { quillSelector, enabledCheckboxId } = this.config.forms[currentFormKey];
const quillContainer = document.querySelector(quillSelector);
const checkbox = document.getElementById(enabledCheckboxId);
const textarea = document.getElementById(`${currentFormKey.replace('-nav', '')}-textarea`);
quillInstance.root.innerHTML = textarea.value;
// Agregar eventos
checkbox.addEventListener('change', () =>
this.handleCheckboxChange(checkbox, quillContainer, currentFormKey)
);
const isEnabled = checkbox.checked;
quillInstance.enable(isEnabled); // Sincronizar habilitación con el checkbox
}
}
switchToForm(formKey) {
if (!this.config.forms[formKey]) return;
this.currentFormKey = formKey;
const { quillSelector, quillPlaceholder, enabledCheckboxId } = this.config.forms[formKey];
const quillContainer = document.querySelector(quillSelector);
const checkbox = document.getElementById(enabledCheckboxId);
// Si la instancia de Quill no existe, inicialízala
if (!this.quillInstances[formKey]) {
this.quillInstances[formKey] = new Quill(quillSelector, {
placeholder: quillPlaceholder,
modules: { toolbar: this.getToolbar() },
theme: 'snow'
});
// Escuchar cambios en el editor
quillContainer.__quill = this.quillInstances[formKey];
this.quillInstances[formKey].on(
'text-change',
this.debounce(() => this.handleContentChange(formKey), 300)
);
setTimeout(() => {
const isEnabled = checkbox.checked;
quillContainer.__quill.enable(isEnabled); // Sincronizar habilitación con el checkbox
});
}
this.toggleButtonsState(this.editState[this.currentFormKey]);
// Asignar evento al checkbox (asegura no duplicar eventos)
if (!checkbox.dataset.bound) {
checkbox.addEventListener('change', () => this.handleCheckboxChange(checkbox, quillContainer, formKey));
checkbox.dataset.bound = true; // Marcar como manejado
}
}
handleContentChange(formKey) {
// Marcar el formulario como editado
this.editState[formKey] = true;
// Habilitar botones
this.toggleButtonsState(true);
}
handleCheckboxChange(checkbox, quillContainer, formKey) {
const isEnabled = checkbox.checked;
if (quillContainer.__quill) {
quillContainer.__quill.enable(isEnabled); // Habilitar o deshabilitar el editor
if (isEnabled) {
quillContainer.__quill.focus(); // Hacer focus si está habilitado
}
}
// Marcar el formulario como editado
this.editState[formKey] = true;
this.toggleButtonsState(true);
}
handleSave() {
const quillInstance = this.quillInstances[this.currentFormKey];
const textarea = document.getElementById(`${this.currentFormKey.replace('-nav', '-textarea')}`);
// Deshabilitar el estado de edición
this.editState[this.currentFormKey] = false;
//this.disableFormFields(this.content);
this.toggleButtonsState(false);
this.setButtonLoadingState(true);
// Actualizar el contenido del textarea para sincronizar con Livewire
textarea.value = quillInstance.root.innerHTML;
// Simular el evento 'input' para actualizar el contenido en el textarea
textarea.dispatchEvent(new Event('input', { bubbles: true }));
// Emitir el evento de guardar en Livewire
Livewire.dispatch('saveLegal');
}
handleCancel() {
// Restablecer el estado de edición del formulario actual
this.editState[this.currentFormKey] = false;
//this.disableFormFields();
this.toggleButtonsState(false);
}
disableFormFields(content) {
content.querySelectorAll('input, select, textarea').forEach(field => {
field.disabled = true;
});
if (this.fullEditor) {
this.fullEditor.enable(false); // Deshabilitar el editor
}
}
toggleButtonsState(state) {
this.saveButton.disabled = !state;
this.cancelButton.disabled = !state;
}
setButtonLoadingState(isLoading) {
const loadingText = this.saveButton.getAttribute('data-loading-text');
if (loadingText && isLoading) {
this.saveButton.innerHTML = loadingText;
} else {
this.saveButton.innerHTML = this.saveButton.getAttribute('data-original-text') || this.saveButton.innerHTML;
}
}
debounce(func, wait) {
let timeout;
return function (...args) {
const context = this;
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(context, args), wait);
};
}
getToolbar() {
return [
[{ font: [] }, { size: [] }],
['bold', 'italic', 'underline', 'strike'],
[{ color: [] }, { background: [] }],
[{ script: 'super' }, { script: 'sub' }],
[{ header: '1' }, { header: '2' }],
[{ list: 'ordered' }, { list: 'bullet' }, { indent: '-1' }, { indent: '+1' }],
['link', 'clean']
];
}
}

View File

@ -0,0 +1,29 @@
import LivewireNotification from '../_class/LivewireNotification';
import FormCustomListener from '../_class/FormCustomListener';
const notification = new LivewireNotification();
new FormCustomListener({
buttonSelectors: ['.btn-save', '.btn-cancel', '.btn-reset'] // Selectores para botones
});
Livewire.on('clearLocalStoregeTemplateCustomizer', event => {
const _deleteCookie = name => {
document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
};
const pattern = 'templateCustomizer-';
// Iterar sobre todas las claves en localStorage
Object.keys(localStorage).forEach(key => {
if (key.startsWith(pattern)) {
localStorage.removeItem(key);
}
});
_deleteCookie('admin-mode');
_deleteCookie('admin-colorPref');
_deleteCookie('colorPref');
_deleteCookie('theme');
_deleteCookie('direction');
});

View File

@ -0,0 +1,27 @@
import LivewireNotification from '../_class/LivewireNotification';
import SmtpSettingsForm from '../_class/SmtpSettingsForm';
import SenderResponseForm from '../_class/SenderResponseForm';
new LivewireNotification();
window.smtpSettingsForm = new SmtpSettingsForm();
window.senderResponseForm = new SenderResponseForm();
Livewire.hook('morphed', ({ component }) => {
switch (component.name) {
case 'mail-smtp-settings':
if (window.smtpSettingsForm) {
window.smtpSettingsForm.reload(); // Recarga el formulario sin destruir la instancia
}
break;
case 'mail-sender-response-settings':
if (window.senderResponseForm) {
window.senderResponseForm.reload(); // Recarga el formulario sin destruir la instancia
}
break;
default:
break;
}
});

View File

@ -0,0 +1,9 @@
import './bootstrap';
/*
Add custom scripts here
*/
import.meta.glob([
'../assets/img/**',
// '../assets/json/**',
'../assets/vendor/fonts/**'
]);

View File

@ -0,0 +1,206 @@
/**
* App user list (jquery)
*/
'use strict';
$(function () {
var dataTablePermissions = $('.datatables-permissions'),
dt_permission,
userList = baseUrl + 'app/user/list';
// Users List datatable
if (dataTablePermissions.length) {
dt_permission = dataTablePermissions.DataTable({
ajax: assetsPath + 'json/permissions-list.json', // JSON file to add data
columns: [
// columns according to JSON
{ data: '' },
{ data: 'id' },
{ data: 'name' },
{ data: 'assigned_to' },
{ data: 'created_date' },
{ data: '' }
],
columnDefs: [
{
// For Responsive
className: 'control',
orderable: false,
searchable: false,
responsivePriority: 2,
targets: 0,
render: function (data, type, full, meta) {
return '';
}
},
{
targets: 1,
searchable: false,
visible: false
},
{
// Name
targets: 2,
render: function (data, type, full, meta) {
var $name = full['name'];
return '<span class="text-nowrap text-heading">' + $name + '</span>';
}
},
{
// User Role
targets: 3,
orderable: false,
render: function (data, type, full, meta) {
var $assignedTo = full['assigned_to'],
$output = '';
var roleBadgeObj = {
Admin: '<a href="' + userList + '"><span class="badge me-4 bg-label-primary">Administrator</span></a>',
Manager: '<a href="' + userList + '"><span class="badge me-4 bg-label-warning">Manager</span></a>',
Users: '<a href="' + userList + '"><span class="badge me-4 bg-label-success">Users</span></a>',
Support: '<a href="' + userList + '"><span class="badge me-4 bg-label-info">Support</span></a>',
Restricted:
'<a href="' + userList + '"><span class="badge me-4 bg-label-danger">Restricted User</span></a>'
};
for (var i = 0; i < $assignedTo.length; i++) {
var val = $assignedTo[i];
$output += roleBadgeObj[val];
}
return '<span class="text-nowrap">' + $output + '</span>';
}
},
{
// remove ordering from Name
targets: 4,
orderable: false,
render: function (data, type, full, meta) {
var $date = full['created_date'];
return '<span class="text-nowrap">' + $date + '</span>';
}
},
{
// Actions
targets: -1,
searchable: false,
title: 'Actions',
orderable: false,
render: function (data, type, full, meta) {
return (
'<div class="d-flex align-items-center">' +
'<span class="text-nowrap"><button class="btn btn-icon btn-text-secondary waves-effect waves-light rounded-pill me-1" data-bs-target="#editPermissionModal" data-bs-toggle="modal" data-bs-dismiss="modal"><i class="ti ti-edit ti-md"></i></button>' +
'<a href="javascript:;" class="btn btn-icon btn-text-secondary waves-effect waves-light rounded-pill dropdown-toggle hide-arrow" data-bs-toggle="dropdown"><i class="ti ti-dots-vertical ti-md mx-1"></i></a>' +
'<div class="dropdown-menu dropdown-menu-end m-0">' +
'<a href="javascript:;"" class="dropdown-item">Edit</a>' +
'<a href="javascript:;" class="dropdown-item">Suspend</a>' +
'</div>' +
'</div>'
);
}
}
],
order: [[1, 'asc']],
dom:
'<"row mx-1"' +
'<"col-sm-12 col-md-3" l>' +
'<"col-sm-12 col-md-9"<"dt-action-buttons text-xl-end text-lg-start text-md-end text-start d-flex align-items-center justify-content-md-end justify-content-center flex-wrap"<"me-4 mt-n6 mt-md-0"f>B>>' +
'>t' +
'<"row"' +
'<"col-sm-12 col-md-6"i>' +
'<"col-sm-12 col-md-6"p>' +
'>',
language: {
sLengthMenu: 'Show _MENU_',
search: '',
searchPlaceholder: 'Search Permissions',
paginate: {
next: '<i class="ti ti-chevron-right ti-sm"></i>',
previous: '<i class="ti ti-chevron-left ti-sm"></i>'
}
},
// Buttons with Dropdown
buttons: [
{
text: '<i class="ti ti-plus ti-xs me-0 me-sm-2"></i><span class="d-none d-sm-inline-block">Add Permission</span>',
className: 'add-new btn btn-primary mb-6 mb-md-0 waves-effect waves-light',
attr: {
'data-bs-toggle': 'modal',
'data-bs-target': '#addPermissionModal'
},
init: function (api, node, config) {
$(node).removeClass('btn-secondary');
}
}
],
// For responsive popup
responsive: {
details: {
display: $.fn.dataTable.Responsive.display.modal({
header: function (row) {
var data = row.data();
return 'Details of ' + data['name'];
}
}),
type: 'column',
renderer: function (api, rowIdx, columns) {
var data = $.map(columns, function (col, i) {
return col.title !== '' // ? Do not show row in modal popup if title is blank (for check box)
? '<tr data-dt-row="' +
col.rowIndex +
'" data-dt-column="' +
col.columnIndex +
'">' +
'<td>' +
col.title +
':' +
'</td> ' +
'<td>' +
col.data +
'</td>' +
'</tr>'
: '';
}).join('');
return data ? $('<table class="table"/><tbody />').append(data) : false;
}
}
},
initComplete: function () {
// Adding role filter once table initialized
this.api()
.columns(3)
.every(function () {
var column = this;
var select = $(
'<select id="UserRole" class="form-select text-capitalize"><option value=""> Select Role </option></select>'
)
.appendTo('.user_role')
.on('change', function () {
var val = $.fn.dataTable.util.escapeRegex($(this).val());
column.search(val ? '^' + val + '$' : '', true, false).draw();
});
column
.data()
.unique()
.sort()
.each(function (d, j) {
select.append('<option value="' + d + '" class="text-capitalize">' + d + '</option>');
});
});
}
});
}
// Delete Record
$('.datatables-permissions tbody').on('click', '.delete-record', function () {
dt_permission.row($(this).parents('tr')).remove().draw();
});
// Filter form control to default size
// ? setTimeout used for multilingual table initialization
setTimeout(() => {
$('.dataTables_filter .form-control').removeClass('form-control-sm');
$('.dataTables_length .form-select').removeClass('form-select-sm');
$('.dataTables_info').addClass('ms-n1');
$('.dataTables_paginate').addClass('me-n1');
}, 300);
});

View File

@ -0,0 +1,428 @@
/**
* App user list
*/
'use strict';
// Datatable (jquery)
$(function () {
var dtUserTable = $('.datatables-users'),
dt_User,
statusObj = {
1: { title: 'Pending', class: 'bg-label-warning' },
2: { title: 'Active', class: 'bg-label-success' },
3: { title: 'Inactive', class: 'bg-label-secondary' }
};
var userView = baseUrl + 'app/user/view/account';
// Users List datatable
if (dtUserTable.length) {
var dtUser = dtUserTable.DataTable({
ajax: assetsPath + 'json/user-list.json', // JSON file to add data
columns: [
// columns according to JSON
{ data: 'id' },
{ data: 'id' },
{ data: 'full_name' },
{ data: 'role' },
{ data: 'current_plan' },
{ data: 'billing' },
{ data: 'status' },
{ data: '' }
],
columnDefs: [
{
// For Responsive
className: 'control',
orderable: false,
searchable: false,
responsivePriority: 2,
targets: 0,
render: function (data, type, full, meta) {
return '';
}
},
{
// For Checkboxes
targets: 1,
orderable: false,
checkboxes: {
selectAllRender: '<input type="checkbox" class="form-check-input">'
},
render: function () {
return '<input type="checkbox" class="dt-checkboxes form-check-input" >';
},
searchable: false
},
{
// User full name and email
targets: 2,
responsivePriority: 4,
render: function (data, type, full, meta) {
var $name = full['full_name'],
$email = full['email'],
$image = full['avatar'];
if ($image) {
// For Avatar image
var $output =
'<img src="' + assetsPath + 'img/avatars/' + $image + '" alt="Avatar" class="rounded-circle">';
} else {
// For Avatar badge
var stateNum = Math.floor(Math.random() * 6);
var states = ['success', 'danger', 'warning', 'info', 'primary', 'secondary'];
var $state = states[stateNum],
$name = full['full_name'],
$initials = $name.match(/\b\w/g) || [];
$initials = (($initials.shift() || '') + ($initials.pop() || '')).toUpperCase();
$output = '<span class="avatar-initial rounded-circle bg-label-' + $state + '">' + $initials + '</span>';
}
// Creates full output for row
var $row_output =
'<div class="d-flex justify-content-left align-items-center">' +
'<div class="avatar-wrapper">' +
'<div class="avatar avatar-sm me-4">' +
$output +
'</div>' +
'</div>' +
'<div class="d-flex flex-column">' +
'<a href="' +
userView +
'" class="text-heading text-truncate"><span class="fw-medium">' +
$name +
'</span></a>' +
'<small>@' +
$email +
'</small>' +
'</div>' +
'</div>';
return $row_output;
}
},
{
// User Role
targets: 3,
render: function (data, type, full, meta) {
var $role = full['role'];
var roleBadgeObj = {
Subscriber: '<i class="ti ti-crown ti-md text-primary me-2"></i>',
Author: '<i class="ti ti-edit ti-md text-warning me-2"></i>',
Maintainer: '<i class="ti ti-user ti-md text-success me-2"></i>',
Editor: '<i class="ti ti-chart-pie ti-md text-info me-2"></i>',
Admin: '<i class="ti ti-device-desktop ti-md text-danger me-2"></i>'
};
return (
"<span class='text-truncate d-flex align-items-center text-heading'>" +
roleBadgeObj[$role] +
$role +
'</span>'
);
}
},
{
// Plans
targets: 4,
render: function (data, type, full, meta) {
var $plan = full['current_plan'];
return '<span class="text-heading">' + $plan + '</span>';
}
},
{
// User Status
targets: 6,
render: function (data, type, full, meta) {
var $status = full['status'];
return (
'<span class="badge ' +
statusObj[$status].class +
'" text-capitalized>' +
statusObj[$status].title +
'</span>'
);
}
},
{
// Actions
targets: -1,
title: 'Actions',
searchable: false,
orderable: false,
render: function (data, type, full, meta) {
return (
'<div class="d-flex align-items-center">' +
'<a href="javascript:;" class="btn btn-icon btn-text-secondary waves-effect waves-light rounded-pill delete-record"><i class="ti ti-trash ti-md"></i></a>' +
'<a href="' +
userView +
'" class="btn btn-icon btn-text-secondary waves-effect waves-light rounded-pill"><i class="ti ti-eye ti-md"></i></a>' +
'<a href="javascript:;" class="btn btn-icon btn-text-secondary waves-effect waves-light rounded-pill dropdown-toggle hide-arrow" data-bs-toggle="dropdown"><i class="ti ti-dots-vertical ti-md"></i></a>' +
'<div class="dropdown-menu dropdown-menu-end m-0">' +
'<a href="javascript:;"" class="dropdown-item">Edit</a>' +
'<a href="javascript:;" class="dropdown-item">Suspend</a>' +
'</div>' +
'</div>'
);
}
}
],
order: [[2, 'desc']],
dom:
'<"row"' +
'<"col-md-2"<l>>' +
'<"col-md-10"<"dt-action-buttons text-xl-end text-lg-start text-md-end text-start d-flex align-items-center justify-content-end flex-md-row flex-column mb-6 mb-md-0"fB>>' +
'>t' +
'<"row"' +
'<"col-sm-12 col-md-6"i>' +
'<"col-sm-12 col-md-6"p>' +
'>',
language: {
sLengthMenu: 'Show _MENU_',
search: '',
searchPlaceholder: 'Search User',
paginate: {
next: '<i class="ti ti-chevron-right ti-sm"></i>',
previous: '<i class="ti ti-chevron-left ti-sm"></i>'
}
},
buttons: [
{
extend: 'collection',
className:
'btn btn-label-secondary dropdown-toggle me-4 waves-effect waves-light border-left-0 border-right-0 rounded',
text: '<i class="ti ti-upload ti-xs me-sm-1 align-text-bottom"></i> <span class="d-none d-sm-inline-block">Export</span>',
buttons: [
{
extend: 'print',
text: '<i class="ti ti-printer me-1" ></i>Print',
className: 'dropdown-item',
exportOptions: {
columns: [3, 4, 5, 6, 7],
// prevent avatar to be display
format: {
body: function (inner, coldex, rowdex) {
if (inner.length <= 0) return inner;
var el = $.parseHTML(inner);
var result = '';
$.each(el, function (index, item) {
if (item.classList !== undefined && item.classList.contains('user-name')) {
result = result + item.lastChild.firstChild.textContent;
} else if (item.innerText === undefined) {
result = result + item.textContent;
} else result = result + item.innerText;
});
return result;
}
}
},
customize: function (win) {
//customize print view for dark
$(win.document.body)
.css('color', config.colors.headingColor)
.css('border-color', config.colors.borderColor)
.css('background-color', config.colors.bodyBg);
$(win.document.body)
.find('table')
.addClass('compact')
.css('color', 'inherit')
.css('border-color', 'inherit')
.css('background-color', 'inherit');
}
},
{
extend: 'csv',
text: '<i class="ti ti-file-text me-1" ></i>Csv',
className: 'dropdown-item',
exportOptions: {
columns: [3, 4, 5, 6, 7],
// prevent avatar to be display
format: {
body: function (inner, coldex, rowdex) {
if (inner.length <= 0) return inner;
var el = $.parseHTML(inner);
var result = '';
$.each(el, function (index, item) {
if (item.classList !== undefined && item.classList.contains('user-name')) {
result = result + item.lastChild.firstChild.textContent;
} else if (item.innerText === undefined) {
result = result + item.textContent;
} else result = result + item.innerText;
});
return result;
}
}
}
},
{
extend: 'excel',
text: '<i class="ti ti-file-spreadsheet me-1"></i>Excel',
className: 'dropdown-item',
exportOptions: {
columns: [3, 4, 5, 6, 7],
// prevent avatar to be display
format: {
body: function (inner, coldex, rowdex) {
if (inner.length <= 0) return inner;
var el = $.parseHTML(inner);
var result = '';
$.each(el, function (index, item) {
if (item.classList !== undefined && item.classList.contains('user-name')) {
result = result + item.lastChild.firstChild.textContent;
} else if (item.innerText === undefined) {
result = result + item.textContent;
} else result = result + item.innerText;
});
return result;
}
}
}
},
{
extend: 'pdf',
text: '<i class="ti ti-file-description me-1"></i>Pdf',
className: 'dropdown-item',
exportOptions: {
columns: [3, 4, 5, 6, 7],
// prevent avatar to be display
format: {
body: function (inner, coldex, rowdex) {
if (inner.length <= 0) return inner;
var el = $.parseHTML(inner);
var result = '';
$.each(el, function (index, item) {
if (item.classList !== undefined && item.classList.contains('user-name')) {
result = result + item.lastChild.firstChild.textContent;
} else if (item.innerText === undefined) {
result = result + item.textContent;
} else result = result + item.innerText;
});
return result;
}
}
}
},
{
extend: 'copy',
text: '<i class="ti ti-copy me-1" ></i>Copy',
className: 'dropdown-item',
exportOptions: {
columns: [3, 4, 5, 6, 7],
// prevent avatar to be display
format: {
body: function (inner, coldex, rowdex) {
if (inner.length <= 0) return inner;
var el = $.parseHTML(inner);
var result = '';
$.each(el, function (index, item) {
if (item.classList !== undefined && item.classList.contains('user-name')) {
result = result + item.lastChild.firstChild.textContent;
} else if (item.innerText === undefined) {
result = result + item.textContent;
} else result = result + item.innerText;
});
return result;
}
}
}
}
]
},
{
text: '<i class="ti ti-plus ti-xs me-md-2"></i><span class="d-md-inline-block d-none">Add new role</span>',
className: 'btn btn-primary waves-effect waves-light rounded border-left-0 border-right-0',
attr: {
'data-bs-toggle': 'modal',
'data-bs-target': '#addRoleModal'
}
}
],
// For responsive popup
responsive: {
details: {
display: $.fn.dataTable.Responsive.display.modal({
header: function (row) {
var data = row.data();
return 'Details of ' + data['full_name'];
}
}),
type: 'column',
renderer: function (api, rowIdx, columns) {
var data = $.map(columns, function (col, i) {
return col.title !== '' // ? Do not show row in modal popup if title is blank (for check box)
? '<tr data-dt-row="' +
col.rowIndex +
'" data-dt-column="' +
col.columnIndex +
'">' +
'<td>' +
col.title +
':' +
'</td> ' +
'<td>' +
col.data +
'</td>' +
'</tr>'
: '';
}).join('');
return data ? $('<table class="table"/><tbody />').append(data) : false;
}
}
},
initComplete: function () {
// Adding role filter once table initialized
this.api()
.columns(3)
.every(function () {
var column = this;
var select = $(
'<select id="UserRole" class="form-select text-capitalize"><option value=""> Select Role </option></select>'
)
.appendTo('.user_role')
.on('change', function () {
var val = $.fn.dataTable.util.escapeRegex($(this).val());
column.search(val ? '^' + val + '$' : '', true, false).draw();
});
column
.data()
.unique()
.sort()
.each(function (d, j) {
select.append('<option value="' + d + '" class="text-capitalize">' + d + '</option>');
});
});
}
});
}
// Delete Record
$('.datatables-users tbody').on('click', '.delete-record', function () {
dtUser.row($(this).parents('tr')).remove().draw();
});
// Filter form control to default size
// ? setTimeout used for multilingual table initialization
setTimeout(() => {
$('.dataTables_filter .form-control').removeClass('form-control-sm');
$('.dataTables_length .form-select').removeClass('form-select-sm');
}, 300);
$('.dataTables_filter').addClass('ms-n4 me-4 mt-0 mt-md-6');
});
(function () {
// On edit role click, update text
var roleEditList = document.querySelectorAll('.role-edit-modal'),
roleAdd = document.querySelector('.add-new-role'),
roleTitle = document.querySelector('.role-title');
roleAdd.onclick = function () {
roleTitle.innerHTML = 'Add New Role'; // reset text
};
if (roleEditList) {
roleEditList.forEach(function (roleEditEl) {
roleEditEl.onclick = function () {
roleTitle.innerHTML = 'Edit Role'; // reset text
};
});
}
})();

View File

@ -0,0 +1,533 @@
/**
* Page User List
*/
'use strict';
// Datatable (jquery)
$(function () {
let borderColor, bodyBg, headingColor;
if (isDarkStyle) {
borderColor = config.colors_dark.borderColor;
bodyBg = config.colors_dark.bodyBg;
headingColor = config.colors_dark.headingColor;
} else {
borderColor = config.colors.borderColor;
bodyBg = config.colors.bodyBg;
headingColor = config.colors.headingColor;
}
// Variable declaration for table
var dt_user_table = $('.datatables-users'),
select2 = $('.select2'),
userView = baseUrl + 'app/user/view/account',
statusObj = {
1: { title: 'Pending', class: 'bg-label-warning' },
2: { title: 'Active', class: 'bg-label-success' },
3: { title: 'Inactive', class: 'bg-label-secondary' }
};
if (select2.length) {
var $this = select2;
$this.wrap('<div class="position-relative"></div>').select2({
placeholder: 'Select Country',
dropdownParent: $this.parent()
});
}
// Users datatable
if (dt_user_table.length) {
var dt_user = dt_user_table.DataTable({
ajax: assetsPath + 'json/user-list.json', // JSON file to add data
columns: [
// columns according to JSON
{ data: 'id' },
{ data: 'id' },
{ data: 'full_name' },
{ data: 'role' },
{ data: 'current_plan' },
{ data: 'billing' },
{ data: 'status' },
{ data: 'action' }
],
columnDefs: [
{
// For Responsive
className: 'control',
searchable: false,
orderable: false,
responsivePriority: 2,
targets: 0,
render: function (data, type, full, meta) {
return '';
}
},
{
// For Checkboxes
targets: 1,
orderable: false,
checkboxes: {
selectAllRender: '<input type="checkbox" class="form-check-input">'
},
render: function () {
return '<input type="checkbox" class="dt-checkboxes form-check-input" >';
},
searchable: false
},
{
// User full name and email
targets: 2,
responsivePriority: 4,
render: function (data, type, full, meta) {
var $name = full['full_name'],
$email = full['email'],
$image = full['avatar'];
if ($image) {
// For Avatar image
var $output =
'<img src="' + assetsPath + 'img/avatars/' + $image + '" alt="Avatar" class="rounded-circle">';
} else {
// For Avatar badge
var stateNum = Math.floor(Math.random() * 6);
var states = ['success', 'danger', 'warning', 'info', 'primary', 'secondary'];
var $state = states[stateNum],
$name = full['full_name'],
$initials = $name.match(/\b\w/g) || [];
$initials = (($initials.shift() || '') + ($initials.pop() || '')).toUpperCase();
$output = '<span class="avatar-initial rounded-circle bg-label-' + $state + '">' + $initials + '</span>';
}
// Creates full output for row
var $row_output =
'<div class="d-flex justify-content-start align-items-center user-name">' +
'<div class="avatar-wrapper">' +
'<div class="avatar avatar-sm me-4">' +
$output +
'</div>' +
'</div>' +
'<div class="d-flex flex-column">' +
'<a href="' +
userView +
'" class="text-heading text-truncate"><span class="fw-medium">' +
$name +
'</span></a>' +
'<small>' +
$email +
'</small>' +
'</div>' +
'</div>';
return $row_output;
}
},
{
// User Role
targets: 3,
render: function (data, type, full, meta) {
var $role = full['role'];
var roleBadgeObj = {
Subscriber: '<i class="ti ti-crown ti-md text-primary me-2"></i>',
Author: '<i class="ti ti-edit ti-md text-warning me-2"></i>',
Maintainer: '<i class="ti ti-user ti-md text-success me-2"></i>',
Editor: '<i class="ti ti-chart-pie ti-md text-info me-2"></i>',
Admin: '<i class="ti ti-device-desktop ti-md text-danger me-2"></i>'
};
return (
"<span class='text-truncate d-flex align-items-center text-heading'>" +
roleBadgeObj[$role] +
$role +
'</span>'
);
}
},
{
// Plans
targets: 4,
render: function (data, type, full, meta) {
var $plan = full['current_plan'];
return '<span class="text-heading">' + $plan + '</span>';
}
},
{
// User Status
targets: 6,
render: function (data, type, full, meta) {
var $status = full['status'];
return (
'<span class="badge ' +
statusObj[$status].class +
'" text-capitalized>' +
statusObj[$status].title +
'</span>'
);
}
},
{
// Actions
targets: -1,
title: 'Actions',
searchable: false,
orderable: false,
render: function (data, type, full, meta) {
return (
'<div class="d-flex align-items-center">' +
'<a href="javascript:;" class="btn btn-icon btn-text-secondary waves-effect waves-light rounded-pill delete-record"><i class="ti ti-trash ti-md"></i></a>' +
'<a href="' +
userView +
'" class="btn btn-icon btn-text-secondary waves-effect waves-light rounded-pill"><i class="ti ti-eye ti-md"></i></a>' +
'<a href="javascript:;" class="btn btn-icon btn-text-secondary waves-effect waves-light rounded-pill dropdown-toggle hide-arrow" data-bs-toggle="dropdown"><i class="ti ti-dots-vertical ti-md"></i></a>' +
'<div class="dropdown-menu dropdown-menu-end m-0">' +
'<a href="javascript:;"" class="dropdown-item">Edit</a>' +
'<a href="javascript:;" class="dropdown-item">Suspend</a>' +
'</div>' +
'</div>'
);
}
}
],
order: [[2, 'desc']],
dom:
'<"row"' +
'<"col-md-2"<"ms-n2"l>>' +
'<"col-md-10"<"dt-action-buttons text-xl-end text-lg-start text-md-end text-start d-flex align-items-center justify-content-end flex-md-row flex-column mb-6 mb-md-0 mt-n6 mt-md-0"fB>>' +
'>t' +
'<"row"' +
'<"col-sm-12 col-md-6"i>' +
'<"col-sm-12 col-md-6"p>' +
'>',
language: {
sLengthMenu: '_MENU_',
search: '',
searchPlaceholder: 'Search User',
paginate: {
next: '<i class="ti ti-chevron-right ti-sm"></i>',
previous: '<i class="ti ti-chevron-left ti-sm"></i>'
}
},
// Buttons with Dropdown
buttons: [
{
extend: 'collection',
className: 'btn btn-label-secondary dropdown-toggle mx-4 waves-effect waves-light',
text: '<i class="ti ti-upload me-2 ti-xs"></i>Export',
buttons: [
{
extend: 'print',
text: '<i class="ti ti-printer me-2" ></i>Print',
className: 'dropdown-item',
exportOptions: {
columns: [1, 2, 3, 4, 5],
// prevent avatar to be print
format: {
body: function (inner, coldex, rowdex) {
if (inner.length <= 0) return inner;
var el = $.parseHTML(inner);
var result = '';
$.each(el, function (index, item) {
if (item.classList !== undefined && item.classList.contains('user-name')) {
result = result + item.lastChild.firstChild.textContent;
} else if (item.innerText === undefined) {
result = result + item.textContent;
} else result = result + item.innerText;
});
return result;
}
}
},
customize: function (win) {
//customize print view for dark
$(win.document.body)
.css('color', headingColor)
.css('border-color', borderColor)
.css('background-color', bodyBg);
$(win.document.body)
.find('table')
.addClass('compact')
.css('color', 'inherit')
.css('border-color', 'inherit')
.css('background-color', 'inherit');
}
},
{
extend: 'csv',
text: '<i class="ti ti-file-text me-2" ></i>Csv',
className: 'dropdown-item',
exportOptions: {
columns: [1, 2, 3, 4, 5],
// prevent avatar to be display
format: {
body: function (inner, coldex, rowdex) {
if (inner.length <= 0) return inner;
var el = $.parseHTML(inner);
var result = '';
$.each(el, function (index, item) {
if (item.classList !== undefined && item.classList.contains('user-name')) {
result = result + item.lastChild.firstChild.textContent;
} else if (item.innerText === undefined) {
result = result + item.textContent;
} else result = result + item.innerText;
});
return result;
}
}
}
},
{
extend: 'excel',
text: '<i class="ti ti-file-spreadsheet me-2"></i>Excel',
className: 'dropdown-item',
exportOptions: {
columns: [1, 2, 3, 4, 5],
// prevent avatar to be display
format: {
body: function (inner, coldex, rowdex) {
if (inner.length <= 0) return inner;
var el = $.parseHTML(inner);
var result = '';
$.each(el, function (index, item) {
if (item.classList !== undefined && item.classList.contains('user-name')) {
result = result + item.lastChild.firstChild.textContent;
} else if (item.innerText === undefined) {
result = result + item.textContent;
} else result = result + item.innerText;
});
return result;
}
}
}
},
{
extend: 'pdf',
text: '<i class="ti ti-file-code-2 me-2"></i>Pdf',
className: 'dropdown-item',
exportOptions: {
columns: [1, 2, 3, 4, 5],
// prevent avatar to be display
format: {
body: function (inner, coldex, rowdex) {
if (inner.length <= 0) return inner;
var el = $.parseHTML(inner);
var result = '';
$.each(el, function (index, item) {
if (item.classList !== undefined && item.classList.contains('user-name')) {
result = result + item.lastChild.firstChild.textContent;
} else if (item.innerText === undefined) {
result = result + item.textContent;
} else result = result + item.innerText;
});
return result;
}
}
}
},
{
extend: 'copy',
text: '<i class="ti ti-copy me-2" ></i>Copy',
className: 'dropdown-item',
exportOptions: {
columns: [1, 2, 3, 4, 5],
// prevent avatar to be display
format: {
body: function (inner, coldex, rowdex) {
if (inner.length <= 0) return inner;
var el = $.parseHTML(inner);
var result = '';
$.each(el, function (index, item) {
if (item.classList !== undefined && item.classList.contains('user-name')) {
result = result + item.lastChild.firstChild.textContent;
} else if (item.innerText === undefined) {
result = result + item.textContent;
} else result = result + item.innerText;
});
return result;
}
}
}
}
]
},
{
text: '<i class="ti ti-plus me-0 me-sm-1 ti-xs"></i><span class="d-none d-sm-inline-block">Add New User</span>',
className: 'add-new btn btn-primary waves-effect waves-light',
attr: {
'data-bs-toggle': 'offcanvas',
'data-bs-target': '#offcanvasAddUser'
}
}
],
// For responsive popup
responsive: {
details: {
display: $.fn.dataTable.Responsive.display.modal({
header: function (row) {
var data = row.data();
return 'Details of ' + data['full_name'];
}
}),
type: 'column',
renderer: function (api, rowIdx, columns) {
var data = $.map(columns, function (col, i) {
return col.title !== '' // ? Do not show row in modal popup if title is blank (for check box)
? '<tr data-dt-row="' +
col.rowIndex +
'" data-dt-column="' +
col.columnIndex +
'">' +
'<td>' +
col.title +
':' +
'</td> ' +
'<td>' +
col.data +
'</td>' +
'</tr>'
: '';
}).join('');
return data ? $('<table class="table"/><tbody />').append(data) : false;
}
}
},
initComplete: function () {
// Adding role filter once table initialized
this.api()
.columns(3)
.every(function () {
var column = this;
var select = $(
'<select id="UserRole" class="form-select text-capitalize"><option value=""> Select Role </option></select>'
)
.appendTo('.user_role')
.on('change', function () {
var val = $.fn.dataTable.util.escapeRegex($(this).val());
column.search(val ? '^' + val + '$' : '', true, false).draw();
});
column
.data()
.unique()
.sort()
.each(function (d, j) {
select.append('<option value="' + d + '">' + d + '</option>');
});
});
// Adding plan filter once table initialized
this.api()
.columns(4)
.every(function () {
var column = this;
var select = $(
'<select id="UserPlan" class="form-select text-capitalize"><option value=""> Select Plan </option></select>'
)
.appendTo('.user_plan')
.on('change', function () {
var val = $.fn.dataTable.util.escapeRegex($(this).val());
column.search(val ? '^' + val + '$' : '', true, false).draw();
});
column
.data()
.unique()
.sort()
.each(function (d, j) {
select.append('<option value="' + d + '">' + d + '</option>');
});
});
// Adding status filter once table initialized
this.api()
.columns(6)
.every(function () {
var column = this;
var select = $(
'<select id="FilterTransaction" class="form-select text-capitalize"><option value=""> Select Status </option></select>'
)
.appendTo('.user_status')
.on('change', function () {
var val = $.fn.dataTable.util.escapeRegex($(this).val());
column.search(val ? '^' + val + '$' : '', true, false).draw();
});
column
.data()
.unique()
.sort()
.each(function (d, j) {
select.append(
'<option value="' +
statusObj[d].title +
'" class="text-capitalize">' +
statusObj[d].title +
'</option>'
);
});
});
}
});
}
// Delete Record
$('.datatables-users tbody').on('click', '.delete-record', function () {
dt_user.row($(this).parents('tr')).remove().draw();
});
// Filter form control to default size
// ? setTimeout used for multilingual table initialization
setTimeout(() => {
$('.dataTables_filter .form-control').removeClass('form-control-sm');
$('.dataTables_length .form-select').removeClass('form-select-sm');
}, 300);
});
// Validation & Phone mask
(function () {
const phoneMaskList = document.querySelectorAll('.phone-mask'),
addNewUserForm = document.getElementById('addNewUserForm');
// Phone Number
if (phoneMaskList) {
phoneMaskList.forEach(function (phoneMask) {
new Cleave(phoneMask, {
phone: true,
phoneRegionCode: 'US'
});
});
}
// Add New User Form Validation
const fv = FormValidation.formValidation(addNewUserForm, {
fields: {
userFullname: {
validators: {
notEmpty: {
message: 'Please enter fullname '
}
}
},
userEmail: {
validators: {
notEmpty: {
message: 'Please enter your email'
},
emailAddress: {
message: 'The value is not a valid email address'
}
}
}
},
plugins: {
trigger: new FormValidation.plugins.Trigger(),
bootstrap5: new FormValidation.plugins.Bootstrap5({
// Use this for enabling/changing valid/invalid class
eleValidClass: '',
rowSelector: function (field, ele) {
// field is the field name & ele is the field element
return '.mb-6';
}
}),
submitButton: new FormValidation.plugins.SubmitButton(),
// Submit the form when all fields are valid
// defaultSubmit: new FormValidation.plugins.DefaultSubmit(),
autoFocus: new FormValidation.plugins.AutoFocus()
}
});
})();

View File

@ -0,0 +1,222 @@
/**
* App User View - Account (jquery)
*/
$(function () {
'use strict';
// Variable declaration for table
var dt_invoice_table = $('.datatable-invoice');
// Invoice datatable
// --------------------------------------------------------------------
if (dt_invoice_table.length) {
var dt_invoice = dt_invoice_table.DataTable({
ajax: assetsPath + 'json/invoice-list.json', // JSON file to add data
columns: [
// columns according to JSON
{ data: '' },
{ data: 'invoice_id' },
{ data: 'invoice_status' },
{ data: 'total' },
{ data: 'issued_date' },
{ data: 'action' }
],
columnDefs: [
{
// For Responsive
className: 'control',
responsivePriority: 2,
targets: 0,
render: function (data, type, full, meta) {
return '';
}
},
{
// Invoice ID
targets: 1,
render: function (data, type, full, meta) {
var $invoice_id = full['invoice_id'];
// Creates full output for row
var $row_output = '<a href="' + baseUrl + 'app/invoice/preview"><span>#' + $invoice_id + '</span></a>';
return $row_output;
}
},
{
// Invoice status
targets: 2,
render: function (data, type, full, meta) {
var $invoice_status = full['invoice_status'],
$due_date = full['due_date'],
$balance = full['balance'];
var roleBadgeObj = {
Sent: '<span class="badge badge-center d-flex align-items-center justify-content-center rounded-pill bg-label-secondary w-px-30 h-px-30"><i class="ti ti-circle-check ti-xs"></i></span>',
Draft:
'<span class="badge badge-center d-flex align-items-center justify-content-center rounded-pill bg-label-primary w-px-30 h-px-30"><i class="ti ti-device-floppy ti-xs"></i></span>',
'Past Due':
'<span class="badge badge-center d-flex align-items-center justify-content-center rounded-pill bg-label-danger w-px-30 h-px-30"><i class="ti ti-info-circle ti-xs"></i></span>',
'Partial Payment':
'<span class="badge badge-center d-flex align-items-center justify-content-center rounded-pill bg-label-success w-px-30 h-px-30"><i class="ti ti-circle-half-2 ti-xs"></i></span>',
Paid: '<span class="badge badge-center d-flex align-items-center justify-content-center rounded-pill bg-label-warning w-px-30 h-px-30"><i class="ti ti-chart-pie ti-xs"></i></span>',
Downloaded:
'<span class="badge badge-center d-flex align-items-center justify-content-center rounded-pill bg-label-info w-px-30 h-px-30"><i class="ti ti-arrow-down-circle ti-xs"></i></span>'
};
return (
"<span class='d-inline-block' data-bs-toggle='tooltip' data-bs-html='true' title='<span>" +
$invoice_status +
'<br> <span class="fw-medium">Balance:</span> ' +
$balance +
'<br> <span class="fw-medium">Due Date:</span> ' +
$due_date +
"</span>'>" +
roleBadgeObj[$invoice_status] +
'</span>'
);
}
},
{
// Total Invoice Amount
targets: 3,
render: function (data, type, full, meta) {
var $total = full['total'];
return '$' + $total;
}
},
{
// Actions
targets: -1,
title: 'Actions',
orderable: false,
render: function (data, type, full, meta) {
return (
'<div class="d-flex align-items-center">' +
'<a href="javascript:;" class="btn btn-icon btn-text-secondary waves-effect waves-light rounded-pill delete-record" data-bs-toggle="tooltip" title="Delete record"><i class="ti ti-trash ti-md"></i></a>' +
'<a href="app-invoice-preview.html" class="btn btn-icon btn-text-secondary waves-effect waves-light rounded-pill" data-bs-toggle="tooltip" title="Preview"><i class="ti ti-eye ti-md"></i></a>' +
'<div class="d-inline-block">' +
'<a href="javascript:;" class="btn btn-sm btn-icon dropdown-toggle hide-arrow btn btn-icon btn-text-secondary waves-effect waves-light rounded-pill" data-bs-toggle="dropdown"><i class="ti ti-dots-vertical ti-md"></i></a>' +
'<ul class="dropdown-menu dropdown-menu-end m-0">' +
'<li><a href="javascript:;" class="dropdown-item">Details</a></li>' +
'<li><a href="javascript:;" class="dropdown-item">Archive</a></li>' +
'</ul>' +
'</div>' +
'</div>'
);
}
}
],
order: [[1, 'desc']],
dom:
'<"row mx-6"' +
'<"col-sm-6 col-12 d-flex align-items-center justify-content-center justify-content-sm-start mt-6 mt-sm-0"<"invoice-head-label">>' +
'<"col-sm-6 col-12 d-flex justify-content-center justify-content-md-end align-items-baseline"<"dt-action-buttons d-flex justify-content-center flex-md-row align-items-baseline gap-2"lB>>' +
'>t' +
'<"row mx-4"' +
'<"col-sm-12 col-xxl-6 text-center text-xxl-start pb-md-2 pb-xxl-0"i>' +
'<"col-sm-12 col-xxl-6 d-md-flex justify-content-xxl-end justify-content-center"p>' +
'>',
language: {
sLengthMenu: 'Show _MENU_',
search: '',
searchPlaceholder: 'Search Invoice',
paginate: {
next: '<i class="ti ti-chevron-right ti-sm"></i>',
previous: '<i class="ti ti-chevron-left ti-sm"></i>'
}
},
// Buttons with Dropdown
buttons: [
{
extend: 'collection',
className: 'btn btn-label-secondary dropdown-toggle float-sm-end mb-3 mb-sm-0 waves-effect waves-light',
text: '<i class="ti ti-upload ti-xs me-2"></i>Export',
buttons: [
{
extend: 'print',
text: '<i class="ti ti-printer me-2" ></i>Print',
className: 'dropdown-item',
exportOptions: { columns: [1, 2, 3, 4] }
},
{
extend: 'csv',
text: '<i class="ti ti-file-text me-2" ></i>Csv',
className: 'dropdown-item',
exportOptions: { columns: [1, 2, 3, 4] }
},
{
extend: 'excel',
text: '<i class="ti ti-file-spreadsheet me-2"></i>Excel',
className: 'dropdown-item',
exportOptions: { columns: [1, 2, 3, 4] }
},
{
extend: 'pdf',
text: '<i class="ti ti-file-description me-2"></i>Pdf',
className: 'dropdown-item',
exportOptions: { columns: [1, 2, 3, 4] }
},
{
extend: 'copy',
text: '<i class="ti ti-copy me-2" ></i>Copy',
className: 'dropdown-item',
exportOptions: { columns: [1, 2, 3, 4] }
}
]
}
],
// For responsive popup
responsive: {
details: {
display: $.fn.dataTable.Responsive.display.modal({
header: function (row) {
var data = row.data();
return 'Details of ' + data['full_name'];
}
}),
type: 'column',
renderer: function (api, rowIdx, columns) {
var data = $.map(columns, function (col, i) {
return col.title !== '' // ? Do not show row in modal popup if title is blank (for check box)
? '<tr data-dt-row="' +
col.rowIndex +
'" data-dt-column="' +
col.columnIndex +
'">' +
'<td>' +
col.title +
':' +
'</td> ' +
'<td>' +
col.data +
'</td>' +
'</tr>'
: '';
}).join('');
return data ? $('<table class="table"/><tbody />').append(data) : false;
}
}
}
});
$('div.invoice-head-label').html('<h5 class="card-title mb-0">Invoice List</h5>');
}
// On each datatable draw, initialize tooltip
dt_invoice_table.on('draw.dt', function () {
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl, {
boundary: document.body
});
});
});
// Delete Record
$('.datatable-invoice tbody').on('click', '.delete-record', function () {
dt_invoice.row($(this).parents('tr')).remove().draw();
});
// Filter form control to default size
// ? setTimeout used for multilingual table initialization
setTimeout(() => {
$('.dataTables_filter .form-control').removeClass('form-control-sm');
$('.dataTables_length .form-select').removeClass('form-select-sm');
}, 300);
});

View File

@ -0,0 +1,57 @@
/**
* App User View - Billing
*/
'use strict';
(function () {
// Cancel Subscription alert
const cancelSubscription = document.querySelector('.cancel-subscription');
// Alert With Functional Confirm Button
if (cancelSubscription) {
cancelSubscription.onclick = function () {
Swal.fire({
text: 'Are you sure you would like to cancel your subscription?',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Yes',
customClass: {
confirmButton: 'btn btn-primary me-2 waves-effect waves-light',
cancelButton: 'btn btn-label-secondary waves-effect waves-light'
},
buttonsStyling: false
}).then(function (result) {
if (result.value) {
Swal.fire({
icon: 'success',
title: 'Unsubscribed!',
text: 'Your subscription cancelled successfully.',
customClass: {
confirmButton: 'btn btn-success waves-effect waves-light'
}
});
} else if (result.dismiss === Swal.DismissReason.cancel) {
Swal.fire({
title: 'Cancelled',
text: 'Unsubscription Cancelled!!',
icon: 'error',
customClass: {
confirmButton: 'btn btn-success waves-effect waves-light'
}
});
}
});
};
}
// On edit address click, update text of add address modal
const addressEdit = document.querySelector('.edit-address'),
addressTitle = document.querySelector('.address-title'),
addressSubTitle = document.querySelector('.address-subtitle');
addressEdit.onclick = function () {
addressTitle.innerHTML = 'Edit Address'; // reset text
addressSubTitle.innerHTML = 'Edit your current address';
};
})();

View File

@ -0,0 +1,63 @@
/**
* App User View - Security
*/
'use strict';
(function () {
const formChangePass = document.querySelector('#formChangePassword');
// Form validation for Change password
if (formChangePass) {
const fv = FormValidation.formValidation(formChangePass, {
fields: {
newPassword: {
validators: {
notEmpty: {
message: 'Please enter new password'
},
stringLength: {
min: 8,
message: 'Password must be more than 8 characters'
}
}
},
confirmPassword: {
validators: {
notEmpty: {
message: 'Please confirm new password'
},
identical: {
compare: function () {
return formChangePass.querySelector('[name="newPassword"]').value;
},
message: 'The password and its confirm are not the same'
},
stringLength: {
min: 8,
message: 'Password must be more than 8 characters'
}
}
}
},
plugins: {
trigger: new FormValidation.plugins.Trigger(),
bootstrap5: new FormValidation.plugins.Bootstrap5({
eleValidClass: '',
rowSelector: '.form-password-toggle'
}),
submitButton: new FormValidation.plugins.SubmitButton(),
// Submit the form when all fields are valid
// defaultSubmit: new FormValidation.plugins.DefaultSubmit(),
autoFocus: new FormValidation.plugins.AutoFocus()
},
init: instance => {
instance.on('plugins.message.placed', function (e) {
if (e.element.parentElement.classList.contains('input-group')) {
e.element.parentElement.insertAdjacentElement('afterend', e.messageElement);
}
});
}
});
}
})();

View File

@ -0,0 +1,89 @@
/**
* App User View - Suspend User Script
*/
'use strict';
(function () {
const suspendUser = document.querySelector('.suspend-user');
// Suspend User javascript
if (suspendUser) {
suspendUser.onclick = function () {
Swal.fire({
title: 'Are you sure?',
text: "You won't be able to revert user!",
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Yes, Suspend user!',
customClass: {
confirmButton: 'btn btn-primary me-2 waves-effect waves-light',
cancelButton: 'btn btn-label-secondary waves-effect waves-light'
},
buttonsStyling: false
}).then(function (result) {
if (result.value) {
Swal.fire({
icon: 'success',
title: 'Suspended!',
text: 'User has been suspended.',
customClass: {
confirmButton: 'btn btn-success waves-effect waves-light'
}
});
} else if (result.dismiss === Swal.DismissReason.cancel) {
Swal.fire({
title: 'Cancelled',
text: 'Cancelled Suspension :)',
icon: 'error',
customClass: {
confirmButton: 'btn btn-success waves-effect waves-light'
}
});
}
});
};
}
//? Billing page have multiple buttons
// Cancel Subscription alert
const cancelSubscription = document.querySelectorAll('.cancel-subscription');
// Alert With Functional Confirm Button
if (cancelSubscription) {
cancelSubscription.forEach(btnCancle => {
btnCancle.onclick = function () {
Swal.fire({
text: 'Are you sure you would like to cancel your subscription?',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Yes',
customClass: {
confirmButton: 'btn btn-primary me-2 waves-effect waves-light',
cancelButton: 'btn btn-label-secondary waves-effect waves-light'
},
buttonsStyling: false
}).then(function (result) {
if (result.value) {
Swal.fire({
icon: 'success',
title: 'Unsubscribed!',
text: 'Your subscription cancelled successfully.',
customClass: {
confirmButton: 'btn btn-success waves-effect waves-light'
}
});
} else if (result.dismiss === Swal.DismissReason.cancel) {
Swal.fire({
title: 'Cancelled',
text: 'Unsubscription Cancelled!!',
icon: 'error',
customClass: {
confirmButton: 'btn btn-success waves-effect waves-light'
}
});
}
});
};
});
}
})();

View File

@ -0,0 +1,73 @@
/**
* Add New Address
*/
'use strict';
// Select2 (jquery)
$(function () {
const select2 = $('.select2');
// Select2 Country
if (select2.length) {
select2.each(function () {
var $this = $(this);
$this.wrap('<div class="position-relative"></div>').select2({
placeholder: 'Select value',
dropdownParent: $this.parent()
});
});
}
});
// Add New Address form validation
document.addEventListener('DOMContentLoaded', function () {
(function () {
// initCustomOptionCheck on modal show to update the custom select
let addNewAddress = document.getElementById('addNewAddress');
addNewAddress.addEventListener('show.bs.modal', function (event) {
// Init custom option check
window.Helpers.initCustomOptionCheck();
});
FormValidation.formValidation(document.getElementById('addNewAddressForm'), {
fields: {
modalAddressFirstName: {
validators: {
notEmpty: {
message: 'Please enter your first name'
},
regexp: {
regexp: /^[a-zA-Zs]+$/,
message: 'The first name can only consist of alphabetical'
}
}
},
modalAddressLastName: {
validators: {
notEmpty: {
message: 'Please enter your last name'
},
regexp: {
regexp: /^[a-zA-Zs]+$/,
message: 'The last name can only consist of alphabetical'
}
}
}
},
plugins: {
trigger: new FormValidation.plugins.Trigger(),
bootstrap5: new FormValidation.plugins.Bootstrap5({
// Use this for enabling/changing valid/invalid class
// eleInvalidClass: '',
eleValidClass: '',
rowSelector: '.col-12'
}),
submitButton: new FormValidation.plugins.SubmitButton(),
// Submit the form when all fields are valid
// defaultSubmit: new FormValidation.plugins.DefaultSubmit(),
autoFocus: new FormValidation.plugins.AutoFocus()
}
});
})();
});

View File

@ -0,0 +1,107 @@
/**
* Add new credit card
*/
'use strict';
document.addEventListener('DOMContentLoaded', function (e) {
(function () {
// Variables
const creditCardMask = document.querySelector('.credit-card-mask'),
expiryDateMask = document.querySelector('.expiry-date-mask'),
cvvMask = document.querySelector('.cvv-code-mask'),
btnReset = document.querySelector('.btn-reset');
let cleave;
// Credit Card
function initCleave() {
if (creditCardMask) {
cleave = new Cleave(creditCardMask, {
creditCard: true,
onCreditCardTypeChanged: function (type) {
if (type != '' && type != 'unknown') {
document.querySelector('.card-type').innerHTML =
'<img src="' +
assetsPath +
'img/icons/payments/' +
type +
'-cc.png" class="cc-icon-image" height="28"/>';
} else {
document.querySelector('.card-type').innerHTML = '';
}
}
});
}
}
// Init cleave on show modal (To fix the cc image issue)
let addNewCCModal = document.getElementById('addNewCCModal');
addNewCCModal.addEventListener('show.bs.modal', function (event) {
initCleave();
});
// Expiry Date Mask
if (expiryDateMask) {
new Cleave(expiryDateMask, {
date: true,
delimiter: '/',
datePattern: ['m', 'y']
});
}
// CVV
if (cvvMask) {
new Cleave(cvvMask, {
numeral: true,
numeralPositiveOnly: true
});
}
// Credit card form validation
FormValidation.formValidation(document.getElementById('addNewCCForm'), {
fields: {
modalAddCard: {
validators: {
notEmpty: {
message: 'Please enter your credit card number'
}
}
}
},
plugins: {
trigger: new FormValidation.plugins.Trigger(),
bootstrap5: new FormValidation.plugins.Bootstrap5({
// Use this for enabling/changing valid/invalid class
// eleInvalidClass: '',
eleValidClass: '',
rowSelector: '.col-12'
}),
submitButton: new FormValidation.plugins.SubmitButton(),
// Submit the form when all fields are valid
// defaultSubmit: new FormValidation.plugins.DefaultSubmit(),
autoFocus: new FormValidation.plugins.AutoFocus()
},
init: instance => {
instance.on('plugins.message.placed', function (e) {
//* Move the error message out of the `input-group` element
if (e.element.parentElement.classList.contains('input-group')) {
e.element.parentElement.insertAdjacentElement('afterend', e.messageElement);
}
});
}
}).on('plugins.message.displayed', function (e) {
if (e.element.parentElement.classList.contains('input-group')) {
//* Move the error message out of the `input-group` element
e.element.parentElement.insertAdjacentElement('afterend', e.messageElement.parentElement);
}
});
// reset card image on click of cancel
btnReset.addEventListener('click', function (e) {
// blank '.card-type' innerHTML to remove image
document.querySelector('.card-type').innerHTML = '';
// destroy cleave and init again on modal open
cleave.destroy();
});
})();
});

View File

@ -0,0 +1,35 @@
/**
* Add Permission Modal JS
*/
'use strict';
// Add permission form validation
document.addEventListener('DOMContentLoaded', function (e) {
(function () {
FormValidation.formValidation(document.getElementById('addPermissionForm'), {
fields: {
modalPermissionName: {
validators: {
notEmpty: {
message: 'Please enter permission name'
}
}
}
},
plugins: {
trigger: new FormValidation.plugins.Trigger(),
bootstrap5: new FormValidation.plugins.Bootstrap5({
// Use this for enabling/changing valid/invalid class
// eleInvalidClass: '',
eleValidClass: '',
rowSelector: '.col-12'
}),
submitButton: new FormValidation.plugins.SubmitButton(),
// Submit the form when all fields are valid
// defaultSubmit: new FormValidation.plugins.DefaultSubmit(),
autoFocus: new FormValidation.plugins.AutoFocus()
}
});
})();
});

View File

@ -0,0 +1,44 @@
/**
* Add new role Modal JS
*/
'use strict';
document.addEventListener('DOMContentLoaded', function (e) {
(function () {
// add role form validation
FormValidation.formValidation(document.getElementById('addRoleForm'), {
fields: {
modalRoleName: {
validators: {
notEmpty: {
message: 'Please enter role name'
}
}
}
},
plugins: {
trigger: new FormValidation.plugins.Trigger(),
bootstrap5: new FormValidation.plugins.Bootstrap5({
// Use this for enabling/changing valid/invalid class
// eleInvalidClass: '',
eleValidClass: '',
rowSelector: '.col-12'
}),
submitButton: new FormValidation.plugins.SubmitButton(),
// Submit the form when all fields are valid
// defaultSubmit: new FormValidation.plugins.DefaultSubmit(),
autoFocus: new FormValidation.plugins.AutoFocus()
}
});
// Select All checkbox click
const selectAll = document.querySelector('#selectAll'),
checkboxList = document.querySelectorAll('[type="checkbox"]');
selectAll.addEventListener('change', t => {
checkboxList.forEach(e => {
e.checked = t.target.checked;
});
});
})();
});

View File

@ -0,0 +1,79 @@
/**
* Edit credit card
*/
'use strict';
document.addEventListener('DOMContentLoaded', function (e) {
(function () {
const editCreditCardMaskEdit = document.querySelector('.credit-card-mask-edit'),
editExpiryDateMaskEdit = document.querySelector('.expiry-date-mask-edit'),
editCVVMaskEdit = document.querySelector('.cvv-code-mask-edit');
// Credit Card
if (editCreditCardMaskEdit) {
new Cleave(editCreditCardMaskEdit, {
creditCard: true,
onCreditCardTypeChanged: function (type) {
if (type != '' && type != 'unknown') {
document.querySelector('.card-type-edit').innerHTML =
'<img src="' + assetsPath + 'img/icons/payments/' + type + '-cc.png" height="28"/>';
} else {
document.querySelector('.card-type-edit').innerHTML = '';
}
}
});
}
// Expiry Date MaskEdit
if (editExpiryDateMaskEdit) {
new Cleave(editExpiryDateMaskEdit, {
date: true,
delimiter: '/',
datePattern: ['m', 'y']
});
}
// CVV MaskEdit
if (editCVVMaskEdit) {
new Cleave(editCVVMaskEdit, {
numeral: true,
numeralPositiveOnly: true
});
}
// Credit card form validation
FormValidation.formValidation(document.getElementById('editCCForm'), {
fields: {
modalEditCard: {
validators: {
notEmpty: {
message: 'Please enter your credit card number'
}
}
}
},
plugins: {
trigger: new FormValidation.plugins.Trigger(),
bootstrap5: new FormValidation.plugins.Bootstrap5({
// Use this for enabling/changing valid/invalid class
// eleInvalidClass: '',
eleValidClass: '',
rowSelector: '.col-12'
}),
submitButton: new FormValidation.plugins.SubmitButton(),
// Submit the form when all fields are valid
// defaultSubmit: new FormValidation.plugins.DefaultSubmit(),
autoFocus: new FormValidation.plugins.AutoFocus()
},
init: instance => {
instance.on('plugins.message.placed', function (e) {
//* Move the error message out of the `input-group` element
if (e.element.parentElement.classList.contains('input-group')) {
e.element.parentElement.insertAdjacentElement('afterend', e.messageElement);
}
});
}
});
})();
});

View File

@ -0,0 +1,35 @@
/**
* Edit Permission Modal JS
*/
'use strict';
// Edit permission form validation
document.addEventListener('DOMContentLoaded', function (e) {
(function () {
FormValidation.formValidation(document.getElementById('editPermissionForm'), {
fields: {
editPermissionName: {
validators: {
notEmpty: {
message: 'Please enter permission name'
}
}
}
},
plugins: {
trigger: new FormValidation.plugins.Trigger(),
bootstrap5: new FormValidation.plugins.Bootstrap5({
// Use this for enabling/changing valid/invalid class
// eleInvalidClass: '',
eleValidClass: '',
rowSelector: '.col-sm-9'
}),
submitButton: new FormValidation.plugins.SubmitButton(),
// Submit the form when all fields are valid
// defaultSubmit: new FormValidation.plugins.DefaultSubmit(),
autoFocus: new FormValidation.plugins.AutoFocus()
}
});
})();
});

View File

@ -0,0 +1,103 @@
/**
* Edit User
*/
'use strict';
// Select2 (jquery)
$(function () {
const select2 = $('.select2');
// Select2 Country
if (select2.length) {
select2.each(function () {
var $this = $(this);
$this.wrap('<div class="position-relative"></div>').select2({
placeholder: 'Select value',
dropdownParent: $this.parent()
});
});
}
});
document.addEventListener('DOMContentLoaded', function (e) {
(function () {
// variables
const modalEditUserTaxID = document.querySelector('.modal-edit-tax-id');
const modalEditUserPhone = document.querySelector('.phone-number-mask');
// Prefix
if (modalEditUserTaxID) {
new Cleave(modalEditUserTaxID, {
prefix: 'TIN',
blocks: [3, 3, 3, 4],
uppercase: true
});
}
// Phone Number Input Mask
if (modalEditUserPhone) {
new Cleave(modalEditUserPhone, {
phone: true,
phoneRegionCode: 'US'
});
}
// Edit user form validation
FormValidation.formValidation(document.getElementById('editUserForm'), {
fields: {
modalEditUserFirstName: {
validators: {
notEmpty: {
message: 'Please enter your first name'
},
regexp: {
regexp: /^[a-zA-Zs]+$/,
message: 'The first name can only consist of alphabetical'
}
}
},
modalEditUserLastName: {
validators: {
notEmpty: {
message: 'Please enter your last name'
},
regexp: {
regexp: /^[a-zA-Zs]+$/,
message: 'The last name can only consist of alphabetical'
}
}
},
modalEditUserName: {
validators: {
notEmpty: {
message: 'Please enter your username'
},
stringLength: {
min: 6,
max: 30,
message: 'The name must be more than 6 and less than 30 characters long'
},
regexp: {
regexp: /^[a-zA-Z0-9 ]+$/,
message: 'The name can only consist of alphabetical, number and space'
}
}
}
},
plugins: {
trigger: new FormValidation.plugins.Trigger(),
bootstrap5: new FormValidation.plugins.Bootstrap5({
// Use this for enabling/changing valid/invalid class
// eleInvalidClass: '',
eleValidClass: '',
rowSelector: '.col-12'
}),
submitButton: new FormValidation.plugins.SubmitButton(),
// Submit the form when all fields are valid
// defaultSubmit: new FormValidation.plugins.DefaultSubmit(),
autoFocus: new FormValidation.plugins.AutoFocus()
}
});
})();
});

View File

@ -0,0 +1,53 @@
/**
* Enable OTP
*/
'use strict';
document.addEventListener('DOMContentLoaded', function (e) {
(function () {
const phoneMask = document.querySelector('.phone-number-otp-mask');
// Phone Number Input Mask
if (phoneMask) {
new Cleave(phoneMask, {
phone: true,
phoneRegionCode: 'US'
});
}
// Enable OTP form validation
FormValidation.formValidation(document.getElementById('enableOTPForm'), {
fields: {
modalEnableOTPPhone: {
validators: {
notEmpty: {
message: 'Please enter your mobile number'
}
}
}
},
plugins: {
trigger: new FormValidation.plugins.Trigger(),
bootstrap5: new FormValidation.plugins.Bootstrap5({
// Use this for enabling/changing valid/invalid class
// eleInvalidClass: '',
eleValidClass: '',
rowSelector: '.col-12'
}),
submitButton: new FormValidation.plugins.SubmitButton(),
// Submit the form when all fields are valid
// defaultSubmit: new FormValidation.plugins.DefaultSubmit(),
autoFocus: new FormValidation.plugins.AutoFocus()
},
init: instance => {
instance.on('plugins.message.placed', function (e) {
//* Move the error message out of the `input-group` element
if (e.element.parentElement.classList.contains('input-group')) {
e.element.parentElement.insertAdjacentElement('afterend', e.messageElement);
}
});
}
});
})();
});

View File

@ -0,0 +1,189 @@
/**
* Account Settings - Account
*/
'use strict';
document.addEventListener('DOMContentLoaded', function (e) {
(function () {
const formAccSettings = document.querySelector('#formAccountSettings'),
deactivateAcc = document.querySelector('#formAccountDeactivation'),
deactivateButton = deactivateAcc.querySelector('.deactivate-account');
// Form validation for Add new record
if (formAccSettings) {
const fv = FormValidation.formValidation(formAccSettings, {
fields: {
firstName: {
validators: {
notEmpty: {
message: 'Please enter first name'
}
}
},
lastName: {
validators: {
notEmpty: {
message: 'Please enter last name'
}
}
}
},
plugins: {
trigger: new FormValidation.plugins.Trigger(),
bootstrap5: new FormValidation.plugins.Bootstrap5({
eleValidClass: '',
rowSelector: '.col-md-6'
}),
submitButton: new FormValidation.plugins.SubmitButton(),
// Submit the form when all fields are valid
// defaultSubmit: new FormValidation.plugins.DefaultSubmit(),
autoFocus: new FormValidation.plugins.AutoFocus()
},
init: instance => {
instance.on('plugins.message.placed', function (e) {
if (e.element.parentElement.classList.contains('input-group')) {
e.element.parentElement.insertAdjacentElement('afterend', e.messageElement);
}
});
}
});
}
if (deactivateAcc) {
const fv = FormValidation.formValidation(deactivateAcc, {
fields: {
accountActivation: {
validators: {
notEmpty: {
message: 'Please confirm you want to delete account'
}
}
}
},
plugins: {
trigger: new FormValidation.plugins.Trigger(),
bootstrap5: new FormValidation.plugins.Bootstrap5({
eleValidClass: ''
}),
submitButton: new FormValidation.plugins.SubmitButton(),
fieldStatus: new FormValidation.plugins.FieldStatus({
onStatusChanged: function (areFieldsValid) {
areFieldsValid
? // Enable the submit button
// so user has a chance to submit the form again
deactivateButton.removeAttribute('disabled')
: // Disable the submit button
deactivateButton.setAttribute('disabled', 'disabled');
}
}),
// Submit the form when all fields are valid
// defaultSubmit: new FormValidation.plugins.DefaultSubmit(),
autoFocus: new FormValidation.plugins.AutoFocus()
},
init: instance => {
instance.on('plugins.message.placed', function (e) {
if (e.element.parentElement.classList.contains('input-group')) {
e.element.parentElement.insertAdjacentElement('afterend', e.messageElement);
}
});
}
});
}
// Deactivate account alert
const accountActivation = document.querySelector('#accountActivation');
// Alert With Functional Confirm Button
if (deactivateButton) {
deactivateButton.onclick = function () {
if (accountActivation.checked == true) {
Swal.fire({
text: 'Are you sure you would like to deactivate your account?',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Yes',
customClass: {
confirmButton: 'btn btn-primary me-2 waves-effect waves-light',
cancelButton: 'btn btn-label-secondary waves-effect waves-light'
},
buttonsStyling: false
}).then(function (result) {
if (result.value) {
Swal.fire({
icon: 'success',
title: 'Deleted!',
text: 'Your file has been deleted.',
customClass: {
confirmButton: 'btn btn-success waves-effect waves-light'
}
});
} else if (result.dismiss === Swal.DismissReason.cancel) {
Swal.fire({
title: 'Cancelled',
text: 'Deactivation Cancelled!!',
icon: 'error',
customClass: {
confirmButton: 'btn btn-success waves-effect waves-light'
}
});
}
});
}
};
}
// CleaveJS validation
const phoneNumber = document.querySelector('#phoneNumber'),
zipCode = document.querySelector('#zipCode');
// Phone Mask
if (phoneNumber) {
new Cleave(phoneNumber, {
phone: true,
phoneRegionCode: 'US'
});
}
// Pincode
if (zipCode) {
new Cleave(zipCode, {
delimiter: '',
numeral: true
});
}
// Update/reset user image of account page
let accountUserImage = document.getElementById('uploadedAvatar');
const fileInput = document.querySelector('.account-file-input'),
resetFileInput = document.querySelector('.account-image-reset');
if (accountUserImage) {
const resetImage = accountUserImage.src;
fileInput.onchange = () => {
if (fileInput.files[0]) {
accountUserImage.src = window.URL.createObjectURL(fileInput.files[0]);
}
};
resetFileInput.onclick = () => {
fileInput.value = '';
accountUserImage.src = resetImage;
};
}
})();
});
// Select2 (jquery)
$(function () {
var select2 = $('.select2');
// For all Select2
if (select2.length) {
select2.each(function () {
var $this = $(this);
$this.wrap('<div class="position-relative"></div>');
$this.select2({
dropdownParent: $this.parent()
});
});
}
});

View File

@ -0,0 +1,194 @@
/**
* Account Settings - Billing & Plans
*/
'use strict';
document.addEventListener('DOMContentLoaded', function (e) {
(function () {
const creditCardMask = document.querySelector('.credit-card-mask'),
expiryDateMask = document.querySelector('.expiry-date-mask'),
CVVMask = document.querySelector('.cvv-code-mask');
// Credit Card
if (creditCardMask) {
new Cleave(creditCardMask, {
creditCard: true,
onCreditCardTypeChanged: function (type) {
if (type != '' && type != 'unknown') {
document.querySelector('.card-type').innerHTML =
'<img src="' + assetsPath + 'img/icons/payments/' + type + '-cc.png" height="28"/>';
} else {
document.querySelector('.card-type').innerHTML = '';
}
}
});
}
// Expiry Date Mask
if (expiryDateMask) {
new Cleave(expiryDateMask, {
date: true,
delimiter: '/',
datePattern: ['m', 'y']
});
}
// CVV Mask
if (CVVMask) {
new Cleave(CVVMask, {
numeral: true,
numeralPositiveOnly: true
});
}
const formAccSettings = document.getElementById('formAccountSettings'),
mobileNumber = document.querySelector('.mobile-number'),
zipCode = document.querySelector('.zip-code'),
creditCardForm = document.getElementById('creditCardForm');
// Form validation
if (formAccSettings) {
const fv = FormValidation.formValidation(formAccSettings, {
fields: {
companyName: {
validators: {
notEmpty: {
message: 'Please enter company name'
}
}
},
billingEmail: {
validators: {
notEmpty: {
message: 'Please enter billing email'
},
emailAddress: {
message: 'Please enter valid email address'
}
}
}
},
plugins: {
trigger: new FormValidation.plugins.Trigger(),
bootstrap5: new FormValidation.plugins.Bootstrap5({
eleValidClass: '',
rowSelector: '.col-sm-6'
}),
submitButton: new FormValidation.plugins.SubmitButton(),
// Submit the form when all fields are valid
// defaultSubmit: new FormValidation.plugins.DefaultSubmit(),
autoFocus: new FormValidation.plugins.AutoFocus()
}
});
}
// Credit card form validation
if (creditCardForm) {
FormValidation.formValidation(creditCardForm, {
fields: {
paymentCard: {
validators: {
notEmpty: {
message: 'Please enter your credit card number'
}
}
}
},
plugins: {
trigger: new FormValidation.plugins.Trigger(),
bootstrap5: new FormValidation.plugins.Bootstrap5({
// Use this for enabling/changing valid/invalid class
// eleInvalidClass: '',
eleValidClass: ''
}),
submitButton: new FormValidation.plugins.SubmitButton(),
// Submit the form when all fields are valid
// defaultSubmit: new FormValidation.plugins.DefaultSubmit(),
autoFocus: new FormValidation.plugins.AutoFocus()
},
init: instance => {
instance.on('plugins.message.placed', function (e) {
//* Move the error message out of the `input-group` element
if (e.element.parentElement.classList.contains('input-group')) {
e.element.parentElement.insertAdjacentElement('afterend', e.messageElement);
}
});
}
});
}
// Cancel Subscription alert
const cancelSubscription = document.querySelector('.cancel-subscription');
// Alert With Functional Confirm Button
if (cancelSubscription) {
cancelSubscription.onclick = function () {
Swal.fire({
text: 'Are you sure you would like to cancel your subscription?',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Yes',
customClass: {
confirmButton: 'btn btn-primary me-2 waves-effect waves-light',
cancelButton: 'btn btn-label-secondary waves-effect waves-light'
},
buttonsStyling: false
}).then(function (result) {
if (result.value) {
Swal.fire({
icon: 'success',
title: 'Unsubscribed!',
text: 'Your subscription cancelled successfully.',
customClass: {
confirmButton: 'btn btn-success waves-effect waves-light'
}
});
} else if (result.dismiss === Swal.DismissReason.cancel) {
Swal.fire({
title: 'Cancelled',
text: 'Unsubscription Cancelled!!',
icon: 'error',
customClass: {
confirmButton: 'btn btn-success waves-effect waves-light'
}
});
}
});
};
}
// CleaveJS validation
// Phone Mask
if (mobileNumber) {
new Cleave(mobileNumber, {
phone: true,
phoneRegionCode: 'US'
});
}
// Pincode
if (zipCode) {
new Cleave(zipCode, {
delimiter: '',
numeral: true
});
}
})();
});
// Select2 (jquery)
$(function () {
var select2 = $('.select2');
// Select2
if (select2.length) {
select2.each(function () {
var $this = $(this);
$this.wrap('<div class="position-relative"></div>');
$this.select2({
dropdownParent: $this.parent()
});
});
}
});

View File

@ -0,0 +1,125 @@
/**
* Account Settings - Security
*/
'use strict';
document.addEventListener('DOMContentLoaded', function (e) {
(function () {
const formChangePass = document.querySelector('#formAccountSettings'),
formApiKey = document.querySelector('#formAccountSettingsApiKey');
// Form validation for Change password
if (formChangePass) {
const fv = FormValidation.formValidation(formChangePass, {
fields: {
currentPassword: {
validators: {
notEmpty: {
message: 'Please current password'
},
stringLength: {
min: 8,
message: 'Password must be more than 8 characters'
}
}
},
newPassword: {
validators: {
notEmpty: {
message: 'Please enter new password'
},
stringLength: {
min: 8,
message: 'Password must be more than 8 characters'
}
}
},
confirmPassword: {
validators: {
notEmpty: {
message: 'Please confirm new password'
},
identical: {
compare: function () {
return formChangePass.querySelector('[name="newPassword"]').value;
},
message: 'The password and its confirm are not the same'
},
stringLength: {
min: 8,
message: 'Password must be more than 8 characters'
}
}
}
},
plugins: {
trigger: new FormValidation.plugins.Trigger(),
bootstrap5: new FormValidation.plugins.Bootstrap5({
eleValidClass: '',
rowSelector: '.col-md-6'
}),
submitButton: new FormValidation.plugins.SubmitButton(),
// Submit the form when all fields are valid
// defaultSubmit: new FormValidation.plugins.DefaultSubmit(),
autoFocus: new FormValidation.plugins.AutoFocus()
},
init: instance => {
instance.on('plugins.message.placed', function (e) {
if (e.element.parentElement.classList.contains('input-group')) {
e.element.parentElement.insertAdjacentElement('afterend', e.messageElement);
}
});
}
});
}
// Form validation for API key
if (formApiKey) {
const fvApi = FormValidation.formValidation(formApiKey, {
fields: {
apiKey: {
validators: {
notEmpty: {
message: 'Please enter API key name'
}
}
}
},
plugins: {
trigger: new FormValidation.plugins.Trigger(),
bootstrap5: new FormValidation.plugins.Bootstrap5({
eleValidClass: ''
}),
submitButton: new FormValidation.plugins.SubmitButton(),
// Submit the form when all fields are valid
// defaultSubmit: new FormValidation.plugins.DefaultSubmit(),
autoFocus: new FormValidation.plugins.AutoFocus()
},
init: instance => {
instance.on('plugins.message.placed', function (e) {
if (e.element.parentElement.classList.contains('input-group')) {
e.element.parentElement.insertAdjacentElement('afterend', e.messageElement);
}
});
}
});
}
})();
});
// Select2 (jquery)
$(function () {
var select2 = $('.select2');
// Select2 API Key
if (select2.length) {
select2.each(function () {
var $this = $(this);
$this.wrap('<div class="position-relative"></div>');
$this.select2({
dropdownParent: $this.parent()
});
});
}
});

View File

@ -0,0 +1,305 @@
/**
* Page auth register multi-steps
*/
'use strict';
// Select2 (jquery)
$(function () {
var select2 = $('.select2');
// select2
if (select2.length) {
select2.each(function () {
var $this = $(this);
$this.wrap('<div class="position-relative"></div>');
$this.select2({
placeholder: 'Select an country',
dropdownParent: $this.parent()
});
});
}
});
// Multi Steps Validation
// --------------------------------------------------------------------
document.addEventListener('DOMContentLoaded', function (e) {
(function () {
const stepsValidation = document.querySelector('#multiStepsValidation');
if (typeof stepsValidation !== undefined && stepsValidation !== null) {
// Multi Steps form
const stepsValidationForm = stepsValidation.querySelector('#multiStepsForm');
// Form steps
const stepsValidationFormStep1 = stepsValidationForm.querySelector('#accountDetailsValidation');
const stepsValidationFormStep2 = stepsValidationForm.querySelector('#personalInfoValidation');
const stepsValidationFormStep3 = stepsValidationForm.querySelector('#billingLinksValidation');
// Multi steps next prev button
const stepsValidationNext = [].slice.call(stepsValidationForm.querySelectorAll('.btn-next'));
const stepsValidationPrev = [].slice.call(stepsValidationForm.querySelectorAll('.btn-prev'));
const multiStepsExDate = document.querySelector('.multi-steps-exp-date'),
multiStepsCvv = document.querySelector('.multi-steps-cvv'),
multiStepsMobile = document.querySelector('.multi-steps-mobile'),
multiStepsPincode = document.querySelector('.multi-steps-pincode'),
multiStepsCard = document.querySelector('.multi-steps-card');
// Expiry Date Mask
if (multiStepsExDate) {
new Cleave(multiStepsExDate, {
date: true,
delimiter: '/',
datePattern: ['m', 'y']
});
}
// CVV
if (multiStepsCvv) {
new Cleave(multiStepsCvv, {
numeral: true,
numeralPositiveOnly: true
});
}
// Mobile
if (multiStepsMobile) {
new Cleave(multiStepsMobile, {
phone: true,
phoneRegionCode: 'US'
});
}
// Pincode
if (multiStepsPincode) {
new Cleave(multiStepsPincode, {
delimiter: '',
numeral: true
});
}
// Credit Card
if (multiStepsCard) {
new Cleave(multiStepsCard, {
creditCard: true,
onCreditCardTypeChanged: function (type) {
if (type != '' && type != 'unknown') {
document.querySelector('.card-type').innerHTML =
'<img src="' + assetsPath + 'img/icons/payments/' + type + '-cc.png" height="28"/>';
} else {
document.querySelector('.card-type').innerHTML = '';
}
}
});
}
let validationStepper = new Stepper(stepsValidation, {
linear: true
});
// Account details
const multiSteps1 = FormValidation.formValidation(stepsValidationFormStep1, {
fields: {
multiStepsUsername: {
validators: {
notEmpty: {
message: 'Please enter username'
},
stringLength: {
min: 6,
max: 30,
message: 'The name must be more than 6 and less than 30 characters long'
},
regexp: {
regexp: /^[a-zA-Z0-9 ]+$/,
message: 'The name can only consist of alphabetical, number and space'
}
}
},
multiStepsEmail: {
validators: {
notEmpty: {
message: 'Please enter email address'
},
emailAddress: {
message: 'The value is not a valid email address'
}
}
},
multiStepsPass: {
validators: {
notEmpty: {
message: 'Please enter password'
}
}
},
multiStepsConfirmPass: {
validators: {
notEmpty: {
message: 'Confirm Password is required'
},
identical: {
compare: function () {
return stepsValidationFormStep1.querySelector('[name="multiStepsPass"]').value;
},
message: 'The password and its confirm are not the same'
}
}
}
},
plugins: {
trigger: new FormValidation.plugins.Trigger(),
bootstrap5: new FormValidation.plugins.Bootstrap5({
// Use this for enabling/changing valid/invalid class
// eleInvalidClass: '',
eleValidClass: '',
rowSelector: '.col-sm-6'
}),
autoFocus: new FormValidation.plugins.AutoFocus(),
submitButton: new FormValidation.plugins.SubmitButton()
},
init: instance => {
instance.on('plugins.message.placed', function (e) {
if (e.element.parentElement.classList.contains('input-group')) {
e.element.parentElement.insertAdjacentElement('afterend', e.messageElement);
}
});
}
}).on('core.form.valid', function () {
// Jump to the next step when all fields in the current step are valid
validationStepper.next();
});
// Personal info
const multiSteps2 = FormValidation.formValidation(stepsValidationFormStep2, {
fields: {
multiStepsFirstName: {
validators: {
notEmpty: {
message: 'Please enter first name'
}
}
},
multiStepsAddress: {
validators: {
notEmpty: {
message: 'Please enter your address'
}
}
}
},
plugins: {
trigger: new FormValidation.plugins.Trigger(),
bootstrap5: new FormValidation.plugins.Bootstrap5({
// Use this for enabling/changing valid/invalid class
// eleInvalidClass: '',
eleValidClass: '',
rowSelector: function (field, ele) {
// field is the field name
// ele is the field element
switch (field) {
case 'multiStepsFirstName':
return '.col-sm-6';
case 'multiStepsAddress':
return '.col-md-12';
default:
return '.row';
}
}
}),
autoFocus: new FormValidation.plugins.AutoFocus(),
submitButton: new FormValidation.plugins.SubmitButton()
}
}).on('core.form.valid', function () {
// Jump to the next step when all fields in the current step are valid
validationStepper.next();
});
// Social links
const multiSteps3 = FormValidation.formValidation(stepsValidationFormStep3, {
fields: {
multiStepsCard: {
validators: {
notEmpty: {
message: 'Please enter card number'
}
}
}
},
plugins: {
trigger: new FormValidation.plugins.Trigger(),
bootstrap5: new FormValidation.plugins.Bootstrap5({
// Use this for enabling/changing valid/invalid class
// eleInvalidClass: '',
eleValidClass: '',
rowSelector: function (field, ele) {
// field is the field name
// ele is the field element
switch (field) {
case 'multiStepsCard':
return '.col-md-12';
default:
return '.col-dm-6';
}
}
}),
autoFocus: new FormValidation.plugins.AutoFocus(),
submitButton: new FormValidation.plugins.SubmitButton()
},
init: instance => {
instance.on('plugins.message.placed', function (e) {
if (e.element.parentElement.classList.contains('input-group')) {
e.element.parentElement.insertAdjacentElement('afterend', e.messageElement);
}
});
}
}).on('core.form.valid', function () {
// You can submit the form
// stepsValidationForm.submit()
// or send the form data to server via an Ajax request
// To make the demo simple, I just placed an alert
alert('Submitted..!!');
});
stepsValidationNext.forEach(item => {
item.addEventListener('click', event => {
// When click the Next button, we will validate the current step
switch (validationStepper._currentIndex) {
case 0:
multiSteps1.validate();
break;
case 1:
multiSteps2.validate();
break;
case 2:
multiSteps3.validate();
break;
default:
break;
}
});
});
stepsValidationPrev.forEach(item => {
item.addEventListener('click', event => {
switch (validationStepper._currentIndex) {
case 2:
validationStepper.previous();
break;
case 1:
validationStepper.previous();
break;
case 0:
default:
break;
}
});
});
}
})();
});

View File

@ -0,0 +1,83 @@
/**
* Page auth two steps
*/
'use strict';
document.addEventListener('DOMContentLoaded', function (e) {
(function () {
let maskWrapper = document.querySelector('.numeral-mask-wrapper');
for (let pin of maskWrapper.children) {
pin.onkeyup = function (e) {
// Check if the key pressed is a number (0-9)
if (/^\d$/.test(e.key)) {
// While entering value, go to next
if (pin.nextElementSibling) {
if (this.value.length === parseInt(this.attributes['maxlength'].value)) {
pin.nextElementSibling.focus();
}
}
} else if (e.key === 'Backspace') {
// While deleting entered value, go to previous
if (pin.previousElementSibling) {
pin.previousElementSibling.focus();
}
}
};
// Prevent the default behavior for the minus key
pin.onkeypress = function (e) {
if (e.key === '-') {
e.preventDefault();
}
};
}
const twoStepsForm = document.querySelector('#twoStepsForm');
// Form validation for Add new record
if (twoStepsForm) {
const fv = FormValidation.formValidation(twoStepsForm, {
fields: {
otp: {
validators: {
notEmpty: {
message: 'Please enter otp'
}
}
}
},
plugins: {
trigger: new FormValidation.plugins.Trigger(),
bootstrap5: new FormValidation.plugins.Bootstrap5({
eleValidClass: '',
rowSelector: '.mb-6'
}),
submitButton: new FormValidation.plugins.SubmitButton(),
defaultSubmit: new FormValidation.plugins.DefaultSubmit(),
autoFocus: new FormValidation.plugins.AutoFocus()
}
});
const numeralMaskList = twoStepsForm.querySelectorAll('.numeral-mask');
const keyupHandler = function () {
let otpFlag = true,
otpVal = '';
numeralMaskList.forEach(numeralMaskEl => {
if (numeralMaskEl.value === '') {
otpFlag = false;
twoStepsForm.querySelector('[name="otp"]').value = '';
}
otpVal = otpVal + numeralMaskEl.value;
});
if (otpFlag) {
twoStepsForm.querySelector('[name="otp"]').value = otpVal;
}
};
numeralMaskList.forEach(numeralMaskEle => {
numeralMaskEle.addEventListener('keyup', keyupHandler);
});
}
})();
});

View File

@ -0,0 +1,112 @@
('use strict');
const formAuthentication = document.querySelector('#formAuthentication');
document.addEventListener('DOMContentLoaded', function (e) {
(function () {
// Form validation for Add new record
if (formAuthentication) {
const fv = FormValidation.formValidation(formAuthentication, {
fields: {
username: {
validators: {
notEmpty: {
message: 'Por favor, introduzca su nombre de usuario'
},
stringLength: {
min: 6,
message: 'El nombre de usuario debe tener más de 6 caracteres'
}
}
},
email: {
validators: {
notEmpty: {
message: 'Por favor, introduzca su correo electrónico'
},
emailAddress: {
message: 'Por favor, introduzca una dirección de correo electrónico válida'
}
}
},
'email-username': {
validators: {
notEmpty: {
message: 'Por favor, introduzca su correo electrónico / nombre de usuario'
},
stringLength: {
min: 6,
message: 'El nombre de usuario debe tener más de 6 caracteres'
}
}
},
password: {
validators: {
notEmpty: {
message: 'Por favor, introduzca su contraseña'
},
stringLength: {
min: 6,
message: 'La contraseña debe tener más de 6 caracteres'
}
}
},
'confirm-password': {
validators: {
notEmpty: {
message: 'Confirme la contraseña'
},
identical: {
compare: function () {
return formAuthentication.querySelector('[name="password"]').value;
},
message: 'La contraseña y su confirmación no son iguales'
},
stringLength: {
min: 6,
message: 'La contraseña debe tener más de 6 caracteres'
}
}
},
terms: {
validators: {
notEmpty: {
message: 'Acepte los términos y condiciones'
}
}
}
},
plugins: {
trigger: new FormValidation.plugins.Trigger(),
bootstrap5: new FormValidation.plugins.Bootstrap5({
eleValidClass: '',
rowSelector: '.fv-row'
}),
submitButton: new FormValidation.plugins.SubmitButton(),
defaultSubmit: new FormValidation.plugins.DefaultSubmit(),
autoFocus: new FormValidation.plugins.AutoFocus()
},
init: instance => {
instance.on('plugins.message.placed', function (e) {
if (e.element.parentElement.classList.contains('input-group')) {
e.element.parentElement.insertAdjacentElement('afterend', e.messageElement);
}
});
}
});
}
// Two Steps Verification
const numeralMask = document.querySelectorAll('.numeral-mask');
// Verification masking
if (numeralMask.length) {
numeralMask.forEach(e => {
new Cleave(e, {
numeral: true
});
});
}
})();
});

View File

@ -0,0 +1,4 @@
import axios from 'axios';
window.axios = axios;
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';

View File

@ -0,0 +1,102 @@
import LivewireNotification from '../_class/LivewireNotification';
import FormCustomListener from '../_class/FormCustomListener';
// Inicializar notificaciones globales
const notification = new LivewireNotification();
// Inicializar listener para estadísticas de cache
new FormCustomListener({
buttonSelectors: ['.btn-clear-cache', '.btn-reload-cache-stats']
});
// Inicializar listener para funciones de cache
new FormCustomListener({
formSelector: '#cache-functions-card',
buttonSelectors: ['.btn', '.btn-config-cache', '.btn-cache-routes'],
callbacks: [
null, // Callback por defecto para .btn
(form, button) => {
// Emitir notificación de carga
notification.emitNotification({
target: '#cache-functions-card .notification-container',
message: 'Generando cache de configuraciones de Laravel...',
type: 'warning'
});
// Generar cache de configuraciones mediante una petición AJAX
fetch('/admin/cache/config/cache', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
},
body: JSON.stringify({})
})
.then(response => {
if (!response.ok) {
throw new Error('Error al generar el cache de configuraciones');
}
return response.json();
})
.then(() => {
// Emitir notificación de éxito con recarga diferida
notification.emitNotification({
target: '#cache-functions-card .notification-container',
message: 'Se ha cacheado la configuración de Laravel...',
type: 'success',
deferReload: true
});
})
.catch(error => {
// Emitir notificación de error
notification.emitNotification({
target: '#cache-functions-card .notification-container',
message: `Error: ${error.message}`,
type: 'danger'
});
console.error('Error al generar el cache:', error);
});
},
(form, button) => {
// Emitir notificación de carga
notification.emitNotification({
target: '#cache-functions-card .notification-container',
message: 'Generando cache de rutas de Laravel...',
type: 'warning'
});
// Recargar estadísticas de cache mediante una petición AJAX
fetch('/admin/cache/route/cache', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
}
})
.then(response => {
if (!response.ok) {
throw new Error('Error al recargar las estadísticas de cache');
}
return response.json();
})
.then(() => {
// Emitir notificación de éxito con recarga diferida
notification.emitNotification({
target: '#cache-functions-card .notification-container',
message: 'Se han cacheado las rutas de Laravel...',
type: 'success',
deferReload: true
});
})
.catch(error => {
// Emitir notificación de error
notification.emitNotification({
target: '#cache-functions-card .notification-container',
message: `Error: ${error.message}`,
type: 'danger'
});
console.error('Error al recargar las estadísticas:', error);
});
}
]
});

View File

@ -0,0 +1,197 @@
/**
* App user list (jquery)
*/
'use strict';
$(function () {
var dataTablePermissions = $('.datatables-permissions'),
dt_permission,
userList = baseUrl + 'app/user/list';
// Users List datatable
if (dataTablePermissions.length) {
dt_permission = dataTablePermissions.DataTable({
ajax: window.location.href,
columns: [
// columns according to JSON
{ data: '' },
{ data: 'id' },
{ data: 'name' },
{ data: 'assigned_to' },
{ data: 'created_date' },
{ data: '' }
],
columnDefs: [
{
// For Responsive
className: 'control',
orderable: false,
searchable: false,
responsivePriority: 2,
targets: 0,
render: function (data, type, full, meta) {
return '';
}
},
{
targets: 1,
searchable: false,
visible: false
},
{
// Name
targets: 2,
render: function (data, type, full, meta) {
var $name = full['name'];
return '<span class="text-nowrap text-heading">' + $name + '</span>';
}
},
{
// User Role
targets: 3,
orderable: false,
render: function (data, type, full, meta) {
var $assignedTo = full['assigned_to'],
$output = '';
var roleBadgeObj = {
Admin:
'<a href="' +
userList +
'"><span class="badge me-4 bg-label-primary">Administrator</span></a>',
Manager:
'<a href="' +
userList +
'"><span class="badge me-4 bg-label-warning">Manager</span></a>',
Users:
'<a href="' + userList + '"><span class="badge me-4 bg-label-success">Users</span></a>',
Support:
'<a href="' + userList + '"><span class="badge me-4 bg-label-info">Support</span></a>',
Restricted:
'<a href="' +
userList +
'"><span class="badge me-4 bg-label-danger">Restricted User</span></a>'
};
for (var i = 0; i < $assignedTo.length; i++) {
var val = $assignedTo[i];
$output += roleBadgeObj[val];
}
return '<span class="text-nowrap">' + $output + '</span>';
}
},
{
// remove ordering from Name
targets: 4,
orderable: false,
render: function (data, type, full, meta) {
var $date = full['created_date'];
return '<span class="text-nowrap">' + $date + '</span>';
}
},
{
// Actions
targets: -1,
searchable: false,
title: 'Actions',
orderable: false,
render: function (data, type, full, meta) {
return (
'<div class="d-flex align-items-center">' +
'<span class="text-nowrap"><button class="btn btn-icon btn-text-secondary waves-effect waves-light rounded-pill me-1" data-bs-target="#editPermissionModal" data-bs-toggle="modal" data-bs-dismiss="modal"><i class="ti ti-edit ti-md"></i></button>' +
'<a href="javascript:;" class="btn btn-icon btn-text-secondary waves-effect waves-light rounded-pill dropdown-toggle hide-arrow" data-bs-toggle="dropdown"><i class="ti ti-dots-vertical ti-md mx-1"></i></a>' +
'<div class="dropdown-menu dropdown-menu-end m-0">' +
'<a href="javascript:;"" class="dropdown-item">Edit</a>' +
'<a href="javascript:;" class="dropdown-item">Suspend</a>' +
'</div>' +
'</div>'
);
}
}
],
order: [[1, 'asc']],
dom:
'<"row mx-1"' +
'<"col-sm-12 col-md-3" l>' +
'<"col-sm-12 col-md-9"<"dt-action-buttons text-xl-end text-lg-start text-md-end text-start d-flex align-items-center justify-content-md-end justify-content-center flex-wrap"<"me-4 mt-n6 mt-md-0"f>B>>' +
'>t' +
'<"row"' +
'<"col-sm-12 col-md-6"i>' +
'<"col-sm-12 col-md-6"p>' +
'>',
language: $.fn.dataTable.ext.datatable_spanish_default,
// Buttons with Dropdown
buttons: [],
// For responsive popup
responsive: {
details: {
display: $.fn.dataTable.Responsive.display.modal({
header: function (row) {
var data = row.data();
return 'Details of ' + data['name'];
}
}),
type: 'column',
renderer: function (api, rowIdx, columns) {
var data = $.map(columns, function (col, i) {
return col.title !== '' // ? Do not show row in modal popup if title is blank (for check box)
? '<tr data-dt-row="' +
col.rowIndex +
'" data-dt-column="' +
col.columnIndex +
'">' +
'<td>' +
col.title +
':' +
'</td> ' +
'<td>' +
col.data +
'</td>' +
'</tr>'
: '';
}).join('');
return data ? $('<table class="table"/><tbody />').append(data) : false;
}
}
},
initComplete: function () {
// Adding role filter once table initialized
this.api()
.columns(3)
.every(function () {
var column = this;
var select = $(
'<select id="UserRole" class="form-select text-capitalize"><option value=""> Select Role </option></select>'
)
.appendTo('.user_role')
.on('change', function () {
var val = $.fn.dataTable.util.escapeRegex($(this).val());
column.search(val ? '^' + val + '$' : '', true, false).draw();
});
column
.data()
.unique()
.sort()
.each(function (d, j) {
select.append('<option value="' + d + '" class="text-capitalize">' + d + '</option>');
});
});
}
});
}
// Delete Record
$('.datatables-permissions tbody').on('click', '.delete-record', function () {
dt_permission.row($(this).parents('tr')).remove().draw();
});
// Filter form control to default size
// ? setTimeout used for multilingual table initialization
setTimeout(() => {
$('.dataTables_filter .form-control').removeClass('form-control-sm');
$('.dataTables_length .form-select').removeClass('form-select-sm');
$('.dataTables_info').addClass('ms-n1');
$('.dataTables_paginate').addClass('me-n1');
}, 300);
});

View File

@ -0,0 +1,10 @@
import LivewireNotification from '../_class/LivewireNotification';
import WebsiteLegalSettingsForm from '../_class/WebsiteLegalSettingsForm';
new LivewireNotification();
window.WebsiteLegalSettingsForm = new WebsiteLegalSettingsForm();
Livewire.hook('morphed', () => {
window.WebsiteLegalSettingsForm.reload();
});

View File

@ -0,0 +1,10 @@
import LivewireNotification from '../_class/LivewireNotification';
import WebsiteLegalSettingsForm from '../_class/WebsiteLegalSettingsForm';
new LivewireNotification();
window.WebsiteLegalSettingsForm = new WebsiteLegalSettingsForm();
Livewire.hook('morphed', () => {
window.WebsiteLegalSettingsForm.reload();
});

View File

@ -0,0 +1,590 @@
import LivewireNotification from '../_class/LivewireNotification';
import FormCustomListener from '../_class/FormCustomListener';
new LivewireNotification();
// Inicializar formularios de ajustes de sitio web
window.WebsiteSettingsForm = new FormCustomListener({
formSelector: '#website-settings-card',
buttonSelectors: ['.btn-save', '.btn-cancel'],
callbacks: [
() => {} // Deshabilitar callback para #save_website_button
],
dispatchOnSubmit: 'saveWebsiteSettings',
validationConfig: {
fields: {
website_title: {
validators: {
stringLength: {
min: 2,
max: 50,
message: 'El título debe tener entre 2 y 50 caracteres.'
}
}
},
website_description: {
validators: {
stringLength: {
max: 160,
message: 'La descripción no puede exceder los 160 caracteres.'
}
}
}
},
plugins: {
trigger: new FormValidation.plugins.Trigger(),
bootstrap5: new FormValidation.plugins.Bootstrap5({
eleValidClass: '',
rowSelector: '.fv-row'
}),
submitButton: new FormValidation.plugins.SubmitButton(),
autoFocus: new FormValidation.plugins.AutoFocus()
}
}
});
// Inicializar formularios de ajustes de favicon
new FormCustomListener({
formSelector: '#website-favicon-settings-card',
buttonSelectors: ['.btn']
});
// Inicializar formularios de ajustes de logo de imagen
new FormCustomListener({
formSelector: '#website-image-logo-settings-card',
buttonSelectors: ['.btn']
});
// Inicializar formularios de ajustes de social media
window.SocialSettingsForm = new FormCustomListener({
formSelector: '#website-social-settings-card',
buttonSelectors: ['.btn-save', '.btn-cancel'],
callbacks: [() => {}],
dispatchOnSubmit: 'saveSocialSettings',
validationConfig: {
fields: {
social_whatsapp: {
validators: {
callback: {
message: 'Por favor, introduce un número de teléfono válido para México.',
callback: function (input) {
// Si el campo está vacío, no hacemos validación
if (input.value.trim() === '') {
return true; // Permitir vacío
}
// Si no está vacío, validamos el formato del número
const cleanValue = input.value.replace(/\D/g, '');
const regex = /^[1-9]\d{9}$/; // Exactamente 10 dígitos
return regex.test(cleanValue); // Valida solo si hay un número
}
}
}
},
social_whatsapp_message: {
validators: {
stringLength: {
max: 500,
message: 'El mensaje no puede exceder los 500 caracteres.'
},
callback: {
message: 'El mensaje es obligatorio.',
callback: function (input) {
// Obtener el valor de 'social_whatsapp'
const whatsappNumber = document.querySelector('#social_whatsapp').value.trim();
// Si 'social_whatsapp' tiene un valor, entonces el mensaje es obligatorio
if (whatsappNumber !== '') {
return input.value.trim() !== ''; // El mensaje no puede estar vacío
}
return true; // Si 'social_whatsapp' está vacío, no validamos 'social_whatsapp_message'
}
}
}
},
social_facebook: {
validators: {
uri: {
message: 'Por favor, introduce una URL válida.'
}
}
},
social_instagram: {
validators: {
uri: {
message: 'Por favor, introduce una URL válida.'
}
}
},
social_linkedin: {
validators: {
uri: {
message: 'Por favor, introduce una URL válida.'
}
}
},
social_tiktok: {
validators: {
uri: {
message: 'Por favor, introduce una URL válida.'
}
}
},
social_x_twitter: {
validators: {
uri: {
message: 'Por favor, introduce una URL válida.'
}
}
},
social_google: {
validators: {
uri: {
message: 'Por favor, introduce una URL válida.'
}
}
},
social_pinterest: {
validators: {
uri: {
message: 'Por favor, introduce una URL válida.'
}
}
},
social_youtube: {
validators: {
uri: {
message: 'Por favor, introduce una URL válida.'
}
}
},
social_vimeo: {
validators: {
uri: {
message: 'Por favor, introduce una URL válida.'
}
}
}
},
plugins: {
trigger: new FormValidation.plugins.Trigger(),
bootstrap5: new FormValidation.plugins.Bootstrap5({
eleValidClass: '',
rowSelector: '.fv-row'
}),
submitButton: new FormValidation.plugins.SubmitButton(),
autoFocus: new FormValidation.plugins.AutoFocus()
}
}
});
// Inicializar formularios de ajustes de Formularios de contacto
window.ContactFormSettingsForm = new FormCustomListener({
formSelector: '#website-contact-form-settings-card',
buttonSelectors: ['.btn-save', '.btn-cancel'],
callbacks: [() => {}],
dispatchOnSubmit: 'saveContactFormSettings',
validationConfig: {
fields: {
// Validación para correo electrónico de recepción
contact_form_email: {
validators: {
emailAddress: {
message: 'Por favor, introduce un correo electrónico válido.'
},
notEmpty: {
message: 'El correo electrónico es obligatorio.'
}
}
},
// Validación para correo electrónico con copia
contact_form_email_cc: {
validators: {
emailAddress: {
message: 'Por favor, introduce un correo electrónico válido.'
},
// Validación personalizada para comparar ambos correos electrónicos
callback: {
message: 'Los correos electrónicos deben ser diferentes.',
callback: function (input) {
const email = document.querySelector('#contact_form_email').value.trim();
const emailCC = input.value.trim();
// Si ambos correos son iguales, la validación falla
if (email === emailCC) {
return false; // Los correos son iguales, por lo que la validación falla
}
return true; // Si son diferentes, la validación pasa
}
}
}
},
// Validación para el asunto del formulario de contacto
contact_form_subject: {
validators: {
stringLength: {
max: 60,
message: 'El título del correo no puede exceder los 60 caracteres.'
},
notEmpty: {
message: 'El título del correo es obligatorio.'
}
}
}
},
plugins: {
trigger: new FormValidation.plugins.Trigger(),
bootstrap5: new FormValidation.plugins.Bootstrap5({
eleValidClass: '',
rowSelector: '.fv-row'
}),
submitButton: new FormValidation.plugins.SubmitButton(),
autoFocus: new FormValidation.plugins.AutoFocus()
}
}
});
// Inicializar formularios de ajustes de información de contacto
window.ContactInfoSettingsForm = new FormCustomListener({
formSelector: '#website-contact-info-settings-card',
buttonSelectors: ['.btn-save', '.btn-cancel'],
callbacks: [() => {}],
dispatchOnSubmit: 'saveContactInfoSettings',
validationConfig: {
fields: {
// Validación para número telefónico
contact_phone_number: {
validators: {
callback: {
message: 'Por favor, introduce un número de teléfono válido para México.',
callback: function (input) {
// Si el campo está vacío, no hacemos validación
if (input.value.trim() === '') {
return true; // Permitir vacío
}
// Si no está vacío, validamos el formato del número
const cleanValue = input.value.replace(/\D/g, '');
const regex = /^[1-9]\d{9}$/; // Exactamente 10 dígitos
return regex.test(cleanValue); // Valida solo si hay un número
}
}
}
},
// Validación para extensión telefónica (opcional, pero solo si contact_phone_number tiene valor)
contact_phone_number_ext: {
validators: {
stringLength: {
max: 10,
message: 'La extensión no debe exceder los 10 caracteres.'
},
callback: {
message: 'La extensión requiere de ingresar un número telefónico.',
callback: function (input) {
// Obtener el valor de 'contact_phone_number'
const phoneNumber = document.querySelector('#contact_phone_number')?.value.trim();
// Si el número telefónico tiene valor, entonces la extensión es obligatoria
if (phoneNumber !== '') {
// Si la extensión está vacía, la validación falla
return true; // Permitir vacío
}
// Si no se ha ingresado un número telefónico, la extensión no debe tener valor
return input.value.trim() === '';
}
}
}
},
// Validación para correo electrónico de contacto (opcional)
contact_email: {
validators: {
emailAddress: {
message: 'Por favor, introduce un correo electrónico válido.'
}
}
}
},
plugins: {
trigger: new FormValidation.plugins.Trigger(),
bootstrap5: new FormValidation.plugins.Bootstrap5({
eleValidClass: '',
rowSelector: '.fv-row'
}),
submitButton: new FormValidation.plugins.SubmitButton(),
autoFocus: new FormValidation.plugins.AutoFocus()
}
}
});
// Inicializar formularios de ajustes de ubicación
window.LocationSettingsForm = new FormCustomListener({
formSelector: '#website-location-settings-card',
buttonSelectors: ['.btn-save', '.btn-cancel'],
callbacks: [() => {}],
dispatchOnSubmit: 'saveLocationSettings',
validationConfig: {
fields: {
// Validación para dirección (No obligatorio, máximo 160 caracteres)
contact_direccion: {
validators: {
stringLength: {
max: 160,
message: 'La dirección no puede exceder los 160 caracteres.'
}
}
},
// Validación para horario (No obligatorio, máximo 160 caracteres)
contact_horario: {
validators: {
stringLength: {
max: 160,
message: 'El horario no puede exceder los 160 caracteres.'
}
}
},
// Validación para latitud (No obligatorio, pero debe ser un número si se ingresa)
contact_location_lat: {
validators: {
numeric: {
message: 'La latitud debe ser un número.'
},
callback: {
message: 'La latitud es obligatoria si se ingresa longitud.',
callback: function (input) {
// Obtener el valor de longitud
const longitude = document.querySelector('#contact_location_lng')?.value.trim();
// Si longitud tiene un valor, entonces latitud es obligatorio
if (longitude !== '') {
return input.value.trim() !== ''; // La latitud no puede estar vacía
}
return true; // Si longitud está vacío, no se valida latitud
}
}
}
},
// Validación para longitud (No obligatorio, pero debe ser un número si se ingresa)
contact_location_lng: {
validators: {
numeric: {
message: 'La longitud debe ser un número.'
},
callback: {
message: 'La longitud es obligatoria si se ingresa latitud.',
callback: function (input) {
// Obtener el valor de latitud
const latitude = document.querySelector('#contact_location_lat')?.value.trim();
// Si latitud tiene un valor, entonces longitud es obligatorio
if (latitude !== '') {
return input.value.trim() !== ''; // La longitud no puede estar vacía
}
return true; // Si latitud está vacío, no se valida longitud
}
}
}
}
},
plugins: {
trigger: new FormValidation.plugins.Trigger(),
bootstrap5: new FormValidation.plugins.Bootstrap5({
eleValidClass: '',
rowSelector: '.fv-row'
}),
submitButton: new FormValidation.plugins.SubmitButton(),
autoFocus: new FormValidation.plugins.AutoFocus()
}
}
});
// Inicializar formularios de ajustes de chat
window.ChatSettingsForm = new FormCustomListener({
formSelector: '#website-chat-settings-card',
buttonSelectors: ['.btn-save', '.btn-cancel'],
callbacks: [() => {}],
dispatchOnSubmit: 'saveChatSettings',
validationConfig: {
fields: {
chat_whatsapp_number: {
validators: {
callback: {
message: 'Por favor, introduce un número de teléfono válido para México.',
callback: function (input) {
// Obtener el proveedor directamente dentro de la validación
const provider = document.querySelector('#chat_provider')?.value;
// Validar solo si el proveedor es WhatsApp
if (provider !== 'whatsapp') return true;
const cleanValue = input.value.replace(/\D/g, '');
const regex = /^[1-9]\d{9}$/; // Exactamente 10 dígitos
return regex.test(cleanValue);
}
},
notEmpty: {
message: 'El número de teléfono es obligatorio.',
enabled: () => {
// Obtener el proveedor directamente dentro de la validación
const provider = document.querySelector('#chat_provider')?.value;
return provider === 'whatsapp'; // Habilita solo si es WhatsApp
}
}
}
},
chat_whatsapp_message: {
validators: {
stringLength: {
max: 500,
message: 'El mensaje no puede exceder los 500 caracteres.'
},
notEmpty: {
message: 'El mensaje es obligatorio.',
enabled: () => {
// Obtener el proveedor directamente dentro de la validación
const provider = document.querySelector('#chat_provider')?.value;
return provider === 'whatsapp'; // Habilita solo si es WhatsApp
}
}
}
}
},
plugins: {
trigger: new FormValidation.plugins.Trigger(),
bootstrap5: new FormValidation.plugins.Bootstrap5({
eleValidClass: '',
rowSelector: '.fv-row'
}),
submitButton: new FormValidation.plugins.SubmitButton(),
autoFocus: new FormValidation.plugins.AutoFocus()
}
}
});
// Inicializar formularios de ajustes de análisis de datos
window.AnalyticsSettingsForm = new FormCustomListener({
formSelector: '#website-analytics-settings-card',
buttonSelectors: ['.btn-save', '.btn-cancel'],
callbacks: [() => {}],
dispatchOnSubmit: 'saveAnalyticsSettings',
validationConfig: {
fields: {
google_analytics_id: {
validators: {
callback: {
message: 'ID de medición de Google Analytics no tienen un formato válido.',
callback: function (input) {
if (document.getElementById('google_analytics_enabled').checked) {
return input.value.trim() !== '';
}
return true;
}
}
}
}
},
plugins: {
trigger: new FormValidation.plugins.Trigger(),
bootstrap5: new FormValidation.plugins.Bootstrap5({
eleValidClass: '',
rowSelector: '.fv-row'
}),
submitButton: new FormValidation.plugins.SubmitButton(),
autoFocus: new FormValidation.plugins.AutoFocus()
}
}
});
// Inicializar formularios de ajustes de plantilla de sitio web
window.TemplateSettingsForm = new FormCustomListener({
formSelector: '#website-template-settings-card',
buttonSelectors: ['.btn-save', '.btn-cancel'],
callbacks: [() => {}],
dispatchOnSubmit: 'saveTemplateSettings',
validationConfig: {
fields: {
website_tpl_footer_text: {
validators: {
stringLength: {
max: 50,
message: 'El mensaje no puede exceder los 50 caracteres.'
}
}
}
},
plugins: {
trigger: new FormValidation.plugins.Trigger(),
bootstrap5: new FormValidation.plugins.Bootstrap5({
eleValidClass: '',
rowSelector: '.fv-row'
}),
submitButton: new FormValidation.plugins.SubmitButton(),
autoFocus: new FormValidation.plugins.AutoFocus()
}
}
});
// Recargar validación de formularios al morphear componentes
Livewire.hook('morphed', ({ component }) => {
switch (component.name) {
case 'website-settings':
if (window.WebsiteSettingsForm) {
window.WebsiteSettingsForm.reloadValidation();
}
break;
case 'website-social-settings':
if (window.SocialSettingsForm) {
window.SocialSettingsForm.reloadValidation();
}
break;
case 'website-contact-info-settings':
if (window.ContactInfoSettingsForm) {
window.ContactInfoSettingsForm.reloadValidation();
}
break;
case 'website-contact-form-settings':
if (window.ContactFormSettingsForm) {
window.ContactFormSettingsForm.reloadValidation();
}
break;
case 'website-location-settings':
if (window.LocationSettingsForm) {
window.LocationSettingsForm.reloadValidation();
}
break;
case 'website-chat-settings':
if (window.ChatSettingsForm) {
window.ChatSettingsForm.reloadValidation();
}
break;
case 'website-analytics-settings':
if (window.AnalyticsSettingsForm) {
window.AnalyticsSettingsForm.reloadValidation();
}
break;
case 'website-template-settings':
if (window.TemplateSettingsForm) {
window.TemplateSettingsForm.reloadValidation();
}
break;
default:
break;
}
});