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/3] 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/3] 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/3] 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; + } }