Compare commits

...

7 Commits

Author SHA1 Message Date
Ron
0238632fdd Merge pull request #477 from fleetbase/dev-v0.7.22
Some checks failed
Fleetbase CI / Build and Start Docker Services (push) Has been cancelled
Organizations can now set their own alpha-numeric sender ID for SMS
2025-12-07 06:37:02 +00:00
Ronald A. Richardson
27652db9c3 Organizations can now set their own alpha-numeric sender ID for SMS 2025-12-07 14:15:31 +08:00
Ronald A. Richardson
5eaf2039d4 Fix: correct version of ember-ui in console/package.json
Some checks failed
Fleetbase CI / Build and Start Docker Services (push) Has been cancelled
2025-12-06 21:49:14 +08:00
Ron
d8c06ae0be Merge pull request #476 from fleetbase/dev-v0.7.21
v0.7.21 ~ 5x faster css compiling and flawless builds
2025-12-06 13:41:42 +00:00
Ronald A. Richardson
c9011b3ffa *critical* patch to load-extensions initializer 2025-12-06 21:38:02 +08:00
Ronald A. Richardson
520de0f6bc fixed formating on console/package.json 2025-12-06 19:39:24 +08:00
Ronald A. Richardson
ba6ed235e3 v0.7.21 ~ 5x faster css compiling and flawless builds 2025-12-06 19:26:17 +08:00
27 changed files with 727 additions and 1048 deletions

View File

@@ -1,281 +1,24 @@
# 🚀 Fleetbase v0.7.20 — 2025-12-05
# 🚀 Fleetbase v0.7.22 — 2025-12-07
> "Major architectural & universe framework refactor for improved build, loading and initialization of extensions and system."
> "Organizations can now set their own alpha-numeric sender ID for SMS"
---
## @fleetbase/ember-core v0.3.7
### 🎯 Major Features
#### Universe Service Refactor
Complete architectural overhaul of the Universe service with separation of concerns into specialized sub-services:
- **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]);
```
## ✨ Highlights
- **Custom Alphanumeric Sender ID for SMS:**
Organizations can now configure their own **Alphanumeric Sender ID** used when sending verification codes and other SMS notifications.
This feature improves brand recognition, enhances trust, and aligns outbound communication with each organizations identity.
Supported in regions/carriers where alphanumeric senders are allowed (e.g., Mongolia and others).
---
## ⚠️ Breaking Changes
- **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.
- None 🙂
---
## 🔧 Upgrade Steps
```bash
# Pull latest version
git pull origin main --no-rebase
@@ -288,12 +31,7 @@ docker compose down && docker compose up -d
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?
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/ember-ui/discussions) or drop by [#fleetbase on Discord](https://discord.com/invite/HnTqQ6zAVn)

View File

@@ -20,10 +20,10 @@
"require": {
"php": ">=8.0 <=8.2.28",
"appstract/laravel-opcache": "^4.0",
"fleetbase/core-api": "^1.6.27",
"fleetbase/fleetops-api": "^0.6.28",
"fleetbase/registry-bridge": "^0.1.1",
"fleetbase/storefront-api": "^0.4.8",
"fleetbase/core-api": "^1.6.28",
"fleetbase/fleetops-api": "^0.6.29",
"fleetbase/registry-bridge": "^0.1.2",
"fleetbase/storefront-api": "^0.4.9",
"guzzlehttp/guzzle": "^7.0.1",
"laravel/framework": "^10.0",
"laravel/octane": "^2.3",

132
api/composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "e7e5ef8f3c23f2e61f59c53c5bfbca39",
"content-hash": "357a3d180da0e49157a3d7057c279056",
"packages": [
{
"name": "appstract/laravel-opcache",
@@ -124,16 +124,16 @@
},
{
"name": "aws/aws-sdk-php",
"version": "3.366.1",
"version": "3.366.2",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
"reference": "981ae91529b990987bdb182c11322dd34848976a"
"reference": "de3b25e2d86aa081814e198fe5cde4ef0ff7f879"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/981ae91529b990987bdb182c11322dd34848976a",
"reference": "981ae91529b990987bdb182c11322dd34848976a",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/de3b25e2d86aa081814e198fe5cde4ef0ff7f879",
"reference": "de3b25e2d86aa081814e198fe5cde4ef0ff7f879",
"shasum": ""
},
"require": {
@@ -215,9 +215,9 @@
"support": {
"forum": "https://github.com/aws/aws-sdk-php/discussions",
"issues": "https://github.com/aws/aws-sdk-php/issues",
"source": "https://github.com/aws/aws-sdk-php/tree/3.366.1"
"source": "https://github.com/aws/aws-sdk-php/tree/3.366.2"
},
"time": "2025-12-04T16:55:00+00:00"
"time": "2025-12-05T19:09:22+00:00"
},
{
"name": "aws/aws-sdk-php-laravel",
@@ -2157,16 +2157,16 @@
},
{
"name": "fleetbase/core-api",
"version": "1.6.27",
"version": "1.6.28",
"source": {
"type": "git",
"url": "https://github.com/fleetbase/core-api.git",
"reference": "831600c1818cbe76cfe280bfaaa3896ff1e2c597"
"reference": "d964db3f4264f0a261364ce0c655f8aa561aeb84"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fleetbase/core-api/zipball/831600c1818cbe76cfe280bfaaa3896ff1e2c597",
"reference": "831600c1818cbe76cfe280bfaaa3896ff1e2c597",
"url": "https://api.github.com/repos/fleetbase/core-api/zipball/d964db3f4264f0a261364ce0c655f8aa561aeb84",
"reference": "d964db3f4264f0a261364ce0c655f8aa561aeb84",
"shasum": ""
},
"require": {
@@ -2252,9 +2252,9 @@
],
"support": {
"issues": "https://github.com/fleetbase/core-api/issues",
"source": "https://github.com/fleetbase/core-api/tree/v1.6.27"
"source": "https://github.com/fleetbase/core-api/tree/v1.6.28"
},
"time": "2025-12-05T11:24:12+00:00"
"time": "2025-12-07T06:10:01+00:00"
},
{
"name": "fleetbase/countries",
@@ -2323,16 +2323,16 @@
},
{
"name": "fleetbase/fleetops-api",
"version": "0.6.28",
"version": "0.6.29",
"source": {
"type": "git",
"url": "https://github.com/fleetbase/fleetops.git",
"reference": "91cf968a2575810380d05a5d58558716f569b7d7"
"reference": "0b5accee01995d04bf99c42130decd52f03a502e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fleetbase/fleetops/zipball/91cf968a2575810380d05a5d58558716f569b7d7",
"reference": "91cf968a2575810380d05a5d58558716f569b7d7",
"url": "https://api.github.com/repos/fleetbase/fleetops/zipball/0b5accee01995d04bf99c42130decd52f03a502e",
"reference": "0b5accee01995d04bf99c42130decd52f03a502e",
"shasum": ""
},
"require": {
@@ -2407,9 +2407,9 @@
],
"support": {
"issues": "https://github.com/fleetbase/fleetops/issues",
"source": "https://github.com/fleetbase/fleetops/tree/v0.6.28"
"source": "https://github.com/fleetbase/fleetops/tree/v0.6.29"
},
"time": "2025-12-05T11:33:09+00:00"
"time": "2025-12-06T10:28:02+00:00"
},
{
"name": "fleetbase/laravel-mysql-spatial",
@@ -2479,16 +2479,16 @@
},
{
"name": "fleetbase/registry-bridge",
"version": "0.1.1",
"version": "0.1.2",
"source": {
"type": "git",
"url": "https://github.com/fleetbase/registry-bridge.git",
"reference": "09215a4c7314ed734d77b2b3c2b8d526640d8367"
"reference": "0e55d24ea1b9125c5219ea3a155dcb64f33a0d8c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fleetbase/registry-bridge/zipball/09215a4c7314ed734d77b2b3c2b8d526640d8367",
"reference": "09215a4c7314ed734d77b2b3c2b8d526640d8367",
"url": "https://api.github.com/repos/fleetbase/registry-bridge/zipball/0e55d24ea1b9125c5219ea3a155dcb64f33a0d8c",
"reference": "0e55d24ea1b9125c5219ea3a155dcb64f33a0d8c",
"shasum": ""
},
"require": {
@@ -2555,22 +2555,22 @@
],
"support": {
"issues": "https://github.com/fleetbase/registry-bridge/issues",
"source": "https://github.com/fleetbase/registry-bridge/tree/v0.1.1"
"source": "https://github.com/fleetbase/registry-bridge/tree/v0.1.2"
},
"time": "2025-12-05T04:35:40+00:00"
"time": "2025-12-06T10:31:23+00:00"
},
{
"name": "fleetbase/storefront-api",
"version": "0.4.8",
"version": "0.4.9",
"source": {
"type": "git",
"url": "https://github.com/fleetbase/storefront.git",
"reference": "3d6695e235061111c1ce128fcf1b529751ba0631"
"reference": "f7c6d42f8a1a5d68d6cfd9bd5dbecb18b63d9420"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fleetbase/storefront/zipball/3d6695e235061111c1ce128fcf1b529751ba0631",
"reference": "3d6695e235061111c1ce128fcf1b529751ba0631",
"url": "https://api.github.com/repos/fleetbase/storefront/zipball/f7c6d42f8a1a5d68d6cfd9bd5dbecb18b63d9420",
"reference": "f7c6d42f8a1a5d68d6cfd9bd5dbecb18b63d9420",
"shasum": ""
},
"require": {
@@ -2638,9 +2638,9 @@
],
"support": {
"issues": "https://github.com/fleetbase/storefront/issues",
"source": "https://github.com/fleetbase/storefront/tree/v0.4.8"
"source": "https://github.com/fleetbase/storefront/tree/v0.4.9"
},
"time": "2025-12-05T11:35:28+00:00"
"time": "2025-12-06T10:36:49+00:00"
},
{
"name": "fleetbase/twilio",
@@ -3820,16 +3820,16 @@
},
{
"name": "google/protobuf",
"version": "v4.33.1",
"version": "v4.33.2",
"source": {
"type": "git",
"url": "https://github.com/protocolbuffers/protobuf-php.git",
"reference": "0cd73ccf0cd26c3e72299cce1ea6144091a57e12"
"reference": "fbd96b7bf1343f4b0d8fb358526c7ba4d72f1318"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/0cd73ccf0cd26c3e72299cce1ea6144091a57e12",
"reference": "0cd73ccf0cd26c3e72299cce1ea6144091a57e12",
"url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/fbd96b7bf1343f4b0d8fb358526c7ba4d72f1318",
"reference": "fbd96b7bf1343f4b0d8fb358526c7ba4d72f1318",
"shasum": ""
},
"require": {
@@ -3858,9 +3858,9 @@
"proto"
],
"support": {
"source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.33.1"
"source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.33.2"
},
"time": "2025-11-12T21:58:05+00:00"
"time": "2025-12-05T22:12:22+00:00"
},
{
"name": "graham-campbell/result-type",
@@ -7755,16 +7755,16 @@
},
{
"name": "nikic/php-parser",
"version": "v5.6.2",
"version": "v5.7.0",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
"reference": "3a454ca033b9e06b63282ce19562e892747449bb"
"reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3a454ca033b9e06b63282ce19562e892747449bb",
"reference": "3a454ca033b9e06b63282ce19562e892747449bb",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82",
"reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82",
"shasum": ""
},
"require": {
@@ -7807,9 +7807,9 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v5.6.2"
"source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0"
},
"time": "2025-10-21T19:32:17+00:00"
"time": "2025-12-06T11:56:16+00:00"
},
{
"name": "nunomaduro/termwind",
@@ -9121,26 +9121,26 @@
},
{
"name": "phrity/util-errorhandler",
"version": "1.2.1",
"version": "1.2.2",
"source": {
"type": "git",
"url": "https://github.com/sirn-se/phrity-util-errorhandler.git",
"reference": "9825f15ef9b4a93252ce53ca8962278832d834da"
"reference": "70a669cc22db2eed6a109ec66fd95168a4332c9b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sirn-se/phrity-util-errorhandler/zipball/9825f15ef9b4a93252ce53ca8962278832d834da",
"reference": "9825f15ef9b4a93252ce53ca8962278832d834da",
"url": "https://api.github.com/repos/sirn-se/phrity-util-errorhandler/zipball/70a669cc22db2eed6a109ec66fd95168a4332c9b",
"reference": "70a669cc22db2eed6a109ec66fd95168a4332c9b",
"shasum": ""
},
"require": {
"php": "^8.1"
},
"require-dev": {
"php-coveralls/php-coveralls": "^2.0",
"phpstan/phpstan": "^2.0",
"phpunit/phpunit": "^10.0 | ^11.0 | ^12.0",
"squizlabs/php_codesniffer": "^3.5"
"phpunit/phpunit": "^10.0 || ^11.0 || ^12.0",
"robiningelbrecht/phpunit-coverage-tools": "^1.9",
"squizlabs/php_codesniffer": "^3.5 || ^4.0"
},
"type": "library",
"autoload": {
@@ -9167,9 +9167,9 @@
],
"support": {
"issues": "https://github.com/sirn-se/phrity-util-errorhandler/issues",
"source": "https://github.com/sirn-se/phrity-util-errorhandler/tree/1.2.1"
"source": "https://github.com/sirn-se/phrity-util-errorhandler/tree/1.2.2"
},
"time": "2025-08-08T09:48:45+00:00"
"time": "2025-12-05T21:25:36+00:00"
},
{
"name": "phrity/websocket",
@@ -10041,16 +10041,16 @@
},
{
"name": "psy/psysh",
"version": "v0.12.15",
"version": "v0.12.16",
"source": {
"type": "git",
"url": "https://github.com/bobthecow/psysh.git",
"reference": "38953bc71491c838fcb6ebcbdc41ab7483cd549c"
"reference": "ee6d5028be4774f56c6c2c85ec4e6bc9acfe6b67"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/bobthecow/psysh/zipball/38953bc71491c838fcb6ebcbdc41ab7483cd549c",
"reference": "38953bc71491c838fcb6ebcbdc41ab7483cd549c",
"url": "https://api.github.com/repos/bobthecow/psysh/zipball/ee6d5028be4774f56c6c2c85ec4e6bc9acfe6b67",
"reference": "ee6d5028be4774f56c6c2c85ec4e6bc9acfe6b67",
"shasum": ""
},
"require": {
@@ -10058,8 +10058,8 @@
"ext-tokenizer": "*",
"nikic/php-parser": "^5.0 || ^4.0",
"php": "^8.0 || ^7.4",
"symfony/console": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4",
"symfony/var-dumper": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4"
"symfony/console": "^8.0 || ^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4",
"symfony/var-dumper": "^8.0 || ^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4"
},
"conflict": {
"symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4"
@@ -10114,9 +10114,9 @@
],
"support": {
"issues": "https://github.com/bobthecow/psysh/issues",
"source": "https://github.com/bobthecow/psysh/tree/v0.12.15"
"source": "https://github.com/bobthecow/psysh/tree/v0.12.16"
},
"time": "2025-11-28T00:00:14+00:00"
"time": "2025-12-07T03:39:01+00:00"
},
{
"name": "ralouphie/getallheaders",
@@ -16770,16 +16770,16 @@
},
{
"name": "phpunit/phpunit",
"version": "10.5.59",
"version": "10.5.60",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "c47fe00df06fb1f68399ef7386edb01c25132473"
"reference": "f2e26f52f80ef77832e359205f216eeac00e320c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c47fe00df06fb1f68399ef7386edb01c25132473",
"reference": "c47fe00df06fb1f68399ef7386edb01c25132473",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f2e26f52f80ef77832e359205f216eeac00e320c",
"reference": "f2e26f52f80ef77832e359205f216eeac00e320c",
"shasum": ""
},
"require": {
@@ -16851,7 +16851,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.59"
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.60"
},
"funding": [
{
@@ -16875,7 +16875,7 @@
"type": "tidelift"
}
],
"time": "2025-12-01T07:37:23+00:00"
"time": "2025-12-06T07:50:42+00:00"
},
{
"name": "sebastian/cli-parser",

View File

@@ -1,31 +0,0 @@
<Overlay @isOpen={{@isOpen}} @onLoad={{this.setOverlayContext}} @position="right" @noBackdrop={{true}} @fullHeight={{true}} @width={{or this.width @width "400px"}}>
<Overlay::Header @title={{t "component.dashboard-widget-panel.select-widgets"}} @hideStatusDot={{true}} @titleWrapperClass="leading-5">
<div class="flex flex-1 justify-end">
<Button @type="default" @icon="times" @helpText={{t "component.dashboard-widget-panel.close-and-save"}} @onClick={{this.onPressClose}} />
</div>
</Overlay::Header>
<Overlay::Body @wrapperClass="new-service-rate-overlay-body px-4 space-y-4 pt-4">
<div class="grid grid-cols-1 gap-4 text-xs dark:text-gray-100">
{{#each this.availableWidgets as |widget|}}
<div
class="rounded-lg border border-gray-300 bg-white dark:border-gray-700 dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-700 transition-all duration-300 ease-in-out shadow-md px-4 py-2 cursor-pointer"
{{on "click" (fn this.addWidgetToDashboard widget)}}
>
<div class="flex flex-row items-center leading-6 mb-2.5">
<div class="w-8 flex items-center justify-start">
<FaIcon @icon={{widget.icon}} class="text-lg text-gray-600 dark:text-gray-300" />
</div>
<p class="text-sm truncate font-semibold dark:text-gray-100 text-gray-800">
{{t "component.dashboard-widget-panel.widget-name" widgetName=widget.name}}
</p>
</div>
<div>
<p class="text-xs dark:text-gray-100 text-gray-800">{{widget.description}}</p>
</div>
</div>
{{/each}}
</div>
<Spacer @height="300px" />
</Overlay::Body>
</Overlay>

View File

@@ -1,60 +0,0 @@
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
export default class DashboardWidgetPanelComponent extends Component {
@service universe;
@tracked availableWidgets = [];
@tracked dashboard;
@tracked isOpen = true;
@service notifications;
/**
* Constructs the component and applies initial state.
*/
constructor(owner, { dashboard }) {
super(...arguments);
this.availableWidgets = this.universe.getDashboardWidgets();
this.dashboard = dashboard;
}
/**
* Sets the overlay context.
*
* @action
* @param {OverlayContextObject} overlayContext
*/
@action setOverlayContext(overlayContext) {
this.context = overlayContext;
if (typeof this.args.onLoad === 'function') {
this.args.onLoad(...arguments);
}
}
@action addWidgetToDashboard(widget) {
// If widget is a component definition/class
if (typeof widget.component === 'function') {
widget.component = widget.component.name;
}
this.args.dashboard.addWidget(widget).catch((error) => {
this.notifications.serverError(error);
});
}
/**
* Handles cancel button press.
*
* @action
*/
@action onPressClose() {
this.isOpen = false;
if (typeof this.args.onClose === 'function') {
this.args.onClose();
}
}
}

View File

@@ -6,35 +6,13 @@ import createNotificationKey from '@fleetbase/ember-core/utils/create-notificati
import { task } from 'ember-concurrency';
export default class ConsoleSettingsNotificationsController extends Controller {
/**
* Inject the notifications service.
*
* @memberof ConsoleSettingsNotificationsController
*/
@service notifications;
/**
* Inject the fetch service.
*
* @memberof ConsoleSettingsNotificationsController
*/
@service fetch;
/**
* The notification settings value JSON.
*
* @memberof ConsoleSettingsNotificationsController
* @var {Object}
*/
@service store;
@service currentUser;
@tracked notificationSettings = {};
/**
* Notification transport methods enabled.
*
* @memberof ConsoleSettingsNotificationsController
* @var {Array}
*/
@tracked notificationTransportMethods = ['email', 'sms'];
@tracked company;
/**
* Creates an instance of ConsoleSettingsNotificationsController.
@@ -45,6 +23,40 @@ export default class ConsoleSettingsNotificationsController extends Controller {
this.getSettings.perform();
}
/**
* Toggles the "Alphanumeric Sender ID" feature for the current company.
*
* Updates the company's `options` object by setting the
* `alpha_numeric_sender_id_enabled` flag. This controls whether the
* organization uses a custom alphanumeric sender ID when sending SMS.
*
* @action
* @param {boolean} enabled - Whether the feature should be enabled or disabled.
* @returns {void}
*/
@action toggleAlphaNumericSenderId(enabled) {
const currentOptions = this.company.options ?? {};
this.company.set('options', { ...currentOptions, alpha_numeric_sender_id_enabled: enabled });
}
/**
* Sets the Alphanumeric Sender ID string for the current company.
*
* Reads the input's value from the event and updates the company's `options`
* object by setting the `alpha_numeric_sender_id` field. This value represents
* the sender name that will appear in outbound SMS messages (subject to carrier
* support and restrictions).
*
* @action
* @param {Event} event - Input event containing the alphanumeric sender ID value.
* @returns {void}
*/
@action setAlphaNumericSenderId(event) {
const value = event.target.value;
const currentOptions = this.company.options ?? {};
this.company.set('options', { ...currentOptions, alpha_numeric_sender_id: value });
}
/**
* Selectes notifiables for settings.
*
@@ -94,7 +106,8 @@ export default class ConsoleSettingsNotificationsController extends Controller {
const { notificationSettings } = this;
try {
yield this.fetch.post('notifications/save-settings', { notificationSettings });
yield this.fetch.post('notifications/save-settings', { notificationSettings: notificationSettings ?? {} });
yield this.saveCompanyOptions.perform();
this.notifications.success('Notification settings successfully saved.');
} catch (error) {
this.notifications.serverError(error);
@@ -114,4 +127,26 @@ export default class ConsoleSettingsNotificationsController extends Controller {
this.notifications.serverError(error);
}
}
/**
* Saves the updated company options to the backend.
*
* This ember-concurrency task attempts to persist the company's modified
* `options` object by calling `company.save()`. If the request fails, a server
* error notification is displayed. No action is taken if no company is loaded.
*
* @task
* @generator
* @yields {Promise} Resolves when the save request completes.
* @returns {Promise<void>} Task completion state.
*/
@task *saveCompanyOptions() {
if (!this.company) return;
try {
yield this.company.save();
} catch (error) {
this.notifications.serverError(error);
}
}
}

View File

@@ -10,9 +10,11 @@
{{content-for "head"}}
<link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon/favicon-16x16.png" />
<link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="192x192" href="/favicon/android-chrome-192x192.png" />
<link rel="icon" type="image/png" sizes="256x256" href="/favicon/android-chrome-256x256.png" />
<link rel="manifest" href="/favicon/site.webmanifest" />
<link rel="mask-icon" href="/favicon/safari-pinned-tab.svg" color="#5bbad5" />
<link integrity="" rel="stylesheet" href="{{rootURL}}assets/vendor.css">

View File

@@ -21,8 +21,7 @@ export function initialize(appInstance) {
debug('[Initializing Router Patch] Applying router refresh bug fix...');
try {
const application = appInstance.application;
applyRouterFix(application);
applyRouterFix(appInstance);
const endTime = performance.now();
debug(`[Initializing Router Patch] Router fix applied in ${(endTime - startTime).toFixed(2)}ms`);

View File

@@ -6,12 +6,10 @@ export async function initialize(appInstance) {
const application = appInstance.application;
const extensionManager = appInstance.lookup('service:universe/extension-manager');
if (!application.extensions || application.extensions.length === 0) {
try {
await extensionManager.loadExtensions(application);
} catch (error) {
console.error('[load-extensions] Error:', error);
}
try {
await extensionManager.loadExtensions(application);
} catch (error) {
console.error('[load-extensions] Error:', error);
}
}

View File

@@ -24,7 +24,7 @@ export default class Company extends Model {
@attr('string') logo_url;
@attr('string') backdrop_url;
@attr('string') description;
@attr('raw') options;
@attr('object') options;
@attr('number') users_count;
@attr('string') type;
@attr('string') currency;

View File

@@ -5,6 +5,7 @@ import groupBy from '@fleetbase/ember-core/utils/group-by';
export default class ConsoleSettingsNotificationsRoute extends Route {
@service fetch;
@service currentUser;
model() {
return hash({
@@ -13,10 +14,11 @@ export default class ConsoleSettingsNotificationsRoute extends Route {
});
}
setupController(controller, { registry, notifiables }) {
async setupController(controller, { registry, notifiables }) {
super.setupController(...arguments);
controller.groupedNotifications = groupBy(registry, 'package');
controller.notifiables = notifiables;
controller.company = await this.currentUser.loadCompany();
}
}

View File

@@ -17,7 +17,7 @@
@selected={{get this.notificationSettings (concat (get-notification-key notification.definition notification.name) ".notifiables")}}
@onChange={{fn this.onSelectNotifiable notification}}
@placeholder="Select notifiables..."
@triggerClass="form-select form-input form-input-sm flex-1"
@triggerClass="form-select form-input flex-1"
as |notifiable|
>
{{notifiable.label}}
@@ -27,6 +27,21 @@
{{/each}}
</ContentPanel>
{{/each-in}}
<ContentPanel @title="SMS Notification Settings" @open={{true}} @wrapperClass="bordered-classic">
<Toggle @isToggled={{this.company.options.alpha_numeric_sender_id_enabled}} @onToggle={{this.toggleAlphaNumericSenderId}} @label="Enable Alpha-Numeric Sender ID" @wrapperClass="mb-4" />
<InputGroup @name="Alpha-Numeric Sender ID" @value={{this.company.options.alpha_numeric_sender_id}} @helpText="Set the custom alphanumeric name that will appear as the sender for all SMS sent by your organization. Up to 11 letters or numbers. Not supported in all countries." @disabled={{not this.company.options.alpha_numeric_sender_id_enabled}} />
<div class="space-y-2 mb-3">
<InfoBlock>
<p>Alphanumeric Sender IDs allow your organization to replace a traditional phone number with a custom text-based sender name when sending SMS notifications (e.g., Fleetbase, MyStore, DispatchHQ). This can improve brand recognition, increase message trust, and enhance deliverability in regions where numeric senders are restricted by local carriers.</p>
<p>When enabled, Fleetbase will use this sender ID for all outbound SMS messages sent on behalf of your organization, including order updates, verification codes, driver notifications, and other automated alerts. Sender IDs can contain up to 11 characters using letters and numbers.</p>
<p>Some countries require or enforce specific messaging rules, and certain carriers may only support alphanumeric senders. Using a Sender ID can significantly improve message delivery in these regions.</p>
</InfoBlock>
<InfoBlock @type="warning">
<p>Delivery of SMS using Alphanumeric Sender IDs depends on local carrier policies. Some regions may restrict or block numeric senders or require the use of alphanumeric senders for successful delivery. While Fleetbase will attempt to deliver all messages using your configured Sender ID, message delivery cannot be guaranteed in countries with carrier-level filtering or regulatory restrictions. Your organization is responsible for ensuring compliance with local messaging regulations in the countries you send SMS to.</p>
</InfoBlock>
</div>
</ContentPanel>
</div>
</div>
<Spacer @height="300px" />

View File

@@ -79,6 +79,12 @@ function getCachedConfig() {
return null;
}
// Application version has changed
if (cachedVersion !== config.APP.version) {
debug(`[Runtime Config] Version mismatch (cached: ${cachedVersion}, current: ${config.APP.version})`);
return null;
}
const cacheData = JSON.parse(cached);
const cacheAge = Date.now() - cacheData.timestamp;
@@ -101,14 +107,14 @@ function getCachedConfig() {
*
* @param {Object} config Config object
*/
function setCachedConfig(config) {
function setCachedConfig(runtimeConfig) {
try {
const cacheData = {
config,
config: runtimeConfig,
timestamp: Date.now(),
};
localStorage.setItem(CACHE_KEY, JSON.stringify(cacheData));
localStorage.setItem(CACHE_VERSION_KEY, '1');
localStorage.setItem(CACHE_VERSION_KEY, config.APP.version);
debug('[Runtime Config] Config cached to localStorage');
} catch (e) {
debug(`[Runtime Config] Failed to cache config: ${e.message}`);
@@ -147,12 +153,15 @@ export default async function loadRuntimeConfig() {
return;
}
// // Try cache first
// const cachedConfig = getCachedConfig();
// if (cachedConfig) {
// applyRuntimeConfig(cachedConfig);
// return;
// }
const isProduction = config?.environment === 'production';
if (isProduction) {
// Try cache first
const cachedConfig = getCachedConfig();
if (cachedConfig) {
applyRuntimeConfig(cachedConfig);
return;
}
}
// Cache miss - fetch from server
try {

View File

@@ -5,16 +5,7 @@ const EmberApp = require('ember-cli/lib/broccoli/ember-app');
const Funnel = require('broccoli-funnel');
const writeFile = require('broccoli-file-creator');
const mergeTrees = require('broccoli-merge-trees');
const postcssImport = require('postcss-import');
const postcssPresetEnv = require('postcss-preset-env');
const postcssEach = require('postcss-each');
const postcssMixins = require('postcss-mixins');
const postcssConditionals = require('postcss-conditionals-renewed');
const postcssAtRulesVariables = require('postcss-at-rules-variables');
const autoprefixer = require('autoprefixer');
const tailwind = require('tailwindcss');
const toBoolean = require('./config/utils/to-boolean');
const environment = process.env.EMBER_ENV;
module.exports = function (defaults) {
const app = new EmberApp(defaults, {
@@ -38,29 +29,6 @@ module.exports = function (defaults) {
useSessionSetupMethod: true,
},
postcssOptions: {
compile: {
enabled: true,
cacheInclude: [/.*\.(css|scss|hbs)$/, /.*\/tailwind\/config\.js$/, /.*tailwind\.js$/],
plugins: [
postcssAtRulesVariables,
postcssImport({
path: ['node_modules'],
plugins: [postcssAtRulesVariables, postcssImport],
}),
postcssMixins,
postcssPresetEnv({ stage: 1 }),
postcssEach,
tailwind('./tailwind.config.js'),
autoprefixer,
],
},
filter: {
enabled: true,
plugins: [postcssAtRulesVariables, postcssMixins, postcssEach, postcssConditionals, tailwind('./tailwind.config.js')],
},
},
babel: {
plugins: [require.resolve('ember-auto-import/babel-plugin')],
},

View File

@@ -1,6 +1,6 @@
{
"name": "@fleetbase/console",
"version": "0.7.20",
"version": "0.7.22",
"private": true,
"description": "Modular logistics and supply chain operating system (LSOS)",
"repository": "https://github.com/fleetbase/fleetbase",
@@ -33,15 +33,15 @@
},
"dependencies": {
"@ember/legacy-built-in-components": "^0.4.2",
"@fleetbase/dev-engine": "^0.2.11",
"@fleetbase/ember-core": "^0.3.7",
"@fleetbase/ember-ui": "^0.3.12",
"@fleetbase/dev-engine": "^0.2.12",
"@fleetbase/ember-core": "^0.3.8",
"@fleetbase/ember-ui": "^0.3.14",
"@fleetbase/fleetops-data": "^0.1.23",
"@fleetbase/fleetops-engine": "^0.6.28",
"@fleetbase/iam-engine": "^0.1.5",
"@fleetbase/fleetops-engine": "^0.6.29",
"@fleetbase/iam-engine": "^0.1.6",
"@fleetbase/leaflet-routing-machine": "^3.2.17",
"@fleetbase/registry-bridge-engine": "^0.1.1",
"@fleetbase/storefront-engine": "^0.4.8",
"@fleetbase/registry-bridge-engine": "^0.1.2",
"@fleetbase/storefront-engine": "^0.4.9",
"@formatjs/intl-datetimeformat": "^6.18.2",
"@formatjs/intl-numberformat": "^8.15.6",
"@formatjs/intl-pluralrules": "^5.4.6",
@@ -82,6 +82,8 @@
"broccoli-asset-rev": "^3.0.0",
"broccoli-file-creator": "^2.1.1",
"broccoli-funnel": "^3.0.8",
"broccoli-merge-trees": "^4.2.0",
"chokidar": "4.0.3",
"concurrently": "^8.2.2",
"date-fns": "^2.30.0",
"dragula": "^3.7.3",
@@ -166,5 +168,6 @@
}
}
]
}
},
"packageManager": "pnpm@9.5.0+sha512.140036830124618d624a2187b50d04289d5a087f326c9edfc0ccd733d76c4f52c3a313d4fc148794a2a9d81553016004e6742e8cf850670268a7387fc220c903"
}

993
console/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,18 +1,6 @@
{
"name": "",
"short_name": "",
"icons": [
{
"src": "/favicon/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/favicon/android-chrome-256x256.png",
"sizes": "256x256",
"type": "image/png"
}
],
"name": "Fleetbase Console",
"short_name": "Fleetbase",
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"

View File

@@ -1,26 +0,0 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from '@fleetbase/console/tests/helpers';
import { render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
module('Integration | Component | dashboard/widget-panel', function (hooks) {
setupRenderingTest(hooks);
test('it renders', async function (assert) {
// Set any properties with this.set('myProperty', 'value');
// Handle any actions with this.set('myAction', function(val) { ... });
await render(hbs`<Dashboard::WidgetPanel />`);
assert.dom().hasText('');
// Template block usage:
await render(hbs`
<Dashboard::WidgetPanel>
template block text
</Dashboard::WidgetPanel>
`);
assert.dom().hasText('template block text');
});
});

View File

@@ -75,7 +75,7 @@ ENV QUEUE_CONNECTION=redis
ENV CADDYFILE_PATH=/fleetbase/Caddyfile
ENV CONSOLE_PATH=/fleetbase/console
ENV OCTANE_SERVER=frankenphp
ENV FLEETBASE_VERSION=0.7.20
ENV FLEETBASE_VERSION=0.7.22
# Set environment
ARG ENVIRONMENT=production