Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9b72307
fix(ai): repair resource search, add AI tools and console commands
roncodes Sep 18, 2026
0c79b2b
Scope internal order lifecycle lookups to the caller's company
roncodes Sep 18, 2026
a9692a0
Merge remote-tracking branch 'origin/main' into feature/ai-audit-hotf…
roncodes Sep 18, 2026
e997778
fix(ai): expose route optimization and import requirements as tools
roncodes Sep 20, 2026
46f0dbf
chore: open release v0.6.69
roncodes Sep 21, 2026
427ae5a
Merge pull request #330 from fleetbase/feature/ai-audit-hotfixes
roncodes Sep 21, 2026
52360a6
Merge pull request #331 from fleetbase/fix/order-lifecycle-tenant-sco…
roncodes Sep 21, 2026
b63e244
chore(deps): upgrade @fleetbase/ember-core and @fleetbase/ember-ui to…
roncodes Sep 21, 2026
1765536
feat: manage driver, customer and contact login accounts from the pro…
roncodes Sep 21, 2026
0055e88
test: restore request authorization coverage and cover orphaned telem…
roncodes Sep 21, 2026
e709e71
fix: don't log from the profile account conversion migration
roncodes Sep 21, 2026
593f1e3
fix: repair driver and customer memberships with their profile role
roncodes Sep 22, 2026
8731897
style(login): use ember-ui's btn-auth for the login page button
roncodes Sep 22, 2026
b9d4890
Add Ukrainian translation
ispdomnet Sep 22, 2026
7dfa0ff
Merge pull request #338 from fleetbase/feature/managed-profile-accounts
roncodes Sep 22, 2026
9ea73d6
Merge pull request #339 from fleetbase/feature/login-button-style
roncodes Sep 22, 2026
2c4583c
Merge pull request #340 from ispdomnet/main
roncodes Sep 22, 2026
ed0f683
Fix ukranian translations yaml
roncodes Sep 22, 2026
cbafa27
fix(details): drop the user account panel from customer and driver de…
roncodes Sep 22, 2026
e0670e8
fix(details): lay out driver details in a three-column grid
roncodes Sep 22, 2026
74ccca5
fix(orders): let the public Track Order lookup find orders again
roncodes Sep 22, 2026
c86b057
small fix to driver details grid
roncodes Sep 22, 2026
8473933
chore(deps): upgrade @fleetbase/fleetops-data to ^0.2.2
roncodes Sep 22, 2026
d813efb
chore(deps): upgrade @fleetbase/ember-ui to ^0.4.3
roncodes Sep 22, 2026
70dd102
test: match the test fakes to core-api 1.6.63's role signatures
roncodes Sep 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
> v0.6.68 ~ "Telematics sync status and SASCO fuel"
> v0.6.69 ~ "Fleet-Ops tools for Fleetbase AI"

---
## What's New
- **SASCO is a native fuel provider.** Connect a SASCO B2B account alongside PetroApp to import fuel transactions, with sandbox and production environments, amounts in SAR, driver name and phone, and receipt images. Transactions are matched to vehicles by plate.
- **"Sync Devices" works while telematics polling is running.** A manual sync requested while a scheduled sweep is queued or running is recorded and completed by the next sweep, instead of failing with "already queued or running".
- **Telematics connection status stays current.** Each completed scheduled sweep updates the connection status and last sync time, so a connection that has recovered no longer shows "Needs attention" or an old last sync date.
- **Fleet-Ops works with Fleetbase AI tool calling.** Creating orders from the AI prompt works again with the tool-calling assistant, and the assistant can also propose an optimized waypoint sequence for an order and explain what an order or resource import needs. Every change is a preview card the user confirms.
- **Fleet-Ops console actions.** The assistant can offer to open Fleet-Ops pages and dialogs, such as **Operations › Orders** and **New Order**, as confirmation cards.
- **Resource search for the assistant.** A `fleetops_search` tool finds orders, vehicles, drivers, work orders, maintenances, devices, sensors and telematics records by id, name, plate, VIN, email or phone.

---
## Fixes
- Manual telematics sync requests can no longer stay queued or failed indefinitely after their job is lost; the next scheduled sweep completes them.
- Fuel provider environment labels and sync run details no longer refer to PetroApp for other providers.
- AI resource search no longer fails on every call with `Unknown column 'sensor_type'`, and database errors are no longer passed to the model.

---
## Dependencies
- `@fleetbase/fleetops-data` upgraded to `^0.2.2`, which adds the read-only login state (`is_staff_linked`, `login_status`) to the driver and contact models.
- `@fleetbase/ember-ui` upgraded to `^0.4.3`, which provides the `btn-auth` style the Track Order button on the login page uses.

---
## Testing
- Backend tests cover manual syncs during scheduled sweeps, stale failure recovery, partial sweeps, and adoption of abandoned requests.
- SASCO provider tests cover login, token caching, re-login after a 401, connection tests, pagination limits, error handling and transaction normalization.
- `docs/TELEMATICS_QUEUES.md` now documents that dedicated telematics workers must share the queue worker's image and `APP_KEY`, with verification commands and troubleshooting.
- Unit tests cover the new AI tools and console commands, and the AI capability registration.

---
## Need help?
Expand Down
15 changes: 13 additions & 2 deletions addon/components/contact/form.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,22 @@
<Input @value={{@resource.title}} @type="text" class="w-full form-input" placeholder={{t "common.title"}} disabled={{cannot-write @resource}} />
</InputGroup>
<InputGroup @name={{t "common.email"}}>
<Input @value={{@resource.email}} @type="text" class="w-full form-input" placeholder={{t "common.email"}} disabled={{cannot-write @resource}} />
<Input
@value={{@resource.email}}
@type="text"
class="w-full form-input"
placeholder={{t "common.email"}}
disabled={{or (cannot-write @resource) @resource.is_staff_linked}}
/>
</InputGroup>
<InputGroup @name={{t "common.phone"}}>
<PhoneInput @value={{@resource.phone}} @onInput={{fn (mut @resource.phone)}} class="form-input w-full" disabled={{cannot-write @resource}} />
<PhoneInput @value={{@resource.phone}} @onInput={{fn (mut @resource.phone)}} class="form-input w-full" disabled={{or (cannot-write @resource) @resource.is_staff_linked}} />
</InputGroup>
{{#if @resource.is_staff_linked}}
<div class="lg:col-span-2 -mt-1 mb-2 text-xs text-gray-500 dark:text-gray-400" data-test-staff-linked-helper>{{t "profile-account.staff-linked-helper"}}</div>
{{else if @resource.isNew}}
<ProfileIdentityHint @email={{@resource.email}} @phone={{@resource.phone}} @type="contact" class="lg:col-span-2" />
{{/if}}
<InputGroup @name={{t "common.internal-id"}}>
<Input @value={{@resource.internal_id}} @type="text" class="w-full form-input" placeholder={{t "common.internal-id"}} disabled={{cannot-write @resource}} />
</InputGroup>
Expand Down
34 changes: 17 additions & 17 deletions addon/components/customer/details.hbs
Original file line number Diff line number Diff line change
@@ -1,20 +1,4 @@
<div class="details-wrapper" ...attributes>
<ContentPanel @title={{t "customer.fields.user-account"}} @open={{true}} @wrapperClass="bordered-top">
<div class="grid grid-cols-2 gap-2 text-xs dark:text-gray-100">
<div class="field-info-container md:col-span-3">
<div class="field-name">{{t "common.name"}}</div>
<div class="field-value">{{n-a @resource.name}}</div>
</div>
<div class="field-info-container">
<div class="field-name">{{t "common.email"}}</div>
<ClickToCopy @value={{@resource.email}} class="field-value">{{n-a @resource.email}}</ClickToCopy>
</div>
<div class="field-info-container">
<div class="field-name">{{t "common.phone"}}</div>
<ClickToCopy @value={{@resource.phone}} class="field-value">{{n-a @resource.phone}}</ClickToCopy>
</div>
</div>
</ContentPanel>
<ContentPanel @title={{t "common.details"}} @open={{true}} @wrapperClass="bordered-top">
<div class="grid grid-cols-2 gap-2 text-xs dark:text-gray-100">
<div class="field-info-container">
Expand All @@ -30,7 +14,7 @@
<ClickToCopy @value={{@resource.email}} class="field-value">{{n-a @resource.email}}</ClickToCopy>
</div>
<div class="field-info-container">
<div class="field-name">{{t "common.email"}}</div>
<div class="field-name">{{t "common.phone"}}</div>
<ClickToCopy @value={{@resource.phone}} class="field-value">{{n-a @resource.phone}}</ClickToCopy>
</div>
<div class="field-info-container">
Expand All @@ -47,5 +31,21 @@
</div>
</div>
</ContentPanel>
<ContentPanel @title={{t "customer.fields.addresses"}} @open={{true}} @wrapperClass="bordered-top">
{{#if @resource.places.length}}
<div class="space-y-2 text-xs dark:text-gray-100">
{{#each @resource.places as |place|}}
<div class="flex items-center justify-between gap-2">
<Place::Pill @place={{place}} @subtitle={{place.address}} />
{{#if (eq place.id @resource.place_uuid)}}
<Badge @status="info" @hideStatusDot={{true}}>{{t "customer.fields.primary-address"}}</Badge>
{{/if}}
</div>
{{/each}}
</div>
{{else}}
<div class="text-xs text-gray-500 dark:text-gray-400">{{t "customer.fields.no-addresses"}}</div>
{{/if}}
</ContentPanel>
<CustomField::Yield @subject={{@resource}} @viewMode={{true}} @wrapperClass="bordered-top" />
</div>
15 changes: 13 additions & 2 deletions addon/components/customer/form.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,22 @@
<Input @value={{@resource.title}} @type="text" class="w-full form-input" placeholder={{t "common.title"}} disabled={{cannot-write @resource}} />
</InputGroup>
<InputGroup @name={{t "common.email"}}>
<Input @value={{@resource.email}} @type="text" class="w-full form-input" placeholder={{t "common.email"}} disabled={{cannot-write @resource}} />
<Input
@value={{@resource.email}}
@type="text"
class="w-full form-input"
placeholder={{t "common.email"}}
disabled={{or (cannot-write @resource) @resource.is_staff_linked}}
/>
</InputGroup>
<InputGroup @name={{t "common.phone"}}>
<PhoneInput @value={{@resource.phone}} @onInput={{fn (mut @resource.phone)}} class="form-input w-full" disabled={{cannot-write @resource}} />
<PhoneInput @value={{@resource.phone}} @onInput={{fn (mut @resource.phone)}} class="form-input w-full" disabled={{or (cannot-write @resource) @resource.is_staff_linked}} />
</InputGroup>
{{#if @resource.is_staff_linked}}
<div class="lg:col-span-2 -mt-1 mb-2 text-xs text-gray-500 dark:text-gray-400" data-test-staff-linked-helper>{{t "profile-account.staff-linked-helper"}}</div>
{{else if @resource.isNew}}
<ProfileIdentityHint @email={{@resource.email}} @phone={{@resource.phone}} @type="customer" class="lg:col-span-2" />
{{/if}}
<InputGroup @name={{t "common.internal-id"}}>
<Input @value={{@resource.internal_id}} @type="text" class="w-full form-input" placeholder={{t "common.internal-id"}} disabled={{cannot-write @resource}} />
</InputGroup>
Expand Down
56 changes: 0 additions & 56 deletions addon/components/customer/form.js
Original file line number Diff line number Diff line change
@@ -1,71 +1,15 @@
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
import { task } from 'ember-concurrency';

export default class CustomerFormComponent extends Component {
@service customerActions;
@service store;
@service fetch;
@service currentUser;
@service notifications;
@service modalsManager;
@service('universe/extension-manager') extensionManager;

@tracked userAccountActionButtons = [
{
icon: 'user-plus',
size: 'xs',
permission: 'iam create user',
onClick: async () => {
// Load IAM engine for user-form modal component
await this.extensionManager.ensureEngineLoaded('@fleetbase/iam-engine');

const user = this.store.createRecord('user', {
status: 'pending',
type: 'user',
});

this.modalsManager.show('modals/user-form', {
title: 'Create a new user',
user,
formPermission: 'iam create user',
uploadNewPhoto: (file) => {
this.fetch.uploadFile.perform(
file,
{
path: `uploads/${this.currentUser.companyId}/users/${user.slug}`,
subject_uui: user.id,
subject_type: 'user',
type: 'user_photo',
},
(uploadedFile) => {
user.setProperties({
avatar_uuid: uploadedFile.id,
avatar_url: uploadedFile.url,
avatar: uploadedFile,
});
}
);
},
confirm: async (modal) => {
modal.startLoading();

try {
await user.save();
this.notifications.success('New user created successfully!');
modal.done();
} catch (error) {
this.notifications.serverError(error);
modal.stopLoading();
}
},
});
},
},
];

get showWelcomeEmailOption() {
return this.args.resource?.isNew && this.extensionManager.isInstalled('@fleetbase/customer-portal-engine');
}
Expand Down
32 changes: 13 additions & 19 deletions addon/components/driver/details.hbs
Original file line number Diff line number Diff line change
@@ -1,23 +1,18 @@
<div class="details-wrapper" ...attributes>
<ContentPanel @title={{t "driver.fields.user-account"}} @open={{true}} @wrapperClass="bordered-top">
<div class="grid grid-cols-2 gap-2 text-xs dark:text-gray-100">
<ContentPanel @title={{t "common.details"}} @open={{true}} @wrapperClass="bordered-top">
<div class="grid grid-cols-1 md:grid-cols-3 gap-2 text-xs dark:text-gray-100">
<div class="field-info-container md:col-span-3">
<div class="field-name">{{t "common.name"}}</div>
<div class="field-value">{{n-a @resource.name}}</div>
</div>
<div class="field-info-container">
<div class="field-name">{{t "common.email"}}</div>
<ClickToCopy @value={{@resource.email}} class="field-value">{{n-a @resource.email}}</ClickToCopy>
</div>
<div class="field-info-container">
<div class="field-name">{{t "common.phone"}}</div>
<ClickToCopy @value={{@resource.phone}} class="field-value">{{n-a @resource.phone}}</ClickToCopy>
</div>
</div>
</ContentPanel>

<ContentPanel @title={{t "driver.fields.driver-details"}} @open={{true}} @wrapperClass="bordered-top">
<div class="grid grid-cols-2 gap-2 text-xs dark:text-gray-100">
<div class="field-info-container md:col-span-2">
<div class="field-name">{{t "common.email"}}</div>
<ClickToCopy @value={{@resource.email}} class="field-value">{{n-a @resource.email}}</ClickToCopy>
</div>
<div class="field-info-container">
<div class="field-name">{{t "common.id"}}</div>
<ClickToCopy @value={{@resource.public_id}} class="field-value">{{n-a @resource.public_id}}</ClickToCopy>
Expand All @@ -30,28 +25,27 @@
<div class="field-name">{{t "driver.fields.driver-license"}}</div>
<div class="field-value">{{n-a @resource.drivers_license_number}}</div>
</div>
<div class="field-info-container">
<div class="field-info-container md:col-span-3">
<div class="field-name">{{t "driver.fields.license-expiry"}}</div>
<div class="field-value">{{n-a (format-date-fns @resource.license_expiry)}}</div>
</div>
<div class="field-info-container">
<div class="field-name">{{t "resource.vendor"}}</div>
<div class="field-name">{{t "resource.vehicle"}}</div>
<div class="field-value">
<Vendor::Pill @vendor={{resource-relation @resource "vendor"}} @titleFallback={{n-a @resource.vendor_name}} />
<Vehicle::Pill @vehicle={{resource-relation @resource "vehicle"}} @titleFallback={{n-a @resource.vehicle_name (t "driver.fields.no-vehicle-assigned")}} />
</div>
</div>
<div class="field-info-container">
<div class="field-name">{{t "resource.vehicle"}}</div>
<div class="field-info-container md:col-span-2">
<div class="field-name">{{t "resource.vendor"}}</div>
<div class="field-value">
<Vehicle::Pill @vehicle={{resource-relation @resource "vehicle"}} @titleFallback={{n-a @resource.vehicle_name (t "driver.fields.no-vehicle-assigned")}} />
<Vendor::Pill @vendor={{resource-relation @resource "vendor"}} @titleFallback={{n-a @resource.vendor_name}} />
</div>
</div>
<div class="col-span-3"></div>
<div class="field-info-container">
<div class="field-name">{{t "common.city"}}</div>
<div class="field-value">{{n-a @resource.city}}</div>
</div>
<div class="field-info-container">
<div class="field-info-container md:col-span-2">
<div class="field-name">{{t "common.country"}}</div>
<div class="field-value">
<CountryName @country={{@resource.country}} />
Expand Down
Loading
Loading