Appearance
July 10, 2026 — Backend
Release date: Friday, July 10, 2026
Summary
This week added two BMS-facing API surfaces: portfolio dashboard read models for buildings and coverage lines (slim, unpaginated lists the UI can join client-side), and per-user UI preferences persisted server-side so layout, theme, and dashboard widget state survive across devices.
Shipped
- Portfolio dashboard APIs:
GET /api/v1/portfolio/buildingsandGET /api/v1/portfolio/coverage-lines?year=return full slim lists for UI aggregation; guarded bybuilding.viewandcoverage.viewrespectively; no backend KPIs or pagination. - User UI preferences:
GETandPATCH /api/v1/me/preferencesstore a versioned JSONpayload(view mode, theme, sidebar, table settings, dashboard widgets/layout); JWT-only; PATCH is a full replace (upsert). - Schema: New
UserUiPreferencetable with one row per user; migration20260710130000_add_user_ui_preference. - Docs and contract:
docs/USER_UI_PREFERENCES.md, portfolio notes indocs/API_REFERENCE_DATA.md,http/api.http, and OpenAPI regenerated.
Engineering (commit recap)
Backend — week of July 5 – July 10, 2026
Window: 2026-07-04 < commit date < 2026-07-11 (git log --no-merges).
Summary
Two feature commits landed on July 10. The portfolio module introduces dedicated read endpoints that avoid changing existing list APIs—buildings and coverage lines are mapped to slim DTOs for dashboard joins on buildingId. The user UI preferences module adds a UserUiPreference entity, validation DTOs with defaults on GET, and full-replace PATCH semantics separate from notification preferences.
Themes
- Portfolio read models: New
PortfolioModulewith controller, service, mapper, and specs; buildings exclude soft-deleted rows; coverage lines filter byyear; optionalpage/limitquery params are accepted but ignored for backward compatibility with existing UI clients. - User UI preferences:
MeUiPreferenceControllerandUserUiPreferenceServicewith nestedpayloadvalidation (buildingsViewMode,theme,sidebarCollapsed,tables,dashboardWidgets,dashboardLayout); defaults returned when no row exists (updatedAt: null). - Testing: Controller, service, mapper, and DTO specs for both features (portfolio query DTO edge cases; preference defaults, full replace, validation).
- Documentation:
USER_UI_PREFERENCES.mddescribes request/response shape and full-replace semantics;NOTIFICATIONS.mdlinks to UI prefs; API reference updated for portfolio endpoints.
Notable fixes or risks (if any)
- PATCH body shape: Clients must send
{ "version": 1, "payload": { ... } }—top-level preference fields are rejected (400) under globalforbidNonWhitelisted. Frontend should wrap prefs inpayloadand readresponse.data.payload. - No server-side aggregates: Portfolio endpoints intentionally omit KPIs, health rules, and summaries; the UI computes those from the returned lists.
- Deploy: Restart the API after build so new routes (
/portfolio/*,/me/preferences) are registered.