Portal Community

Example 1: CRM Account Linker

Links a form record to a CRM account record by creating a cross-system relationship.

// handlers/linkCrmAccount.ts
import type { FormActionContext } from '@atlas-forms/types-js';
import { crmApi } from '../api/crmApi';

export const linkCrmAccountHandler = async (ctx: FormActionContext): Promise<void> => {
  const { formValues, tenantId, config } = ctx;
  const formRecordId = formValues['record-id'];
  const crmAccountId = formValues['crm-account-id'];

  if (!crmAccountId) {
    ctx.fail('Please select a CRM account first.');
    return;
  }

  try {
    await crmApi.linkRecord({
      tenantId,
      formId: ctx.formId,
      formRecordId,
      crmAccountId,
      linkType: config?.linkType ?? 'associated'
    });

    ctx.formEngine.setFieldValue('crm-link-status', 'linked');
    ctx.complete('CRM account linked successfully.');
  } catch (err) {
    ctx.fail(`CRM link failed: ${err instanceof Error ? err.message : 'Unknown error'}`);
  }
};

// index.ts
export function registerCrmActions(): void {
  registerFormAction('crm:link-account', linkCrmAccountHandler);
}

Example 2: Workflow Approval Handler

Approve or reject a workflow instance from a HIL form. Configurable outcome via handlerConfig.

// handlers/workflowApproval.ts
import type { FormActionContext } from '@atlas-forms/types-js';
import { workflowApi } from '../api/workflowApi';

export const workflowApprovalHandler = async (ctx: FormActionContext): Promise<void> => {
  const { formValues, tenantId, config } = ctx;
  const outcome = config?.outcome ?? 'approved';
  const requireComment = config?.requireComment ?? false;

  const instanceId = formValues['workflow-instance-id'];
  const comment = formValues['approver-comment'];

  if (!instanceId) { ctx.fail('Workflow instance not found.'); return; }
  if (requireComment && !comment) { ctx.fail('A comment is required.'); return; }

  try {
    await workflowApi.transition({ instanceId, tenantId, outcome, comment });
    ctx.complete(outcome === 'approved' ? 'Approved successfully.' : 'Rejected.');
    ctx.navigate('/workdesk/inbox');
  } catch (err) {
    ctx.fail('Could not complete approval. Please try again.');
  }
};

// Used in schema as:
// { "type": "custom", "label": "Approve", "config": { "handlerType": "hil:approve", "handlerConfig": { "outcome": "approved" } } }
// { "type": "custom", "label": "Reject", "config": { "handlerType": "hil:approve", "handlerConfig": { "outcome": "rejected", "requireComment": true } } }

Example 3: Bulk PDF Export

Generates a PDF from the form's current values and triggers a browser download.

// handlers/exportPdf.ts
import type { FormActionContext } from '@atlas-forms/types-js';

export const exportPdfHandler = async (ctx: FormActionContext): Promise<void> => {
  const { formValues, formId, tenantId, config } = ctx;
  const template = config?.template ?? 'default';
  const filename = config?.filename ?? `form-${formId}.pdf`;

  try {
    const response = await fetch('/api/pdf/generate', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ formId, tenantId, template, values: formValues })
    });

    if (!response.ok) throw new Error(`HTTP ${response.status}`);

    const blob = await response.blob();
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = filename;
    a.click();
    URL.revokeObjectURL(url);

    ctx.complete('PDF downloaded.');
  } catch (err) {
    ctx.fail('PDF export failed. Please try again.');
  }
};