Adding a custom control

Step-by-step to wire a new custom_control modal

Backend row action

Ensure the control table row action includes:

{
  "action_type": "custom_control",
  "onSuccess": "OpenModalForm",
  "method": "POST",
  "action": { "api": "/api/your/feature/..." }
}

Prefetch response should contain fields your form needs (payload, ids, labels).

Create the component

Add under:

./ApiTables/table-modals/custom-controls/YourFeatureForm.tsx

Pattern:

'use client';

import useUtilsProvider from '@/app/ApiTables/table-providers/useUtilsProvider';
import { useDispatch } from 'react-redux';
import { _triggerOutscopeTableRefresher } from '@/store/slices/appSlice';

export default function YourFeatureForm({ action, closeModal }: { action: any; closeModal: () => void }) {
  const { rowActionsPostHandler, triggerTableReload } = useUtilsProvider();
  const dispatch = useDispatch();

  async function onSubmit(data: FormData) {
    await rowActionsPostHandler(
      action.method,
      action.url?.api?.replace('/api', ''),
      { ...data },
      action
    );
    // Choose one:
    triggerTableReload();
    // dispatch(_triggerOutscopeTableRefresher());
    closeModal();
  }

  return (/* form UI */);
}

Match save/reload behavior to sibling features on the same page.

Wire in ./ApiTables/table-modals/ApiTablesModals.tsx

  1. Import your component
  2. Add a conditional inside ./ApiTables/table-modals/ApiTablesModals.tsx customControlAction popup:
{customControlAction?.url?.api?.includes('your-feature-path') && (
  <YourFeatureForm action={customControlAction} closeModal={handleCloseModal} />
)}

Use a unique URL fragment to avoid collisions. Prefer api path over web when both exist.

Test checklist

  • Click row action → loader → modal opens with prefilled data
  • Submit success closes modal and refreshes table
  • Close modal clears customControlAction (no ghost popup)
  • Error paths show toast via errorHandling
  • Works in RTL / locale if form uses translations

Bulk-only custom modals

Bulk flows do not use custom_control the same way. Example: create_withdrawal_bulk uses selectedBulkAction and a separate Popup block — copy AddToCreditBulkModal pattern.

Anti-patterns

  • Do not add controls only in feature pages — they will not open without ApiTablesModals wiring
  • Do not rely on auto-discovery by action_key alone (unless you extend the router)
  • Avoid overlapping URL includes patterns

On this page