FleetbaseFleetbase
Order PresentationCustomizing

The Order Form

Compose the order form for your order type with form.sections — Fleet-Ops' native sections by name and your own as ExtensionComponents — with the arguments your sections receive and patterns for reading and writing the order.

The Order Form

form.sections is the order form, top to bottom. Each entry is either:

  • a native section's name, a string such as 'details' or 'route', which renders Fleet-Ops' own section; or
  • an ExtensionComponent, which renders a component from your engine.
import { ExtensionComponent } from '@fleetbase/ember-core/contracts';

const section = (path) => new ExtensionComponent('@acme/rental-engine', path);

form: {
    sections: ['details', section('rental/form/equipment'), 'route', 'notes', 'documents'],
},

The standard order form next to a profiled one

The list is the form: anything you leave out isn't rendered, and an empty or missing list renders an empty form.

Native Sections

NameRenders
detailsThe order type selector, internal id, schedule, customer, facilitator, driver, vehicle, the ad-hoc, dispatch and proof-of-delivery toggles, and then the order config's custom fields. Fields can be hidden or added
routePickup, dropoff and waypoints, with the route map. Its title can change, and multi-drop can be hidden
payloadThe entities (items) the order carries
service-rateService rate and quote selection
notesOrder notes
documentsFile attachments
orchestrator-constraintsTime windows, required skills and priority for the orchestrator
metadataFree-form key/value metadata

Keep details in your form. It holds the order type selector, which is how users choose your type for a new order, and the order config's custom fields. Leave it out only if your own section sets order_config and handles custom fields.

Fleet-Ops' order validation still requires a pickup and dropoff (or at least two waypoints), so keep route too, unless your section sets them.

The Standard Layout

To keep the form exactly as it is for other order types:

form: {
    sections: ['details', 'route', 'payload', 'service-rate', 'notes', 'documents', 'orchestrator-constraints', 'metadata'],
},

Your Own Sections

An ExtensionComponent section is rendered with LazyEngineComponent, so it can live in your engine. Your engine is loaded the first time the section renders.

ArgumentValue
@orderThe order being created or edited
@resourceThe same order
@controllerThe host controller, when the host passes one
@profileYour profile object
@hiddenFieldsThe profile's hidden.fields

Use the same building blocks as Fleet-Ops' own sections, so yours look native: a ContentPanel with @wrapperClass="bordered-top", and InputGroups in a grid.

<ContentPanel @title="Equipment" @open={{true}} @wrapperClass="bordered-top">
    <div class="grid grid-cols-1 lg:grid-cols-2 gap-2">
        …
    </div>
</ContentPanel>

Storing What Your Section Collects

Whatever your section sets on the order is saved with it.

On the Order's meta

For values that have no order column, use meta. Replace meta with a new object rather than changing a key inside it, so the change is noticed:

// addon/components/rental/form/equipment.js
import Component from '@glimmer/component';
import { action } from '@ember/object';

export default class RentalFormEquipmentComponent extends Component {
    get rental() {
        return this.args.order.meta?.rental ?? {};
    }

    @action update(key, event) {
        const order = this.args.order;
        order.set('meta', { ...(order.meta ?? {}), rental: { ...this.rental, [key]: event.target.value } });
    }
}
{{! addon/components/rental/form/equipment.hbs }}
<ContentPanel @title="Equipment" @open={{true}} @wrapperClass="bordered-top">
    <div class="grid grid-cols-1 lg:grid-cols-2 gap-2">
        <InputGroup @name="Asset tag" @value={{this.rental.asset_tag}} {{on "input" (fn this.update "asset_tag")}} />
        <InputGroup @name="Rental days" @type="number" @value={{this.rental.days}} {{on "input" (fn this.update "days")}} />
    </div>
</ContentPanel>

InputGroup passes extra attributes and modifiers to its input, which is how {{on "input"}} reaches it.

On the Order's Own Fields

Your section can set any order field Fleet-Ops' sections would, such as scheduled_at, customer or the payload's pickup:

@action selectDepot(place) {
    this.args.order.payload.set('pickup', place);
}

In Your Own Tables

For data that belongs to your extension, keep a draft in meta while the form is open, then save it to your own tables on the server when the order is created, from an observer on Fleet-Ops' Order model. See Expansions & Observers.

Editing an Existing Order

The same sections are used when an order is edited: Edit details in the order's menu opens the profiled form in a side panel. Check @order.isNew where your section should behave differently for a new order, for example to lock a field once the order exists:

<InputGroup @name="Asset tag" @value={{this.rental.asset_tag}} @disabled={{not @order.isNew}} {{on "input" (fn this.update "asset_tag")}} />

Section Order

Sections render in list order. A few patterns:

// Your section first, before the order type
sections: [section('loan/form/customer'), 'details', 'route', 'notes'],

// Your section between two native ones
sections: ['details', 'route', section('rental/form/handover'), 'notes'],

// Several of your own
sections: ['details', section('rental/form/equipment'), section('rental/form/period'), 'route', 'documents'],
The Order Form | Fleetbase