@fleetbase/fleetops-data
Version:
Fleetbase Fleet-Ops based models, serializers, transforms, adapters and GeoJson utility functions.
140 lines (118 loc) • 4.15 kB
JavaScript
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 */
public_id;
internal_id;
company_uuid;
driver_uuid;
vehicle_uuid;
/** @relationships */
driver;
vehicle;
stops;
/** @attributes */
status;
notes;
/** @computed/appended by backend */
driver_name;
vehicle_name;
stop_count;
completed_stops;
pending_stops;
/** @totals from VROOM */
total_distance_m;
total_duration_s;
/** @meta */
meta;
/** @dates */
scheduled_date;
started_at;
completed_at;
deleted_at;
created_at;
updated_at;
/** @computed */
get isDraft() {
return this.status === 'draft';
}
get isActive() {
return this.status === 'active';
}
get isInProgress() {
return this.status === 'in_progress';
}
get isCompleted() {
return this.status === 'completed';
}
get isCancelled() {
return this.status === 'cancelled';
}
get statusLabel() {
const labels = {
draft: 'Draft',
active: 'Active',
in_progress: 'In Progress',
completed: 'Completed',
cancelled: 'Cancelled',
};
return labels[this.status] ?? this.status;
}
get progressPercent() {
if (!this.stop_count || this.stop_count === 0) {
return 0;
}
return Math.round(((this.completed_stops ?? 0) / this.stop_count) * 100);
}
get totalDistanceKm() {
return this.total_distance_m ? (this.total_distance_m / 1000).toFixed(1) : '0.0';
}
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`;
}
get updatedAgo() {
if (!isValidDate(this.updated_at)) {
return null;
}
return formatDistanceToNow(this.updated_at);
}
get updatedAt() {
if (!isValidDate(this.updated_at)) {
return null;
}
return formatDate(this.updated_at, 'yyyy-MM-dd HH:mm');
}
get createdAt() {
if (!isValidDate(this.created_at)) {
return null;
}
return formatDate(this.created_at, 'yyyy-MM-dd HH:mm');
}
get createdAgo() {
if (!isValidDate(this.created_at)) {
return null;
}
return formatDistanceToNow(this.created_at);
}
get scheduledDateFormatted() {
if (!isValidDate(this.scheduled_date)) {
return null;
}
return formatDate(this.scheduled_date, 'dd MMM yyyy');
}
}