From dbb29dc467de57a87990e3af7aefc79b49906d04 Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Thu, 3 Sep 2026 17:02:07 +0800 Subject: [PATCH 1/5] feat: add first-class trailer operations --- addon/components/cell/attached-vehicle.hbs | 2 +- addon/components/cell/attached-vehicle.js | 6 +- .../components/device/panel-tabs/vehicle.hbs | 26 +- addon/components/device/panel-tabs/vehicle.js | 8 + addon/components/equipment/form.js | 15 +- addon/components/layout/fleet-ops-sidebar.js | 1 + addon/components/maintenance-schedule/form.js | 20 +- addon/components/maintenance/form.js | 16 +- addon/components/modals/attach-equipment.hbs | 9 + addon/components/modals/attach-equipment.js | 3 + .../modals/attach-telematic-device.hbs | 21 +- .../modals/attach-telematic-device.js | 12 +- addon/components/modals/attach-trailer.hbs | 18 + addon/components/modals/attach-trailer.js | 1 + addon/components/modals/select-trailer.hbs | 1 + addon/components/modals/select-trailer.js | 1 + addon/components/trailer/card.hbs | 5 + addon/components/trailer/card.js | 5 + addon/components/trailer/connections.hbs | 1 + addon/components/trailer/connections.js | 2 + addon/components/trailer/details.hbs | 8 + addon/components/trailer/details.js | 2 + addon/components/trailer/equipment.hbs | 1 + addon/components/trailer/equipment.js | 31 ++ addon/components/trailer/form.hbs | 43 ++ addon/components/trailer/form.js | 54 +++ addon/components/trailer/panel-header.hbs | 1 + addon/components/trailer/panel-header.js | 2 + addon/components/trailer/related-list.hbs | 1 + addon/components/trailer/related-list.js | 34 ++ .../components/vehicle/details/equipment.hbs | 1 + addon/components/vehicle/details/equipment.js | 25 + addon/components/vehicle/details/trailers.hbs | 1 + addon/components/vehicle/details/trailers.js | 48 ++ addon/components/work-order/form.js | 16 +- .../controllers/connectivity/devices/index.js | 1 + .../connectivity/devices/index/details.js | 6 +- .../devices/index/details/vehicle.js | 11 + .../telematics/details/attachments.js | 75 ++- .../telematics/details/devices.js | 1 + .../controllers/management/trailers/index.js | 241 ++++++++++ .../management/trailers/index/details.js | 76 +++ .../management/trailers/index/edit.js | 45 ++ .../management/trailers/index/new.js | 30 ++ .../management/vehicles/index/details.js | 8 + addon/controllers/settings/custom-fields.js | 8 + addon/extension.js | 7 + addon/routes.js | 17 + addon/routes/management/trailers/index.js | 40 ++ .../management/trailers/index/details.js | 23 + .../trailers/index/details/connections.js | 2 + .../trailers/index/details/devices.js | 2 + .../trailers/index/details/equipment.js | 2 + .../trailers/index/details/index.js | 2 + .../index/details/maintenance-history.js | 2 + .../trailers/index/details/positions.js | 2 + .../trailers/index/details/schedules.js | 2 + .../trailers/index/details/work-orders.js | 2 + .../routes/management/trailers/index/edit.js | 23 + addon/routes/management/trailers/index/new.js | 14 + .../vehicles/index/details/trailers.js | 2 + addon/services/device-actions.js | 42 +- addon/services/trailer-actions.js | 127 +++++ .../devices/index/details/vehicle.hbs | 34 +- .../telematics/details/attachments.hbs | 28 +- addon/templates/management/trailers/index.hbs | 12 + .../management/trailers/index/details.hbs | 1 + .../trailers/index/details/connections.hbs | 1 + .../trailers/index/details/devices.hbs | 1 + .../trailers/index/details/equipment.hbs | 1 + .../trailers/index/details/index.hbs | 1 + .../index/details/maintenance-history.hbs | 1 + .../trailers/index/details/positions.hbs | 1 + .../trailers/index/details/schedules.hbs | 1 + .../trailers/index/details/work-orders.hbs | 1 + .../management/trailers/index/edit.hbs | 1 + .../management/trailers/index/new.hbs | 1 + .../vehicles/index/details/equipment.hbs | 2 +- .../vehicles/index/details/trailers.hbs | 1 + addon/utils/device-table-columns.js | 16 +- app/services/trailer-actions.js | 1 + ..._class_trailer_columns_to_assets_table.php | 58 +++ ..._000002_create_asset_connections_table.php | 43 ++ ...0003_create_device_installations_table.php | 32 ++ server/src/Auth/Schemas/FleetOps.php | 7 + server/src/Events/TrailerLocationChanged.php | 70 +++ server/src/Exports/TrailerExport.php | 31 ++ .../Concerns/ResolvesFleetOpsApiResources.php | 10 +- .../Controllers/Api/v1/DeviceController.php | 34 +- .../Api/v1/EquipmentController.php | 47 ++ .../Controllers/Api/v1/TrailerController.php | 237 +++++++++ .../Internal/v1/SearchController.php | 16 + .../Internal/v1/TrailerController.php | 147 ++++++ server/src/Http/Filter/EquipmentFilter.php | 20 + server/src/Http/Filter/TrailerFilter.php | 167 +++++++ .../src/Http/Requests/CreateDeviceRequest.php | 2 +- .../Http/Requests/CreateEquipmentRequest.php | 2 +- .../Http/Requests/CreateTrailerRequest.php | 53 +++ .../Http/Requests/UpdateTrailerRequest.php | 7 + .../src/Http/Resources/v1/AssetConnection.php | 24 + server/src/Http/Resources/v1/Device.php | 4 +- server/src/Http/Resources/v1/Equipment.php | 4 +- server/src/Http/Resources/v1/Trailer.php | 53 +++ server/src/Imports/TrailerImport.php | 34 ++ server/src/Models/Asset.php | 47 +- server/src/Models/AssetConnection.php | 43 ++ server/src/Models/Device.php | 41 +- server/src/Models/DeviceInstallation.php | 27 ++ server/src/Models/Equipment.php | 2 + server/src/Models/Trailer.php | 133 ++++++ server/src/Models/Vehicle.php | 13 + .../src/Providers/FleetOpsServiceProvider.php | 6 +- .../Support/Ai/FleetOpsAiQueryResources.php | 22 + .../Reporting/FleetOpsReportSchema.php | 38 ++ .../Support/Telematics/TelematicService.php | 35 ++ server/src/routes.php | 25 + .../Api/DeviceControllerContractsTest.php | 10 +- .../Api/TrailerControllerLifecycleTest.php | 450 ++++++++++++++++++ .../PublicApiControllerInputContractsTest.php | 11 + .../tests/PublicFleetOpsRestEndpointsTest.php | 18 +- server/tests/ReportSchemaContractsTest.php | 4 + server/tests/TelematicsHardeningTest.php | 6 +- server/tests/TrailerSupportCoverageTest.php | 381 +++++++++++++++ server/tests/Unit/Models/EquipmentTest.php | 22 + .../Models/ModelLookupFallbackSeamsTest.php | 10 +- server/tests/Unit/Models/TrailerTest.php | 204 ++++++++ .../TelematicServiceTelemetryTest.php | 93 ++++ .../routes/management-trailers-index-test.js | 52 ++ tests/unit/services/trailer-actions-test.js | 15 + translations/ar-ae.yml | 134 ++++++ translations/bg-bg.yaml | 167 +++++++ translations/en-us.yaml | 171 +++++++ translations/es-pa.yaml | 167 +++++++ translations/fr-fr.yaml | 167 +++++++ translations/mn-mn.yaml | 167 +++++++ translations/pt-br.yaml | 167 +++++++ translations/ru-ru.yaml | 167 +++++++ translations/vi-vn.yaml | 175 +++++++ 138 files changed, 5448 insertions(+), 203 deletions(-) create mode 100644 addon/components/modals/attach-equipment.hbs create mode 100644 addon/components/modals/attach-equipment.js create mode 100644 addon/components/modals/attach-trailer.hbs create mode 100644 addon/components/modals/attach-trailer.js create mode 100644 addon/components/modals/select-trailer.hbs create mode 100644 addon/components/modals/select-trailer.js create mode 100644 addon/components/trailer/card.hbs create mode 100644 addon/components/trailer/card.js create mode 100644 addon/components/trailer/connections.hbs create mode 100644 addon/components/trailer/connections.js create mode 100644 addon/components/trailer/details.hbs create mode 100644 addon/components/trailer/details.js create mode 100644 addon/components/trailer/equipment.hbs create mode 100644 addon/components/trailer/equipment.js create mode 100644 addon/components/trailer/form.hbs create mode 100644 addon/components/trailer/form.js create mode 100644 addon/components/trailer/panel-header.hbs create mode 100644 addon/components/trailer/panel-header.js create mode 100644 addon/components/trailer/related-list.hbs create mode 100644 addon/components/trailer/related-list.js create mode 100644 addon/components/vehicle/details/equipment.hbs create mode 100644 addon/components/vehicle/details/equipment.js create mode 100644 addon/components/vehicle/details/trailers.hbs create mode 100644 addon/components/vehicle/details/trailers.js create mode 100644 addon/controllers/management/trailers/index.js create mode 100644 addon/controllers/management/trailers/index/details.js create mode 100644 addon/controllers/management/trailers/index/edit.js create mode 100644 addon/controllers/management/trailers/index/new.js create mode 100644 addon/routes/management/trailers/index.js create mode 100644 addon/routes/management/trailers/index/details.js create mode 100644 addon/routes/management/trailers/index/details/connections.js create mode 100644 addon/routes/management/trailers/index/details/devices.js create mode 100644 addon/routes/management/trailers/index/details/equipment.js create mode 100644 addon/routes/management/trailers/index/details/index.js create mode 100644 addon/routes/management/trailers/index/details/maintenance-history.js create mode 100644 addon/routes/management/trailers/index/details/positions.js create mode 100644 addon/routes/management/trailers/index/details/schedules.js create mode 100644 addon/routes/management/trailers/index/details/work-orders.js create mode 100644 addon/routes/management/trailers/index/edit.js create mode 100644 addon/routes/management/trailers/index/new.js create mode 100644 addon/routes/management/vehicles/index/details/trailers.js create mode 100644 addon/services/trailer-actions.js create mode 100644 addon/templates/management/trailers/index.hbs create mode 100644 addon/templates/management/trailers/index/details.hbs create mode 100644 addon/templates/management/trailers/index/details/connections.hbs create mode 100644 addon/templates/management/trailers/index/details/devices.hbs create mode 100644 addon/templates/management/trailers/index/details/equipment.hbs create mode 100644 addon/templates/management/trailers/index/details/index.hbs create mode 100644 addon/templates/management/trailers/index/details/maintenance-history.hbs create mode 100644 addon/templates/management/trailers/index/details/positions.hbs create mode 100644 addon/templates/management/trailers/index/details/schedules.hbs create mode 100644 addon/templates/management/trailers/index/details/work-orders.hbs create mode 100644 addon/templates/management/trailers/index/edit.hbs create mode 100644 addon/templates/management/trailers/index/new.hbs create mode 100644 addon/templates/management/vehicles/index/details/trailers.hbs create mode 100644 app/services/trailer-actions.js create mode 100644 server/migrations/2026_09_03_000001_add_first_class_trailer_columns_to_assets_table.php create mode 100644 server/migrations/2026_09_03_000002_create_asset_connections_table.php create mode 100644 server/migrations/2026_09_03_000003_create_device_installations_table.php create mode 100644 server/src/Events/TrailerLocationChanged.php create mode 100644 server/src/Exports/TrailerExport.php create mode 100644 server/src/Http/Controllers/Api/v1/TrailerController.php create mode 100644 server/src/Http/Controllers/Internal/v1/TrailerController.php create mode 100644 server/src/Http/Filter/TrailerFilter.php create mode 100644 server/src/Http/Requests/CreateTrailerRequest.php create mode 100644 server/src/Http/Requests/UpdateTrailerRequest.php create mode 100644 server/src/Http/Resources/v1/AssetConnection.php create mode 100644 server/src/Http/Resources/v1/Trailer.php create mode 100644 server/src/Imports/TrailerImport.php create mode 100644 server/src/Models/AssetConnection.php create mode 100644 server/src/Models/DeviceInstallation.php create mode 100644 server/src/Models/Trailer.php create mode 100644 server/tests/Feature/Http/Api/TrailerControllerLifecycleTest.php create mode 100644 server/tests/TrailerSupportCoverageTest.php create mode 100644 server/tests/Unit/Models/TrailerTest.php create mode 100644 tests/unit/routes/management-trailers-index-test.js create mode 100644 tests/unit/services/trailer-actions-test.js diff --git a/addon/components/cell/attached-vehicle.hbs b/addon/components/cell/attached-vehicle.hbs index 6ad15e212..a79290f88 100644 --- a/addon/components/cell/attached-vehicle.hbs +++ b/addon/components/cell/attached-vehicle.hbs @@ -4,7 +4,7 @@ {{else}}
- Unattached + {{t "trailer.attachment.detached"}}
{{/if}} diff --git a/addon/components/cell/attached-vehicle.js b/addon/components/cell/attached-vehicle.js index 42c1645a8..b34175b44 100644 --- a/addon/components/cell/attached-vehicle.js +++ b/addon/components/cell/attached-vehicle.js @@ -31,13 +31,13 @@ export default class CellAttachedVehicleComponent extends Component { } get hasVehicle() { - return Boolean(this.device?.attachable_uuid && this.isVehicleAttachment); + return Boolean(this.device?.attachable_uuid && this.isSupportedAttachment); } - get isVehicleAttachment() { + get isSupportedAttachment() { const attachableType = `${this.device?.attachable_type ?? ''}`.toLowerCase(); - return !attachableType || attachableType.includes('vehicle'); + return !attachableType || attachableType.includes('vehicle') || attachableType.includes('trailer'); } @action onClick(_vehicle, event) { diff --git a/addon/components/device/panel-tabs/vehicle.hbs b/addon/components/device/panel-tabs/vehicle.hbs index 292b514a8..b591cdf5d 100644 --- a/addon/components/device/panel-tabs/vehicle.hbs +++ b/addon/components/device/panel-tabs/vehicle.hbs @@ -1,21 +1,21 @@
-

Vehicle Attachment

-

Fleet context for telemetry from this device.

+

{{t "device.attachment.title"}}

+

{{t "device.attachment.description"}}

{{#if this.hasVehicle}} {{#if this.canOpenVehicle}} -
@@ -39,15 +39,15 @@
{{n-a this.vehicleSubtitle}}
-
Driver
+
{{t "resource.driver"}}
{{n-a this.vehicleDriverName}}
-
Attached Device
+
{{t "device.attachment.attached-device"}}
{{n-a this.device.displayName this.device.name}}
-
Device Last Seen
+
{{t "device.attachment.last-seen"}}
{{n-a (format-date-fns this.device.last_online_at "dd MMM yyyy, HH:mm")}}
@@ -58,9 +58,9 @@ diff --git a/addon/components/device/panel-tabs/vehicle.js b/addon/components/device/panel-tabs/vehicle.js index 49c1e8d28..05862f2bc 100644 --- a/addon/components/device/panel-tabs/vehicle.js +++ b/addon/components/device/panel-tabs/vehicle.js @@ -7,6 +7,7 @@ export default class DevicePanelTabsVehicleComponent extends Component { @service hostRouter; @service mapManager; @service vehicleActions; + @service trailerActions; get device() { return this.args.resource ?? this.args.model; @@ -16,6 +17,10 @@ export default class DevicePanelTabsVehicleComponent extends Component { return this.device?.attachable; } + get isTrailer() { + return `${this.device?.attachable_type ?? ''}`.toLowerCase().includes('trailer'); + } + get vehicleName() { return this.device?.attached_to_name ?? this.vehicle?.displayName ?? this.vehicle?.display_name ?? this.vehicle?.name; } @@ -58,6 +63,9 @@ export default class DevicePanelTabsVehicleComponent extends Component { @action openVehicle() { if (this.vehicle?.id) { + if (this.isTrailer) { + return this.trailerActions.transition.view(this.vehicle); + } return this.vehicleActions.panel?.view ? this.vehicleActions.panel.view(this.vehicle) : this.hostRouter.transitionTo('console.fleet-ops.management.vehicles.index.details', this.vehicle); diff --git a/addon/components/equipment/form.js b/addon/components/equipment/form.js index cb855a906..d7b369569 100644 --- a/addon/components/equipment/form.js +++ b/addon/components/equipment/form.js @@ -11,20 +11,24 @@ import { task } from 'ember-concurrency'; const TYPE_TO_MODEL = { 'fleet-ops:vehicle': 'vehicle', 'fleet-ops:driver': 'driver', + 'fleet-ops:trailer': 'trailer', 'Fleetbase\\FleetOps\\Models\\Vehicle': 'vehicle', 'Fleetbase\\FleetOps\\Models\\Driver': 'driver', + 'Fleetbase\\FleetOps\\Models\\Trailer': 'trailer', 'fleet-ops:equipment': 'equipment', }; const TYPE_TO_OPTION_VALUE = { 'Fleetbase\\FleetOps\\Models\\Vehicle': 'fleet-ops:vehicle', 'Fleetbase\\FleetOps\\Models\\Driver': 'fleet-ops:driver', + 'Fleetbase\\FleetOps\\Models\\Trailer': 'fleet-ops:trailer', }; export default class EquipmentFormComponent extends Component { @service fetch; @service currentUser; @service notifications; + @service intl; /** Equipment type options. */ equipmentTypeOptions = ['ppe', 'refrigeration_unit', 'tool', 'liftgate', 'ramp', 'container', 'pallet_jack', 'forklift', 'safety_equipment', 'communication_device', 'other']; @@ -36,10 +40,13 @@ export default class EquipmentFormComponent extends Component { * Polymorphic equipable type options — the asset this equipment is attached to. * Each entry has a `value` (stored on the model) and a `label` (displayed in the UI). */ - equipableTypeOptions = [ - { value: 'fleet-ops:vehicle', label: 'Vehicle' }, - { value: 'fleet-ops:driver', label: 'Driver' }, - ]; + get equipableTypeOptions() { + return [ + { value: 'fleet-ops:vehicle', label: this.intl.t('resource.vehicle') }, + { value: 'fleet-ops:trailer', label: this.intl.t('resource.trailer') }, + { value: 'fleet-ops:driver', label: this.intl.t('resource.driver') }, + ]; + } /** Derived Ember Data model name for the currently selected equipable type. */ @tracked equipableModelName = null; diff --git a/addon/components/layout/fleet-ops-sidebar.js b/addon/components/layout/fleet-ops-sidebar.js index f045dd88f..feaee9553 100644 --- a/addon/components/layout/fleet-ops-sidebar.js +++ b/addon/components/layout/fleet-ops-sidebar.js @@ -142,6 +142,7 @@ export default class LayoutFleetOpsSidebarComponent extends Component { ]), this.createItem('menu.drivers', 'id-card', 'management.drivers', 'fleet-ops list driver', 'fleet-ops see driver', ['driver', 'online drivers']), this.createItem('menu.vehicles', 'truck', 'management.vehicles', 'fleet-ops list vehicle', 'fleet-ops see vehicle', ['vehicle', 'track vehicles', 'online vehicles']), + this.createItem('menu.trailers', 'trailer', 'management.trailers', 'fleet-ops list trailer', 'fleet-ops see trailer', ['trailer', 'towed asset', 'reefer', 'flatbed']), this.createItem('menu.fleets', 'user-group', 'management.fleets', 'fleet-ops list fleet', 'fleet-ops see fleet', ['fleet', 'teams']), this.createItem('menu.vendors', 'warehouse', 'management.vendors', 'fleet-ops list vendor', 'fleet-ops see vendor'), this.createItem('menu.contacts', 'address-book', 'management.contacts', 'fleet-ops list contact', 'fleet-ops see contact'), diff --git a/addon/components/maintenance-schedule/form.js b/addon/components/maintenance-schedule/form.js index 7b957b306..a9aeca8a2 100644 --- a/addon/components/maintenance-schedule/form.js +++ b/addon/components/maintenance-schedule/form.js @@ -2,12 +2,14 @@ import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking'; import { isArray } from '@ember/array'; import { action } from '@ember/object'; +import { inject as service } from '@ember/service'; /** * Maps a polymorphic type string to the Ember Data model name used by ModelSelect. */ const TYPE_TO_MODEL = { 'fleet-ops:vehicle': 'vehicle', + 'fleet-ops:trailer': 'trailer', 'fleet-ops:equipment': 'equipment', 'fleet-ops:vendor': 'vendor', 'fleet-ops:contact': 'contact', @@ -21,9 +23,11 @@ const TYPE_TO_MODEL = { */ const MODEL_TO_TYPE = { 'maintenance-subject-vehicle': 'fleet-ops:vehicle', + 'maintenance-subject-trailer': 'fleet-ops:trailer', 'maintenance-subject-equipment': 'fleet-ops:equipment', // Fall-through for raw vehicle/equipment models passed from vehicle-actions.js vehicle: 'fleet-ops:vehicle', + trailer: 'fleet-ops:trailer', equipment: 'fleet-ops:equipment', vendor: 'fleet-ops:vendor', contact: 'fleet-ops:contact', @@ -44,11 +48,6 @@ const ASSIGNEE_MODEL_TO_TYPE = { driver: 'fleet-ops:driver', }; -const SUBJECT_TYPE_OPTIONS = [ - { label: 'Vehicle', value: 'fleet-ops:vehicle' }, - { label: 'Equipment', value: 'fleet-ops:equipment' }, -]; - const ASSIGNEE_TYPE_OPTIONS = [ { label: 'Vendor', value: 'fleet-ops:vendor' }, { label: 'Contact', value: 'fleet-ops:contact' }, @@ -62,6 +61,7 @@ const INTERVAL_METHOD_OPTIONS = [ ]; export default class MaintenanceScheduleFormComponent extends Component { + @service intl; @tracked selectedSubjectType = null; @tracked selectedAssigneeType = null; @tracked subjectModelName = null; @@ -70,7 +70,13 @@ export default class MaintenanceScheduleFormComponent extends Component { @tracked reminderOffsets = []; @tracked newReminderOffset = ''; - subjectTypeOptions = SUBJECT_TYPE_OPTIONS; + get subjectTypeOptions() { + return [ + { label: this.intl.t('resource.vehicle'), value: 'fleet-ops:vehicle' }, + { label: this.intl.t('resource.trailer'), value: 'fleet-ops:trailer' }, + { label: this.intl.t('resource.equipment'), value: 'fleet-ops:equipment' }, + ]; + } assigneeTypeOptions = ASSIGNEE_TYPE_OPTIONS; intervalMethodOptions = INTERVAL_METHOD_OPTIONS; @@ -103,7 +109,7 @@ export default class MaintenanceScheduleFormComponent extends Component { const modelName = subject.constructor?.modelName ?? subject.modelName; const typeValue = MODEL_TO_TYPE[modelName] ?? null; if (typeValue) { - this.selectedSubjectType = SUBJECT_TYPE_OPTIONS.find((o) => o.value === typeValue) ?? null; + this.selectedSubjectType = this.subjectTypeOptions.find((o) => o.value === typeValue) ?? null; this.subjectModelName = TYPE_TO_MODEL[typeValue] ?? null; } } diff --git a/addon/components/maintenance/form.js b/addon/components/maintenance/form.js index cd633049e..d5339843c 100644 --- a/addon/components/maintenance/form.js +++ b/addon/components/maintenance/form.js @@ -1,12 +1,14 @@ import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking'; import { action } from '@ember/object'; +import { inject as service } from '@ember/service'; /** * Maps a polymorphic type value to the Ember Data model name used by ModelSelect. */ const TYPE_TO_MODEL = { 'fleet-ops:vehicle': 'vehicle', + 'fleet-ops:trailer': 'trailer', 'fleet-ops:driver': 'driver', 'fleet-ops:equipment': 'equipment', 'fleet-ops:vendor': 'vendor', @@ -20,9 +22,11 @@ const TYPE_TO_MODEL = { */ const MAINTAINABLE_MODEL_TO_TYPE = { 'maintenance-subject-vehicle': 'fleet-ops:vehicle', + 'maintenance-subject-trailer': 'fleet-ops:trailer', 'maintenance-subject-equipment': 'fleet-ops:equipment', // Fall-through for raw models passed from vehicle-actions.js vehicle: 'fleet-ops:vehicle', + trailer: 'fleet-ops:trailer', equipment: 'fleet-ops:equipment', }; @@ -40,6 +44,7 @@ const PERFORMED_BY_MODEL_TO_TYPE = { }; export default class MaintenanceFormComponent extends Component { + @service intl; /** Maintenance type options — the category of maintenance activity. */ maintenanceTypeOptions = ['preventive', 'corrective', 'predictive', 'routine', 'emergency', 'inspection', 'repair', 'replacement', 'calibration']; @@ -54,10 +59,13 @@ export default class MaintenanceFormComponent extends Component { * Uses label/value objects so the PowerSelect trigger shows a human-readable * label instead of the raw model type string. */ - maintainableTypeOptions = [ - { value: 'fleet-ops:vehicle', label: 'Vehicle' }, - { value: 'fleet-ops:equipment', label: 'Equipment' }, - ]; + get maintainableTypeOptions() { + return [ + { value: 'fleet-ops:vehicle', label: this.intl.t('resource.vehicle') }, + { value: 'fleet-ops:trailer', label: this.intl.t('resource.trailer') }, + { value: 'fleet-ops:equipment', label: this.intl.t('resource.equipment') }, + ]; + } /** * Polymorphic performed-by type options — who carried out the maintenance. diff --git a/addon/components/modals/attach-equipment.hbs b/addon/components/modals/attach-equipment.hbs new file mode 100644 index 000000000..45d8e87af --- /dev/null +++ b/addon/components/modals/attach-equipment.hbs @@ -0,0 +1,9 @@ + + + diff --git a/addon/components/modals/attach-equipment.js b/addon/components/modals/attach-equipment.js new file mode 100644 index 000000000..197f863c3 --- /dev/null +++ b/addon/components/modals/attach-equipment.js @@ -0,0 +1,3 @@ +import Component from '@glimmer/component'; + +export default class ModalsAttachEquipmentComponent extends Component {} diff --git a/addon/components/modals/attach-telematic-device.hbs b/addon/components/modals/attach-telematic-device.hbs index 933de41a2..0ae518173 100644 --- a/addon/components/modals/attach-telematic-device.hbs +++ b/addon/components/modals/attach-telematic-device.hbs @@ -1,25 +1,30 @@ diff --git a/addon/components/modals/attach-telematic-device.js b/addon/components/modals/attach-telematic-device.js index a975b9960..21daea432 100644 --- a/addon/components/modals/attach-telematic-device.js +++ b/addon/components/modals/attach-telematic-device.js @@ -1,3 +1,13 @@ import Component from '@glimmer/component'; +import { inject as service } from '@ember/service'; -export default class ModalsAttachTelematicDeviceComponent extends Component {} +export default class ModalsAttachTelematicDeviceComponent extends Component { + @service intl; + + get assetTypeOptions() { + return [ + { value: 'fleet-ops:vehicle', model: 'vehicle', label: this.intl.t('resource.vehicle') }, + { value: 'fleet-ops:trailer', model: 'trailer', label: this.intl.t('resource.trailer') }, + ]; + } +} diff --git a/addon/components/modals/attach-trailer.hbs b/addon/components/modals/attach-trailer.hbs new file mode 100644 index 000000000..c7aa94ea2 --- /dev/null +++ b/addon/components/modals/attach-trailer.hbs @@ -0,0 +1,18 @@ + + + diff --git a/addon/components/modals/attach-trailer.js b/addon/components/modals/attach-trailer.js new file mode 100644 index 000000000..94a5bfdba --- /dev/null +++ b/addon/components/modals/attach-trailer.js @@ -0,0 +1 @@ +export { default } from '@fleetbase/ember-core/components/modal/base'; diff --git a/addon/components/modals/select-trailer.hbs b/addon/components/modals/select-trailer.hbs new file mode 100644 index 000000000..69d184f76 --- /dev/null +++ b/addon/components/modals/select-trailer.hbs @@ -0,0 +1 @@ + diff --git a/addon/components/modals/select-trailer.js b/addon/components/modals/select-trailer.js new file mode 100644 index 000000000..94a5bfdba --- /dev/null +++ b/addon/components/modals/select-trailer.js @@ -0,0 +1 @@ +export { default } from '@fleetbase/ember-core/components/modal/base'; diff --git a/addon/components/trailer/card.hbs b/addon/components/trailer/card.hbs new file mode 100644 index 000000000..402727e96 --- /dev/null +++ b/addon/components/trailer/card.hbs @@ -0,0 +1,5 @@ + +
{{or @resource.name @resource.yearMakeModel}}
{{or @resource.plate_number @resource.vin @resource.public_id}}
+
{{@resource.status}}
+
{{t "trailer.fields.updated"}}: {{@resource.updatedAt}}
+
diff --git a/addon/components/trailer/card.js b/addon/components/trailer/card.js new file mode 100644 index 000000000..d9776060c --- /dev/null +++ b/addon/components/trailer/card.js @@ -0,0 +1,5 @@ +import Component from '@glimmer/component'; +import { inject as service } from '@ember/service'; +export default class TrailerCardComponent extends Component { + @service trailerActions; +} diff --git a/addon/components/trailer/connections.hbs b/addon/components/trailer/connections.hbs new file mode 100644 index 000000000..c8c10a881 --- /dev/null +++ b/addon/components/trailer/connections.hbs @@ -0,0 +1 @@ +

{{t "trailer.tabs.connections"}}

{{#if @resource.connections.length}}{{#each @resource.connections as |connection|}}{{/each}}
{{t "trailer.fields.vehicle"}}{{t "trailer.fields.connected-at"}}{{t "trailer.fields.disconnected-at"}}{{t "common.status"}}
{{n-a connection.vehicle.name}}{{n-a connection.connected_at}}{{n-a connection.disconnected_at}}
{{else}}
{{t "trailer.empty.connections"}}
{{/if}}
diff --git a/addon/components/trailer/connections.js b/addon/components/trailer/connections.js new file mode 100644 index 000000000..b1d2fe501 --- /dev/null +++ b/addon/components/trailer/connections.js @@ -0,0 +1,2 @@ +import Component from '@glimmer/component'; +export default class TrailerConnectionsComponent extends Component {} diff --git a/addon/components/trailer/details.hbs b/addon/components/trailer/details.hbs new file mode 100644 index 000000000..a2db1abbf --- /dev/null +++ b/addon/components/trailer/details.hbs @@ -0,0 +1,8 @@ +
+
{{t "trailer.fields.type"}}
{{n-a @resource.type}}
{{t "column.plate-number"}}
{{n-a @resource.plate_number}}
{{t "trailer.fields.vin"}}
{{n-a @resource.vin}}
{{t "trailer.fields.serial-number"}}
{{n-a @resource.serial_number}}
{{t "column.make"}}
{{n-a @resource.make}}
{{t "column.model"}}
{{n-a @resource.model}}
{{t "column.year"}}
{{n-a @resource.year}}
{{t "common.status"}}
+
{{t "trailer.fields.vehicle"}}
{{n-a @resource.current_vehicle.name}}
{{t "trailer.fields.attachment-state"}}
+
{{t "trailer.fields.connectivity"}}
{{t "trailer.fields.last-online"}}
{{n-a @resource.last_online_at}}
{{t "common.location"}}
{{n-a @resource.current_location}}
{{t "trailer.fields.devices-count"}}
{{or @resource.devices_count 0}}
+
{{t "trailer.fields.length"}}
{{n-a @resource.length}}
{{t "trailer.fields.gvwr"}}
{{n-a @resource.gvwr}}
{{t "trailer.fields.payload"}}
{{n-a @resource.payload_capacity}}
{{t "trailer.fields.axles"}}
{{n-a @resource.axle_count}}
+ + +
diff --git a/addon/components/trailer/details.js b/addon/components/trailer/details.js new file mode 100644 index 000000000..9c8b13021 --- /dev/null +++ b/addon/components/trailer/details.js @@ -0,0 +1,2 @@ +import Component from '@glimmer/component'; +export default class TrailerDetailsComponent extends Component {} diff --git a/addon/components/trailer/equipment.hbs b/addon/components/trailer/equipment.hbs new file mode 100644 index 000000000..42a026698 --- /dev/null +++ b/addon/components/trailer/equipment.hbs @@ -0,0 +1 @@ +
{{#if this.load.isRunning}}{{else if this.equipment.length}}
{{#each this.equipment as |item|}}
{{item.name}}
{{or item.type item.serial_number}}
{{/each}}
{{else}}
{{t "trailer.empty.equipment"}}
{{/if}}
diff --git a/addon/components/trailer/equipment.js b/addon/components/trailer/equipment.js new file mode 100644 index 000000000..26e9a1aeb --- /dev/null +++ b/addon/components/trailer/equipment.js @@ -0,0 +1,31 @@ +import Component from '@glimmer/component'; +import { inject as service } from '@ember/service'; +import { tracked } from '@glimmer/tracking'; +import { task } from 'ember-concurrency'; +export default class TrailerEquipmentComponent extends Component { + @service store; + @service notifications; + @service equipmentActions; + @service trailerActions; + @tracked equipment = []; + constructor() { + super(...arguments); + this.load.perform(); + } + @task *load() { + try { + this.equipment = yield this.store.query('equipment', { + equipable_type: 'fleet-ops:trailer', + equipable: this.args.resource.public_id ?? this.args.resource.id, + sort: '-created_at', + }); + } catch (error) { + this.notifications.serverError(error); + } + } + attach = () => this.trailerActions.attachEquipment(this.args.resource); + detach = async (equipment) => { + await this.trailerActions.detachEquipment(this.args.resource, equipment); + this.load.perform(); + }; +} diff --git a/addon/components/trailer/form.hbs b/addon/components/trailer/form.hbs new file mode 100644 index 000000000..348def397 --- /dev/null +++ b/addon/components/trailer/form.hbs @@ -0,0 +1,43 @@ +
+ + +
{{@resource.name}}
+
+ + +