Mojolicious-Plugin-Fondation-Group-UI-Bootstrap

 view release on metacpan or  search on metacpan

share/public/js/DatatableGroup.js  view on Meta::CPAN

var tableau;

$(function() {
    if (!$('#panel-group').length) return;
    initialiseTabGroup();
    $('#menuAddGroup').on('click', function(event) {
        addGroup();
    });

    // ────────────────────────────────────────────────
    // Form validation using validators.js
    // ────────────────────────────────────────────────
    async function validateGroupForm() {
        const form = document.getElementById('group-form');
        if (!form) {
            console.error("Form #group-form not found");
            return { valid: false, errors: [{ field: null, message: l("Form not found") }], data: {} };
        }

        const formDataObj = Object.fromEntries(new FormData(form).entries());

        // Cleanup (trim all strings)
        Object.keys(formDataObj).forEach(key => {
            if (typeof formDataObj[key] === 'string') {
                formDataObj[key] = formDataObj[key].trim();
            }
        });

        // Validate via FondationValidators
        if (typeof window.FondationValidators === 'undefined' || typeof FondationValidators.validate !== 'function') {
            console.error("FondationValidators not loaded or invalid");
            return { valid: false, errors: [{ field: null, message: l("Validator not available") }], data: {} };
        }

        const schemaName = 'Group';

        const result = FondationValidators.validate(schemaName, formDataObj);

        // Collect perm assignments (provided by Fondation::Perm::UI::Bootstrap)
        if (typeof collectPermAssignments === 'function') {
            formDataObj.perms = collectPermAssignments();
        }

        return {
            valid: result.valid,
            errors: result.errors.map(msg => ({
                field: null,
                message: msg
            })),
            data: formDataObj
        };
    }

    // Display errors (Bootstrap 5 style)
    function displayValidationErrors(errors) {
        $('.is-invalid').removeClass('is-invalid');
        $('.invalid-feedback').remove();

        errors.forEach(err => {
            let input = err.field ? document.getElementById(err.field) : null;

            if (!input) {
                const alert = $(`<div class="alert alert-danger mt-3">${err.message}</div>`);
                $('#group-form').prepend(alert);
                setTimeout(() => alert.fadeOut(3000), 8000);
                return;
            }

            input.classList.add('is-invalid');
            const feedback = document.createElement('div');
            feedback.className = 'invalid-feedback';
            feedback.textContent = err.message;
            input.parentNode.appendChild(feedback);
        });
    }

    // ────────────────────────────────────────────────
    // "Save" button
    // ────────────────────────────────────────────────
    $('#saveObject').on('click', async function() {
        const button = $(this);
        button.prop('disabled', true).html('<i class="fa fa-spinner fa-spin"></i> ' + l("Saving..."));

        try {
            const validation = await validateGroupForm();
            if (!validation.valid) {
                displayValidationErrors(validation.errors);
                return;
            }

            const id = validation.data.id;
            const method = id ? 'PUT' : 'POST';
            const url = id ? `/api/group/${id}` : '/api/group';

            // id is in the URL path, not the request body
            delete validation.data.id;

            const response = await fetch(url, {
                method,
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(validation.data)
            });

            if (!response.ok) {
                const body = await response.json();
                // OpenAPI validation errors: {errors: [{message: "...", path: "..."}]}
                const msg = body?.errors?.map(e => e.message).join('; ')
                    || body?.message
                    || response.statusText;
                throw new Error(msg);
            }

            successBox(l("Group saved successfully"));
            $('#group-form')[0].reset();
            $('#group-modal').modal('hide');
            $('#table-group').DataTable().ajax.reload(null, false);
        } catch (err) {
            console.error("Error saving group:", err);
            alert(l("Error: ") + (err.message || l("Unknown error")));
        } finally {
            button.prop('disabled', false).text(l("Save"));
        }
    });
});

/**
 * Initialize the group table
 */
function initialiseTabGroup() {
    if (!$('#table-group').length) return;
    $("#loading-icon").hide();
    $("#panel-group").show();

    tableau = $('#table-group').DataTable({
        "dom": "<'row'<'col-sm-4'l><'col-sm-4'B><'col-sm-4'f>>" + "<'row'<'col-sm-12'tr>>" + "<'row'<'col-sm-5'i><'col-sm-7'p>>",
        "buttons": ['copy', 'csv', 'excel'],
        "aLengthMenu": [[10,50,100,200,500,1000,-1], [10,50, 100, 200, 500, 1000, l("All")]],
        "iDisplayLength": 10,
        mark: true,
        "search": {
            "regex": true,
        },
        "bPaginate": true,
        "bLengthChange": false,
        "bInfo": true,



( run in 0.827 second using v1.01-cache-2.11-cpan-ad19def0cd9 )