Skip to content

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 (with calibration_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 (via FrontendGridSetting::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 (with user_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:

  1. frontend_grid_setting table stores per user + grid: comma-separated column names
  2. admin_grid_setting_template table defines system templates as default
  3. Fallback chain: User setting → user default template → system template
  4. Grid name can be extended with role and property: {gridName}_{role}_{property}

4. Advanced Filter System

Formula Engine:

  • SQL-like WHERE clauses in formula field
  • 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 * * * *saveAsStatistic command
  • Stores monthly or daily COUNT for each filter
  • Chart data from filter_statistic table

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-filters with 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:

frontend-v2/composables/useDashboardWidgets.ts

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:

frontend-v2/components/dashboard/widgets/DashboardTableWidget.vue

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:

GET /api/v2/dashboards/widgets/{type}?settings=...

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_*)

frontend-v2/components/dashboard/widgets/InventoryListWidget.vue
- Shared component with 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)

frontend-v2/components/dashboard/widgets/AssetsStatusWidget.vue
- Table/chart toggle (settingDisplay) - Dynamic columns from grid settings - Chart: Doughnut/Pie with PrimeVue Charts

2.3 Last Calibration Widget (last_calibration)

frontend-v2/components/dashboard/widgets/LastCalibrationWidget.vue
- Fully dynamic columns - Column configuration dialog (like EColumnsDialog in v1) - Settings: date_range, customerKtag, status, hasDocumentColumn, hasDocumentType

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)

frontend-v2/components/dashboard/widgets/ActivityFeedWidget.vue
- NO table — custom feed layout - Elements: avatar, title, description, footer (user, time, status)

2.9 Risk Matrix Widget (support_risk_entries)

frontend-v2/components/dashboard/widgets/RiskMatrixWidget.vue
- NO table — custom matrix (Impact × Probability) - Colored cells with ticket counts

2.10 Risk Priority Widget (support_risk_priority)

frontend-v2/components/dashboard/widgets/RiskPriorityWidget.vue
- NO table — bar visualization

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 title values 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:

  1. Keep widget titles — JSON compatibility with v1
  2. Keep settings format — Same keys, same values
  3. Dynamic columns — Use grid settings API instead of hardcoding
  4. Permission gating — Secure widget catalog + widget rendering
  5. Generic table — One configurable DashboardTableWidget for all grid-based widgets
  6. Custom renderers — Separate components for feed, matrix, bar widgets