Commit Graph
1665 Commits
Author SHA1 Message Date
Juan Denis 7aa257e1fd feat(servers): bulk edit — assign selected servers to a group
The fleet table gains native selection (DataTable selectable) and a floating
bulk bar with a Set group… picker (including Ungrouped) that applies via
updateServer per row.
2026-08-09 14:04:54 -04:00
Juan Denis a958b05c97 feat(service-detail): unified activity timeline
The Events tab becomes the CRM record timeline: deployments (expandable,
as before) interleaved newest-first with environment-variable changes and —
for admins — audit-log entries targeting the app, via the new target_id
feed filter. Compact non-expandable rows for config/audit entries.
2026-08-09 14:04:54 -04:00
Juan Denis ede0f0fdbd feat(activity): audit feed filters by target_id
AuditService.get_logs gains a target_id filter (paired with target_type) and
/admin/activity/feed passes both through — the per-resource activity
timeline can now ask 'what happened to THIS app'.
2026-08-09 13:53:59 -04:00
Juan Denis f8bfb94630 feat(recents): visit tracking + favorite stars on detail pages
Service/Server/Monitor/Workspace/Project detail pages record visits for the
palette's Recently used section and host a FavoriteStar in their title areas
that pins the entity into the palette's Favorites group.
2026-08-09 13:46:30 -04:00
Juan Denis acfce7dbeb feat(ux): undo toasts for reversible deletes + cron create deep-link
Removing an IP list entry or deleting a cron job now toasts with an Undo
action that re-POSTs the captured row (comment/description preserved). The
useConfirm gate stays; cron's ?focus=create:cron deep-link rides along.
2026-08-09 13:46:30 -04:00
Juan Denis e980104d5a feat(quick-create): create deep-links on the list pages
Each QuickCreate destination opens its create surface via useFocusParam:
?focus=create:server|monitor|domain|workspace|project (cron lands with the
jobs commit).
2026-08-09 13:46:30 -04:00
Juan Denis 364aa592d5 feat(chrome): global quick-create button + favorite star
The CRM '+' lives in the sidebar footer and mobile top bar next to the
notification bell: one menu for New Service/Server/Monitor/Domain/Cron/
Workspace/Project. Route-based flows navigate directly; modal-based flows
use ?focus=create:<kind> deep links. FavoriteStar pins entities from detail
pages.
2026-08-09 04:31:36 -04:00
Juan Denis fd775fc629 feat(palette): Favorites + Recent entity visits sections
utils/recents.js tracks detail-page visits (10 max) and pinned favorites in
localStorage; useRecordVisit is the one-line detail-page hook. The palette
gains a Favorites group (weighted high, searchable) and folds visited
entities into Recently used.
2026-08-09 04:31:35 -04:00
Juan Denis 084638984c feat(services,workspaces,servers): group-by + native selection + row cursor
Services groups by project or status, Workspaces and Servers by status; the
hand-rolled __select columns are gone in favor of DataTable's native
selection; Servers captures groupBy in its saved views.
2026-08-09 03:53:43 -04:00
Juan Denis 3a0e047038 feat(resource-list): wire grouping, GroupMenu, native selection, keyboard nav
ResourceListPage owns persisted groupBy state (captured in saved views),
renders GroupMenu in the toolbar, accepts selectable/selectedIds/
onToggleSelect/onSelectAll for the native checkbox column, and turns the row
cursor on by default for list pages.
2026-08-09 03:53:43 -04:00
Juan Denis ddb557ec1e feat(views): dirty indicator — Update/Reset on modified views
useTableViews deep-compares the live table state against the active view
(hiddenKeys set-normalized, sorts order-sensitive); a dirty view shows an
amber dot on the trigger and Update / Reset-to-saved actions in the popover.
2026-08-09 03:53:43 -04:00
Juan Denis 99dbef56ad feat(datatable): row grouping, keyboard nav, native selection
Group-by: groupable columns + groupBy prop render collapsible group header
rows (chevron + label + count); GroupMenu popover picks the grouping. Keyboard
nav: j/k (or arrows) move a cursor row, Enter opens it, x toggles selection —
ignored while typing or in dialogs. Selection: selectable + controlled
selectedKeys/onToggleRow/onToggleAll render a real checkbox column with an
indeterminate select-all, ending the hand-rolled __select column pattern.
2026-08-09 03:53:43 -04:00
Juan Denis 2939542b44 docs(agents): codify the list-page anatomy
One anatomy diagram, the toolbar placement rule, the drawer width scale, and
the EmptyState/Modal/useConfirm/row-click conventions; primitives list gains
ListToolbar, SortChipBar, ViewMenu and the views hook.
2026-08-09 03:33:23 -04:00
Juan Denis cb085fafb7 refactor(list-page): one toolbar placement rule
ResourceListPage renders the shared ListToolbar and gains a searchInTopbar
flag; Workspaces moves its search into the shared topbar to match Services
(search state still flows through for views). Superseded .wp-list__toolbar /
__tabletools SCSS removed; the .empty-state specificity comment is updated
now that the page-partial copies are gone.
2026-08-09 03:33:23 -04:00
Juan Denis 27acadebaf refactor(drawers,modals): standard width scale + Modal.jsx wrapper
Drawer widths snap to the 380/520/640/720 scale; thirteen raw ui/dialog uses
(services, projects, vaults, serverdetail tabs, webhooks, doctor panel)
convert to the shared Modal wrapper. ConnectProviderModal keeps its custom
chrome (branded header + scroll region Modal can't express).
2026-08-09 03:33:22 -04:00
Juan Denis 037e8842f7 refactor(security): shared EmptyState + useConfirm everywhere
Security tabs' legacy empty-state markup becomes the component, their bespoke
confirmDialog flows (firewall, fail2ban, ip lists, ssh keys) move to
useConfirm, and the remaining window.confirm calls (telemetry cleanup,
service settings, connections hub) are replaced. The duplicate .empty-state
block in _security.scss is gone.
2026-08-09 03:33:06 -04:00
Juan Denis e6f9a88352 refactor(empty-states): shared EmptyState across jobs/delivery/docs/settings
DataTable emptyState overrides and legacy empty-state markup become the
shared component (wording and CTAs preserved). Also fixes DataTable passing
emptyMessage to a nonexistent EmptyState prop — it now lands on
'description', so custom empty messages actually render.
2026-08-09 03:33:06 -04:00
Juan Denis df34943b8a refactor(servers,deployments,email,serverdetail): ListToolbar everywhere
servers/deployments/server-services/packages/survey toolbars and the email
accounts/queue rows move to ListToolbar (selects get .sk-listhead__select);
email table emptyState overrides become emptyTitle/emptyMessage; the legacy
unscoped .empty-state in _servers.scss is scoped under .servers-page.
2026-08-09 03:32:51 -04:00
Juan Denis 897f9a8764 refactor(cron,backups,incidents): ListToolbar + shared EmptyState
cron/bk/incidents listheads move to ListToolbar (counts and menus preserved);
bk-empty and mon-empty become EmptyState; run-history drawer width snapped to
the 640 step of the standard scale.
2026-08-09 03:32:51 -04:00
Juan Denis 106a83778d refactor(domains,git): ListToolbar + shared EmptyState
The .dom-listhead rows (incl. Git's six borrowed section headers) move to the
shared ListToolbar; .dom-empty filtered-empty blocks become EmptyState;
retired SCSS deleted.
2026-08-09 03:32:51 -04:00
Juan Denis b0dd554216 feat(ds): ListToolbar — the one list-page toolbar row
.sk-listhead [title][filters][extras] [count][tools] replaces the five
hand-rolled listhead patterns; includes a standard __select style for
secondary pickers. Codifies the placement rule in its docstring.
2026-08-09 03:10:23 -04:00
Juan Denis f2c7924493 feat(email): saved views for accounts and mail queue
Sort-preset builtins (Active first, Largest quota, Largest first, By sender);
ViewMenu in each table's toolbar before SortMenu.
2026-08-09 02:47:11 -04:00
Juan Denis 5d3f6a6a33 feat(jobs,telemetry): saved views — Failed / Errors / Warnings / Newest builtins
Both tables switch to controlled sort/cols (same storageKeys); views capture
the FilterDrawer value plus sorts and columns, never server-side page state.
ViewMenu publishes into the topbar via useTopbarActions with a primitive
active-key dep to avoid re-publish loops.
2026-08-09 02:47:10 -04:00
Juan Denis 5f9ddb3384 feat(cronjobs): saved views — Paused / Active / Recently run builtins 2026-08-09 02:47:10 -04:00
Juan Denis 411200487c feat(domains): saved views — Expiring soon / No SSL builtins
ViewMenu in the listhead tools; a view captures the seg filter, search, sorts
and hidden columns.
2026-08-09 02:46:59 -04:00
Juan Denis eccc93ea29 feat(monitors): saved views — Down / Degraded / Slowest builtins
ViewMenu published into the shared topbar before the filter button; the table
switches to controlled sort/cols (same storageKeys, choices survive). A view
captures search, the FilterDrawer status/type value, sorts and columns.
2026-08-09 02:46:58 -04:00
Juan Denis eb9d5914e9 feat(servers): saved views — Online / Offline / By CPU builtins
ViewMenu in the listhead; a view captures status, group, search, sorts and
hidden columns. Also drops an unused eslint-disable in useTableViews.
2026-08-09 02:46:58 -04:00
Juan Denis 1c226b0ad6 feat(views): view picker UI + ResourceListPage integration
useTableViews merges builtin views with the user's saved views from the API
(auto-applying the default on load); ViewMenu is the picker popover — apply
on click, star for default, delete, save-current-as form, update-active. The
ResourceListPage toolbar grows the menu (state = filter/search/sorts/hidden
columns/page size), so Services and Workspaces get views by declaring their
builtin defs.
2026-08-09 02:38:48 -04:00
Juan Denis b6c257bfc7 feat(views): per-user saved table views API
SavedView model (user/page/name/state JSON/is_default, unique per
user+page+name), idempotent 082 migration, saved_view_service with
one-default-per-page enforcement, and a JWT-scoped /api/v1/views CRUD
blueprint. Covered by 9 endpoint tests.
2026-08-09 02:32:55 -04:00
Juan Denis fa6b9f3e26 refactor(queues,docker,misc): move remaining tables onto DataTable
Queue messages/operations, delivery log, process table, env-history and
docker containers migrated. The containers tab's select+button sort UI is
deleted in favor of native header sorting (running-first default kept) with
controlled menus; FilterDrawer pairings untouched.
2026-08-09 02:12:17 -04:00
Juan Denis 65703c165d refactor(serverdetail): move server-detail tab tables onto DataTable
Docker containers/images (size parses to bytes for numeric sort), cloudflared
tunnels, cron, systemd services and survey sites now use the shared primitive;
tabs with toolbars (cloudflared, cron, services) get controlled menus.
2026-08-09 02:12:16 -04:00
Juan Denis 5d87b42fa6 refactor(settings): move settings tables onto DataTable
Users, invitations, API keys, webhook deliveries, top endpoints and migration
history gain sortable columns (compact variants preserved) and standard
footers. ActivityTab keeps its server-paged fetch; its ad-hoc pager swaps to
DataTableFooter's paged mode.
2026-08-09 02:11:56 -04:00
Juan Denis 1a0ca61c41 refactor(workspaces,dns,packages): move remaining tab tables onto DataTable
Workspace sites/services/servers/members, the domain DNS record grid and the
service packages table now use the shared primitive with sortable columns and
footers; conditional DNS columns and fixed-layout classes preserved.
2026-08-09 02:11:56 -04:00
Juan Denis 0cf847b739 refactor(ftp,fleet): move FTP and fleet-monitoring tables onto DataTable
FTP users/connections and the nine fleet tables (versions, rollouts, command
queue, approvals, alerts, anomalies, thresholds, capacity) gain sortable
columns, persisted choices and standard footers; tables with an in-page
toolbar get controlled SortMenu/ColumnsMenu.
2026-08-09 02:11:55 -04:00
Juan Denis 3811c4004b refactor(git): move repo/webhook/deploy tables onto DataTable
Repos and webhooks gain controlled SortMenu/ColumnsMenu in the listhead and
filtered footers; the webhook drawer's recent-deployments table sorts
uncontrolled. The commits feed is not table-shaped and stays as-is.
2026-08-08 23:56:52 -04:00
Juan Denis 3d026208f4 refactor(backups): move snapshot/schedule/history tables onto DataTable
Snapshots table gains controlled sort/column menus in the listhead plus a
sort chip bar; schedules, latest-snapshots digest and run history sort
uncontrolled with persisted choices; standard footers throughout.
2026-08-08 23:56:51 -04:00
Juan Denis 249546720a refactor(security): move all security tab tables onto DataTable
Firewall, Fail2ban, IP allow/blocklists, integrity changes, quarantine,
scanner findings/history and SSH keys now use the shared primitive: sortable
columns (epoch/numeric sortValues), SortMenu+ColumnsMenu in card headers
(.sec-tableactions slot), standard footers, persisted choices.
2026-08-08 23:56:51 -04:00
Juan Denis 7b4cf90b0c feat(datatable): active-sort chip bar (Twenty-style)
SortChipBar renders one chip per sort level above the table — click to flip
direction, x to remove, Reset to clear — and collapses to nothing when no
sort is active. Wired into ResourceListPage (list view) and the Servers page.
2026-08-08 23:41:30 -04:00
Juan Denis 5aa4ddc6b2 docs(agents): document the table primitives (SortMenu, ColumnsMenu, DataTableFooter, hooks) 2026-08-08 23:37:43 -04:00
Juan Denis f64a537144 refactor(servers): move the hand-rolled table onto DataTable
The servers list now runs on the shared primitive: identical cell markup and
style hooks, sortable name/status/CPU/mem/disk/last-seen (nulls last),
SortMenu + ColumnsMenu in the listhead, a shared footer, and the page's
sticky header preserved. Meter cells generated by a small helper.
2026-08-08 23:37:43 -04:00
Juan Denis e79538eb37 feat(services,workspaces): opt columns into sorting, persist table state
Both pages declare sortable columns with meaningful sortValue accessors
(status severity order, timestamps, numerics, nulls last) and pass a
storageKey so ResourceListPage persists sort/columns/page-size.
2026-08-08 23:37:42 -04:00
Juan Denis 11460670e9 feat(jobs,telemetry): standard table footer replaces ad-hoc pagers
Jobs' custom Prev/Next 'Page N' pager and Telemetry's bare 'Load more' swap
to the shared DataTableFooter (same state setters, fetch logic untouched);
both tables gain sortable columns and persisted choices. Orphaned pager SCSS
removed.
2026-08-08 23:37:31 -04:00
Juan Denis d73f0c4b31 feat(email,fleet): sortable tables with menus and footers
Email components/accounts/queue tables gain sortable columns; accounts and
queue toolbars get SortMenu + ColumnsMenu (new .sk-email__tabletools slot).
FleetProxy keeps its renderRow layout but gains header sorting (column
visibility intentionally off — renderRow emits every cell).
2026-08-08 23:37:30 -04:00
Juan Denis 2fbacb2fd1 feat(cron,domains): sortable columns, table menus, footers
Cron jobs + run history and the domains list get sortable headers (schedule,
last/next run, duration, expiry sort by real values), SortMenu/ColumnsMenu in
the in-page listheads (new .dom-listhead__tools slot), and standard footers.
2026-08-08 23:37:20 -04:00
Juan Denis a394bc38c6 feat(monitors): enable sortable columns and table footers
Monitor list sorts by name/type/status/response/uptime/last check; check log
gains SortMenu + ColumnsMenu in its panel header (controlled mode), sortable
time/result/latency columns, and a shared footer. Choices persist via
storageKey.
2026-08-08 23:37:20 -04:00
Juan Denis d3490a4aaf feat(resource-list): wire sort/columns menus, footer, floating bulk bar
ResourceListPage now owns the table chrome end to end: SortMenu + ColumnsMenu
in the toolbar, DataTableFooter with client-side page-size slicing under the
table, and a floating bottom bulk-actions pill replacing the permanent bar.
Optional storageKey persists sort/columns/page-size per page. Sorting flips
on globally (columns still opt in via sortable: true).
2026-08-08 23:37:10 -04:00
Juan Denis 334226a86b style(tables): sticky headers, sort priority badge, table menus, footer, floating bulk bar
Extends .sk-dtable with is-sorted accent + multi-sort priority numbers and an
opt-in sticky-header wrap; adds .sk-tablemenu (sort/columns popovers),
.sk-dtable-footer, and the floating .sk-bulkbar pill. Also restores the
.wp-list chrome styles (toolbar/search/viewswitch/cards) that were lost when
the WordPress frontend moved into its extension, plus the new tabletools slot.
2026-08-08 23:37:02 -04:00
Juan Denis fc2c4f6697 feat(datatable): multi-column sorting, column visibility, standard footer
DataTable v2: sorts become an ordered array (datatables.net model) — plain
header click cycles asc/desc/none, shift+click stacks sort levels with a
priority badge. New useTableSort/useColumnVisibility hooks (optional
localStorage persistence), SortMenu/ColumnsMenu toolbar popovers, and a
shared DataTableFooter (Shown X of Y, page-size segment, load-more, pager).
All backward compatible; existing call sites keep working unchanged.
2026-08-08 23:36:52 -04:00
Juan Denis 73a79a20e0 Merge branch 'dev' of https://github.com/jhd3197/ServerKit into dev 2026-08-08 23:28:35 -04:00
Juan Denis 6e03c0a523 perf(templates): cache parsed local templates on file mtime
With the remote index memoized, re-parsing every bundled YAML on each call
was the entire remaining cost of a Templates page load. Entries are now
memoized per file and keyed on (st_mtime_ns, st_size), so a sync or an edit
invalidates exactly the file that changed: measured on the real catalog,
list_local_templates() goes from 304ms to 0.89ms (118 templates), and the
full catalog listing from 1707ms to 1.26ms.

The writers need no invalidation call - mtime keying covers create, overwrite
and delete, the last because the listing only returns what the directory scan
finds. Cache entries are pruned to what is still on disk so a long-running
panel that churns templates cannot grow it without bound.

Failed parses are cached too, keyed on the same stat tuple, so one malformed
YAML no longer costs a parse on every render; it is retried the moment the
file changes. A file that fails to parse still does not claim its id, so a
valid file of the same name in the fallback directory can supply it - pinned
by a test, since that is easy to lose in a refactor.

Entries are deep-copied out: a shallow copy would share the nested
categories/repo/engine containers, so a caller appending to categories would
corrupt every later listing.

Also swaps os.listdir for os.scandir (DirEntry.stat() reuses data the scan
already returned) and sorts directory entries, which makes a same-id
collision (foo.yaml vs foo.yml) resolve identically on every platform instead
of depending on filesystem order.
2026-08-06 09:32:40 -04:00