๐ Broker Forms
Form and input components for broker creation and editing.
๐ BrokerForm
The create/edit form for a broker (BrokerForm.svelte), used inside
BrokerModal. It dispatches submit with the trimmed values, and cancel;
the modal makes the API calls.
๐ Fields
| Field | Component | Required | Description |
|---|---|---|---|
| Name | Text input (max 100) | โ | Broker display name; unique among all brokers of the instance |
| Description | Textarea (max 500) | โ | Optional description |
| Default Import Plugin | ImportPluginSelect | โ | BRIM parser proposed for this broker's files |
| Portal URL | URL input | โ | Broker website; its /favicon.ico is an icon fallback |
| Icon | ImagePickerWrapper (preset="broker-icon") |
โ | Custom broker icon, previewed with BrokerIcon |
| Opened at | SingleDatePicker | โ | Account opening date, never in the future (allowFuture={false}); today for a new broker |
| Active | Toggle | โ | is_active, on by default |
| Allow Overdraft | Checkbox | โ | Allow a negative cash balance |
| Allow Shorting | Checkbox | โ | Allow a negative asset quantity |
| Initial Balances | Amount + CurrencySearchSelect rows | โ | Create mode only: opening cash per currency; the first row uses the user's base currency |
In edit mode a cleared optional text field is sent as '', which clears it on the server; in
create mode it is left out.
โ Validation
Client side, the submit button stays disabled while a save is running or while:
- Name is empty after trimming, or longer than 100 characters (
isValid); - Initial balances repeat a currency (
hasDuplicateCurrencies, with a warning under the rows).
Balance rows whose amount is zero or negative are dropped on submit. The form has no base-currency field: currencies appear only in the initial balance rows.
On the server, BrokerService.create_bulk() turns each remaining balance row into a DEPOSIT
dated today (today_date(), not opened_at), written through
execute_batch(creates_raw=โฆ); a deposit refused by validation adds its issue to the broker's
create errors.
Duplicate names
Broker names are unique across the instance (Broker.name is a unique column), so the server
rejects a create whose name is taken. BrokerModal turns the backend's English message into a
localized one (localizeDuplicateName), chosen by who owns the existing broker, and appends the
recovery guidance:
| Backend message | Key (frontend/src/lib/i18n/brokers/en.json) |
English text |
|---|---|---|
You already have a broker named '<name>' |
brokers.duplicateNameOwn |
You already have a broker named "{name}". |
Broker '<name>' already exists (owned by '<owner>') |
brokers.duplicateNameOwned |
Broker "{name}" already exists (owned by "{owner}"). |
Broker with name '<name>' already exists, or the 409 A broker with that name already exists |
brokers.duplicateNameExists |
A broker named "{name}" already exists. |
| appended to each of the above | brokers.duplicateNameRecovery |
To resolve this, rename the existing broker or choose a different name for the broker you are adding. |
The message fills the dialog's error banner. The 409 comes from a creation that lost a race on the unique name; it also raises an error toast. Any other error is shown as received. The error is reset whenever a new dialog opens, so it never carries over into the next create or edit.
๐ Feedback
- Create โ a success toast, Broker "{name}" created. (
brokers.created), fromBrokerModalonce the new broker is merged intobrokerStore. - Delete โ a success toast, Broker "{name}" deleted. or Broker "{name}" and {count}
transactions deleted. (
brokers.deleted,brokers.deletedWithTransactions), from the broker list page onceDELETE /brokerssucceeds โ see DeleteBrokerDialog. - Edit โ the dialog closes without a toast.
๐ผ๏ธ BrokerIcon
A smart icon component (BrokerIcon.svelte) with a 4-step fallback chain. The chain itself
lives in lib/utils/broker/brokerIconChain.svelte.ts: it lists the candidate URLs in order and
moves to the next one when an image fails to load.
graph LR
A["icon_url<br/><small>Custom uploaded</small>"] -->|not set or fails| B["portal_url favicon<br/><small>origin + /favicon.ico</small>"]
B -->|not set or fails| C["Plugin icon<br/><small>default_import_plugin</small>"]
C -->|not set or fails| D["Briefcase<br/><small>Lucide icon</small>"]
style A fill:#e8f5e9,stroke:#2e7d32
style B fill:#e3f2fd,stroke:#1565c0
style C fill:#fff3e0,stroke:#e65100
style D fill:#f3e5f5,stroke:#7b1fa2
When a brokerId is given but none of the three fields is, the icon asks brokerStore for them
(ensureBrokerIconFieldsLoaded), which reads GET /brokers/{id} at most once per broker until the
cache is refreshed or the broker invalidated โ see
Reference State.
๐ Sizes
| Size | Dimensions | Used in |
|---|---|---|
sm |
24ร24 px | Select options (BrokerSearchSelect), inline badges |
md |
40ร40 px | BrokerCard |
lg |
64ร64 px | Broker detail page header, BrokerForm icon preview |
| a number | that many px | Custom size; the briefcase takes 60% of it (at least 12 px) |
๐ Key Props
| Prop | Type | Description |
|---|---|---|
brokerId |
number \| null |
Lets the icon fetch missing icon fields from brokerStore |
iconUrl |
string \| null |
Custom icon URL (step 1) |
portalUrl |
string \| null |
Broker website for favicon (step 2) |
pluginCode |
string \| null |
BRIM plugin code for icon (step 3) |
size |
'sm' \| 'md' \| 'lg' \| number |
Icon size (default md) |
altText |
string |
Accessible alt text |
Used by: BrokerCard, BrokerSearchSelect, broker detail page.