From 458c8af7175366302720f4d5ab4b0504d4293978 Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Mon, 21 Sep 2026 23:34:59 +0800 Subject: [PATCH 1/4] feat: expose managed login state on driver and contact models Adds read-only is_staff_linked and login_status attributes to the driver and contact models. The console uses them to show or hide the login account actions (reset password, send credentials, deactivate/reactivate login) and to lock email and phone on profiles linked to a team member's account. Driver and contact login accounts are now managed by the server from the profile's name, email and phone, so the serializers no longer send user_uuid, the embedded contact user, or these server-maintained fields. --- addon/models/contact.js | 4 ++++ addon/models/driver.js | 4 ++++ addon/serializers/contact.js | 6 +++++- addon/serializers/driver.js | 4 ++++ 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/addon/models/contact.js b/addon/models/contact.js index 9a34db7..763c951 100644 --- a/addon/models/contact.js +++ b/addon/models/contact.js @@ -34,6 +34,10 @@ export default class ContactModel extends Model { photo_url; @attr('string') slug; + /** @managed-login (read-only, maintained by the server) */ + @attr('boolean') is_staff_linked; + @attr('string') login_status; + /** @dates */ @attr('date') deleted_at; @attr('date') created_at; diff --git a/addon/models/driver.js b/addon/models/driver.js index d560639..0f761fc 100644 --- a/addon/models/driver.js +++ b/addon/models/driver.js @@ -62,6 +62,10 @@ export default class DriverModel extends Model { @attr('string') city; @attr('string', { defaultValue: 'available' }) status; @attr('boolean') online; + + /** @managed-login (read-only, maintained by the server) */ + @attr('boolean') is_staff_linked; + @attr('string') login_status; @attr('raw') meta; /** @dates */ diff --git a/addon/serializers/contact.js b/addon/serializers/contact.js index f419096..b018499 100644 --- a/addon/serializers/contact.js +++ b/addon/serializers/contact.js @@ -9,7 +9,11 @@ export default class ContactSerializer extends ApplicationSerializer.extend(Embe */ get attrs() { return { - user: { embedded: 'always' }, + // The login account is managed by the server from name/email/phone + user: { embedded: 'always', serialize: false }, + user_uuid: { serialize: false }, + is_staff_linked: { serialize: false }, + login_status: { serialize: false }, place: { embedded: 'always' }, places: { embedded: 'always' }, photo: { embedded: 'always' }, diff --git a/addon/serializers/driver.js b/addon/serializers/driver.js index 4a5dc66..6ce25c6 100644 --- a/addon/serializers/driver.js +++ b/addon/serializers/driver.js @@ -17,6 +17,10 @@ export default class DriverSerializer extends ApplicationSerializer.extend(Embed current_job: { embedded: 'always' }, jobs: { embedded: 'always' }, custom_field_values: { embedded: 'always' }, + // The login account is managed by the server from name/email/phone + user_uuid: { serialize: false }, + is_staff_linked: { serialize: false }, + login_status: { serialize: false }, }; } From 7a407d7114b870d6d53643f1d43c3a974dafadbc Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Tue, 22 Sep 2026 01:08:30 +0800 Subject: [PATCH 2/4] test: update serializer contracts for managed login fields --- tests/unit/serializers/contact-test.js | 14 +++++++++++++- tests/unit/serializers/customer-test.js | 5 ++++- tests/unit/serializers/driver-test.js | 11 +++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/tests/unit/serializers/contact-test.js b/tests/unit/serializers/contact-test.js index 8a30d09..5f2af2f 100644 --- a/tests/unit/serializers/contact-test.js +++ b/tests/unit/serializers/contact-test.js @@ -20,7 +20,10 @@ module('Unit | Serializer | contact', function (hooks) { test('it declares exactly the expected relationship serialization contract', function (assert) { assertEmbeddedAttrs(assert, this.store, 'contact', { - user: { embedded: 'always' }, + user: { embedded: 'always', serialize: false }, + user_uuid: { serialize: false }, + is_staff_linked: { serialize: false }, + login_status: { serialize: false }, place: { embedded: 'always' }, places: { embedded: 'always' }, photo: { embedded: 'always' }, @@ -40,6 +43,15 @@ module('Unit | Serializer | contact', function (hooks) { assert.strictEqual(record.serialize().name, 'contract-value'); }); + test('the server-managed login fields never travel back to the server', function (assert) { + const record = this.store.createRecord('contact', { name: 'contract-value', is_staff_linked: true, login_status: 'active' }); + const json = record.serialize(); + + assert.false('is_staff_linked' in json); + assert.false('login_status' in json); + assert.false('user' in json); + }); + test('the place relationship travels inline with the record', function (assert) { const record = this.store.createRecord('contact'); record.set('place', this.store.createRecord('place', { name: 'related-value' })); diff --git a/tests/unit/serializers/customer-test.js b/tests/unit/serializers/customer-test.js index 4cb7b37..c431009 100644 --- a/tests/unit/serializers/customer-test.js +++ b/tests/unit/serializers/customer-test.js @@ -20,7 +20,10 @@ module('Unit | Serializer | customer', function (hooks) { test('it declares exactly the expected relationship serialization contract', function (assert) { assertEmbeddedAttrs(assert, this.store, 'customer', { - user: { embedded: 'always' }, + user: { embedded: 'always', serialize: false }, + user_uuid: { serialize: false }, + is_staff_linked: { serialize: false }, + login_status: { serialize: false }, place: { embedded: 'always' }, places: { embedded: 'always' }, photo: { embedded: 'always' }, diff --git a/tests/unit/serializers/driver-test.js b/tests/unit/serializers/driver-test.js index e1eaf05..429faaf 100644 --- a/tests/unit/serializers/driver-test.js +++ b/tests/unit/serializers/driver-test.js @@ -28,9 +28,20 @@ module('Unit | Serializer | driver', function (hooks) { current_job: EMBEDDED, jobs: EMBEDDED, custom_field_values: EMBEDDED, + user_uuid: { serialize: false }, + is_staff_linked: { serialize: false }, + login_status: { serialize: false }, }); }); + test('the server-managed login fields never travel back to the server', function (assert) { + const driver = this.store.createRecord('driver', { name: 'Ada Lovelace', is_staff_linked: true, login_status: 'active' }); + const json = driver.serialize(); + + assert.false('is_staff_linked' in json); + assert.false('login_status' in json); + }); + test('a server payload is normalized onto a record keyed by uuid', function (assert) { const driver = assertNormalizesUuidAsId(assert, this.store, 'driver', { name: 'Ada Lovelace', status: 'available' }); From 115428c59c9ee70cb58c5ea49c281691f005b60d Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Tue, 22 Sep 2026 01:27:05 +0800 Subject: [PATCH 3/4] fix: never send the contact's login account reference --- addon/serializers/contact.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/addon/serializers/contact.js b/addon/serializers/contact.js index b018499..ded067b 100644 --- a/addon/serializers/contact.js +++ b/addon/serializers/contact.js @@ -20,4 +20,21 @@ export default class ContactSerializer extends ApplicationSerializer.extend(Embe custom_field_values: { embedded: 'always' }, }; } + + /** + * The login account is resolved by the server from the contact's name, + * email and phone, so the account reference is never sent back. + * + * @param {Snapshot} snapshot + * @param {Object} options + * @return {Object} json + */ + serialize() { + const json = super.serialize(...arguments); + + delete json.user; + delete json.user_uuid; + + return json; + } } From ebc281ce6b9f874ff22cb0da4431a7bb0d5cd1f2 Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Tue, 22 Sep 2026 11:23:36 +0800 Subject: [PATCH 4/4] chore(release): open v0.2.2 release branch Bump package.json to 0.2.2 and seed RELEASE.md, so the release-tag workflow can validate the version once this branch merges to main. --- RELEASE.md | 9 ++++----- package.json | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/RELEASE.md b/RELEASE.md index 7db2865..3a0a99d 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,12 +1,11 @@ -> v0.2.1 ~ "Inspection display dates" + +> v0.2.2 ~ "Managed login state" --- ## Highlights -- **Inspection display dates are formatted** — `inspection-form` and `inspection-submission` format their display-date getters as `yyyy-MM-dd HH:mm` and answer `null` for a date they cannot read, the way every other model in this package does. The console's inspection indexes were showing a raw datetime instance string. -- **`frequency` is gone from `inspection-form`** — nothing scheduled an inspection from it, and it is being dropped from the FleetOps API resource, report schema and console in fleetbase/fleetops#319. - -The underscored attributes (`created_at`, `published_at`, …) are untouched, so anything needing a real `Date` is unaffected. +- **Driver and contact models expose their login state.** New read-only `is_staff_linked` and `login_status` attributes let the console show Reset Password, Send Credentials and Deactivate/Reactivate Login. They also lock email and phone on a profile linked to a team member's account. +- **The login account is never sent back.** Driver and contact login accounts are now managed by the server from the profile's name, email and phone (fleetbase/fleetops#338). The driver serializer no longer sends `user_uuid` or the login fields. The contact serializer no longer sends `user`, `user_uuid` or the login fields. --- ## Need help? diff --git a/package.json b/package.json index 77c38ad..a466422 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@fleetbase/fleetops-data", - "version": "0.2.1", + "version": "0.2.2", "description": "Fleetbase Fleet-Ops based models, serializers, transforms, adapters and GeoJson utility functions.", "keywords": [ "fleetbase-data",