Conventions

Common patterns

Reusable JavaScript patterns that appear across most Paradigm CMS extensions: searchable lists, create/edit windows, form submission, and delete confirmations.

List with search, filters, and pagination

Most admin list views follow this pattern: a search/filter state object, a load function that calls the API, and render functions for items and pagination controls.

$block.searchParams = {
    search_str: '',
    status: '',
    page: 1,
    limit: 20
};

$block.loadItems = async function() {
    const response = await ParadigmAPI.REQUEST(SITEURL + '/API/index.php', {
        action: 'extension:items->list',
        actionVars: {
            organization_ID: $block.organizationId,
            searchParams: $block.searchParams
        }
    });

    if (response.errors) {
        ParadigmAPI.processErrors(response.errors);
        return;
    }

    $block.items = response.items;
    $block.totalPages = response.totalPages;
    $block.renderItems();
    $block.renderPagination();
}

$block.renderItems = function() {
    const container = document.getElementById('$block-items-container');
    // Build and insert HTML...
    ParadigmCSS.render();
}

$block.applyFilters = async function() {
    $block.searchParams.page = 1;
    await $block.loadItems();
}

Create/edit form in a window

Opening a create or edit form uses WindowManager.createWindow() with ParadigmPages.fetchBlock() to load the form block. The onClose callback refreshes the list.

$block.openCreateWindow = function() {
    WindowManager.createWindow({
        title: 'Create Item',
        width: 600,
        focus: 'exclusive',
        content: async function() {
            return await ParadigmPages.fetchBlock(
                'extension:admin->items->create',
                { organization_ID: $block.organizationId }
            );
        },
        onClose: function() {
            $block.loadItems();
        }
    });
}

$block.openEditWindow = function(itemId) {
    WindowManager.createWindow({
        title: 'Edit Item',
        width: 600,
        focus: 'exclusive',
        content: async function() {
            return await ParadigmPages.fetchBlock(
                'extension:admin->items->edit',
                { item_ID: itemId }
            );
        },
        onClose: function() {
            $block.loadItems();
        }
    });
}

Form submission

Form submit handlers prevent the default event, validate inputs, call the API, handle errors, and close the window on success.

$block.submit = async function(event) {
    if (event) event.preventDefault();

    const label = document.getElementById('$block-label-input').value.trim();

    if (!label) {
        alert('Please enter a name.');
        return;
    }

    const response = await ParadigmAPI.REQUEST(SITEURL + '/API/index.php', {
        action: 'extension:items->create',
        actionVars: {
            organization_ID: $block.organizationId,
            label: label
        }
    });

    if (response.errors) {
        ParadigmAPI.processErrors(response.errors);
        return;
    }

    if (response.success) {
        WindowManager.closeCurrentWindow();
    }
}

Confirmation before delete

Delete actions always prompt the user for confirmation before making the API call. The event is stopped to prevent parent click handlers from firing.

$block.deleteItem = async function(event, itemId) {
    if (event) {
        event.preventDefault();
        event.stopPropagation();
    }

    if (!confirm('Are you sure you want to delete this item? This action cannot be undone.')) {
        return;
    }

    const response = await ParadigmAPI.REQUEST(SITEURL + '/API/index.php', {
        action: 'extension:items->delete',
        actionVars: {
            item_ID: itemId
        }
    });

    if (response.errors) {
        ParadigmAPI.processErrors(response.errors);
        return;
    }

    if (response.success) {
        $block.loadItems();
    }
}

Putting it together

A typical admin list block combines all four patterns: the list loads items on init, the header contains search and a create button, each row has edit and delete actions, and the footer shows pagination. See JavaScript conventions for the full block structure and naming rules.