# Configuration

URL: https://zerodev.ca/docs/prism/developer-documentation/configuration

> Declare a settings form and Prism builds it in the dashboard.



A config class describes a settings form. Prism draws the form on the **Configs** page, saves what the admin enters and gives your code the values. You write no web code.

Put config classes in `resources/config/`. Each file is one group of settings.

```js title="modules/example/resources/config/settings.js"
import { Configs } from "#prism/modules";

export class SettingsConfig extends Configs {
    constructor(prism) {
        super(prism, { name: "settings", label: "Example", description: "Sync and alert settings" });
    }

    schema() {
        return {
            api: {
                type: "section",
                label: "API",
                fields: {
                    token: { type: "secret", default: "", label: "API Token" },
                    sync_every: { type: "number", default: 5, label: "Sync Every", unit: "sync_unit" },
                    sync_unit: { type: "select", default: "minutes", label: "Unit", options: [{ value: "minutes", label: "Minutes" }, { value: "hours", label: "Hours" }] }
                }
            },
            alerts: {
                type: "section",
                label: "Alerts",
                fields: {
                    enabled: { type: "boolean", default: false, label: "Send Alerts" },
                    product: { type: "select", default: "", label: "Product", source: { module: "core", endpoint: "/products", label: "name", value: "id" }, empty: "No products found", visibleWhen: { field: "enabled", value: true } }
                }
            }
        };
    }
}
```

| Option        | What it does                                               |
| ------------- | ---------------------------------------------------------- |
| `name`        | The group ID. Used to read and save the values.            |
| `label`       | The heading shown on the Configs page. Defaults to `name`. |
| `description` | A line under the heading.                                  |
| `preview`     | An image URL shown beside the group.                       |

## Field Types

| Type          | Saved as  | Control                                             |
| ------------- | --------- | --------------------------------------------------- |
| `string`      | string    | Text input.                                         |
| `text`        | string    | Multi-line text.                                    |
| `secret`      | string    | Hidden input with a show button.                    |
| `number`      | number    | Number input.                                       |
| `boolean`     | boolean   | Switch.                                             |
| `select`      | string    | Dropdown.                                           |
| `multiselect` | string\[] | Dropdown with several picks.                        |
| `tags`        | string\[] | Free text chips.                                    |
| `color`       | string    | Colour picker with a hex value.                     |
| `list`        | object\[] | Rows of sub-fields, added and removed by the admin. |

Any other type throws when the module loads. A field can also be a bare value, such as `retries: 3`, which becomes a `number` field with that default.

## Field Options

| Option         | What it does                                                                        |
| -------------- | ----------------------------------------------------------------------------------- |
| `default`      | The value before anything is saved.                                                 |
| `label`        | The field label. Defaults to the key. Leave out `description` so rows stay aligned. |
| `options`      | `{ value, label }` choices for `select` and `multiselect`.                          |
| `source`       | Load choices from a route instead of `options`. See below.                          |
| `empty`        | Text shown when `source` returns nothing. The dropdown stays disabled.              |
| `unit`         | The key of a `select` to join onto this `number`, such as minutes or hours.         |
| `visibleWhen`  | Only show the field when a condition is true.                                       |
| `disabledWhen` | Grey the field out when a condition is true.                                        |
| `item`         | The sub-fields of a `list`.                                                         |
| `migrate`      | `(stored) => value`, used once to fill a new key from older saved settings.         |

## Sections

A `section` entry groups fields under a heading. It holds its own `fields` object and is not a value itself, so keys inside it are read as if they were at the top level.

## Conditions

`visibleWhen` and `disabledWhen` compare another field in the same group:

| Condition                              | True when                         |
| -------------------------------------- | --------------------------------- |
| `{ field: "mode", value: "auto" }`     | `mode` is `"auto"`.               |
| `{ field: "mode", not: "off" }`        | `mode` is anything but `"off"`.   |
| `{ field: "mode", in: ["a", "b"] }`    | `mode` is one of the list.        |
| `{ field: "mode", notIn: ["a", "b"] }` | `mode` is none of the list.       |
| `{ field: "token", notEmpty: true }`   | `token` has a value.              |
| `{ field: "enabled" }`                 | `enabled` is set and not `false`. |

## Options From a Route

`source` fills a dropdown from a route that returns `{ rows: [...] }`:

```js
source: { module: "core", endpoint: "/products", label: "name", value: "id" }
```

`module` defaults to your own module. `label` and `value` name the columns to use. Prism adds `limit=500` to the request.

When the route returns nothing, the dropdown shows the `empty` text and stays disabled. Never fall back to a text input or a "None" option.

## Lists

```js
rules: {
    type: "list",
    label: "Rules",
    default: [],
    item: {
        name: { type: "string", default: "", label: "Name" },
        valid: { type: "boolean", default: true, label: "Type", states: ["Allow", "Block"] },
        message: { type: "string", default: "", label: "Message", labelBy: { field: "valid", on: "Allow Message", off: "Block Message" } }
    }
}
```

List sub-fields take the same options, plus:

| Option    | What it does                                 |
| --------- | -------------------------------------------- |
| `states`  | Labels for a `boolean`'s on and off states.  |
| `labelBy` | Change the label based on another sub-field. |
| `wide`    | Span two columns in the row.                 |

## Reading Values

```js
const settings = this.prism.modules.config.values("example", "settings");
const token = this.prism.modules.config.value("example", "settings", "token");
```

`values` returns every field in the group, with defaults filled in for anything not saved yet. Values are converted to their type on save, so a `number` is always a number and `tags` are always an array.

When an admin saves, Prism fires `config.saved` with `{ module, group, values }`. Listen for it to react straight away:

```js
export class SettingsSaved extends Events {
    constructor(prism) {
        super(prism, { name: "settingsSaved", event: "config.saved" });
    }

    async run({ module, group, values }) {
        if (module === "example" && group === "settings") await this.prism.modules.handlers.get("example", "sync").restart(values);
    }
}
```

Settings are stored in the database with the rest of Prism's settings, under `module:<id>`.
