feat(fleet): open Fleet Actions tab to Community (admin-only) (#1153)

* feat(fleet): open Fleet Actions tab to Community (admin-only)

Removes the requirePaid guard from the five Fleet Actions endpoints
(fleet-stop, fleet-prune, match-preview, prune/estimate, bulk-assign)
and drops the matching isPaid parent gate on FleetActionsTab so Community
admins can run fleet-wide bulk operations. requireAdmin stays on every
endpoint; operator and viewer roles still 403 on apply.

Tests flipped from "403 PAID_REQUIRED on community" to positive
"reachable on community + admin" assertions. Docs (fleet-actions,
fleet-view, licensing, overview, stack-labels) rewritten to state the
admin-role requirement once and drop the prior Skipper framing.

* fix(fleet): apply audit findings from PR #1153 review

- stack-labels.mdx: fix the page intro that still framed fleet label
  actions as "Operators on a Skipper or Admiral license". The cards are
  now Community + admin, so the intro reads "Admins also get a pair of
  fleet-wide actions".
- Collapse redundant role-rule statements on the two affected pages.
  fleet-actions.mdx now states the admin gate once in the lead-in Note
  and again only in the troubleshooting accordion (the Prerequisites
  row was duplicative). stack-labels.mdx trims the "Limits and rules"
  bullet to the value-add half (label authoring is open to every role)
  and drops the Fleet Actions repetition.
- Strip now-no-op mockTier('paid') calls from non-tier tests across the
  three fleet test files, plus the test-wide default in the
  fleet-action-card-endpoints beforeEach. Those mocks were misleading
  after the routes stopped consulting tier; if a future change re-adds
  requirePaid the tests will fail loudly instead of silently passing.
This commit is contained in:
Anso
2026-05-22 01:27:27 -04:00
committed by GitHub
parent 2f2401df68
commit 519a59ed2e
11 changed files with 46 additions and 86 deletions
@@ -1,5 +1,3 @@
import { Tags } from 'lucide-react';
import { useLicense } from '@/context/LicenseContext';
import type { FleetNode } from '@/components/FleetView/types';
import { LabelFleetStopCard } from './cards/LabelFleetStopCard';
import { BulkLabelAssignCard } from './cards/BulkLabelAssignCard';
@@ -10,18 +8,12 @@ interface Props {
}
export function FleetActionsTab({ nodes }: Props) {
const { isPaid } = useLicense();
if (nodes.length === 0) {
return (
<div className="text-sm text-stat-subtitle">Add a node to the fleet to use bulk actions.</div>
);
}
if (!isPaid) {
return <EmptyState />;
}
// Grid: 1 col under lg, 2 cols at lg and above. auto-rows-fr distributes
// available height evenly across the auto-created rows, and each card uses
// `flex flex-col h-full` (in <FleetActionCard>) with a `flex-1` body so it
@@ -36,20 +28,3 @@ export function FleetActionsTab({ nodes }: Props) {
</div>
);
}
// Note: the §18.8 Community-tier "single full-width card with cyan rail + locked
// footer (shell only, no upsell)" redesign is deferred to a follow-up PR.
// Tracked in docs/internal/refactor/fleet-action-card-migration.md.
function EmptyState() {
return (
<div className="rounded-lg border border-card-border/60 bg-card p-8 text-center">
<div className="mx-auto mb-3 inline-flex h-10 w-10 items-center justify-center rounded-md bg-glass-highlight">
<Tags className="h-5 w-5 text-stat-subtitle" strokeWidth={1.5} />
</div>
<h3 className="text-sm font-medium text-stat-value mb-1">Fleet-wide bulk actions</h3>
<p className="text-xs text-stat-subtitle max-w-md mx-auto">
Stop stacks across every node by label name, assign labels to many stacks at once, and reclaim Docker disk space fleet-wide. Available on Skipper and Admiral.
</p>
</div>
);
}