Inside Opsdash: Turning Nextcloud Calendar Data Into a Dashboard
A dashboard can make questionable data look convincing. Before choosing charts for Opsdash, I needed a data path that could explain which calendars were included, what the time range meant, and how the numbers were calculated.
Opsdash runs inside Nextcloud, with a PHP backend and a Vue frontend. Keeping data access on the server lets it use the current user's calendar permissions rather than asking the browser to reconstruct them.
Separate configuration from heavy aggregation
The application loads a core payload first: available calendars, saved selection, theme, widgets, and user settings. A separate data request obtains the statistics and chart payload.
This gives the interface enough information to establish its structure before calendar aggregation completes. It also keeps the difference between loading preferences and calculating results visible in the application architecture.
The split does not remove the need for request coordination. If the user changes the selected period during a load, the frontend must avoid presenting an older response as the result of the newer selection.
Normalize before calculating
Calendar access, parsing, colors, aggregation, and chart construction are separate services. That separation matters because source compatibility and mathematical correctness are different problems.
The access layer resolves calendars available to the user. Parsing produces event rows with usable dates and durations. Aggregation computes totals and distributions. Chart builders then shape those results for presentation.
A chart component should not need to discover calendar permissions or interpret an event's original time representation. Those responsibilities belong earlier in the pipeline.
Make selection an authorization intersection
A saved list of calendar IDs is user input. It is not evidence that the user still has access to those calendars.
The server intersects requested calendars with the user's accessible scope. This also matters after permissions change: a previously valid preference must not become a permanent access grant.
Writes use dedicated persistence paths with CSRF protection and input sanitization. Reading a dashboard and changing its configuration have different side effects, so keeping those paths separate makes both easier to inspect.
Reuse the summary, not the screen
Reporting reuses a normalized summary service instead of trying to scrape rendered widgets. The delivery layer can then render an email inside Nextcloud's mail template and send it through the native mailer.
Weekly and monthly scheduling have their own settings and delivery state. A local send time is a threshold for the background job, rather than a promise that a process will execute at an exact second.
That is an example of an architectural benefit becoming a product benefit: the dashboard and the recap can share calculation semantics without sharing a browser layout.
The app's useful abstraction is the period summary. Widgets, charts, and emails are different views of it. When they disagree, the first question should be which summary and scope they used, rather than which CSS component looks wrong.