FleetbaseFleetbase

Modals Manager Service

The `modals-manager` service is the programmatic API for opening, stacking, and dismissing modals. Returns a Promise that resolves on confirm and rejects on decline.

Modals Manager Service

@service modalsManager;

modals-manager is the central service for working with modals from JavaScript. Every modal in the Fleetbase Console — confirmations, alerts, prompts, bulk-action sheets, progress trackers, and custom modals — flows through this service.

It supports a stack of nested modals (z-index is auto-incremented) and returns a Promise so you can await user confirmation.

Inject the Service

import Component from '@glimmer/component';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';

export default class MyComponent extends Component {
  @service modalsManager;
}

Show a Modal

this.modalsManager.show('my-modal-template', {
  title: 'Confirm action',
  body: 'Are you sure?',
  confirm: async (modal) => { /* … */ },
});

show(componentToRender, options) mounts componentToRender inside a modal and returns a Promise that resolves when the modal closes.

Built-In Layouts

The service provides convenience methods that wrap show() with pre-set layouts.

confirm(options)

A small confirmation modal — used for destructive actions, "Are you sure?" prompts, and any binary decision.

@action async deleteDriver() {
  this.modalsManager.confirm({
    title: 'Delete driver?',
    body: 'This action cannot be undone.',
    acceptButtonText: 'Delete',
    acceptButtonScheme: 'danger',
    confirm: async (modal) => {
      await this.driver.destroyRecord();
    },
  });
}

alert(options)

A small alert modal — single OK button.

this.modalsManager.alert({
  title: 'Update available',
  body: 'A new version of the console is available.',
});

prompt(options)

A modal that asks the user for a single text input.

this.modalsManager.prompt({
  title: 'Rename',
  body: 'Enter a new name',
  confirm: async (modal) => {
    const name = modal.options.value;
    /* save */
  },
});

bulk(options)

A modal designed for bulk-action confirmations (apply across N selected rows).

this.modalsManager.bulk({
  title: 'Delete selected drivers',
  body: `Delete ${this.selected.length} drivers?`,
  selected: this.selected,
  confirm: async () => { /* … */ },
});

progress(options)

Displays the progress of an array of promises with a running percentage.

this.modalsManager.progress({
  title: 'Processing',
  promises: this.records.map((r) => r.save()),
});

options.promises is required.

process(options)

Displays a single async process with stages.

this.modalsManager.process({
  title: 'Importing',
  process: async () => { /* … */ },
});

options.process is required.

loader(options) / displayLoader(options)

A blocking loader modal (no buttons).

this.modalsManager.loader({ title: 'Loading drivers…' });
// later
this.modalsManager.done();

userSelectOption(title, promptOptions, modalOptions)

A radio-list selection modal. Returns a Promise that resolves with the selected value.

const selected = await this.modalsManager.userSelectOption(
  'Pick a wallet',
  [
    { value: 'main',    label: 'Main' },
    { value: 'reserve', label: 'Reserve' },
  ],
);

Custom Modals

To show a fully custom modal, pass the component's resolution path as the first arg:

this.modalsManager.show('modals/edit-driver', {
  title: 'Edit driver',
  driver: this.driver,
  confirm: async () => { /* … */ },
});

Your custom modal component lives at addon/components/modals/edit-driver.{hbs,js} and receives @modalIsOpened, @options, @onConfirm, and @onDecline arguments.

Common Options

These options work for every modal layout:

Title / Body

OptionTypeDescription
titlestringModal title
bodystringBody text (or yield a custom body in your component)
footerstringFooter text
hideTitlebooleanDon't render the title bar

Buttons

OptionDefaultDescription
acceptButtonTextYesConfirm button text
acceptButtonSchemeprimaryConfirm button color (primary, danger, success, warning)
acceptButtonIconFontAwesome icon
declineButtonTextNoDecline button text
declineButtonSchemedefaultDecline button color
hideAcceptButtonfalseHide the confirm button
hideDeclineButtonfalseHide the decline button
confirmButtonDefaultText / confirmButtonPendingText / confirmButtonFulfilledText / confirmButtonRejectedTextYesPer-state texts when using promise-aware buttons

Behavior

OptionDefaultDescription
backdroptrueShow the dimming overlay
backdropClosetrueClick the backdrop to dismiss
keyboardtrueAllow Escape to dismiss
fadetrueFade transition
positiontoptop, center
sizenullsm, md, lg, xl, etc.
scrollablefalseAllow the body to scroll
renderInPlacefalseRender in DOM order rather than in a portal
transitionDuration300Transition duration in ms
keepOpenfalseDon't auto-close on confirm — useful for multi-step flows

Callbacks

OptionSignatureDescription
confirmasync (modal, done) => voidCalled when the user clicks the confirm button. Return a Promise to keep the button in pending state
declineasync (modal, done) => voidCalled when the user clicks the decline button

modal is the manager instance; done is a callback to manually close the modal (useful when keepOpen: true).

Promise Result

show(), confirm(), alert(), etc. return a Promise. The Promise resolves when the modal is dismissed via done(modalId, 'onConfirm') and rejects on 'onDecline'.

try {
  await this.modalsManager.confirm({
    title: 'Delete driver?',
    confirm: async () => await this.driver.destroyRecord(),
  });
  // user confirmed
} catch (e) {
  // user declined
}

Stacking Modals

Multiple modals can be open at once. The manager tracks them in a stack with auto-incremented z-index (1060, 1070, …). The most recently opened modal is the "top modal" returned by getTopModal().

Imperative Control

Useful methods for advanced control:

MethodPurpose
getTopModal()The currently-active modal
getModalById(id)Find a specific modal in the stack
done(modalId?, type?)Close a modal — pass 'onConfirm' or 'onDecline' to drive the Promise resolution
dismiss(modalId?)Close without resolving the Promise
setOptionForModal(modalId, key, value)Update an option on a specific modal at runtime
getOptionForModal(modalId, key)Read an option on a specific modal
startLoadingForModal(modalId)Toggle the modal's confirm button to pending state
stopLoadingForModal(modalId)Reverse of the above

Real-World Examples

// Confirmation with permission gating
@action async deleteOrder() {
  this.modalsManager.confirm({
    title: 'Cancel order?',
    body: `Order ${this.order.public_id} will be cancelled.`,
    acceptButtonText: 'Cancel order',
    acceptButtonScheme: 'danger',
    confirm: async () => {
      await this.order.cancel();
      this.notifications.success('Order cancelled');
    },
  });
}

// Custom modal with extra props
@action editProfile(driver) {
  this.modalsManager.show('modals/driver-form', {
    title: 'Edit driver',
    driver,
    confirm: async (modal) => {
      await modal.options.driver.save();
    },
  });
}

// Loader during a long-running fetch
@action async syncWithUpstream() {
  this.modalsManager.loader({ title: 'Syncing…' });
  try {
    await this.upstream.sync();
  } finally {
    this.modalsManager.done();
  }
}

// User selection
const choice = await this.modalsManager.userSelectOption(
  'Choose a destination',
  this.zones.map(z => ({ value: z.id, label: z.name })),
);

Source

FileDescription
addon/services/modals-manager.jsService class
Modals Manager Service | Fleetbase