# Developer guide: JSON dropdowns

kView INVESTOR uses JSON-backed dropdowns for finite lists of values. This replaces hardcoded `select_values` lists on dynamic fields and avoids scattering option lists across Blade views, seeders, and JavaScript.

The mechanism is inspired by TIKEASY:

- dropdown definitions live as JSON files in `storage/app/dropdowns`;
- fields reference a dropdown by name through `dropdown_value`;
- forms load the JSON through `/api/dropdown/{name}.json`;
- server-side validation reads the same JSON values.

## 1. Where dropdown JSON files live

Create dropdown files here:

```text
storage/app/dropdowns/<dropdown_name>.json
```

Example:

```text
storage/app/dropdowns/fund_status.json
```

`storage/app/.gitignore` is configured to keep `storage/app` ignored while allowing dropdown JSON files to be versioned.

## 2. JSON format

Use an array of objects. For compatibility with both kView INVESTOR Blade forms and lncshared/TIKEASY-style Vue dropdowns, include both `code` and `value`.

```json
[
    {
        "code": "active",
        "value": "active",
        "name": "Active"
    },
    {
        "code": "closed",
        "value": "closed",
        "name": "Closed"
    }
]
```

Meaning:

| Property | Meaning |
| --- | --- |
| `code` | Stored value, used by the lncshared Vue dropdown component |
| `value` | Stored value, used by kView INVESTOR Blade dropdown loader |
| `name` | Human-readable label |
| `label` | Optional label override |
| `code2` | Optional secondary code displayed by legacy dropdown UIs |

Recommended rule: `code` and `value` should be identical unless there is a strong legacy reason not to.

## 3. Naming convention

Use lowercase snake case:

```text
<model_or_domain>_<field>.json
```

Examples:

```text
fund_status.json
fund_strategy.json
fund_asset_class.json
investment_status.json
investor_type.json
calldistrib_type.json
calldistrib_status.json
```

The value stored in `dropdown_value` is the filename without `.json`.

Example:

```php
'dropdown_value' => 'fund_status',
```

## 4. DynamicFields: use `dropdown`, not `select`

For dynamic fields, do not use:

```php
'input_type' => 'select',
'select_values' => "draft\nactive\nclosed",
```

Use:

```php
'input_type' => 'dropdown',
'dropdown_value' => 'fund_status',
'select_values' => null,
```

In `database/seeders/DynamicFieldsSeeder.php`, the helper `select()` now creates a dropdown-backed field:

```php
private function select(string $model, string $name, string $displayName, int $order, array $values): array
{
    $dropdownName = strtolower(class_basename($model) . '_' . $name);

    return $this->base($model, $name, 'string', $displayName, $order, [
        'input_type' => 'dropdown',
        'dropdown_value' => $dropdownName,
        'select_values' => null,
    ]);
}
```

If you add a new call to `select()`, also create the matching JSON file.

## 5. Blade form usage

For dynamic fields, no Blade change is needed. The generic component handles it:

```blade
<x-dynamic-fields :model="$fund" />
```

When a `DynamicField` has:

```php
input_type = dropdown
dropdown_value = fund_status
```

the component renders:

```html
<select class="dynamic-dropdown" data-dropdown-value="fund_status">
```

JavaScript loads:

```text
/api/dropdown/fund_status.json
```

For a hardcoded finite field in a Blade form, use the same class and data attribute:

```blade
<select
    id="type"
    name="type"
    class="dynamic-dropdown"
    data-dropdown-value="investor_type"
    data-selected="{{ old('type', $investor->type) }}"
    required
>
    <option value="">- Select type -</option>
</select>
```

## 6. API endpoints

Available web routes:

```text
GET /api/dropdowns
GET /api/dropdown/{filename}
```

Examples:

```text
/api/dropdowns
/api/dropdown/fund_status.json
```

`DropdownController` first looks in:

```text
storage/app/dropdowns
```

Then falls back to:

```text
lncshared/resources/assets/js/data
```

This fallback keeps legacy dropdowns such as `countries` working.

## 7. Server-side validation

`LncShared\App\Models\DynamicField::getValidationRuleAttribute()` validates dropdown fields with `Rule::in(...)` using the JSON file values.

For a dropdown field:

```php
input_type = dropdown
dropdown_value = fund_status
```

the accepted values are read from:

```text
storage/app/dropdowns/fund_status.json
```

The accepted stored value is resolved in this order:

```text
value -> code -> name
```

This means the recommended JSON entry:

```json
{ "code": "active", "value": "active", "name": "Active" }
```

validates the submitted value:

```text
active
```

## 8. Display labels

Read-only field display uses `DynamicFieldPresenter::dropdownOptions()` to resolve the stored value to its label.

For example, stored value:

```text
due_diligence
```

from:

```json
{ "code": "due_diligence", "value": "due_diligence", "name": "Due diligence" }
```

is displayed as:

```text
Due diligence
```

## 9. Current dropdown files

At the time of writing, kView INVESTOR has:

```text
storage/app/dropdowns/calldistrib_status.json
storage/app/dropdowns/calldistrib_type.json
storage/app/dropdowns/fund_asset_class.json
storage/app/dropdowns/fund_status.json
storage/app/dropdowns/fund_strategy.json
storage/app/dropdowns/investment_status.json
storage/app/dropdowns/investor_type.json
```

## 10. Adding a new dropdown

Checklist:

1. Create the JSON file in `storage/app/dropdowns`.
2. Use lowercase snake case for the filename.
3. Include `code`, `value`, and `name` for every option.
4. Reference the dropdown from the DynamicField:

```php
'input_type' => 'dropdown',
'dropdown_value' => '<dropdown_name>',
```

5. If the field is hardcoded in a Blade form, add:

```blade
class="dynamic-dropdown"
data-dropdown-value="<dropdown_name>"
data-selected="{{ old('<field>', $model-><field>) }}"
```

6. Run validation:

```powershell
& 'C:\Program Files\php\PHP 8.4.22\php.exe' artisan view:cache
```

7. If the dropdown is created through the seeder, run:

```powershell
& 'C:\Program Files\php\PHP 8.4.22\php.exe' artisan db:seed --class=DynamicFieldsSeeder --no-interaction
```

## 11. Common pitfalls

- Do not submit the display label. The stored value should be `value` or `code`, not `name`.
- Do not put `.json` in `dropdown_value`; use `fund_status`, not `fund_status.json`.
- Do not use `select_values` for new dynamic dropdowns.
- Keep JSON files committed; otherwise forms will render empty dropdowns on another machine.
- If a dropdown appears empty, check `/api/dropdown/<name>.json` in the browser first.
