Preparing major release

This commit is contained in:
Ronald A. Richardson
2025-12-05 10:57:18 +08:00
parent 08dabaf138
commit 9719632289
10 changed files with 285 additions and 34 deletions

View File

@@ -1,22 +1,277 @@
# 🚀 Fleetbase v0.7.19 — 2025-11-17 # 🚀 Fleetbase v0.7.20 — 2025-12-05
> "A major leap forward in scheduling, reporting, and user interface capabilities." > "Major architectural & universe framework refactor for improved build, loading and initialization of extensions and system."
--- ---
## ✨ Highlights ## @fleetbase/ember-core v0.3.7
- **Core Scheduling Module**: A comprehensive, polymorphic, and reusable scheduling system has been integrated into the core API, providing the foundation for a wide range of scheduling applications.
- **Driver Scheduling with HOS Compliance**: FleetOps now includes built-in compliance for FMCSA Hours of Service regulations. ### 🎯 Major Features
- **Computed Columns in Query Builder**: The query builder now supports computed columns, allowing for more complex and powerful data queries with secure expression validation.
- **Advanced Table Functionality**: The Ember UI table component now supports multi-column sorting, horizontal scrolling, and sticky columns. #### Universe Service Refactor
- **New Filter Components**: New filter components for multi-input and range selection have been added to the Ember UI. Complete architectural overhaul of the Universe service with separation of concerns into specialized sub-services:
- **Dispatched Flag Control**: The order creation process in FleetOps now allows for explicit control over the dispatch behavior.
- **Vehicle Attributes Enhancement**: The vehicle model and API resources in FleetOps have been enhanced with additional attributes. - **ExtensionManager**: Manages extension loading, engine instances, and lifecycle hooks
- **RegistryService**: Fully dynamic, Map-based registry system for cross-engine resource sharing
- **MenuService**: Dedicated menu item and panel management with event support
- **WidgetService**: Dashboard widget registration and retrieval system
- **HookService**: Application lifecycle hooks and custom event hooks
**Key Benefits:**
- Improved performance with lazy loading and proper engine lifecycle management
- Better separation of concerns and maintainability
- Enhanced cross-engine resource sharing via Ember's application container
- Backward compatibility maintained through facade pattern
#### Enhanced Extension System
**New Extension Patterns:**
- `extension.js` hook patterns with `setupExtension()` and `onEngineLoaded()` support
- `whenEngineLoaded()` utility method that handles both loaded and not-yet-loaded engines
- Comprehensive extension checking methods: `isInstalled()`, `isEngineLoaded()`, `isEngineLoading()`
- Automatic hook execution for router-loaded engines
**Engine Loading Improvements:**
- Fixed race conditions between engine loading and hook registration
- Proper tracking of router-loaded engines in `loadedEngines` Map
- Patched `buildChildEngineInstance` to run hooks for router-loaded engines
- Two-phase extension setup: registration first, then hook execution
**Performance Optimizations:**
- `extensions.json` loading with localStorage caching
- Reduced console noise by removing excessive debug logs
- Performance monitoring for extension load times
#### Helper Registration System
- Cross-engine helper sharing via application container
- `registerHelper()` method for template helpers
- Support for both class-based and function-based helpers
- Automatic registration to application container for global access
#### Component Registration Enhancements
- `registerRenderableComponent()` for cross-engine component sharing
- Support for component classes via `ExtensionComponent`
- Automatic component registration to engine containers
- `getRenderableComponents()` for retrieving registered components
### 🔧 API Improvements
#### UniverseService
- `getService()` method with flexible naming pattern resolution:
- Short names: `"menu"``universe/menu-service`
- Plural variations: `"hooks"` or `"hook"``universe/hook-service`
- CamelCase: `"menuService"``universe/menu-service`
- Full names: `"menu-service"``universe/menu-service`
- With namespace: `"universe/menu-service"``universe/menu-service`
- `whenEngineLoaded()` for simplified engine-dependent setup
- `getServiceFromEngine()` for accessing engine-specific services
- `getApplicationInstance()` with fallback to `window.Fleetbase`
#### MenuService
- Computed getters for template access: `headerMenuItems`, `adminMenuItems`, `adminMenuPanels`
- Event triggers for backward compatibility
- Proper `finalView` normalization for all menu items
- `onClick` handlers wrapped with `menuItem` and `universe` parameters
- Fixed panel item slug/view structure to match original behavior
#### WidgetService
- Per-dashboard widget registration
- `registerWidgets()` for making widgets available on dashboards
- `registerDefaultWidgets()` for auto-loading specific widgets
- Proper widget filtering by dashboard ID
### 🐛 Bug Fixes
#### Extension Loading
- Resolved timeout errors during engine boot
- Fixed duplicate dashboard ID errors
- Corrected engine instance tracking and retrieval
- Fixed race conditions in extension setup
#### Menu System
- Fixed registry name mismatch for header menu items
- Corrected panel item structure for proper routing
- Added null checks before `dasherize` calls
- Fixed `onClick` handler parameter passing
#### Registry System
- Fixed widget registry lookup to work with array storage
- Proper filtering by registration key
- Shared registries Map across all engines via application container
- Triggered reactivity when creating new registries
#### Service Resolution
- Fixed service injection on engine boot
- Corrected service map injection
- Added missing `getServiceFromEngine` method
- Fixed `virtualRouteRedirect` and `getViewFromTransition` methods
### 📦 Exports & Structure
- Named exports on "exports" namespace
- Separated default and named exports in app/exports re-exports
### 🔄 ResourceActionService
- Allow properties to be set from initialization options
- Improved flexibility for service configuration
## @fleetbase/ember-ui v0.3.12
### 🎯 Major Features
#### Dashboard Service Refactor
Complete rewrite to support the new Universe service architecture:
- Direct injection of `widgetService` instead of going through universe
- Updated to use new widget service API
- Proper dashboard cleanup and recreation on route revisit
- Race condition fixes with `drop` task modifier
#### Widget Panel Enhancements
**Search & Filter:**
- Real-time keyword search for widgets by name and description
- `@tracked searchQuery` with `updateSearchQuery` action
- Improved widget discoverability
**Floating Pagination:**
- New floating pagination style (bottom-right, rounded, shadowed)
- Reveals horizontal scrollbar for better table navigation
- `@useTfootPagination` arg for backward compatibility
- Default to floating, opt-in to table footer pagination
**Thin Scrollbars:**
- Consistent 8px scrollbar height for both axes
- Webkit scrollbar styles for modern browsers
- Firefox `scrollbar-width: thin` support
- Dark mode scrollbar colors
### 🔧 Component Improvements
#### LazyEngineComponent
- Support for both path-based (lazy) and class-based (immediate) components
- Automatic component registration to engine containers
- Proper engine component lookup
- Helper for use with `{{component}}` syntax
#### RegistryYield
- Converted `yieldables` to computed getter for automatic reactivity
- Automatic wrapping of components with `LazyEngineComponent`
- `isComponent` getter to detect component types vs menu items
- Updated to use `registryService.getRenderableComponents()`
- Removed event listener approach (now uses reactive getters)
#### LoadEngine Component
- New component for explicit engine loading
- Complements `LazyEngineComponent` for different use cases
### 🐛 Bug Fixes
#### Dashboard Service
- Fixed duplicate dashboard ID errors with `drop` task modifier
- Added try-catch in `_createDefaultDashboard` for race conditions
- Proper state checks (`isDeleted`, `isDestroying`, `isDestroyed`)
- Fixed reset() to unload dashboard-widgets before dashboards
- Prevents identity map conflicts when recreating dashboards
#### Widget Panel
- Fixed `availableWidgets` reactivity by converting to getter
- Use `args.defaultDashboardId` first, then fallback to `this.defaultDashboardId`
- Ensures widgets registered after component creation are visible
- Handles string-represented widgets and components
#### RegistryYield
- Fixed TypeError by removing `registryService.on` event listener
- RegistryService doesn't have Evented mixin, now uses reactive getters
- Components registered after construction now properly yielded
#### Table Styling
- Fixed floating pagination spacing (reduced padding, adjusted margins)
- Applied `overflow-x` to `.next-table-wrapper` instead of table
- Horizontal scrollbar now visible and not hidden by pagination
- Added `overflow-y: visible` to prevent vertical scroll issues
### 🎨 UX Improvements
- Minor UX tweaks to widget panel component
- Improved widget panel layout and spacing
- Better visual hierarchy for widget selection
- Enhanced table navigation with floating pagination
## Migration Guide
### For Extension Developers
#### Using the New Extension Patterns
**Before:**
```javascript
// In addon/engine.js
export default class MyEngine extends Engine {
setupExtension() {
// Setup code here
}
}
```
**After:**
```javascript
// In addon/extension.js
import { Widget } from '@fleetbase/ember-core/contracts';
export default {
setupExtension(universe) {
// Get widget service
const widgetService = universe.getService('widget');
// Register widgets, menu items, etc.
widgetService.registerWidgets('dashboard', [new Widget({ ... })]);
// Register hooks for when other engines load
universe.whenEngineLoaded('@fleetbase/fleetops-engine', (engineInstance) => {
// Setup integration with FleetOps
});
}
}
```
#### Accessing Universe Sub-Services
**Flexible Service Resolution:**
```javascript
// All of these work:
const menuService = universe.getService('menu');
const menuService = universe.getService('menu-service');
const menuService = universe.getService('menuService');
const menuService = universe.getService('universe/menu-service');
const hookService = universe.getService('hooks');
const widgetService = universe.getService('widgets');
const registry = universe.getService('registry');
```
#### Widget Registration
**Before:**
```javascript
universe.registerWidget(widgetObject);
```
**After:**
```javascript
// Register widgets available for a dashboard
universe.registerWidgets('dashboard', [widget1, widget2]);
// Register widgets that auto-load on a dashboard
universe.registerDefaultWidgets('dashboard', [widget1]);
```
--- ---
## ⚠️ Breaking Changes ## ⚠️ Breaking Changes
- None - **NO MAJOR BREAKING CHANGED** Both releases maintain full backward compatibility with existing code. The refactor uses a facade pattern to ensure all existing APIs continue to work while providing new, improved patterns for future development.
### Notice
- Extensions must adapt to the new `extension.js` pattern for initialization setup.
- Extension dependencies are not auto-loaded and initialized, dependencies must be explicitly loaded from the setup `extension.js`
- `UniverseService` has major changes which refactored core responsibilities to sub-services. There is backwards compatability, but plan to migrate to the new integration architecture.
--- ---
@@ -33,5 +288,12 @@ docker compose down && docker compose up -d
docker compose exec application bash -c "./deploy.sh" docker compose exec application bash -c "./deploy.sh"
``` ```
### Post-Upgrade
1. Clear browser cache and localStorage
2. Restart your development server
3. Test extension loading and dashboard functionality
4. Check console for any deprecation warnings
## Need help? ## Need help?
Join the discussion on [GitHub Discussions](https://github.com/fleetbase/fleetbase/discussions) or drop by [#fleetbase on Discord](https://discord.com/invite/HnTqQ6zAVn) Join the discussion on [GitHub Discussions](https://github.com/fleetbase/fleetbase/discussions) or drop by [#fleetbase on Discord](https://discord.com/invite/HnTqQ6zAVn)

View File

@@ -2,10 +2,6 @@ import Controller from '@ember/controller';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
export default class ConsoleAccountController extends Controller { export default class ConsoleAccountController extends Controller {
/** @service('universe/menu-service') menuService;
* Inject the `universe` service.
*
* @memberof ConsoleAdminController
*/
@service universe; @service universe;
} }

View File

@@ -2,10 +2,6 @@ import Controller from '@ember/controller';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
export default class ConsoleAdminController extends Controller { export default class ConsoleAdminController extends Controller {
/** @service('universe/menu-service') menuService;
* Inject the `universe` service.
*
* @memberof ConsoleAdminController
*/
@service universe; @service universe;
} }

View File

@@ -2,10 +2,6 @@ import Controller from '@ember/controller';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
export default class ConsoleSettingsController extends Controller { export default class ConsoleSettingsController extends Controller {
/** @service('universe/menu-service') menuService;
* INject the `universe` service
*
* @memberof ConsoleSettingsController
*/
@service universe; @service universe;
} }

View File

@@ -4,11 +4,11 @@
<Layout::Sidebar::Item @route="console.account.index" @icon="user">Profile</Layout::Sidebar::Item> <Layout::Sidebar::Item @route="console.account.index" @icon="user">Profile</Layout::Sidebar::Item>
<Layout::Sidebar::Item @route="console.account.auth" @icon="key">Auth</Layout::Sidebar::Item> <Layout::Sidebar::Item @route="console.account.auth" @icon="key">Auth</Layout::Sidebar::Item>
<Layout::Sidebar::Item @route="console.account.organizations" @icon="building">Organizations</Layout::Sidebar::Item> <Layout::Sidebar::Item @route="console.account.organizations" @icon="building">Organizations</Layout::Sidebar::Item>
{{#each this.universe.accountMenuItems as |menuItem|}} {{#each this.menuService.accountMenuItems as |menuItem|}}
<Layout::Sidebar::Item @onClick={{fn this.universe.transitionMenuItem "console.account.virtual" menuItem}} @item={{menuItem}} @icon={{menuItem.icon}}>{{menuItem.title}}</Layout::Sidebar::Item> <Layout::Sidebar::Item @onClick={{fn this.universe.transitionMenuItem "console.account.virtual" menuItem}} @item={{menuItem}} @icon={{menuItem.icon}}>{{menuItem.title}}</Layout::Sidebar::Item>
{{/each}} {{/each}}
</Layout::Sidebar::Panel> </Layout::Sidebar::Panel>
{{#each this.universe.accountMenuPanels as |menuPanel|}} {{#each this.menuService.accountMenuPanels as |menuPanel|}}
<Layout::Sidebar::Panel @open={{menuPanel.open}} @title={{menuPanel.title}}> <Layout::Sidebar::Panel @open={{menuPanel.open}} @title={{menuPanel.title}}>
{{#each menuPanel.items as |menuItem|}} {{#each menuPanel.items as |menuItem|}}
<Layout::Sidebar::Item @onClick={{fn this.universe.transitionMenuItem "console.account.virtual" menuItem}} @item={{menuItem}} @icon={{menuItem.icon}}>{{menuItem.title}}</Layout::Sidebar::Item> <Layout::Sidebar::Item @onClick={{fn this.universe.transitionMenuItem "console.account.virtual" menuItem}} @item={{menuItem}} @icon={{menuItem.icon}}>{{menuItem.title}}</Layout::Sidebar::Item>

View File

@@ -7,7 +7,7 @@
<Layout::Sidebar::Item @route="console.admin.two-fa-settings" @icon="shield-halved">{{t "console.admin.menu.2fa-config"}}</Layout::Sidebar::Item> <Layout::Sidebar::Item @route="console.admin.two-fa-settings" @icon="shield-halved">{{t "console.admin.menu.2fa-config"}}</Layout::Sidebar::Item>
<Layout::Sidebar::Item @route="console.admin.schedule-monitor" @icon="calendar-check">{{t "console.admin.schedule-monitor.schedule-monitor"}}</Layout::Sidebar::Item> <Layout::Sidebar::Item @route="console.admin.schedule-monitor" @icon="calendar-check">{{t "console.admin.schedule-monitor.schedule-monitor"}}</Layout::Sidebar::Item>
{{#each this.universe.adminMenuItems as |menuItem|}} {{#each this.menuService.adminMenuItems as |menuItem|}}
<Layout::Sidebar::Item <Layout::Sidebar::Item
@onClick={{fn this.universe.transitionMenuItem "console.admin.virtual" menuItem}} @onClick={{fn this.universe.transitionMenuItem "console.admin.virtual" menuItem}}
@item={{menuItem}} @item={{menuItem}}
@@ -15,7 +15,7 @@
>{{menuItem.title}}</Layout::Sidebar::Item> >{{menuItem.title}}</Layout::Sidebar::Item>
{{/each}} {{/each}}
{{#each this.universe.adminMenuPanels as |menuPanel|}} {{#each this.menuService.adminMenuPanels as |menuPanel|}}
<Layout::Sidebar::Panel @open={{menuPanel.open}} @title={{menuPanel.title}}> <Layout::Sidebar::Panel @open={{menuPanel.open}} @title={{menuPanel.title}}>
{{#each menuPanel.items as |menuItem|}} {{#each menuPanel.items as |menuItem|}}
<Layout::Sidebar::Item <Layout::Sidebar::Item

View File

@@ -4,7 +4,7 @@
<Layout::Sidebar::Item @route="console.settings.index" @icon="cog">{{t "common.organization"}}</Layout::Sidebar::Item> <Layout::Sidebar::Item @route="console.settings.index" @icon="cog">{{t "common.organization"}}</Layout::Sidebar::Item>
<Layout::Sidebar::Item @route="console.settings.two-fa" @icon="shield-halved">{{t "common.two-factor"}}</Layout::Sidebar::Item> <Layout::Sidebar::Item @route="console.settings.two-fa" @icon="shield-halved">{{t "common.two-factor"}}</Layout::Sidebar::Item>
<Layout::Sidebar::Item @route="console.settings.notifications" @icon="bell">{{t "common.notifications"}}</Layout::Sidebar::Item> <Layout::Sidebar::Item @route="console.settings.notifications" @icon="bell">{{t "common.notifications"}}</Layout::Sidebar::Item>
{{#each this.universe.settingsMenuItems as |menuItem|}} {{#each this.menuService.settingsMenuItems as |menuItem|}}
<Layout::Sidebar::Item <Layout::Sidebar::Item
@onClick={{fn this.universe.transitionMenuItem "console.settings.virtual" menuItem}} @onClick={{fn this.universe.transitionMenuItem "console.settings.virtual" menuItem}}
@item={{menuItem}} @item={{menuItem}}
@@ -12,7 +12,7 @@
>{{menuItem.title}}</Layout::Sidebar::Item> >{{menuItem.title}}</Layout::Sidebar::Item>
{{/each}} {{/each}}
</Layout::Sidebar::Panel> </Layout::Sidebar::Panel>
{{#each this.universe.settingsMenuPanels as |menuPanel|}} {{#each this.menuService.settingsMenuPanels as |menuPanel|}}
<Layout::Sidebar::Panel @open={{menuPanel.open}} @title={{menuPanel.title}}> <Layout::Sidebar::Panel @open={{menuPanel.open}} @title={{menuPanel.title}}>
{{#each menuPanel.items as |menuItem|}} {{#each menuPanel.items as |menuItem|}}
<Layout::Sidebar::Item <Layout::Sidebar::Item

View File

@@ -454,6 +454,7 @@ dashboard-widget-panel:
{widgetName} Widget {widgetName} Widget
select-widgets: Select Widgets select-widgets: Select Widgets
close-and-save: Close and Save close-and-save: Close and Save
filter-widgets: Filter widgets by keyword
filters-picker: filters-picker:
filters: Filters filters: Filters