Skip to content

feat: activate 3 dashboard widgets + apply ADR-004 build pattern#742

Closed
rubenvdlinde wants to merge 2 commits into
developmentfrom
feat/activate-dashboard-widgets
Closed

feat: activate 3 dashboard widgets + apply ADR-004 build pattern#742
rubenvdlinde wants to merge 2 commits into
developmentfrom
feat/activate-dashboard-widgets

Conversation

@rubenvdlinde
Copy link
Copy Markdown
Contributor

Three widget classes (JobQueueWidget, RecentCallsWidget, SourceSyncWidget) plus matching src/*Widget.js entries plus Vue components (~113-125 lines each) all existed in the tree but were never wired up:

  • AppInfo/Application.php had no registerDashboardWidget() calls
  • webpack.config.js had no entries for them
  • The PHP load() methods called Util::addScript on filenames that did not exist on disk (would 404 if anyone wired this up partially)

This PR activates them and brings openconnector inline with the org-wide ADR-004 "Build / bundling" pattern.

What changed

Change Why
3 webpack entries: jobQueueWidget, recentCallsWidget, sourceSyncWidget Build their bundles
3 $context->registerDashboardWidget() calls in Application.php Make Nextcloud surface them in the picker
3 widget load() methods updated to attach runtime + 2 shared chunks before the per-widget bundle Order: runtime → vendor → nc-vue → widget
.babelrc: add @babel/preset-typescript 74 .ts files in src/ go through the same babel-loader as .js files
webpack.config.js: filter out base's ts-loader rule, push babel-loader rule for .ts$ One module-ID space for splitChunks (per ADR-004)
webpack.config.js: optimization.runtimeChunk: { name: 'runtime' } Consolidate runtime; required for cross-chunk module resolution on TypeScript apps with broader graphs
webpack.config.js: optimization.splitChunks with stable-filename shared chunks Standard pattern from pipelinq/procest/docudesk/opencatalogi/zaakafhandelapp

New chunks after npm run build

Bundle Size
openconnector-runtime.js 0.003 MB
openconnector-shared-vendor.js 0.34 MB
openconnector-shared-nc-vue.js 1.64 MB
openconnector-jobQueueWidget.js 1.74 MB
openconnector-recentCallsWidget.js 1.74 MB
openconnector-sourceSyncWidget.js 1.74 MB

Note on local browser validation

This PR could not be browser-validated against the dev install because the local docker volume has a nested mount layout (custom_apps/openconnector/openconnector/ instead of custom_apps/openconnector/); Nextcloud refuses to load the app there with "appinfo file cannot be read". That's a pre-existing docker-dev env quirk, not caused by this PR. CI build + a clean install will surface the correct behaviour.

Test plan

  • CI build succeeds
  • Fresh install: open /apps/dashboard/ (or mydash), confirm 3 new widgets in the picker (Job Queue, Recent Calls, Source Sync)
  • Place each widget on a dashboard, confirm renders without console errors
  • npx tsc --noEmit (separate command) for type-check; not gated by webpack

The widget classes (JobQueueWidget, RecentCallsWidget, SourceSyncWidget)
plus matching src/*Widget.js entries plus Vue components (~113-125
lines each) all existed in the tree but were never wired up:
- AppInfo/Application.php had no registerDashboardWidget() calls,
  so IManager::getWidgets() never included them
- webpack.config.js had no entries for them, so 'npm run build'
  never produced openconnector-*Widget.js
- The PHP load() methods called Util::addScript on file names that
  did not exist on disk (would 404 if anyone wired this up partially)

This activates them properly and brings openconnector inline with the
org-wide ADR-004 'Build / bundling' pattern that pipelinq/procest/
docudesk/zaakafhandelapp/opencatalogi already follow:

- 3 webpack entries added (jobQueueWidget, recentCallsWidget,
  sourceSyncWidget)
- 3 $context->registerDashboardWidget() calls added in Application.php
- Each widget's PHP load() now Util::addScript()s the runtime + 2
  shared chunks before the per-widget bundle (runtime → vendor →
  nc-vue → widget)
- .babelrc gains @babel/preset-typescript so .ts files (74 of them
  in this app) go through the same babel-loader as the .js files
- webpack.config.js filters out the base config's ts-loader rule and
  pushes a babel-loader rule for .ts (one consistent module-ID space)
- optimization.runtimeChunk: { name: 'runtime' } consolidates the
  runtime so cross-chunk module resolution survives splitChunks on
  apps with broader graphs (TS + many entries — same pattern needed
  for zaakafhandelapp)
- optimization.splitChunks extracts Vue + @nextcloud/vue + pinia +
  icons + @conduction/nextcloud-vue into stable-filename shared chunks

After 'npm run build':

  openconnector-runtime.js          (new)        0.003 MB
  openconnector-shared-vendor.js    (new)        0.34 MB
  openconnector-shared-nc-vue.js    (new)        1.64 MB
  openconnector-jobQueueWidget.js   (new)        1.74 MB
  openconnector-recentCallsWidget.js (new)       1.74 MB
  openconnector-sourceSyncWidget.js (new)        1.74 MB

Type-checking moves to 'npx tsc --noEmit' (separate command, opt-in).

NOTE: this PR could not be browser-validated against this dev install
because the local docker volume has a nested mount layout
('custom_apps/openconnector/openconnector/' instead of
'custom_apps/openconnector/'); Nextcloud refuses to load the app there
('appinfo file cannot be read'). That's a pre-existing docker-dev env
quirk, not caused by this PR. CI build + a clean install will surface
the correct behaviour.
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 5, 2026

Quality Report — ConductionNL/openconnector @ 76aef53

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 148/148
npm ❌ 1/573 denied
PHPUnit ⏭️
Newman ⏭️
Playwright ⏭️

❌ Denied npm licenses

Package Version License
@fortawesome/free-solid-svg-icons 6.7.2 (CC-BY-4.0 AND MIT)

Quality workflow — 2026-05-05 09:13 UTC

Download the full PDF report from the workflow artifacts.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[BLOCKER] Widget titles are Dutch strings missing from translation files — broken on non-Dutch installs

All three PHP widget getTitle() methods return Dutch strings via $this->l10n->t(): 'Taken wachtrij' (JobQueueWidget), 'Recente calls' (RecentCallsWidget), 'Bron synchronisatie status' (SourceSyncWidget). None of these strings appear in l10n/en.json or l10n/nl.json. On a non-Dutch Nextcloud installation getTitle() will return the raw Dutch key. Add the strings to both l10n/en.json (with English translations) and l10n/nl.json (with Dutch translations) and regenerate the .js l10n files.

Copy link
Copy Markdown
Contributor

@WilcoLouwerse WilcoLouwerse left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review

🔴 Blockers (2)

  • splitChunks extracts shared bundles never loaded by index.php — runtime errors (templates/index.php:6)
    The webpack.config.js diff adds splitChunks: { chunks: 'all', ... } with enforce: true cacheGroups for shared-vendor (Vue, pinia, etc.) and shared-nc-vue. With chunks: 'all' and enforce: true, webpack WILL extract Vue and pinia out of openconnector-main.js into openconnector-shared-vendor.js. The template templates/index.php only calls Util::addScript($appId, $appId . '-main') — the shared chunks are never loaded, so the main app page will throw webpack runtime errors about missing modules.

templates/index.php and templates/settings/admin.php must be updated to load openconnector-runtime, openconnector-shared-vendor, and openconnector-shared-nc-vue before the main/settings scripts, exactly as the widget load() methods now do. Alternatively, restrict splitChunks.chunks to 'async' so synchronous entry bundles are not affected.

🟡 Concerns (4)

  • Dutch strings used as-is in English translation file — English users see Dutch text (l10n/en.json:1)
    Widget UI strings appear in l10n/en.json with the Dutch string as both key AND value, e.g. "Bekijk bron": "Bekijk bron", "Actief": "Actief". English users will see Dutch text. Either change the t() call keys to English and provide Dutch translations in nl.json, or keep Dutch keys but provide proper English translations in en.json (e.g. "Bekijk bron": "View source").
  • Hardcoded /index.php path in onShow() — breaks pretty-URL Nextcloud configurations (src/views/widgets/JobQueueWidget.vue:77)
    All three widgets use window.location.href = '/index.php/apps/openconnector/...' for navigation instead of generateUrl() from @nextcloud/router (which is already imported). Nextcloud supports pretty URLs (mod_rewrite) where /index.php/ is stripped. The hardcoded path breaks any Nextcloud installation without index.php in the URL. Fix: window.location.href = generateUrl('/apps/openconnector/jobs') (and similarly for the other two widgets).
  • API error silently shows empty-content state — user cannot distinguish no-data from error (src/views/widgets/JobQueueWidget.vue:104)
    In all three widget fetchData() methods, a caught exception sets the data array to []. The widget then renders the empty-content slot (e.g. 'Geen taken gevonden'), which is indistinguishable from the legitimate no-data case. A failed API call (network error, 403, 500) should display a distinct error message. Add an error data property, set it in the catch block, and render an error variant of NcEmptyContent with a retry button.
  • onShow() ignores the item argument — all item clicks navigate to the same list page (src/views/widgets/JobQueueWidget.vue:76)
    The NcDashboardWidget @show event passes the clicked item as argument, but all three onShow(item) implementations ignore item and navigate to a generic list URL. Consider using generateUrl('/apps/openconnector/jobs/' + item.id) (or similar) so clicking a specific item navigates to its detail page rather than the full list.

🟢 Minor (2)

  • IURLGenerator injected but never used — dead constructor dependency (lib/Dashboard/JobQueueWidget.php:20)
    All three PHP widget classes inject IURLGenerator $url in the constructor but getUrl() returns null unconditionally and $this->url is never referenced. Remove the IURLGenerator constructor parameter and the corresponding use OCP\IURLGenerator; import.
  • PHP widget files missing SPDX license header and declare(strict_types=1) (lib/Dashboard/JobQueueWidget.php:1)
    All three dashboard widget PHP files (JobQueueWidget.php, RecentCallsWidget.php, SourceSyncWidget.php) lack the SPDX copyright/license header block required by the project and are missing declare(strict_types=1);. Add the standard header and strict-types declaration to each file to satisfy the SPDX hydra gate.

Reviewed by WilcoLouwerse via automated batch review.

@github-actions
Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/openconnector @ 225aec6

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 148/148
npm ✅ 685/685
PHPUnit ⏭️
Newman ⏭️
Playwright ⏭️

Quality workflow — 2026-05-19 04:09 UTC

Download the full PDF report from the workflow artifacts.

@rubenvdlinde
Copy link
Copy Markdown
Contributor Author

Closing — the legacy IDashboardWidgetV2 activation this PR proposed is superseded by the manifest-driven dashboard pattern that's now in flight.

State on feature/i18n-complete-translations:

  • src/manifest.json declares a Dashboard page with type: "dashboard" (line 117–122). nc-vue's built-in CnDashboardPage renders the KPI tiles + charts declaratively.
  • feat(dashboard): 6 KPI tiles via manifest stats-block widgets #831 (6 KPI tiles via manifest stats-block widgets) and feat(dashboard): 6 daily/hourly charts + date-range header (full legacy parity) #838 (6 daily/hourly charts + date-range header — full legacy parity) are the active follow-up PRs filling that page with content via the manifest.
  • The legacy lib/Dashboard/JobQueueWidget.php / RecentCallsWidget.php / SourceSyncWidget.php + src/views/widgets/*Widget.vue are still on disk but unregistered. They're orphaned, not paused — calling registerDashboardWidget() for them would create two competing Dashboard surfaces (Nextcloud's "Dashboard" app slot vs. the in-app /dashboard route).

The ADR-004 build-pattern strand (.babelrc + webpack.config.js widget-entrypoint scaffolding) targets that legacy bundle and similarly doesn't apply to the manifest flow, where widgets are rendered by the host CnDashboardPage.

Action: the deletion of the legacy lib/Dashboard/ and src/views/widgets/ files can be tracked separately once #831/#838 land. Filing or piggybacking on chain-C cleanup is more appropriate than activating dead code here.

Credit @rubenvdlinde for the ADR-004 build pattern itself — that scaffolding lives elsewhere in the fleet for apps still on IDashboardWidgetV2.

@rubenvdlinde rubenvdlinde deleted the feat/activate-dashboard-widgets branch May 22, 2026 13:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants