UNPKG

@fleetbase/fleetops-data

Version:

Fleetbase Fleet-Ops based models, serializers, transforms, adapters and GeoJson utility functions.

140 lines (118 loc) 4.15 kB
import Model, { attr, belongsTo, hasMany } from '@ember-data/model'; import { computed } from '@ember/object'; import { format as formatDate, isValid as isValidDate, formatDistanceToNow } from 'date-fns'; /** * Manifest model * * Represents a committed delivery plan for a specific vehicle and (optionally) * driver. Generated by the Orchestrator commit step. Contains an ordered list * of ManifestStops representing the physical locations to visit in sequence. * * Status lifecycle: draft → active → in_progress → completed | cancelled */ export default class ManifestModel extends Model { /** @ids */ @attr('string') public_id; @attr('string') internal_id; @attr('string') company_uuid; @attr('string') driver_uuid; @attr('string') vehicle_uuid; /** @relationships */ @belongsTo('driver', { async: false }) driver; @belongsTo('vehicle', { async: false }) vehicle; @hasMany('manifest-stop', { async: false }) stops; /** @attributes */ @attr('string') status; @attr('string') notes; /** @computed/appended by backend */ @attr('string') driver_name; @attr('string') vehicle_name; @attr('number') stop_count; @attr('number') completed_stops; @attr('number') pending_stops; /** @totals from VROOM */ @attr('number') total_distance_m; @attr('number') total_duration_s; /** @meta */ @attr('raw') meta; /** @dates */ @attr('date') scheduled_date; @attr('date') started_at; @attr('date') completed_at; @attr('date') deleted_at; @attr('date') created_at; @attr('date') updated_at; /** @computed */ @computed('status') get isDraft() { return this.status === 'draft'; } @computed('status') get isActive() { return this.status === 'active'; } @computed('status') get isInProgress() { return this.status === 'in_progress'; } @computed('status') get isCompleted() { return this.status === 'completed'; } @computed('status') get isCancelled() { return this.status === 'cancelled'; } @computed('status') get statusLabel() { const labels = { draft: 'Draft', active: 'Active', in_progress: 'In Progress', completed: 'Completed', cancelled: 'Cancelled', }; return labels[this.status] ?? this.status; } @computed('stop_count', 'completed_stops') get progressPercent() { if (!this.stop_count || this.stop_count === 0) { return 0; } return Math.round(((this.completed_stops ?? 0) / this.stop_count) * 100); } @computed('total_distance_m') get totalDistanceKm() { return this.total_distance_m ? (this.total_distance_m / 1000).toFixed(1) : '0.0'; } @computed('total_duration_s') get totalDurationFormatted() { if (!this.total_duration_s) { return '0m'; } const hours = Math.floor(this.total_duration_s / 3600); const minutes = Math.floor((this.total_duration_s % 3600) / 60); return hours > 0 ? `${hours}h ${minutes}m` : `${minutes}m`; } @computed('updated_at') get updatedAgo() { if (!isValidDate(this.updated_at)) { return null; } return formatDistanceToNow(this.updated_at); } @computed('updated_at') get updatedAt() { if (!isValidDate(this.updated_at)) { return null; } return formatDate(this.updated_at, 'yyyy-MM-dd HH:mm'); } @computed('created_at') get createdAt() { if (!isValidDate(this.created_at)) { return null; } return formatDate(this.created_at, 'yyyy-MM-dd HH:mm'); } @computed('created_at') get createdAgo() { if (!isValidDate(this.created_at)) { return null; } return formatDistanceToNow(this.created_at); } @computed('scheduled_date') get scheduledDateFormatted() { if (!isValidDate(this.scheduled_date)) { return null; } return formatDate(this.scheduled_date, 'dd MMM yyyy'); } }