Dashboard Widget System v1 — Complete Analysis & v2 Migration Plan¶
1. Architecture Overview v1¶
Core Components¶
| File | Role |
|---|---|
FrontendDashboard Model |
Widget types, JSON configuration, loadDashBoard() dispatcher |
FrontendDashboardController |
22 AJAX action methods for loading widget data |
blocks/_view_*.php |
Widget containers (header, buttons, settings, hidden fields) |
_parts/_table_*.php |
Table rendering via GridViewHelper::renderGrid() |
_parts/_chart_*.php |
Chart rendering via Chart.js |
_parts/_box_advanced_filter.php |
Info box for advanced filter counts |
FrontendGridSetting Model |
User-specific column visibility per grid |
FrontendAdvancedFilter Model |
Formula-based filters (6 types) |
FrontendFilterStatistic Model |
Time-based statistic snapshots for charts |
Data Model¶
dashboard (table)
├── uID (VARCHAR 80, PK)
├── user_uID (VARCHAR 80, FK → user)
├── name (VARCHAR 255)
├── value (TEXT) ← JSON array of all widgets
├── status (TINYINT 1) ← 1=active, 0=inactive
└── created_at (DATETIME)
Widget JSON Structure (one element in the value array):
{
"title": "widget_type_name",
"position": { "x": 0, "y": 0, "width": 6, "height": 4 },
"settings": { /* widget-specific */ }
}
Layout System¶
- GridStack.js with 12-column grid
CELL_HEIGHT = 80px,VERTICAL_MARGIN = 16px- Drag & drop + resize via GridStack
- Widget positions are saved via AJAX
2. Complete Widget Catalog¶
2.1 Inventory Widgets (Tables)¶
due_inventory — Due Devices¶
- Controller:
actionListInventory()→FrontendInventory::doAjaxLoadInventory() - View:
_parts/_table_due_inventory.php - Settings:
rowsPerPage,portletInventoryConditionFilterVal,customerKtag - Columns:
| Column | Field | Condition |
|---|---|---|
| Customer | ktag (→ customer.getName()) |
Only when customerKtag = "all" |
| Device No. | I4201 |
Always |
| Manufacturer | I4202 |
Always |
| Model | I4203 |
Always |
| Serial No. | I4204 |
Always |
| Due Date | duedate |
Always |
| Actions | View link → /frontendInventory/view |
With inventory_view permission |
overdue_inventory — Overdue Devices¶
- Identical column structure as
due_inventory - Settings: Additionally
fn_due = "overdue"
critical_1_inventory, critical_2_inventory, critical_3_inventory — Critical Devices¶
- Identical column structure as
due_inventory - Settings: Additionally
fn_critical = "critical_1/2/3"
2.2 Calibration Widgets¶
due_calibration_per_month — Calibrations Per Month¶
- Controller:
actionListDueCalibrationPerMonth()→FrontendInventory::countFutureInventory() - View:
_parts/_table_future_inventory.php - Settings:
rowsPerPage,portletInventoryConditionFilterVal,customerKtag,numberMonth,calibrationDueType - Columns: Dynamic via
FrontendGridSetting::getVisibleFields('calibration_due_per_month')
last_calibration — Last Calibrations¶
- Controller:
actionListLastCalibration()→FrontendCalibration::search(LAST_CALIBRATION_TABLE) - View:
_parts/_table_last_calibration.php - Settings:
rowsPerPage,date_range,customerKtag,status,hasDocumentColumn,hasDocumentType - Columns: Fully dynamic via
FrontendProperty::getGridViewColumns(LAST_CALIBRATION_TABLE) - With EColumnsDialog → user can show/hide and reorder columns
- Action column: View link →
/frontendCalibration/view(withcalibration_view)
2.3 Status Widgets (Table OR Chart)¶
assets_status — Inventory Status¶
- Controller:
actionListStatusAssets()→FrontendStatus::countInventorySearch() - View (Table):
_parts/_table_assets_status.php - View (Chart):
_parts/_chart_assets_status.php - Settings:
rowsPerPage,portletInventoryConditionFilterVal,customerKtag,settingDisplay(table/chart) - Columns: Dynamic via
FrontendGridSetting::getVisibleFields('status_dashboard')
| Column | Field | Rendering |
|---|---|---|
| Status | title |
Link to filtered inventory |
| Count | itemCount |
Centered |
- Chart mode: Doughnut/Pie with Chart.js, legend with title + count
ticket_status — Ticket Status¶
- Controller:
actionListTicketStatus()→FrontendStatus::countTicketSearch() - View (Table):
_parts/_table_ticket_status.php - View (Chart):
_parts/_chart_ticket_status.php - Settings:
rowsPerPage,project,category,type,settingDisplay - Columns: Identical to
assets_status(viaFrontendGridSetting::getVisibleFields('status_dashboard'))
2.4 Order Widgets (Tables)¶
new_offers — New Offers¶
- Controller:
actionListBooking()→FrontendBooking::doAjaxLoadInventory() - View:
_parts/_table_new_offers.php - Settings:
rowsPerPage,customerKtag - Columns:
| Column | Field |
|---|---|
| Number | number |
| Date | date |
| Status | status |
| Actions | View → /frontendBooking/view (with bookings_view) |
order_entries_by_status — Orders by Status¶
- Controller:
actionListOrders()→FrontendBooking::getItemByStatus() - View:
_parts/_table_orders_by_status.php - Settings:
rowsPerPage,status - Columns:
| Column | Field |
|---|---|
| Customer | customer_KTAG → customer.getName() |
| Number | number |
| Date | date |
| Comment | comment |
| Status | status |
| Actions | View → /frontendBooking/view (with bookings_view) |
2.5 Notepad Widgets¶
notepad_status — Status Overview¶
- Controller:
actionListNotepadStatus()→FrontendStatus::countNotepadSearch() - View:
_parts/_table_notepad_status.php - Columns:
title(link),count(getTotalItemCount)
notepad_notification — Notifications¶
- Controller:
actionListNotepadNotification()→FrontendNotepad::countItemByNotification() - View:
_parts/_table_notepad_notification.php - Columns:
notification_at(link),count(itemCount)
notepad_entries_by_status_and_assignee — Entries by Status & Assignee¶
- Controller:
actionListNotepad()→FrontendNotepad::getItemByStatusAndAssignee() - View:
_parts/_table_notepad_by_status_and_assignee.php - Settings:
rowsPerPage,status,assignee - Columns:
| Column | Field |
|---|---|
| Device No. | inventory.I4201 |
| Manufacturer | inventory.I4202 |
| Model | inventory.I4203 |
| Date | date_time |
| Assignee | assignUser.getFullName() |
| Status | own_status (conditional: not with OVERDUE/FINISH_TODAY) |
| Actions | View Notepad + View Inventory (with permissions) |
2.6 Support Widgets¶
ticket_statistic — Ticket Statistics (Table)¶
- Controller:
actionListTicketStatistic()→Tickets::countTicketStatistic() - View:
_parts/_table_ticket_statistic.php - Settings:
rowsPerPage,numberMonth,project,category - Columns:
| Column | Field |
|---|---|
| Period | date_range |
| Ticket Type | ticket_type |
| Open | count_open |
| Closed | count_closed |
support_activity_entries — Activity Feed (NOT a Grid!)¶
- Controller:
actionListSupportActivities()→Activity::search() - View:
_parts/_table_support_activities.php - Rendering: Custom HTML newsfeed (NOT
GridViewHelper) - Elements per entry:
- Avatar (image + link)
- Title (link to ticket)
- Description (issue + project)
- Footer: user, timestamp, status badge
support_risk_entries — Risk Matrix (NOT a Grid!)¶
- Controller:
actionListSupportRisks()→TicketRisk::getDataForDashWidget() - View:
_parts/_table_support_risks.php - Rendering: Custom HTML table (Impact × Probability matrix)
- Elements:
- Rows = impact level, columns = probability level
- Cells: color + ticket count with link
- Axis labels: "Impact" / "Probability"
support_risk_priority — Risk Priority (NOT a Grid!)¶
- Controller:
actionListSupportRisksPriority()→TicketRisk::getDataForPriorityWidget() - View:
_parts/_table_support_risks_priority.php - Rendering: 4-column bar visualization
- Elements:
- 4 risk columns (risk1, risk2, risk3, risk)
- Colored bars with ticket count and height
2.7 DMS Widget¶
dms_entries — Document Entries¶
- Controller:
actionListDocument()→FrontendDms::getItemByTypeAndStatus() - View:
_parts/_table_dms_entries.php - Settings:
rowsPerPage,status,type - Columns:
| Column | Field | Condition |
|---|---|---|
| Name | name |
Always |
| Manufacturer | I4202 (getI4202()) |
Only when type=inventory |
| Model | I4203 (getI4203()) |
Only when type=inventory |
| Version | version (getVersion()) |
Always |
| File Type | filetype (getFileTypeHtml()) |
Always |
| Status | action_title (getStatusHtml()) |
Always |
| Type | link_table (getTypeForDashboard()) |
Always |
| Created | created (formatted) |
Always |
| Actions | View → /adminDocument/view |
With dms_view |
2.8 Advanced Filter Widgets¶
advanced_filter — Filter Result (Info Box)¶
- Controller:
actionGetAdvancedFilterStatistics()→ dynamic by type - View:
blocks/_view_advanced_filter_{type}.php→_parts/_box_advanced_filter.php - Settings:
filter_id,filter_name,color,type - Rendering: NO table — info box with:
- Icon (type-specific: bar-chart, wrench, gavel, shopping-cart)
- Filter name (as link to admin page with filter)
- Result count (
count_item, formatted) - Colored background/icon by
color - 6 Subtypes: inventory, calibration, repair, location, booking, support (ticket)
- Type → Admin Link Mapping:
| Type | Admin Page | Permission |
|---|---|---|
| inventory | /frontendInventory/admin |
inventory_view/edit/delete |
| calibration | /frontendCalibration/admin |
calibration_view/edit/delete |
| repair | /frontendRepair/admin |
repair_view/edit/delete |
| location | /frontendRental/admin |
location_view/edit/delete |
| booking | /frontendBooking/admin |
bookings_view/edit/delete |
| support | /support/ticket/admin |
support_management_admin |
filter_statistic — Filter Statistics (Chart)¶
- Controller:
actionGetFilterStatisticForChart()→FrontendFilterStatistic::getDataForChart() - View:
_parts/_chart_filter_statistic.php - Settings:
chart_filters(array of filter IDs),chart_type(LineChart/AreaChart/BarChart),year - Chart types: LineChart, AreaChart, BarChart (from
FrontendFilterStatistic::CHART_TYPES)
2.9 Status Variable Widget (Dynamic)¶
status_variable — Variable Statistics¶
- Controller:
actionListItemOfStatistics()→ dynamic by variable prefix - Settings:
variable,filter,order(asc/desc) - 6 Subtypes by variable prefix:
| Prefix | Model | View | Columns |
|---|---|---|---|
| (default) | FrontendInventory | _table_status_variable_inventory |
I4201, I4202, I4203, I4204, [dynamic] |
| booking... | FrontendBooking | _table_status_variable_booking |
Order-specific |
| calibration... | FrontendCalibration | _table_status_variable_calibration |
Calibration-specific |
| notepad... | FrontendNotepad | _table_status_variable_notepad |
Notepad-specific |
| repair... | FrontendRepair | _table_status_variable_repair |
Repair-specific |
| support_tickets... | Tickets | _table_status_variable_ticket |
Ticket-specific |
Inventory Columns (Example):
| Column | Field |
|---|---|
| Device No. | I4201 |
| Manufacturer | I4202 |
| Model | I4203 |
| Serial No. | I4204 |
| [Variable] | Dynamic via AdminStatusSetting::getFormat() |
| Actions | View → /frontendInventory/view |
2.10 Admin Widgets¶
pending_user — Pending Users¶
- Controller:
actionListPendingUser()→FrontendUser::search() - View:
_parts/_table_pending_user.php - Settings:
rowsPerPage,userStatus,portletInventoryConditionFilterVal - Columns: Dynamic via
FrontendGridSetting::getVisibleFields('user_dashboard') - Action: Update →
/adminUser/update(withuser_management_admin)
2.11 Energy Monitoring¶
emoncms — emonCMS Integration¶
- Controller:
actionLoadFeedId()→InventoryHelper::getAllFeeds() - View:
_parts/_table_emoncms.php - Settings:
feed_id - Rendering: Specific embed/iFrame
3. Column Source System¶
Three Types of Column Definitions:¶
| Type | Method | Usage |
|---|---|---|
| Hardcoded | Array directly in view | Simple widgets (due_inventory, new_offers, notepad, orders, dms, ticket_statistic) |
| GridSetting | FrontendGridSetting::getVisibleFields(gridName) |
Configurable widgets (assets_status, ticket_status, pending_user, calibration_due_per_month) |
| Property-based | FrontendProperty::getGridViewColumns(tableName) |
Fully dynamic widgets (last_calibration) |
Grid Settings Mechanism:¶
frontend_grid_settingtable stores per user + grid: comma-separated column namesadmin_grid_setting_templatetable defines system templates as default- Fallback chain: User setting → user default template → system template
- Grid name can be extended with role and property:
{gridName}_{role}_{property}
4. Advanced Filter System¶
Formula Engine:¶
- SQL-like WHERE clauses in
formulafield - Variables:
[date_1],[integer_1]— placeholders for dynamic values - Date presets: TODAY, CURRENTMONTH_STARTDAY/ENDDAY, LASTMONTH_STARTDAY/ENDDAY, CURRENTYEAR_STARTDAY/ENDDAY
- Date arithmetic:
[date_field+5],[date_field-3](days) - Operators:
=,!=,<,>,<=,>=,LIKE,IS NULL,IS NOT NULL - Logic:
AND,OR
6 Filter Types:¶
| Type | Model | Fields from |
|---|---|---|
inventory |
FrontendInventory | inventory table |
calibration |
FrontendCalibration | calibration table |
repair |
FrontendRepair | repair table |
location |
FrontendLocation | location table |
booking |
FrontendBooking | booking table |
support |
Tickets | support_tickets table |
Statistics System:¶
- Cron-based:
*/5 * * * *→saveAsStatisticcommand - Stores monthly or daily COUNT for each filter
- Chart data from
filter_statistictable
5. v2 Current State¶
Already implemented:¶
- Dashboard CRUD (create, rename, delete, activate)
- Widget JSON format (compatible with v1)
- CSS grid layout (12 columns, no GridStack)
- Widget catalog with categories
- Count widgets:
inventory_count,calibration_count,overdue_count,due_count - Table widgets (basic):
recent_calibrations,recent_bookings,recent_notepads - Chart widgets:
status_overview(Pie),calibrations_per_month(Bar) - Advanced filter count widget
- Filter statistics widget
- API:
/dashboards,/advanced-filterswith CRUD + stats
Missing for v1 Compatibility:¶
| Category | Missing Widgets |
|---|---|
| Inventory | due_inventory, overdue_inventory, critical_1/2/3_inventory with correct columns |
| Calibration | due_calibration_per_month (with dynamic columns), last_calibration (with column configuration) |
| Status | assets_status (with table/chart toggle), ticket_status (with table/chart toggle) |
| Orders | new_offers, order_entries_by_status |
| Notepad | notepad_status, notepad_notification, notepad_entries_by_status_and_assignee |
| Support | ticket_statistic, support_activity_entries (feed), support_risk_entries (matrix), support_risk_priority (bars) |
| DMS | dms_entries |
| Status Variable | status_variable (6 subtypes) |
| Admin | pending_user |
| Special | emoncms |
6. Migration Plan v1 → v2¶
Phase 1: Infrastructure¶
1.1 Widget Type Registry¶
Create a central widget registry system in v2:
Structure:
- WidgetType interface with: type, label, icon, category, defaultSize, component, settingsComponent
- Registration of all 23+ widget types
- Dynamic component loading via <component :is="...">
1.2 Generic Widget Table¶
Extend TableWidget.vue to a configurable dashboard table:
Features: - Column definition via props (like v1 but as TypeScript interface) - Pagination (rowsPerPage from widget settings) - AJAX reload via API endpoint - Action column with permission gating - Empty state - Scroll wrapper
1.3 API Endpoints for Widget Data¶
New Laravel endpoints for each widget:
Or type-specific endpoints:
| Endpoint | Widget |
|---|---|
GET /api/v2/inventories/dashboard/due |
due_inventory |
GET /api/v2/inventories/dashboard/overdue |
overdue_inventory |
GET /api/v2/inventories/dashboard/critical/{level} |
critical_1/2/3 |
GET /api/v2/inventories/dashboard/status |
assets_status |
GET /api/v2/calibrations/dashboard/per-month |
due_calibration_per_month |
GET /api/v2/calibrations/dashboard/last |
last_calibration |
GET /api/v2/tickets/dashboard/status |
ticket_status |
GET /api/v2/tickets/dashboard/statistics |
ticket_statistic |
GET /api/v2/bookings/dashboard/offers |
new_offers |
GET /api/v2/bookings/dashboard/by-status |
order_entries_by_status |
GET /api/v2/notepads/dashboard/status |
notepad_status |
GET /api/v2/notepads/dashboard/notifications |
notepad_notification |
GET /api/v2/notepads/dashboard/by-status-assignee |
notepad_entries |
GET /api/v2/dms/dashboard/entries |
dms_entries |
GET /api/v2/support/dashboard/activities |
support_activities |
GET /api/v2/support/dashboard/risks |
support_risks |
GET /api/v2/support/dashboard/risk-priority |
support_risk_priority |
GET /api/v2/users/dashboard/pending |
pending_user |
GET /api/v2/inventories/dashboard/status-variable |
status_variable |
1.4 Grid Setting API¶
GET /api/v2/grid-settings/{gridName} → visible columns
PUT /api/v2/grid-settings/{gridName} → save columns
POST /api/v2/grid-settings/{gridName}/reset → reset to template
GET /api/v2/grid-settings/templates/{gridName} → available templates
Phase 2: Widget Components (Priority by Usage)¶
Priority A — Core Widgets (Most Frequently Used)¶
2.1 Inventory Table Widgets (due_inventory, overdue_inventory, critical_*)
inventoryType prop (due/overdue/critical_1/2/3)
- Columns: Customer (conditional), I4201, I4202, I4203, I4204, duedate
- Settings: rowsPerPage, portletInventoryConditionFilterVal, customerKtag
2.2 Assets Status Widget (assets_status)
settingDisplay)
- Dynamic columns from grid settings
- Chart: Doughnut/Pie with PrimeVue Charts
2.3 Last Calibration Widget (last_calibration)
Priority B — Orders & Notepad¶
2.4 New Offers Widget (new_offers)
- Columns: number, date, status + action
2.5 Orders By Status Widget (order_entries_by_status)
- Columns: customer, number, date, comment, status + action
2.6 Notepad Widgets (3 variants)
- NotepadStatusWidget.vue — title + count
- NotepadNotificationWidget.vue — notification_at + count
- NotepadEntriesWidget.vue — assetNumber, manufacturer, model, date, assignee, status
Priority C — Support Widgets¶
2.7 Ticket Statistic Widget (ticket_statistic)
- Columns: date_range, ticket_type, count_open, count_closed
2.8 Activity Feed Widget (support_activity_entries)
2.9 Risk Matrix Widget (support_risk_entries)
2.10 Risk Priority Widget (support_risk_priority)
Priority D — DMS & Special¶
2.11 DMS Entries Widget (dms_entries)
- Columns: name, (I4202, I4203 for inventory type), version, filetype, status, type, created
2.12 Status Variable Widget (status_variable)
- Dynamic by variable prefix
- 6 subtypes with their own columns each
2.13 Pending User Widget (pending_user)
- Dynamic columns from grid settings
2.14 emoncms Widget (emoncms)
- Feed embedding
Phase 3: Widget Settings Dialogs¶
Each widget needs a settings dialog for configuration:
| Widget | Settings Fields |
|---|---|
| Inventory | rowsPerPage, customerKtag, conditionFilter |
| Calibration/Month | rowsPerPage, customerKtag, numberMonth, calibrationDueType |
| Last Calibration | rowsPerPage, dateRange, customerKtag, status, hasDocumentColumn, hasDocumentType |
| Assets Status | rowsPerPage, customerKtag, settingDisplay (table/chart) |
| Ticket Status | rowsPerPage, project, category, type, settingDisplay |
| Ticket Statistics | rowsPerPage, numberMonth, project, category |
| Orders | rowsPerPage, status |
| Notepad Entries | rowsPerPage, status, assignee |
| DMS | rowsPerPage, status, type |
| Support Activities | rowsPerPage, project, category, status |
| Advanced Filter | filter_id, filter_name, color, type |
| Filter Statistics | chart_filters, chart_type, year |
| Status Variable | variable, filter, order |
| Pending User | rowsPerPage, userStatus |
| emoncms | feed_id |
Phase 4: Permission System¶
Each widget requires certain permissions:
| Widget | Permission |
|---|---|
| Inventory widgets | inventory_view |
| Calibration widgets | calibration_view |
| Order widgets | bookings_view |
| Notepad widgets | notepad_view |
| DMS widget | dms_view |
| Support widgets | support_view |
| Pending User | user_management_admin |
| Advanced Filter | Depends on type |
→ Widget catalog should only show widgets for which the user has permissions.
Phase 5: Compatibility Layer¶
JSON Format Compatibility¶
v1 and v2 share the same dashboard table. Widget configurations must be bidirectionally compatible:
- v1
titlevalues must be correctly mapped in v2 - v2-specific widget types (e.g.
inventory_count) should be ignored as fallback in v1 - Settings format: Keep identical (same keys, same value types)
Mapping v1 → v2 Widget Types:¶
| v1 title | v2 type (proposed) |
|---|---|
due_inventory |
due_inventory (keep) |
overdue_inventory |
overdue_inventory (keep) |
critical_1_inventory |
critical_1_inventory (keep) |
critical_2_inventory |
critical_2_inventory (keep) |
critical_3_inventory |
critical_3_inventory (keep) |
due_calibration_per_month |
due_calibration_per_month (keep) |
last_calibration |
last_calibration (keep) |
assets_status |
assets_status (keep) |
ticket_status |
ticket_status (keep) |
ticket_statistic |
ticket_statistic (keep) |
new_offers |
new_offers (keep) |
order_entries_by_status |
order_entries_by_status (keep) |
notepad_status |
notepad_status (keep) |
notepad_notification |
notepad_notification (keep) |
notepad_entries_by_status_and_assignee |
notepad_entries_by_status_and_assignee (keep) |
dms_entries |
dms_entries (keep) |
support_activity_entries |
support_activity_entries (keep) |
support_risk_entries |
support_risk_entries (keep) |
support_risk_priority |
support_risk_priority (keep) |
advanced_filter |
advanced_filter (keep) |
filter_statistic |
filter_statistic (keep) |
status_variable |
status_variable (keep) |
pending_user |
pending_user (keep) |
emoncms |
emoncms (keep) |
Recommendation: Adopt all v1 title values 1:1 as v2 type. No renaming. This way v1 and v2 can read the same JSON configuration.
7. Summary¶
Key Metrics:¶
- 23+ widget types in v1
- 15 grid tables, 3 charts, 3 custom layouts, 2 info boxes
- 6 advanced filter types with formula engine
- 3 column sources: hardcoded, GridSetting, property-based
- 11 permission groups control widget visibility
Core Principles for v2:¶
- Keep widget titles — JSON compatibility with v1
- Keep settings format — Same keys, same values
- Dynamic columns — Use grid settings API instead of hardcoding
- Permission gating — Secure widget catalog + widget rendering
- Generic table — One configurable
DashboardTableWidgetfor all grid-based widgets - Custom renderers — Separate components for feed, matrix, bar widgets