Files
sencho/frontend/src/components/settings/SectionGate.tsx
T
Anso 1f8ce773ff feat(ui): hide paid features from community-tier dashboard (#891)
* feat(ui): hide paid features from community-tier dashboard

Community installs render only the features they can use. Tier-locked
sections, lock badges, upsell cards, and "Upgrade" buttons no longer
appear anywhere except the License page in Settings, which is the
single discoverable upgrade path.

Concretely:

- PaidGate and AdmiralGate now render null for non-qualifying tiers
  instead of upsell cards.
- SectionGate (settings) hides tier-locked sections entirely.
- Settings sidebar and command palette filter out items the operator
  cannot reach.
- Configuration Status widget on the dashboard drops the Automation
  section for community and hides any locked rows in remaining
  sections.
- Fleet > Status node cards drop locked summary rows.
- Stack action menu, sidebar bulk bar, file upload / download, scan
  comparison, network topology toggle, node label picker all hide
  for community instead of showing disabled affordances or "Upgrade"
  literal text.
- Removes tierUpsell, TierLockChip, and useDismissalState (no longer
  referenced).

Backend tier guards remain authoritative; this changes UI discovery
only.

* test(e2e): assert upload control is absent in community tier

The community-clean-ui change removes the "Upgrade to unlock upload"
pill from the file explorer. Update the matching e2e assertion to
verify the upload control is not rendered, instead of waiting for a
pill that no longer exists.
2026-05-03 01:17:02 -04:00

42 lines
1.3 KiB
TypeScript

import React from 'react';
import { useAuth } from '@/context/AuthContext';
import { useLicense } from '@/context/LicenseContext';
import { useNodes } from '@/context/NodeContext';
import { getSettingsItem, isItemVisible, isItemLocked } from './registry';
import type { VisibilityContext } from './registry';
import type { SectionId } from './types';
interface SectionGateProps {
sectionId: SectionId;
children: React.ReactNode;
}
/**
* Renders the section body only if the registry says it is visible AND
* the operator has the entitlement to use it. Tier-locked sections are
* hidden entirely from operators who do not qualify. Backend tier
* guards remain the authoritative enforcement.
*/
export function SectionGate({ sectionId, children }: SectionGateProps) {
const { isAdmin } = useAuth();
const { isPaid, license } = useLicense();
const { activeNode } = useNodes();
const isAdmiral = isPaid && license?.variant === 'admiral';
const isRemote = activeNode?.type === 'remote';
const visibility: VisibilityContext = {
isAdmin,
isPaid,
isAdmiral,
isRemote,
};
const item = getSettingsItem(sectionId);
if (!item || !isItemVisible(item, visibility)) return null;
if (isItemLocked(item, visibility)) return null;
return <>{children}</>;
}