From 7e27d5a60d6f096f2ef52228bf5ee63c1306c528 Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Fri, 9 Oct 2026 16:57:26 +0800 Subject: [PATCH] refactor(descriptors): build the shared resource descriptors from fleetops-data The driver, vehicle, customer, contact, place, order, vendor and fleet descriptors, their pill, summary, identity-cell and select-option wrappers, and the styled placeholder images now come from @fleetbase/fleetops-data, so every extension that lists those resources renders them the same way without loading this engine. FleetOps keeps registering all 53 descriptors; for the eight shared ones it takes the shared descriptor and attaches its own panel opener, which the registry keeps over the lazy opener the shared package registers. The helpers module re-exports the shared building blocks and keeps the openers that only make sense inside this engine; the placeholder-images module keeps its import path as a re-export. --- addon/components/cell/contact-identity.hbs | 1 - addon/components/cell/customer-identity.hbs | 1 - addon/components/cell/driver-identity.hbs | 1 - addon/components/cell/fleet-identity.hbs | 1 - addon/components/cell/order-identity.hbs | 1 - addon/components/cell/place-identity.hbs | 1 - addon/components/cell/vehicle-identity.hbs | 1 - addon/components/cell/vendor-identity.hbs | 1 - addon/components/contact/pill.hbs | 24 --- addon/components/contact/summary.hbs | 1 - addon/components/customer/pill.hbs | 24 --- addon/components/customer/summary.hbs | 1 - addon/components/driver/pill.hbs | 24 --- addon/components/driver/summary.hbs | 1 - addon/components/fleet/pill.hbs | 24 --- addon/components/fleet/summary.hbs | 1 - addon/components/order/pill.hbs | 24 --- addon/components/order/summary.hbs | 1 - addon/components/place/pill.hbs | 24 --- addon/components/place/summary.hbs | 1 - addon/components/select-option/contact.hbs | 12 -- addon/components/select-option/customer.hbs | 12 -- addon/components/select-option/driver.hbs | 12 -- addon/components/select-option/fleet.hbs | 12 -- addon/components/select-option/order.hbs | 12 -- addon/components/select-option/place.hbs | 12 -- addon/components/select-option/vehicle.hbs | 12 -- addon/components/select-option/vendor.hbs | 12 -- addon/components/vehicle/pill.hbs | 24 --- addon/components/vehicle/summary.hbs | 1 - addon/components/vendor/pill.hbs | 24 --- addon/components/vendor/summary.hbs | 1 - addon/utils/placeholder-images.js | 73 +-------- addon/utils/resource-descriptors/assets.js | 59 +------ addon/utils/resource-descriptors/helpers.js | 145 +++--------------- .../utils/resource-descriptors/operations.js | 63 +------- addon/utils/resource-descriptors/people.js | 145 +----------------- addon/utils/resource-descriptors/shared.js | 18 +++ app/components/cell/contact-identity.js | 1 - app/components/cell/customer-identity.js | 1 - app/components/cell/driver-identity.js | 1 - app/components/cell/fleet-identity.js | 1 - app/components/cell/order-identity.js | 1 - app/components/cell/place-identity.js | 1 - app/components/cell/vehicle-identity.js | 1 - app/components/cell/vendor-identity.js | 1 - app/components/contact/pill.js | 1 - app/components/contact/summary.js | 1 - app/components/customer/pill.js | 1 - app/components/customer/summary.js | 1 - app/components/driver/pill.js | 1 - app/components/driver/summary.js | 1 - app/components/fleet/pill.js | 1 - app/components/fleet/summary.js | 1 - app/components/order/pill.js | 1 - app/components/order/summary.js | 1 - app/components/place/pill.js | 1 - app/components/place/summary.js | 1 - app/components/select-option/contact.js | 1 - app/components/select-option/customer.js | 1 - app/components/select-option/driver.js | 1 - app/components/select-option/fleet.js | 1 - app/components/select-option/order.js | 1 - app/components/select-option/place.js | 1 - app/components/select-option/vehicle.js | 1 - app/components/select-option/vendor.js | 1 - app/components/vehicle/pill.js | 1 - app/components/vehicle/summary.js | 1 - app/components/vendor/pill.js | 1 - app/components/vendor/summary.js | 1 - 70 files changed, 52 insertions(+), 787 deletions(-) delete mode 100644 addon/components/cell/contact-identity.hbs delete mode 100644 addon/components/cell/customer-identity.hbs delete mode 100644 addon/components/cell/driver-identity.hbs delete mode 100644 addon/components/cell/fleet-identity.hbs delete mode 100644 addon/components/cell/order-identity.hbs delete mode 100644 addon/components/cell/place-identity.hbs delete mode 100644 addon/components/cell/vehicle-identity.hbs delete mode 100644 addon/components/cell/vendor-identity.hbs delete mode 100644 addon/components/contact/pill.hbs delete mode 100644 addon/components/contact/summary.hbs delete mode 100644 addon/components/customer/pill.hbs delete mode 100644 addon/components/customer/summary.hbs delete mode 100644 addon/components/driver/pill.hbs delete mode 100644 addon/components/driver/summary.hbs delete mode 100644 addon/components/fleet/pill.hbs delete mode 100644 addon/components/fleet/summary.hbs delete mode 100644 addon/components/order/pill.hbs delete mode 100644 addon/components/order/summary.hbs delete mode 100644 addon/components/place/pill.hbs delete mode 100644 addon/components/place/summary.hbs delete mode 100644 addon/components/select-option/contact.hbs delete mode 100644 addon/components/select-option/customer.hbs delete mode 100644 addon/components/select-option/driver.hbs delete mode 100644 addon/components/select-option/fleet.hbs delete mode 100644 addon/components/select-option/order.hbs delete mode 100644 addon/components/select-option/place.hbs delete mode 100644 addon/components/select-option/vehicle.hbs delete mode 100644 addon/components/select-option/vendor.hbs delete mode 100644 addon/components/vehicle/pill.hbs delete mode 100644 addon/components/vehicle/summary.hbs delete mode 100644 addon/components/vendor/pill.hbs delete mode 100644 addon/components/vendor/summary.hbs create mode 100644 addon/utils/resource-descriptors/shared.js delete mode 100644 app/components/cell/contact-identity.js delete mode 100644 app/components/cell/customer-identity.js delete mode 100644 app/components/cell/driver-identity.js delete mode 100644 app/components/cell/fleet-identity.js delete mode 100644 app/components/cell/order-identity.js delete mode 100644 app/components/cell/place-identity.js delete mode 100644 app/components/cell/vehicle-identity.js delete mode 100644 app/components/cell/vendor-identity.js delete mode 100644 app/components/contact/pill.js delete mode 100644 app/components/contact/summary.js delete mode 100644 app/components/customer/pill.js delete mode 100644 app/components/customer/summary.js delete mode 100644 app/components/driver/pill.js delete mode 100644 app/components/driver/summary.js delete mode 100644 app/components/fleet/pill.js delete mode 100644 app/components/fleet/summary.js delete mode 100644 app/components/order/pill.js delete mode 100644 app/components/order/summary.js delete mode 100644 app/components/place/pill.js delete mode 100644 app/components/place/summary.js delete mode 100644 app/components/select-option/contact.js delete mode 100644 app/components/select-option/customer.js delete mode 100644 app/components/select-option/driver.js delete mode 100644 app/components/select-option/fleet.js delete mode 100644 app/components/select-option/order.js delete mode 100644 app/components/select-option/place.js delete mode 100644 app/components/select-option/vehicle.js delete mode 100644 app/components/select-option/vendor.js delete mode 100644 app/components/vehicle/pill.js delete mode 100644 app/components/vehicle/summary.js delete mode 100644 app/components/vendor/pill.js delete mode 100644 app/components/vendor/summary.js diff --git a/addon/components/cell/contact-identity.hbs b/addon/components/cell/contact-identity.hbs deleted file mode 100644 index 853719f46..000000000 --- a/addon/components/cell/contact-identity.hbs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/addon/components/cell/customer-identity.hbs b/addon/components/cell/customer-identity.hbs deleted file mode 100644 index 2a25fec37..000000000 --- a/addon/components/cell/customer-identity.hbs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/addon/components/cell/driver-identity.hbs b/addon/components/cell/driver-identity.hbs deleted file mode 100644 index 24624a8bd..000000000 --- a/addon/components/cell/driver-identity.hbs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/addon/components/cell/fleet-identity.hbs b/addon/components/cell/fleet-identity.hbs deleted file mode 100644 index 2efe4afc3..000000000 --- a/addon/components/cell/fleet-identity.hbs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/addon/components/cell/order-identity.hbs b/addon/components/cell/order-identity.hbs deleted file mode 100644 index f6e6ddebb..000000000 --- a/addon/components/cell/order-identity.hbs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/addon/components/cell/place-identity.hbs b/addon/components/cell/place-identity.hbs deleted file mode 100644 index eea619a5e..000000000 --- a/addon/components/cell/place-identity.hbs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/addon/components/cell/vehicle-identity.hbs b/addon/components/cell/vehicle-identity.hbs deleted file mode 100644 index 3374e7e79..000000000 --- a/addon/components/cell/vehicle-identity.hbs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/addon/components/cell/vendor-identity.hbs b/addon/components/cell/vendor-identity.hbs deleted file mode 100644 index 364f72b01..000000000 --- a/addon/components/cell/vendor-identity.hbs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/addon/components/contact/pill.hbs b/addon/components/contact/pill.hbs deleted file mode 100644 index 43118e203..000000000 --- a/addon/components/contact/pill.hbs +++ /dev/null @@ -1,24 +0,0 @@ - - {{yield resource}} - diff --git a/addon/components/contact/summary.hbs b/addon/components/contact/summary.hbs deleted file mode 100644 index b27212aac..000000000 --- a/addon/components/contact/summary.hbs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/addon/components/customer/pill.hbs b/addon/components/customer/pill.hbs deleted file mode 100644 index 160ea62ba..000000000 --- a/addon/components/customer/pill.hbs +++ /dev/null @@ -1,24 +0,0 @@ - - {{yield resource}} - diff --git a/addon/components/customer/summary.hbs b/addon/components/customer/summary.hbs deleted file mode 100644 index 6c168790d..000000000 --- a/addon/components/customer/summary.hbs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/addon/components/driver/pill.hbs b/addon/components/driver/pill.hbs deleted file mode 100644 index cefeed497..000000000 --- a/addon/components/driver/pill.hbs +++ /dev/null @@ -1,24 +0,0 @@ - - {{yield resource}} - diff --git a/addon/components/driver/summary.hbs b/addon/components/driver/summary.hbs deleted file mode 100644 index 2f2834fa9..000000000 --- a/addon/components/driver/summary.hbs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/addon/components/fleet/pill.hbs b/addon/components/fleet/pill.hbs deleted file mode 100644 index 68f25548d..000000000 --- a/addon/components/fleet/pill.hbs +++ /dev/null @@ -1,24 +0,0 @@ - - {{yield resource}} - diff --git a/addon/components/fleet/summary.hbs b/addon/components/fleet/summary.hbs deleted file mode 100644 index fc035f943..000000000 --- a/addon/components/fleet/summary.hbs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/addon/components/order/pill.hbs b/addon/components/order/pill.hbs deleted file mode 100644 index 434fc468c..000000000 --- a/addon/components/order/pill.hbs +++ /dev/null @@ -1,24 +0,0 @@ - - {{yield resource}} - diff --git a/addon/components/order/summary.hbs b/addon/components/order/summary.hbs deleted file mode 100644 index f3f42a464..000000000 --- a/addon/components/order/summary.hbs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/addon/components/place/pill.hbs b/addon/components/place/pill.hbs deleted file mode 100644 index 0b96aa8f5..000000000 --- a/addon/components/place/pill.hbs +++ /dev/null @@ -1,24 +0,0 @@ - - {{yield resource}} - diff --git a/addon/components/place/summary.hbs b/addon/components/place/summary.hbs deleted file mode 100644 index a96ae6a4b..000000000 --- a/addon/components/place/summary.hbs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/addon/components/select-option/contact.hbs b/addon/components/select-option/contact.hbs deleted file mode 100644 index 08e782a09..000000000 --- a/addon/components/select-option/contact.hbs +++ /dev/null @@ -1,12 +0,0 @@ - diff --git a/addon/components/select-option/customer.hbs b/addon/components/select-option/customer.hbs deleted file mode 100644 index 5c626e92f..000000000 --- a/addon/components/select-option/customer.hbs +++ /dev/null @@ -1,12 +0,0 @@ - diff --git a/addon/components/select-option/driver.hbs b/addon/components/select-option/driver.hbs deleted file mode 100644 index 06f2f8fa1..000000000 --- a/addon/components/select-option/driver.hbs +++ /dev/null @@ -1,12 +0,0 @@ - diff --git a/addon/components/select-option/fleet.hbs b/addon/components/select-option/fleet.hbs deleted file mode 100644 index 88894a8dd..000000000 --- a/addon/components/select-option/fleet.hbs +++ /dev/null @@ -1,12 +0,0 @@ - diff --git a/addon/components/select-option/order.hbs b/addon/components/select-option/order.hbs deleted file mode 100644 index d8da9184c..000000000 --- a/addon/components/select-option/order.hbs +++ /dev/null @@ -1,12 +0,0 @@ - diff --git a/addon/components/select-option/place.hbs b/addon/components/select-option/place.hbs deleted file mode 100644 index 1e65f260b..000000000 --- a/addon/components/select-option/place.hbs +++ /dev/null @@ -1,12 +0,0 @@ - diff --git a/addon/components/select-option/vehicle.hbs b/addon/components/select-option/vehicle.hbs deleted file mode 100644 index d144b4962..000000000 --- a/addon/components/select-option/vehicle.hbs +++ /dev/null @@ -1,12 +0,0 @@ - diff --git a/addon/components/select-option/vendor.hbs b/addon/components/select-option/vendor.hbs deleted file mode 100644 index 83355a005..000000000 --- a/addon/components/select-option/vendor.hbs +++ /dev/null @@ -1,12 +0,0 @@ - diff --git a/addon/components/vehicle/pill.hbs b/addon/components/vehicle/pill.hbs deleted file mode 100644 index 16e8dc110..000000000 --- a/addon/components/vehicle/pill.hbs +++ /dev/null @@ -1,24 +0,0 @@ - - {{yield resource}} - diff --git a/addon/components/vehicle/summary.hbs b/addon/components/vehicle/summary.hbs deleted file mode 100644 index 577d3938c..000000000 --- a/addon/components/vehicle/summary.hbs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/addon/components/vendor/pill.hbs b/addon/components/vendor/pill.hbs deleted file mode 100644 index 4fcf04ef5..000000000 --- a/addon/components/vendor/pill.hbs +++ /dev/null @@ -1,24 +0,0 @@ - - {{yield resource}} - diff --git a/addon/components/vendor/summary.hbs b/addon/components/vendor/summary.hbs deleted file mode 100644 index c231423b0..000000000 --- a/addon/components/vendor/summary.hbs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/addon/utils/placeholder-images.js b/addon/utils/placeholder-images.js index 28b8e36f4..f3af16abc 100644 --- a/addon/utils/placeholder-images.js +++ b/addon/utils/placeholder-images.js @@ -1,71 +1,6 @@ -import config from 'ember-get-config'; - /** - * Flat silhouettes shown wherever a resource has no photo, in one consistent style - * across the console. They are inline data URIs so the engine does not depend on a - * hosted asset; a console can override any of them through - * `defaultValues.placeholders.` in its environment config. + * The styled placeholder silhouettes now live in `@fleetbase/fleetops-data`, + * where every extension that shows a driver, vehicle or contact can reach + * them. This module keeps the FleetOps import path working. */ -export const PLACEHOLDER_IMAGES = { - trailer: - 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NDAgNDgwIiB3aWR0aD0iNjQwIiBoZWlnaHQ9IjQ4MCI+PGcgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIj48cmVjdCB4PSI4OCIgeT0iMTI4IiB3aWR0aD0iNDcwIiBoZWlnaHQ9IjE5NiIgcng9IjE2IiBmaWxsPSIjYmRiZGJkIiBzdHJva2U9IiNkZWRlZGUiIHN0cm9rZS13aWR0aD0iMTAiLz48cmVjdCB4PSIxMDgiIHk9IjE1MCIgd2lkdGg9IjQzMCIgaGVpZ2h0PSIxNTIiIHJ4PSI4IiBmaWxsPSIjYzljOWM5Ii8+PHBhdGggZD0iTTEyOCAxNzhoMzkwTTEyOCAyMjZoMzkwTTEyOCAyNzRoMzkwIiBzdHJva2U9IiNiZGJkYmQiIHN0cm9rZS13aWR0aD0iNiIvPjxyZWN0IHg9IjUyMiIgeT0iMTQ2IiB3aWR0aD0iMTAiIGhlaWdodD0iMTYwIiByeD0iNCIgZmlsbD0iI2IwYjBiMCIvPjxyZWN0IHg9Ijg4IiB5PSIzMjQiIHdpZHRoPSI0NzAiIGhlaWdodD0iMTgiIHJ4PSI2IiBmaWxsPSIjYTlhOWE5Ii8+PHJlY3QgeD0iMTU2IiB5PSIzNDIiIHdpZHRoPSIxNCIgaGVpZ2h0PSI1MiIgcng9IjQiIGZpbGw9IiNhOWE5YTkiLz48cmVjdCB4PSIxNDYiIHk9IjM5MiIgd2lkdGg9IjM0IiBoZWlnaHQ9IjEwIiByeD0iNCIgZmlsbD0iI2E5YTlhOSIvPjxyZWN0IHg9Ijg4IiB5PSIzNDIiIHdpZHRoPSI1MiIgaGVpZ2h0PSIxNiIgcng9IjYiIGZpbGw9IiNhOWE5YTkiLz48Y2lyY2xlIGN4PSI0MTIiIGN5PSIzNjYiIHI9IjQwIiBmaWxsPSIjOGY4ZjhmIiBzdHJva2U9IiNkZWRlZGUiIHN0cm9rZS13aWR0aD0iOCIvPjxjaXJjbGUgY3g9IjQxMiIgY3k9IjM2NiIgcj0iMTYiIGZpbGw9IiNkOWQ5ZDkiLz48Y2lyY2xlIGN4PSI1MDAiIGN5PSIzNjYiIHI9IjQwIiBmaWxsPSIjOGY4ZjhmIiBzdHJva2U9IiNkZWRlZGUiIHN0cm9rZS13aWR0aD0iOCIvPjxjaXJjbGUgY3g9IjUwMCIgY3k9IjM2NiIgcj0iMTYiIGZpbGw9IiNkOWQ5ZDkiLz48L2c+PC9zdmc+', - vehicle: - 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NDAgNDgwIiB3aWR0aD0iNjQwIiBoZWlnaHQ9IjQ4MCI+PGcgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIDApIHNjYWxlKDEuMCkiIG9wYWNpdHk9IjEuMCI+PHJlY3QgeD0iMjE2IiB5PSIxMzIiIHdpZHRoPSIzNTAiIGhlaWdodD0iMTkyIiByeD0iMTQiIGZpbGw9IiNiZGJkYmQiIHN0cm9rZT0iI2RlZGVkZSIgc3Ryb2tlLXdpZHRoPSIxMCIvPjxyZWN0IHg9IjIzNiIgeT0iMTU0IiB3aWR0aD0iMzEwIiBoZWlnaHQ9IjE0OCIgcng9IjgiIGZpbGw9IiNjOWM5YzkiLz48cGF0aCBkPSJNMjU2IDE4MmgyNzBNMjU2IDIzMGgyNzBNMjU2IDI3OGgyNzAiIHN0cm9rZT0iI2JkYmRiZCIgc3Ryb2tlLXdpZHRoPSI2Ii8+PHBhdGggZD0iTTIxNiAzMjRWMTkwSDk2Yy0xMiAwLTIyIDgtMjYgMjBMNjAgMjQ0djgweiIgZmlsbD0iI2JkYmRiZCIgc3Ryb2tlPSIjZGVkZWRlIiBzdHJva2Utd2lkdGg9IjEwIi8+PHBhdGggZD0iTTEwNCAyMDZoODh2NTJIODR6IiBmaWxsPSIjZDlkOWQ5Ii8+PHJlY3QgeD0iNjAiIHk9IjMyNCIgd2lkdGg9IjUwNiIgaGVpZ2h0PSIxOCIgcng9IjYiIGZpbGw9IiNhOWE5YTkiLz48cmVjdCB4PSI1NiIgeT0iMjkyIiB3aWR0aD0iMTgiIGhlaWdodD0iMzAiIHJ4PSI1IiBmaWxsPSIjZDlkOWQ5Ii8+PGNpcmNsZSBjeD0iMTUwIiBjeT0iMzY2IiByPSI0MCIgZmlsbD0iIzhmOGY4ZiIgc3Ryb2tlPSIjZGVkZWRlIiBzdHJva2Utd2lkdGg9IjgiLz48Y2lyY2xlIGN4PSIxNTAiIGN5PSIzNjYiIHI9IjE2IiBmaWxsPSIjZDlkOWQ5Ii8+PGNpcmNsZSBjeD0iNDcwIiBjeT0iMzY2IiByPSI0MCIgZmlsbD0iIzhmOGY4ZiIgc3Ryb2tlPSIjZGVkZWRlIiBzdHJva2Utd2lkdGg9IjgiLz48Y2lyY2xlIGN4PSI0NzAiIGN5PSIzNjYiIHI9IjE2IiBmaWxsPSIjZDlkOWQ5Ii8+PC9nPjwvZz48L3N2Zz4=', - driver: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NDAgNDgwIiB3aWR0aD0iNjQwIiBoZWlnaHQ9IjQ4MCI+PGcgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIj48cGF0aCBkPSJNMTEwIDQ4MGMwLTk2IDc2LTE2NiAyMTAtMTY2czIxMCA3MCAyMTAgMTY2eiIgZmlsbD0iI2JkYmRiZCIgc3Ryb2tlPSIjZGVkZWRlIiBzdHJva2Utd2lkdGg9IjEwIi8+PHBhdGggZD0iTTI3MCAzMjJsNTAgNTQgNTAtNTQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2Q5ZDlkOSIgc3Ryb2tlLXdpZHRoPSI4Ii8+PGNpcmNsZSBjeD0iMzIwIiBjeT0iMjA0IiByPSI5MiIgZmlsbD0iI2JkYmRiZCIgc3Ryb2tlPSIjZGVkZWRlIiBzdHJva2Utd2lkdGg9IjEwIi8+PHBhdGggZD0iTTIzMCAxODZjMC02MiA0Mi05NiA5MC05NnM5MCAzNCA5MCA5NnoiIGZpbGw9IiNhOWE5YTkiIHN0cm9rZT0iI2RlZGVkZSIgc3Ryb2tlLXdpZHRoPSIxMCIvPjxwYXRoIGQ9Ik0zMjAgOTZ2ODgiIHN0cm9rZT0iI2RlZGVkZSIgc3Ryb2tlLXdpZHRoPSI2Ii8+PGNpcmNsZSBjeD0iMzIwIiBjeT0iOTIiIHI9IjkiIGZpbGw9IiNhOWE5YTkiIHN0cm9rZT0iI2RlZGVkZSIgc3Ryb2tlLXdpZHRoPSI2Ii8+PHBhdGggZD0iTTIyMiAxODZoMTkwYzY2IDAgOTYgMTAgOTYgMjJzLTMwIDIyLTk2IDIySDIyMnoiIGZpbGw9IiNhOWE5YTkiIHN0cm9rZT0iI2RlZGVkZSIgc3Ryb2tlLXdpZHRoPSI4Ii8+PC9nPjwvc3ZnPg==', - contact: - 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NDAgNDgwIiB3aWR0aD0iNjQwIiBoZWlnaHQ9IjQ4MCI+PGcgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIj48cGF0aCBkPSJNMTEwIDQ4MGMwLTk2IDc2LTE2NiAyMTAtMTY2czIxMCA3MCAyMTAgMTY2eiIgZmlsbD0iI2JkYmRiZCIgc3Ryb2tlPSIjZGVkZWRlIiBzdHJva2Utd2lkdGg9IjEwIi8+PHBhdGggZD0iTTI3MCAzMjJsNTAgNTQgNTAtNTQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2Q5ZDlkOSIgc3Ryb2tlLXdpZHRoPSI4Ii8+PGNpcmNsZSBjeD0iMzIwIiBjeT0iMTg4IiByPSI5NiIgZmlsbD0iI2JkYmRiZCIgc3Ryb2tlPSIjZGVkZWRlIiBzdHJva2Utd2lkdGg9IjEwIi8+PC9nPjwvc3ZnPg==', - customer: - 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NDAgNDgwIiB3aWR0aD0iNjQwIiBoZWlnaHQ9IjQ4MCI+PGcgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIj48cGF0aCBkPSJNMTEwIDQ4MGMwLTk2IDc2LTE2NiAyMTAtMTY2czIxMCA3MCAyMTAgMTY2eiIgZmlsbD0iI2JkYmRiZCIgc3Ryb2tlPSIjZGVkZWRlIiBzdHJva2Utd2lkdGg9IjEwIi8+PHBhdGggZD0iTTI3MCAzMjJsNTAgNTQgNTAtNTQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2Q5ZDlkOSIgc3Ryb2tlLXdpZHRoPSI4Ii8+PGNpcmNsZSBjeD0iMzIwIiBjeT0iMTg4IiByPSI5NiIgZmlsbD0iI2JkYmRiZCIgc3Ryb2tlPSIjZGVkZWRlIiBzdHJva2Utd2lkdGg9IjEwIi8+PGNpcmNsZSBjeD0iNDkwIiBjeT0iMzkyIiByPSI2MCIgZmlsbD0iIzhmOGY4ZiIgc3Ryb2tlPSIjZGVkZWRlIiBzdHJva2Utd2lkdGg9IjEwIi8+PHBhdGggZD0iTTQ1OCAzOTRsMjIgMjIgNDQtNDgiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2Q5ZDlkOSIgc3Ryb2tlLXdpZHRoPSIxNCIvPjwvZz48L3N2Zz4=', - vendor: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NDAgNDgwIiB3aWR0aD0iNjQwIiBoZWlnaHQ9IjQ4MCI+PGcgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIj48cGF0aCBkPSJNNzIgMjAwTDMyMCA5NmwyNDggMTA0djI0SDcyeiIgZmlsbD0iI2E5YTlhOSIgc3Ryb2tlPSIjZGVkZWRlIiBzdHJva2Utd2lkdGg9IjEwIi8+PHJlY3QgeD0iMTAwIiB5PSIyMjIiIHdpZHRoPSI0NDAiIGhlaWdodD0iMTkwIiByeD0iOCIgZmlsbD0iI2JkYmRiZCIgc3Ryb2tlPSIjZGVkZWRlIiBzdHJva2Utd2lkdGg9IjEwIi8+PHJlY3QgeD0iMjcyIiB5PSIyOTIiIHdpZHRoPSI5NiIgaGVpZ2h0PSIxMjAiIHJ4PSI2IiBmaWxsPSIjOGY4ZjhmIiBzdHJva2U9IiNkZWRlZGUiIHN0cm9rZS13aWR0aD0iOCIvPjxyZWN0IHg9IjE0MCIgeT0iMjYyIiB3aWR0aD0iODgiIGhlaWdodD0iNjYiIHJ4PSI2IiBmaWxsPSIjZDlkOWQ5Ii8+PHJlY3QgeD0iNDEyIiB5PSIyNjIiIHdpZHRoPSI4OCIgaGVpZ2h0PSI2NiIgcng9IjYiIGZpbGw9IiNkOWQ5ZDkiLz48cGF0aCBkPSJNMTg0IDI2MnY2Nk0xNDAgMjk1aDg4TTQ1NiAyNjJ2NjZNNDEyIDI5NWg4OCIgc3Ryb2tlPSIjYmRiZGJkIiBzdHJva2Utd2lkdGg9IjYiLz48cmVjdCB4PSI2MCIgeT0iNDEyIiB3aWR0aD0iNTIwIiBoZWlnaHQ9IjE2IiByeD0iNiIgZmlsbD0iI2E5YTlhOSIvPjwvZz48L3N2Zz4=', - fleet: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NDAgNDgwIiB3aWR0aD0iNjQwIiBoZWlnaHQ9IjQ4MCI+PGcgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxNTAgLTQwKSBzY2FsZSgwLjcyKSIgb3BhY2l0eT0iMC40NSI+PHJlY3QgeD0iMjE2IiB5PSIxMzIiIHdpZHRoPSIzNTAiIGhlaWdodD0iMTkyIiByeD0iMTQiIGZpbGw9IiNiZGJkYmQiIHN0cm9rZT0iI2RlZGVkZSIgc3Ryb2tlLXdpZHRoPSIxMCIvPjxyZWN0IHg9IjIzNiIgeT0iMTU0IiB3aWR0aD0iMzEwIiBoZWlnaHQ9IjE0OCIgcng9IjgiIGZpbGw9IiNjOWM5YzkiLz48cGF0aCBkPSJNMjU2IDE4MmgyNzBNMjU2IDIzMGgyNzBNMjU2IDI3OGgyNzAiIHN0cm9rZT0iI2JkYmRiZCIgc3Ryb2tlLXdpZHRoPSI2Ii8+PHBhdGggZD0iTTIxNiAzMjRWMTkwSDk2Yy0xMiAwLTIyIDgtMjYgMjBMNjAgMjQ0djgweiIgZmlsbD0iI2JkYmRiZCIgc3Ryb2tlPSIjZGVkZWRlIiBzdHJva2Utd2lkdGg9IjEwIi8+PHBhdGggZD0iTTEwNCAyMDZoODh2NTJIODR6IiBmaWxsPSIjZDlkOWQ5Ii8+PHJlY3QgeD0iNjAiIHk9IjMyNCIgd2lkdGg9IjUwNiIgaGVpZ2h0PSIxOCIgcng9IjYiIGZpbGw9IiNhOWE5YTkiLz48cmVjdCB4PSI1NiIgeT0iMjkyIiB3aWR0aD0iMTgiIGhlaWdodD0iMzAiIHJ4PSI1IiBmaWxsPSIjZDlkOWQ5Ii8+PGNpcmNsZSBjeD0iMTUwIiBjeT0iMzY2IiByPSI0MCIgZmlsbD0iIzhmOGY4ZiIgc3Ryb2tlPSIjZGVkZWRlIiBzdHJva2Utd2lkdGg9IjgiLz48Y2lyY2xlIGN4PSIxNTAiIGN5PSIzNjYiIHI9IjE2IiBmaWxsPSIjZDlkOWQ5Ii8+PGNpcmNsZSBjeD0iNDcwIiBjeT0iMzY2IiByPSI0MCIgZmlsbD0iIzhmOGY4ZiIgc3Ryb2tlPSIjZGVkZWRlIiBzdHJva2Utd2lkdGg9IjgiLz48Y2lyY2xlIGN4PSI0NzAiIGN5PSIzNjYiIHI9IjE2IiBmaWxsPSIjZDlkOWQ5Ii8+PC9nPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDgwIDEwKSBzY2FsZSgwLjgpIiBvcGFjaXR5PSIwLjciPjxyZWN0IHg9IjIxNiIgeT0iMTMyIiB3aWR0aD0iMzUwIiBoZWlnaHQ9IjE5MiIgcng9IjE0IiBmaWxsPSIjYmRiZGJkIiBzdHJva2U9IiNkZWRlZGUiIHN0cm9rZS13aWR0aD0iMTAiLz48cmVjdCB4PSIyMzYiIHk9IjE1NCIgd2lkdGg9IjMxMCIgaGVpZ2h0PSIxNDgiIHJ4PSI4IiBmaWxsPSIjYzljOWM5Ii8+PHBhdGggZD0iTTI1NiAxODJoMjcwTTI1NiAyMzBoMjcwTTI1NiAyNzhoMjcwIiBzdHJva2U9IiNiZGJkYmQiIHN0cm9rZS13aWR0aD0iNiIvPjxwYXRoIGQ9Ik0yMTYgMzI0VjE5MEg5NmMtMTIgMC0yMiA4LTI2IDIwTDYwIDI0NHY4MHoiIGZpbGw9IiNiZGJkYmQiIHN0cm9rZT0iI2RlZGVkZSIgc3Ryb2tlLXdpZHRoPSIxMCIvPjxwYXRoIGQ9Ik0xMDQgMjA2aDg4djUySDg0eiIgZmlsbD0iI2Q5ZDlkOSIvPjxyZWN0IHg9IjYwIiB5PSIzMjQiIHdpZHRoPSI1MDYiIGhlaWdodD0iMTgiIHJ4PSI2IiBmaWxsPSIjYTlhOWE5Ii8+PHJlY3QgeD0iNTYiIHk9IjI5MiIgd2lkdGg9IjE4IiBoZWlnaHQ9IjMwIiByeD0iNSIgZmlsbD0iI2Q5ZDlkOSIvPjxjaXJjbGUgY3g9IjE1MCIgY3k9IjM2NiIgcj0iNDAiIGZpbGw9IiM4ZjhmOGYiIHN0cm9rZT0iI2RlZGVkZSIgc3Ryb2tlLXdpZHRoPSI4Ii8+PGNpcmNsZSBjeD0iMTUwIiBjeT0iMzY2IiByPSIxNiIgZmlsbD0iI2Q5ZDlkOSIvPjxjaXJjbGUgY3g9IjQ3MCIgY3k9IjM2NiIgcj0iNDAiIGZpbGw9IiM4ZjhmOGYiIHN0cm9rZT0iI2RlZGVkZSIgc3Ryb2tlLXdpZHRoPSI4Ii8+PGNpcmNsZSBjeD0iNDcwIiBjeT0iMzY2IiByPSIxNiIgZmlsbD0iI2Q5ZDlkOSIvPjwvZz48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIDYwKSBzY2FsZSgwLjg4KSIgb3BhY2l0eT0iMS4wIj48cmVjdCB4PSIyMTYiIHk9IjEzMiIgd2lkdGg9IjM1MCIgaGVpZ2h0PSIxOTIiIHJ4PSIxNCIgZmlsbD0iI2JkYmRiZCIgc3Ryb2tlPSIjZGVkZWRlIiBzdHJva2Utd2lkdGg9IjEwIi8+PHJlY3QgeD0iMjM2IiB5PSIxNTQiIHdpZHRoPSIzMTAiIGhlaWdodD0iMTQ4IiByeD0iOCIgZmlsbD0iI2M5YzljOSIvPjxwYXRoIGQ9Ik0yNTYgMTgyaDI3ME0yNTYgMjMwaDI3ME0yNTYgMjc4aDI3MCIgc3Ryb2tlPSIjYmRiZGJkIiBzdHJva2Utd2lkdGg9IjYiLz48cGF0aCBkPSJNMjE2IDMyNFYxOTBIOTZjLTEyIDAtMjIgOC0yNiAyMEw2MCAyNDR2ODB6IiBmaWxsPSIjYmRiZGJkIiBzdHJva2U9IiNkZWRlZGUiIHN0cm9rZS13aWR0aD0iMTAiLz48cGF0aCBkPSJNMTA0IDIwNmg4OHY1Mkg4NHoiIGZpbGw9IiNkOWQ5ZDkiLz48cmVjdCB4PSI2MCIgeT0iMzI0IiB3aWR0aD0iNTA2IiBoZWlnaHQ9IjE4IiByeD0iNiIgZmlsbD0iI2E5YTlhOSIvPjxyZWN0IHg9IjU2IiB5PSIyOTIiIHdpZHRoPSIxOCIgaGVpZ2h0PSIzMCIgcng9IjUiIGZpbGw9IiNkOWQ5ZDkiLz48Y2lyY2xlIGN4PSIxNTAiIGN5PSIzNjYiIHI9IjQwIiBmaWxsPSIjOGY4ZjhmIiBzdHJva2U9IiNkZWRlZGUiIHN0cm9rZS13aWR0aD0iOCIvPjxjaXJjbGUgY3g9IjE1MCIgY3k9IjM2NiIgcj0iMTYiIGZpbGw9IiNkOWQ5ZDkiLz48Y2lyY2xlIGN4PSI0NzAiIGN5PSIzNjYiIHI9IjQwIiBmaWxsPSIjOGY4ZjhmIiBzdHJva2U9IiNkZWRlZGUiIHN0cm9rZS13aWR0aD0iOCIvPjxjaXJjbGUgY3g9IjQ3MCIgY3k9IjM2NiIgcj0iMTYiIGZpbGw9IiNkOWQ5ZDkiLz48L2c+PC9nPjwvc3ZnPg==', -}; - -/** - * Hosted images the data models and console config fall back to when a record has no - * photo. Treat them as "no photo" so the styled placeholder wins over the legacy PNG. - */ -const LEGACY_DEFAULT_PATTERN = /(no-avatar|vehicle-placeholder|image-file-icon|fallback-placeholder|default-extension-icon)\.(png|svg|jpe?g)/i; - -const CONFIG_KEYS = { - vehicle: 'vehicleImage', - driver: 'driverImage', - vendor: 'vendorImage', - contact: 'contactImage', - customer: 'customerImage', - fleet: 'fleetImage', - trailer: 'trailerImage', -}; - -export function getPlaceholderImage(type = 'contact') { - const overrides = config?.defaultValues?.placeholders ?? {}; - // A console that already replaced its hosted defaults with inline images wins; - // hosted PNG defaults are ignored so the silhouette set stays consistent. - const configured = config?.defaultValues?.[CONFIG_KEYS[type]]; - const inlineConfigured = typeof configured === 'string' && configured.startsWith('data:') ? configured : null; - - return overrides[type] || inlineConfigured || PLACEHOLDER_IMAGES[type] || PLACEHOLDER_IMAGES.contact; -} - -export function isDefaultImage(url) { - if (typeof url !== 'string' || url.trim() === '') { - return true; - } - - if (LEGACY_DEFAULT_PATTERN.test(url)) { - return true; - } - - const defaults = Object.values(config?.defaultValues ?? {}).filter((value) => typeof value === 'string'); - - return defaults.includes(url); -} - -/** - * The image to show for a record: its own photo, or the styled placeholder when the - * photo is missing or one of the legacy hosted defaults. - */ -export function resolveResourceImage(url, type) { - return isDefaultImage(url) ? getPlaceholderImage(type) : url; -} - -export default getPlaceholderImage; +export { PLACEHOLDER_IMAGES, getPlaceholderImage, isDefaultImage, resolveResourceImage, default } from '@fleetbase/fleetops-data/utils/placeholder-images'; diff --git a/addon/utils/resource-descriptors/assets.js b/addon/utils/resource-descriptors/assets.js index 3875af786..3b38dce42 100644 --- a/addon/utils/resource-descriptors/assets.js +++ b/addon/utils/resource-descriptors/assets.js @@ -1,68 +1,13 @@ import { get } from '@ember/object'; import { first, present, relation, photo, icon, badge, badges, fact, relatedFact, money, join, panelOpener } from './helpers'; +import { sharedDescriptors } from './shared'; /** * Vehicles, trailers, equipment, parts, generic assets and warranties. */ export default function buildAssetDescriptors(owner) { return [ - { - key: 'vehicle', - labelKey: 'resource.vehicle', - icon: 'truck', - modelNames: ['vehicle'], - aliases: ['attachable-vehicle', 'maintenance-subject-vehicle'], - polymorphicTypes: ['fleet-ops:vehicle', 'Fleetbase\\FleetOps\\Models\\Vehicle'], - permission: 'fleet-ops view vehicle', - statusTones: { - available: 'text-green-500', - active: 'text-green-500', - in_service: 'text-green-500', - maintenance: 'text-yellow-500', - unavailable: 'text-gray-400', - inactive: 'text-gray-400', - out_of_service: 'text-red-500', - }, - title: (vehicle) => - first(vehicle, 'displayName', 'display_name', 'name', 'yearMakeModel', 'public_id') ?? join([get(vehicle, 'year'), get(vehicle, 'make'), get(vehicle, 'model')]), - identifier: (vehicle) => first(vehicle, 'plate_number', 'call_sign', 'vin', 'serial_number'), - image: (vehicle) => photo(vehicle, 'vehicle'), - online: (vehicle) => { - const online = get(vehicle, 'online'); - - return typeof online === 'boolean' ? online : undefined; - }, - status: (vehicle) => first(vehicle, 'status'), - badges: (vehicle) => { - const driver = relation(owner, vehicle, 'driver'); - - return badges( - badge('plate', 'id-card', first(vehicle, 'plate_number', 'call_sign', 'vehicle_number')), - badge('driver', 'user', first(driver, 'name', 'displayName') ?? first(vehicle, 'driver_name'), { - relatedType: 'driver', - relatedId: driver?.id ?? get(vehicle, 'driver_uuid'), - }) - ); - }, - selectDetails: (vehicle) => [first(vehicle, 'plate_number', 'vin', 'serial_number', 'call_sign'), first(vehicle, 'driver_name')], - facts: (vehicle) => { - const driver = relation(owner, vehicle, 'driver'); - const trailers = relation(owner, vehicle, 'trailers'); - const trailer = trailers && typeof trailers.objectAt === 'function' ? trailers.objectAt(0) : Array.isArray(trailers) ? trailers[0] : null; - const extra = trailers?.length > 1 ? ` (+${trailers.length - 1})` : ''; - - return [ - fact('make-model', join([get(vehicle, 'year'), get(vehicle, 'make'), get(vehicle, 'model'), get(vehicle, 'trim')])), - fact('plate', join([first(vehicle, 'plate_number'), first(vehicle, 'vin')], ' · ')), - relatedFact('driver', driver, 'driver', first(vehicle, 'driver_name')), - trailer ? { ...relatedFact('trailer', trailer, 'trailer', first(trailer, 'displayName', 'display_name', 'name')), suffix: extra } : fact('trailer', null), - fact('odometer', present(get(vehicle, 'odometer')) ? join([get(vehicle, 'odometer'), get(vehicle, 'odometer_unit')]) : null), - fact('status', first(vehicle, 'status'), { format: 'humanize' }), - ]; - }, - open: panelOpener(owner, 'vehicle-actions'), - components: { identity: 'cell/vehicle-identity' }, - }, + ...sharedDescriptors(owner, ['vehicle']), { key: 'trailer', labelKey: 'resource.trailer', diff --git a/addon/utils/resource-descriptors/helpers.js b/addon/utils/resource-descriptors/helpers.js index ce786123f..8d2065ad6 100644 --- a/addon/utils/resource-descriptors/helpers.js +++ b/addon/utils/resource-descriptors/helpers.js @@ -1,59 +1,29 @@ -import formatDate from '@fleetbase/ember-ui/utils/format-date'; -import { get } from '@ember/object'; -import { getPlaceholderImage, resolveResourceImage } from '../placeholder-images'; +import { first, relation } from '@fleetbase/fleetops-data/utils/resource-descriptors/helpers'; /** - * Small building blocks the FleetOps descriptors share. They are written - * against attribute names, so an identity stub or an API payload renders - * the same as an Ember Data record. + * The building blocks the FleetOps descriptors share. The ones a descriptor + * needs to read a record live in `@fleetbase/fleetops-data`, so the shared + * resources render the same in every extension; this module re-exports them + * and adds the openers that only make sense with the FleetOps engine's own + * services and routes. */ - -export function present(value) { - return value !== undefined && value !== null && String(value).trim() !== ''; -} - -/** The first present value at any of the paths. */ -export function first(record, ...paths) { - if (!record) { - return null; - } - - for (const path of paths) { - const value = get(record, path); - - if (present(value)) { - return value; - } - } - - return null; -} - -/** A related record read through its reference, never a promise proxy. */ -export function relation(owner, record, name) { - const registry = owner?.lookup?.('service:resource-registry'); - - if (registry && typeof registry.relationValue === 'function') { - return registry.relationValue(record, name); - } - - const value = record ? get(record, name) : null; - - if (value && typeof value === 'object' && typeof value.then === 'function') { - return value.content ?? null; - } - - return value ?? null; -} - -/** A photo with the styled placeholder as fallback. */ -export function photo(record, type, path = 'photo_url') { - return { url: resolveResourceImage(get(record, path), type), fallback: getPlaceholderImage(type), shape: type === 'vehicle' || type === 'trailer' ? 'square' : 'round' }; -} - -export function icon(name, iconClass) { - return iconClass ? { icon: name, iconClass } : { icon: name }; -} +export { + present, + first, + lookupService, + relation, + photo, + icon, + badge, + badges, + fact, + relatedFact, + dateLabel, + typeLabel, + money, + join, + polymorphicType, +} from '@fleetbase/fleetops-data/utils/resource-descriptors/helpers'; /** A coloured tile for records that carry their own colour (service areas, zones). */ export function colourTile(record, name) { @@ -62,75 +32,6 @@ export function colourTile(record, name) { return colour ? { icon: name, iconClass: 'resource-colour-tile', colour } : { icon: name }; } -export function badge(key, iconName, label, extra = {}) { - return present(label) ? { key, icon: iconName, label, ...extra } : null; -} - -export function badges(...list) { - return list.filter(Boolean); -} - -export function fact(label, value, extra = {}) { - return { labelKey: `resource-summary.facts.${label}`, value, ...extra }; -} - -/** A fact that renders as the related record's pill when the record is loaded. */ -export function relatedFact(label, related, relatedType, fallbackValue) { - return { labelKey: `resource-summary.facts.${label}`, related: related ?? null, relatedType, value: fallbackValue ?? null }; -} - -/** - * A date the way the console writes one everywhere else (date-fns, not the - * browser locale), or null for anything that is not a date. - */ -export function dateLabel(value, pattern = 'dd MMM yyyy, HH:mm') { - if (!present(value)) { - return null; - } - - const date = value instanceof Date ? value : new Date(value); - - return Number.isNaN(date.getTime()) ? null : formatDate(date, pattern); -} - -/** - * A contact's type the way a person reads it: "customer-fleet-ops:contact" - * and "customer" both become "Customer"; anything unreadable becomes null. - */ -export function typeLabel(value) { - if (!present(value)) { - return null; - } - - const head = String(value) - .split(':')[0] - .replace(/-fleet-ops$/, '') - .replace(/[-_]+/g, ' ') - .trim(); - - return head ? head.charAt(0).toUpperCase() + head.slice(1) : null; -} - -export function money(amount, currency) { - if (!present(amount)) { - return null; - } - - return present(currency) ? `${amount} ${currency}` : String(amount); -} - -export function join(parts, separator = ' ') { - return parts.filter(present).join(separator) || null; -} - -/** The resolved `*_type` for a polymorphic relation, from the record or a `_type` attribute. */ -export function polymorphicType(record, relationName, typeAttr) { - const related = record ? get(record, relationName) : null; - const modelName = related?.constructor?.modelName ?? related?.content?.constructor?.modelName; - - return modelName ?? (typeAttr ? get(record, typeAttr) : null) ?? null; -} - /** * An opener that hands the record to an action service's context panel, * falling back to its route transition. Resolved lazily from the engine diff --git a/addon/utils/resource-descriptors/operations.js b/addon/utils/resource-descriptors/operations.js index 6247438b9..ed6721e4c 100644 --- a/addon/utils/resource-descriptors/operations.js +++ b/addon/utils/resource-descriptors/operations.js @@ -1,5 +1,6 @@ import { get } from '@ember/object'; import { first, present, relation, icon, colourTile, fact, relatedFact, money, join, panelOpener, routeOpener, polymorphicType, dateLabel } from './helpers'; +import { sharedDescriptors } from './shared'; /** * Places, orders, order configs, service rates, service areas, zones, @@ -7,67 +8,7 @@ import { first, present, relation, icon, colourTile, fact, relatedFact, money, j */ export default function buildOperationsDescriptors(owner) { return [ - { - key: 'place', - labelKey: 'resource.place', - icon: 'location-dot', - modelNames: ['place'], - polymorphicTypes: ['fleet-ops:place', 'Fleetbase\\FleetOps\\Models\\Place'], - permission: 'fleet-ops view place', - title: (place) => first(place, 'displayName', 'name', 'address', 'street1', 'public_id'), - identifier: (place) => join([first(place, 'city'), first(place, 'country')], ', ') ?? first(place, 'public_id'), - image: () => icon('location-dot'), - selectDetails: (place) => [first(place, 'address', 'street1'), join([first(place, 'city'), first(place, 'country')], ', ')], - facts: (place) => [ - fact('address', first(place, 'address', 'street1')), - fact('phone', first(place, 'phone')), - fact('type', first(place, 'type'), { format: 'humanize' }), - relatedFact('vendor', relation(owner, place, 'vendor'), 'vendor', first(place, 'vendor_name')), - fact('coordinates', first(place, 'positionString') ?? (present(get(place, 'latitude')) ? `${get(place, 'latitude')}, ${get(place, 'longitude')}` : null)), - ], - open: panelOpener(owner, 'place-actions'), - }, - { - key: 'order', - labelKey: 'resource.order', - icon: 'box', - modelNames: ['order'], - polymorphicTypes: ['fleet-ops:order', 'Fleetbase\\FleetOps\\Models\\Order'], - permission: 'fleet-ops view order', - statusTones: { - created: 'text-gray-400', - preparing: 'text-yellow-500', - dispatched: 'text-yellow-500', - started: 'text-yellow-500', - in_progress: 'text-yellow-500', - enroute: 'text-yellow-500', - completed: 'text-green-500', - delivered: 'text-green-500', - canceled: 'text-red-500', - cancelled: 'text-red-500', - failed: 'text-red-500', - }, - title: (order) => first(order, 'tracking', 'tracking_number.tracking_number', 'public_id'), - identifier: (order) => first(order, 'public_id', 'internal_id'), - image: () => icon('box'), - status: (order) => first(order, 'status'), - selectDetails: (order) => [first(order, 'public_id'), first(order, 'status')], - facts: (order) => [ - fact('status', first(order, 'status'), { format: 'humanize' }), - relatedFact('customer', relation(owner, order, 'customer'), polymorphicType(order, 'customer', 'customer_type'), first(order, 'customer_name')), - relatedFact('driver', relation(owner, order, 'driver_assigned'), 'driver', first(order, 'driver_name')), - fact('route', join([first(order, 'pickupName', 'pickup_name'), first(order, 'dropoffName', 'dropoff_name')], ' → ')), - fact('scheduled', get(order, 'scheduled_at'), { format: 'date' }), - fact('order-config', first(order, 'order_config.name', 'type')), - ], - open: (order) => { - if (!order || order.isIdentityStub || !first(order, 'id', 'uuid')) { - return false; - } - - return panelOpener(owner, 'order-actions')(order); - }, - }, + ...sharedDescriptors(owner, ['place', 'order']), { key: 'order-config', labelKey: 'resource.order-config', diff --git a/addon/utils/resource-descriptors/people.js b/addon/utils/resource-descriptors/people.js index 7a8eec6bc..926ce1014 100644 --- a/addon/utils/resource-descriptors/people.js +++ b/addon/utils/resource-descriptors/people.js @@ -1,88 +1,13 @@ import { get } from '@ember/object'; -import { first, present, relation, photo, badge, badges, fact, relatedFact, join, panelOpener, dateLabel, typeLabel } from './helpers'; +import { first, photo, fact, panelOpener } from './helpers'; +import { sharedDescriptors } from './shared'; /** * Drivers, vendors, contacts, customers and fleets. */ export default function buildPeopleDescriptors(owner) { return [ - { - key: 'driver', - labelKey: 'resource.driver', - icon: 'id-card', - modelNames: ['driver'], - aliases: ['attachable-driver', 'facilitator-driver', 'fleet-driver'], - polymorphicTypes: ['fleet-ops:driver', 'Fleetbase\\FleetOps\\Models\\Driver'], - permission: 'fleet-ops view driver', - statusTones: { - available: 'text-green-500', - active: 'text-green-500', - on_duty: 'text-green-500', - busy: 'text-yellow-500', - assigned: 'text-yellow-500', - unavailable: 'text-gray-400', - offline: 'text-gray-400', - suspended: 'text-red-500', - }, - title: (driver) => first(driver, 'name', 'displayName', 'display_name', 'public_id'), - identifier: (driver) => first(driver, 'phone', 'email'), - image: (driver) => photo(driver, 'driver'), - online: (driver) => { - const online = get(driver, 'online'); - - return typeof online === 'boolean' ? online : undefined; - }, - status: (driver) => first(driver, 'status'), - badges: (driver) => { - const vehicle = relation(owner, driver, 'vehicle'); - const label = first(vehicle, 'displayName', 'display_name', 'name') ?? first(driver, 'vehicle_assigned.display_name', 'vehicle_name'); - - return badges(badge('vehicle', 'car', label, { relatedType: 'vehicle', relatedId: vehicle?.id ?? get(driver, 'vehicle_uuid') })); - }, - selectDetails: (driver) => [first(driver, 'phone'), first(driver, 'email')], - facts: (driver) => [ - fact('phone', first(driver, 'phone')), - fact('email', first(driver, 'email')), - relatedFact('vehicle', relation(owner, driver, 'vehicle'), 'vehicle', first(driver, 'vehicle_name')), - fact( - 'licence', - join( - [ - first(driver, 'drivers_license_number'), - dateLabel(get(driver, 'license_expiry'), 'dd MMM yyyy') ? `expires ${dateLabel(get(driver, 'license_expiry'), 'dd MMM yyyy')}` : null, - ], - ' · ' - ) - ), - relatedFact('vendor', relation(owner, driver, 'vendor'), 'vendor', first(driver, 'vendor_name')), - fact('status', first(driver, 'status'), { format: 'humanize' }), - ], - open: panelOpener(owner, 'driver-actions'), - components: { identity: 'cell/driver-identity' }, - }, - { - key: 'vendor', - labelKey: 'resource.vendor', - icon: 'building', - modelNames: ['vendor'], - aliases: ['facilitator-vendor', 'customer-vendor'], - polymorphicTypes: ['fleet-ops:vendor', 'Fleetbase\\FleetOps\\Models\\Vendor'], - permission: 'fleet-ops view vendor', - title: (vendor) => first(vendor, 'name', 'public_id'), - identifier: (vendor) => first(vendor, 'prettyType', 'internal_id', 'business_id'), - image: (vendor) => photo(vendor, 'vendor', 'logo_url'), - status: (vendor) => first(vendor, 'status'), - selectDetails: (vendor) => [first(vendor, 'email'), first(vendor, 'phone')], - facts: (vendor) => [ - fact('type', first(vendor, 'prettyType', 'type'), { format: 'humanize' }), - fact('email', first(vendor, 'email')), - fact('phone', first(vendor, 'phone')), - fact('website', first(vendor, 'website_url')), - fact('address', first(vendor, 'address', 'place.address', 'address_street')), - fact('country', first(vendor, 'country')), - ], - open: panelOpener(owner, 'vendor-actions'), - }, + ...sharedDescriptors(owner, ['driver', 'vendor', 'contact', 'customer', 'fleet']), { key: 'integrated-vendor', labelKey: 'resource.integrated-vendor', @@ -105,69 +30,5 @@ export default function buildPeopleDescriptors(owner) { ], open: panelOpener(owner, 'integrated-vendor-actions'), }, - { - key: 'contact', - labelKey: 'resource.contact', - icon: 'address-book', - modelNames: ['contact'], - aliases: ['facilitator-contact', 'customer-contact'], - polymorphicTypes: ['fleet-ops:contact', 'Fleetbase\\FleetOps\\Models\\Contact'], - permission: 'fleet-ops view contact', - title: (contact) => first(contact, 'name', 'public_id'), - identifier: (contact) => first(contact, 'title') ?? typeLabel(first(contact, 'type')), - image: (contact) => photo(contact, 'contact'), - selectDetails: (contact) => [first(contact, 'email'), first(contact, 'phone')], - facts: (contact) => [ - fact('title', first(contact, 'title')), - fact('email', first(contact, 'email')), - fact('phone', first(contact, 'phone')), - relatedFact('place', relation(owner, contact, 'place'), 'place', first(contact, 'address', 'place.address')), - relatedFact('user', relation(owner, contact, 'user'), 'user', first(contact, 'user.name')), - ], - open: panelOpener(owner, 'contact-actions'), - }, - { - key: 'customer', - labelKey: 'resource.customer', - icon: 'user-tag', - modelNames: ['customer'], - aliases: ['facilitator-customer'], - polymorphicTypes: ['fleet-ops:customer', 'Fleetbase\\FleetOps\\Models\\Customer'], - permission: 'fleet-ops view contact', - title: (customer) => first(customer, 'name', 'public_id'), - identifier: (customer) => typeLabel(first(customer, 'customer_type', 'type')) ?? 'Customer', - image: (customer) => photo(customer, 'customer'), - selectDetails: (customer) => [first(customer, 'email'), first(customer, 'phone')], - facts: (customer) => [ - fact('type', typeLabel(first(customer, 'customer_type', 'type'))), - fact('email', first(customer, 'email')), - fact('phone', first(customer, 'phone')), - fact('address', first(customer, 'address', 'address_street')), - relatedFact('place', relation(owner, customer, 'place'), 'place', first(customer, 'place.address')), - ], - open: panelOpener(owner, 'customer-actions'), - }, - { - key: 'fleet', - labelKey: 'resource.fleet', - icon: 'layer-group', - modelNames: ['fleet'], - polymorphicTypes: ['fleet-ops:fleet', 'Fleetbase\\FleetOps\\Models\\Fleet'], - permission: 'fleet-ops view fleet', - title: (fleet) => first(fleet, 'name', 'public_id'), - identifier: (fleet) => first(fleet, 'task', 'public_id'), - image: (fleet) => photo(fleet, 'fleet'), - status: (fleet) => first(fleet, 'status'), - selectDetails: (fleet) => [first(fleet, 'task'), present(get(fleet, 'drivers_count')) ? `${get(fleet, 'drivers_count')} drivers` : null], - facts: (fleet) => [ - fact('task', first(fleet, 'task')), - relatedFact('service-area', relation(owner, fleet, 'service_area'), 'service-area', first(fleet, 'service_area.name')), - relatedFact('zone', relation(owner, fleet, 'zone'), 'zone', first(fleet, 'zone.name')), - relatedFact('vendor', relation(owner, fleet, 'vendor'), 'vendor', first(fleet, 'vendor.name')), - fact('drivers', present(get(fleet, 'drivers_count')) ? `${get(fleet, 'drivers_online_count') ?? 0} online of ${get(fleet, 'drivers_count')}` : null), - fact('vehicles', present(get(fleet, 'vehicles_count')) ? `${get(fleet, 'vehicles_online_count') ?? 0} online of ${get(fleet, 'vehicles_count')}` : null), - ], - open: panelOpener(owner, 'fleet-actions'), - }, ]; } diff --git a/addon/utils/resource-descriptors/shared.js b/addon/utils/resource-descriptors/shared.js new file mode 100644 index 000000000..52996a19f --- /dev/null +++ b/addon/utils/resource-descriptors/shared.js @@ -0,0 +1,18 @@ +import { buildSharedResourceDescriptors, guardOrder } from '@fleetbase/fleetops-data/utils/resource-descriptors'; +import { panelOpener } from './helpers'; + +/** + * The descriptors `@fleetbase/fleetops-data` shares with every extension, + * opened through this engine's own action services instead of through a + * lazy engine load: inside FleetOps the services are already here, and the + * registry keeps these richer versions over the shared ones. + */ +export function sharedDescriptors(owner, keys) { + const byKey = Object.fromEntries(buildSharedResourceDescriptors(owner).map((descriptor) => [descriptor.key, descriptor])); + + return keys.map((key) => { + const open = panelOpener(owner, `${key}-actions`); + + return { ...byKey[key], canOpen: undefined, open: key === 'order' ? guardOrder(open) : open }; + }); +} diff --git a/app/components/cell/contact-identity.js b/app/components/cell/contact-identity.js deleted file mode 100644 index 1376da5c5..000000000 --- a/app/components/cell/contact-identity.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@fleetbase/fleetops-engine/components/cell/contact-identity'; diff --git a/app/components/cell/customer-identity.js b/app/components/cell/customer-identity.js deleted file mode 100644 index c6c49f586..000000000 --- a/app/components/cell/customer-identity.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@fleetbase/fleetops-engine/components/cell/customer-identity'; diff --git a/app/components/cell/driver-identity.js b/app/components/cell/driver-identity.js deleted file mode 100644 index b5d3ccad4..000000000 --- a/app/components/cell/driver-identity.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@fleetbase/fleetops-engine/components/cell/driver-identity'; diff --git a/app/components/cell/fleet-identity.js b/app/components/cell/fleet-identity.js deleted file mode 100644 index 68a9502ea..000000000 --- a/app/components/cell/fleet-identity.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@fleetbase/fleetops-engine/components/cell/fleet-identity'; diff --git a/app/components/cell/order-identity.js b/app/components/cell/order-identity.js deleted file mode 100644 index 76de50a6a..000000000 --- a/app/components/cell/order-identity.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@fleetbase/fleetops-engine/components/cell/order-identity'; diff --git a/app/components/cell/place-identity.js b/app/components/cell/place-identity.js deleted file mode 100644 index 839bb7cfb..000000000 --- a/app/components/cell/place-identity.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@fleetbase/fleetops-engine/components/cell/place-identity'; diff --git a/app/components/cell/vehicle-identity.js b/app/components/cell/vehicle-identity.js deleted file mode 100644 index 75d5c291b..000000000 --- a/app/components/cell/vehicle-identity.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@fleetbase/fleetops-engine/components/cell/vehicle-identity'; diff --git a/app/components/cell/vendor-identity.js b/app/components/cell/vendor-identity.js deleted file mode 100644 index f950c37d3..000000000 --- a/app/components/cell/vendor-identity.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@fleetbase/fleetops-engine/components/cell/vendor-identity'; diff --git a/app/components/contact/pill.js b/app/components/contact/pill.js deleted file mode 100644 index 0750ead81..000000000 --- a/app/components/contact/pill.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@fleetbase/fleetops-engine/components/contact/pill'; diff --git a/app/components/contact/summary.js b/app/components/contact/summary.js deleted file mode 100644 index cd939f193..000000000 --- a/app/components/contact/summary.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@fleetbase/fleetops-engine/components/contact/summary'; diff --git a/app/components/customer/pill.js b/app/components/customer/pill.js deleted file mode 100644 index 52396929a..000000000 --- a/app/components/customer/pill.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@fleetbase/fleetops-engine/components/customer/pill'; diff --git a/app/components/customer/summary.js b/app/components/customer/summary.js deleted file mode 100644 index 015e5e9a3..000000000 --- a/app/components/customer/summary.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@fleetbase/fleetops-engine/components/customer/summary'; diff --git a/app/components/driver/pill.js b/app/components/driver/pill.js deleted file mode 100644 index b83229abd..000000000 --- a/app/components/driver/pill.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@fleetbase/fleetops-engine/components/driver/pill'; diff --git a/app/components/driver/summary.js b/app/components/driver/summary.js deleted file mode 100644 index e5f6dea18..000000000 --- a/app/components/driver/summary.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@fleetbase/fleetops-engine/components/driver/summary'; diff --git a/app/components/fleet/pill.js b/app/components/fleet/pill.js deleted file mode 100644 index 6035f0a78..000000000 --- a/app/components/fleet/pill.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@fleetbase/fleetops-engine/components/fleet/pill'; diff --git a/app/components/fleet/summary.js b/app/components/fleet/summary.js deleted file mode 100644 index 1e6ad64c2..000000000 --- a/app/components/fleet/summary.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@fleetbase/fleetops-engine/components/fleet/summary'; diff --git a/app/components/order/pill.js b/app/components/order/pill.js deleted file mode 100644 index 25f6c506a..000000000 --- a/app/components/order/pill.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@fleetbase/fleetops-engine/components/order/pill'; diff --git a/app/components/order/summary.js b/app/components/order/summary.js deleted file mode 100644 index f9fb14c7d..000000000 --- a/app/components/order/summary.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@fleetbase/fleetops-engine/components/order/summary'; diff --git a/app/components/place/pill.js b/app/components/place/pill.js deleted file mode 100644 index 81613fc7f..000000000 --- a/app/components/place/pill.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@fleetbase/fleetops-engine/components/place/pill'; diff --git a/app/components/place/summary.js b/app/components/place/summary.js deleted file mode 100644 index f76f00435..000000000 --- a/app/components/place/summary.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@fleetbase/fleetops-engine/components/place/summary'; diff --git a/app/components/select-option/contact.js b/app/components/select-option/contact.js deleted file mode 100644 index eb901f6c2..000000000 --- a/app/components/select-option/contact.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@fleetbase/fleetops-engine/components/select-option/contact'; diff --git a/app/components/select-option/customer.js b/app/components/select-option/customer.js deleted file mode 100644 index 6cfc77a27..000000000 --- a/app/components/select-option/customer.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@fleetbase/fleetops-engine/components/select-option/customer'; diff --git a/app/components/select-option/driver.js b/app/components/select-option/driver.js deleted file mode 100644 index 85f6b655b..000000000 --- a/app/components/select-option/driver.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@fleetbase/fleetops-engine/components/select-option/driver'; diff --git a/app/components/select-option/fleet.js b/app/components/select-option/fleet.js deleted file mode 100644 index e2a5c0d97..000000000 --- a/app/components/select-option/fleet.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@fleetbase/fleetops-engine/components/select-option/fleet'; diff --git a/app/components/select-option/order.js b/app/components/select-option/order.js deleted file mode 100644 index 0cdbb5fb1..000000000 --- a/app/components/select-option/order.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@fleetbase/fleetops-engine/components/select-option/order'; diff --git a/app/components/select-option/place.js b/app/components/select-option/place.js deleted file mode 100644 index 3616ffca7..000000000 --- a/app/components/select-option/place.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@fleetbase/fleetops-engine/components/select-option/place'; diff --git a/app/components/select-option/vehicle.js b/app/components/select-option/vehicle.js deleted file mode 100644 index faa85864d..000000000 --- a/app/components/select-option/vehicle.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@fleetbase/fleetops-engine/components/select-option/vehicle'; diff --git a/app/components/select-option/vendor.js b/app/components/select-option/vendor.js deleted file mode 100644 index 7c771c3ff..000000000 --- a/app/components/select-option/vendor.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@fleetbase/fleetops-engine/components/select-option/vendor'; diff --git a/app/components/vehicle/pill.js b/app/components/vehicle/pill.js deleted file mode 100644 index c1dcb8086..000000000 --- a/app/components/vehicle/pill.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@fleetbase/fleetops-engine/components/vehicle/pill'; diff --git a/app/components/vehicle/summary.js b/app/components/vehicle/summary.js deleted file mode 100644 index 261636caa..000000000 --- a/app/components/vehicle/summary.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@fleetbase/fleetops-engine/components/vehicle/summary'; diff --git a/app/components/vendor/pill.js b/app/components/vendor/pill.js deleted file mode 100644 index da3df5298..000000000 --- a/app/components/vendor/pill.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@fleetbase/fleetops-engine/components/vendor/pill'; diff --git a/app/components/vendor/summary.js b/app/components/vendor/summary.js deleted file mode 100644 index 635d4c80e..000000000 --- a/app/components/vendor/summary.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@fleetbase/fleetops-engine/components/vendor/summary';