Workshop: fix vote color semantics (primary accent for active state,
contextual score colors), conditional badge positioning, inline delete
confirm, shorter sign-in CTA, comment count on cards, visual anchor
for imageless cards, always-visible gallery nav, footer density polish,
Mantine bg prop for type dot, ghost button back-link.
Custom widgets table: fixed layout with truncation, responsive columns
that hide URL/display on smaller screens to keep name visible.
Move the type badge (colored dot + label) to absolute top-left overlay
on the card. Title now truncates to a single line with a native tooltip
on hover showing the full name. Vote controls remain fixed to the right
via CardAction.
Rename all user-facing "Marketplace" references to "Workshop" including
the route (/workshop), navbar label, page titles, button labels, env vars,
docs, and error messages. The PocketBase view collection stays `marketplace`
as an internal API detail.
Replace icon-based type badges with colored dots (blue = CSS, yellow = widget)
on both the filter bar and submission cards for clearer visual distinction.
Adds a PocketBase-backed marketplace where users can share custom widgets
and CSS themes. Includes a Docusaurus storefront with GitHub OAuth login,
submission/voting/comments, and a PocketBase SQL view that computes author
names, vote counts, and comment counts without fragile hooks. The Next.js
app gets a modal browser for one-click install from the marketplace plus
clipboard paste-to-import on the custom widgets pages.
Beszel only stores stats at 1m, 10m, 20m, 120m, 480m intervals.
The 1440m type doesn't exist, so queries for 1w/30d returned empty
results. Use 480m (8h) with appropriate perPage (21 for 1w, 90 for 30d).
* fix(beszel): harden cache layer and prevent blank widgets
- Add debug logging across Beszel integration, request handler, and tRPC
router (auth, fetch, SSE, per-procedure entry/exit) gated behind
LOG_LEVEL=debug
- cached-request-handler: retry with backoff, isValid predicate to skip
caching invalid data, in-flight dedup (single-flight per channel) to
avoid thundering herd after cache invalidation
- redis channel: set-then-publish so storage is always consistent even
when pubsub publish fails
- Beszel handlers: opt into fallbackToStaleOnError and retry so brief
upstream hiccups don't throw away good cached data
- beszel-system-stats: add refetchInterval matching each time period's
cache TTL so historical charts refresh in the background
- tRPC router: switch Promise.all -> Promise.allSettled for getSystems
and getAlerts; getSystemStats returns an error object instead of
throwing. Each result carries an optional error field so one failed
integration no longer nukes the whole widget
- Widgets: add explicit isPending loader, empty state for grid and
stats, and a shared per-integration error indicator (tooltip with
failed integration names). Alerts and table now only show their
empty state after loading completes
- Translation: add empty.noSystems key to beszelSystemGrid and
beszelSystemStats
Addresses #6030
* style: auto-fix lint and format
---------
Co-authored-by: ajnart <ajnart@users.noreply.github.com>