# Controller Component

`Controller Component` can be used to create a page level custom component (one time) for some advanced use cases. The below example controller code exports a `Controller Component` named `QuickEntry` that renders couple quick entry input fields that makes parallel async calls to cloud functions and create records on tab out enabling users to perform high speed data entry.

{% code title="Page Controller" overflow="wrap" lineNumbers="true" %}

```tsx
import type { Store, BaseFunctionProps } from 'cloudio';
import { useStringAttribute, useNumberAttribute } from 'cloudio';
import { OmsPreOrderLines } from '@cloudio-saas/datasource-types';
import React, { useRef } from 'react';
import { Box, TextField } from '@mui/material';
import platform from 'platform';

async function createOrUpdateLine(
  linesStore: Store<OmsPreOrderLines>,
  id: string,
  resp: OmsPreOrderLines,
): Promise<string> {
  if (id) {
    // update record
    linesStore.updateRecord(id, resp);
  } else {
    // create record
    const newid = await linesStore.createNew({
      partialRecord: resp,
      addOnTop: true,
      rs: 'I',
    });
    if (!newid) {
      throw new Error('Failed to create new record!');
    }
    id = newid;
  }
  return id;
}

async function onQuickEntryQtyChange({
  appUid,
  pageId,
  itemId,
  platform,
}: BaseFunctionProps) {
  // get the stores
  const quickEntryStore = platform.getStore<OmsPreOrderLines>(
    appUid,
    pageId,
    'quickEntryAlias',
  );
  const linesStore = platform.getStore<OmsPreOrderLines>(
    appUid,
    pageId,
    'omsPreOrderLinesAlias',
  );
  if (!quickEntryStore || !linesStore) {
    platform.showError(`Store not found for ${itemId}`);
    return;
  }
  // get the current quick entry row
  const quickRow = quickEntryStore.getCurrentRecord();
  const { itemCode, itemQty } = quickRow;
  quickEntryStore.clearQuietyly().then(() => {
    quickEntryStore.createNew({ partialRecord: {}, rs: 'N' });
  });
  let id = '';
  // invoke cloud functions in parallel
  await Promise.all([
    platform
      .invokeCloudFunction(appUid, 'get-oms-item-details', {
        itemCode,
        itemQty,
      })
      .then(async (resp: OmsPreOrderLines) => {
        const { itemCode, itemQty, itemDesc } = resp;
        id = await createOrUpdateLine(linesStore, id, {
          itemCode,
          itemQty,
          itemDesc,
        });
        platform.redrawCanvasGrid(appUid, pageId, 'canvasGrid');
      }),
    platform
      .invokeCloudFunction(appUid, 'get-oms-item-price', { itemCode, itemQty })
      .then(async (resp: OmsPreOrderLines) => {
        const { sellPrice } = resp;
        id = await createOrUpdateLine(linesStore, id, {
          itemCode,
          itemQty,
          sellPrice,
        });
        platform.redrawCanvasGrid(appUid, pageId, 'canvasGrid');
      }),
  ]);
}

function QuickEntry(props: BaseFunctionProps) {
  // similar to useState hook in react for an attribute in the current record
  const [itemCode, setItemCode] = useStringAttribute('itemCode');
  const [itemQty, setItemQty] = useNumberAttribute('itemQty');
  // ref used for focus
  const ref = useRef<HTMLInputElement>(null);
  // ref to store the focus value
  const focusVal = useRef<number | undefined>(undefined);

  return (
    <Box sx={{ flex: 1, display: 'flex', minWidth: '400px', gap: '8px' }}>
      <TextField
        inputRef={ref}
        variant="standard"
        label="Item Code"
        value={itemCode ?? ''}
        onChange={(e) => {
          setItemCode(e.target.value);
        }}
        fullWidth
      />
      <TextField
        type="number"
        variant="standard"
        label="Item Qty"
        value={itemQty ?? ''}
        onChange={(e) => {
          let val: number | undefined = Number(e.target.value);
          if (Number.isNaN(val)) {
            val = undefined;
          }
          setItemQty(val);
        }}
        onFocus={() => {
          // store the existing value, so we can check against this in onBlur
          focusVal.current = itemQty;
        }}
        onBlur={(e) => {
          if (focusVal.current !== itemQty) {
            // value changed, create line
            onQuickEntryQtyChange(props);
            ref.current?.focus();
          }
        }}
        fullWidth
      />
    </Box>
  );
}

// must export a default object with all the functions to be used by this page
export default {
  QuickEntry,
};
```

{% endcode %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://next-docs.cloudio.io/ui/controller-component.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
