# Detail View Builder

The Detail View Builder provides a drag‑and‑drop interface for creating custom layouts that display record details. Layouts are saved to the `detail_views` table so modules can load them dynamically.

## UI Layout

- **Palette** – left column listing available elements such as **Heading**, **Paragraph**, **Div**, **Image** and **Aggregate**. Drag items from here onto the canvas.
- **Canvas** – central area where elements are arranged. Items include a drag handle for reordering.
- **Inspector** – right column showing properties for the selected item. When nothing is selected it displays the view name and a **Save** button.

Element properties include text or image source, the data column to bind when rendering, optional CSS classes and inline style.

### Aggregate Component

The palette now includes an **Aggregate** element for displaying a calculated value from another table. When selected, the inspector lets you set:

- **Table** – name of the table to query.
- **Column** – column to aggregate.
- **Function** – aggregate function such as `sum`, `avg` or `count` (defaults to `sum`).
- **Filter Field** – optional field name used to match the current record ID.

Each aggregate item is exported as `{ "type": "aggregate", "table": "table", "column": "col", "function": "sum", "filterField": "field" }`.

When a detail view is loaded through `ModalBuilder`, provide `tableName` and `filterField` options on the `detail-view` element so the record and its aggregates refresh when the filter changes.

### Related Record Sources

Include data from related tables by defining a `sources` array in the view configuration. Each source specifies the table, the foreign key on the base record and an alias used when rendering items.

```json
{
  "sources": [
    { "table": "clients", "foreignKey": "client_id", "alias": "client" }
  ],
  "items": [
    { "type": "paragraph", "data_column": "client.name" }
  ]
}
```

`ModalBuilder` fetches each source record and merges it under the given alias so items can reference nested fields like `client.name` without additional network requests.

### Fishbone Diagram Element

Include a `{ "type": "fishbone-diagram", "options": { "diagramId": 1 } }` item
to render a stored cause-and-effect diagram. Create diagrams by POSTing
`problem` and a `categories` array to `/api/fishbone_diagrams` and use the
returned ID as `diagramId`. The editor and manager workflows are described in
[docs/FishboneDiagram.md](FishboneDiagram.md).

### Fault Tree Diagram Element

Use `{ "type": "fault-tree-diagram", "options": { "diagramId": 1 } }` to display
a fault tree from the `ohs_fault_trees` table. Edit diagrams through the
**Fault Tree Manager** modal which saves layout data and returns an `id` that you
reference in the element options.

## Saving a Detail View

After arranging the layout enter a view name in the inspector and click **Save**. The builder collects the canvas configuration and posts it to `/api/detail_views` using `saveDetailView()`. The resulting record stores the name and JSON returned by `exportConfig()`.

## Loading a Detail View in a Modal

Fetch the saved view then mount the builder inside a modal body and load the configuration:

```javascript
import { ModalBuilder } from '../FrontEnd/JsLibs2/core/ModalBuilder.js';
import DetailViewBuilder from '../FrontEnd/JsLibs2/modules/DetailViewBuilder/DetailViewBuilder.js';

$.getJSON('../api/detail_views/5', view => {
  const modal = new ModalBuilder({
    title: view.name,
    body: [{ type: 'html', html: '<div id="detail-container"></div>' }],
    footer: [{ type: 'button', label: 'Close', onClick: 'closeModal' }]
  });
  modal.render('#module-modal');
  DetailViewBuilder.mount('#detail-container');
  DetailViewBuilder.loadConfig(view.config);
});
```

Hide the palette or inspector with CSS if a read‑only display is desired. See the source under `FrontEnd/JsLibs2/modules/DetailViewBuilder` for available helpers.

Run `./test_setup.sh` followed by `phpunit -c api/phpunit.xml` and `npm test` to verify changes before committing.
