Disable admin plan section when no login (#5039)

Admin plan section matches other admin sections

<img width="1013" height="629" alt="image"
src="https://github.com/user-attachments/assets/39e9fad7-461c-491d-99cb-4b140292f2f4"
/>
<img width="730" height="595" alt="image"
src="https://github.com/user-attachments/assets/b26354d2-5401-40b3-8ca7-3b48b26b644e"
/>
This commit is contained in:
ConnorYoh
2025-11-27 13:57:04 +00:00
committed by GitHub
parent f63df148ad
commit 04c4aec0d8
3 changed files with 33 additions and 7 deletions
@@ -13,9 +13,12 @@ import { InfoBanner } from '@app/components/shared/InfoBanner';
import { useLicenseAlert } from '@app/hooks/useLicenseAlert'; import { useLicenseAlert } from '@app/hooks/useLicenseAlert';
import { isSupabaseConfigured } from '@app/services/supabaseClient'; import { isSupabaseConfigured } from '@app/services/supabaseClient';
import { getPreferredCurrency, setCachedCurrency } from '@app/utils/currencyDetection'; import { getPreferredCurrency, setCachedCurrency } from '@app/utils/currencyDetection';
import { useLoginRequired } from '@app/hooks/useLoginRequired';
import LoginRequiredBanner from '@core/components/shared/config/LoginRequiredBanner';
const AdminPlanSection: React.FC = () => { const AdminPlanSection: React.FC = () => {
const { t, i18n } = useTranslation(); const { t, i18n } = useTranslation();
const { loginEnabled, validateLoginEnabled } = useLoginRequired();
const { openCheckout } = useCheckout(); const { openCheckout } = useCheckout();
const { licenseInfo, refetchLicense } = useLicense(); const { licenseInfo, refetchLicense } = useLicense();
const [currency, setCurrency] = useState<string>(() => { const [currency, setCurrency] = useState<string>(() => {
@@ -39,6 +42,11 @@ const AdminPlanSection: React.FC = () => {
}, [error]); }, [error]);
const handleSaveLicense = async () => { const handleSaveLicense = async () => {
// Block save if login is disabled
if (!validateLoginEnabled()) {
return;
}
try { try {
setSavingLicense(true); setSavingLicense(true);
// Allow empty string to clear/remove license // Allow empty string to clear/remove license
@@ -86,6 +94,11 @@ const AdminPlanSection: React.FC = () => {
]; ];
const handleManageClick = useCallback(async () => { const handleManageClick = useCallback(async () => {
// Block access if login is disabled
if (!validateLoginEnabled()) {
return;
}
try { try {
// Only allow PRO or ENTERPRISE licenses to access billing portal // Only allow PRO or ENTERPRISE licenses to access billing portal
if (!licenseInfo?.licenseType || licenseInfo.licenseType === 'NORMAL') { if (!licenseInfo?.licenseType || licenseInfo.licenseType === 'NORMAL') {
@@ -112,7 +125,7 @@ const AdminPlanSection: React.FC = () => {
body: error.message || 'Please try again or contact support.', body: error.message || 'Please try again or contact support.',
}); });
} }
}, [licenseInfo, t]); }, [licenseInfo, t, validateLoginEnabled]);
const handleCurrencyChange = useCallback((newCurrency: string) => { const handleCurrencyChange = useCallback((newCurrency: string) => {
setCurrency(newCurrency); setCurrency(newCurrency);
@@ -122,6 +135,11 @@ const AdminPlanSection: React.FC = () => {
const handleUpgradeClick = useCallback( const handleUpgradeClick = useCallback(
(planGroup: PlanTierGroup) => { (planGroup: PlanTierGroup) => {
// Block access if login is disabled
if (!validateLoginEnabled()) {
return;
}
// Only allow upgrades for server and enterprise tiers // Only allow upgrades for server and enterprise tiers
if (planGroup.tier === 'free') { if (planGroup.tier === 'free') {
return; return;
@@ -151,7 +169,7 @@ const AdminPlanSection: React.FC = () => {
}, },
}); });
}, },
[openCheckout, currency, refetch, licenseInfo, t] [openCheckout, currency, refetch, licenseInfo, t, validateLoginEnabled]
); );
const shouldShowLicenseWarning = licenseAlert.active && licenseAlert.audience === 'admin'; const shouldShowLicenseWarning = licenseAlert.active && licenseAlert.audience === 'admin';
@@ -200,6 +218,8 @@ const AdminPlanSection: React.FC = () => {
return ( return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '2rem' }}> <div style={{ display: 'flex', flexDirection: 'column', gap: '2rem' }}>
<LoginRequiredBanner show={!loginEnabled} />
{shouldShowLicenseWarning && ( {shouldShowLicenseWarning && (
<InfoBanner <InfoBanner
icon="warning-rounded" icon="warning-rounded"
@@ -231,6 +251,7 @@ const AdminPlanSection: React.FC = () => {
currency={currency} currency={currency}
onCurrencyChange={handleCurrencyChange} onCurrencyChange={handleCurrencyChange}
currencyOptions={currencyOptions} currencyOptions={currencyOptions}
loginEnabled={loginEnabled}
/> />
<Divider /> <Divider />
@@ -288,11 +309,11 @@ const AdminPlanSection: React.FC = () => {
onChange={(e) => setLicenseKeyInput(e.target.value)} onChange={(e) => setLicenseKeyInput(e.target.value)}
placeholder={licenseInfo?.licenseKey || '00000000-0000-0000-0000-000000000000'} placeholder={licenseInfo?.licenseKey || '00000000-0000-0000-0000-000000000000'}
type="password" type="password"
disabled={savingLicense} disabled={!loginEnabled || savingLicense}
/> />
<Group justify="flex-end"> <Group justify="flex-end">
<Button onClick={handleSaveLicense} loading={savingLicense} size="sm"> <Button onClick={handleSaveLicense} loading={savingLicense} size="sm" disabled={!loginEnabled}>
{t('admin.settings.save', 'Save Changes')} {t('admin.settings.save', 'Save Changes')}
</Button> </Button>
</Group> </Group>
@@ -15,6 +15,7 @@ interface AvailablePlansSectionProps {
currency?: string; currency?: string;
onCurrencyChange?: (currency: string) => void; onCurrencyChange?: (currency: string) => void;
currencyOptions?: { value: string; label: string }[]; currencyOptions?: { value: string; label: string }[];
loginEnabled?: boolean;
} }
const AvailablePlansSection: React.FC<AvailablePlansSectionProps> = ({ const AvailablePlansSection: React.FC<AvailablePlansSectionProps> = ({
@@ -25,6 +26,7 @@ const AvailablePlansSection: React.FC<AvailablePlansSectionProps> = ({
currency, currency,
onCurrencyChange, onCurrencyChange,
currencyOptions, currencyOptions,
loginEnabled = true,
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [showComparison, setShowComparison] = useState(false); const [showComparison, setShowComparison] = useState(false);
@@ -91,6 +93,7 @@ const AvailablePlansSection: React.FC<AvailablePlansSectionProps> = ({
clearable={false} clearable={false}
w={300} w={300}
comboboxProps={{ withinPortal: true, zIndex: Z_INDEX_OVER_CONFIG_MODAL }} comboboxProps={{ withinPortal: true, zIndex: Z_INDEX_OVER_CONFIG_MODAL }}
disabled={!loginEnabled}
/> />
)} )}
</Group> </Group>
@@ -113,6 +116,7 @@ const AvailablePlansSection: React.FC<AvailablePlansSectionProps> = ({
currentTier={currentTier} currentTier={currentTier}
onUpgradeClick={onUpgradeClick} onUpgradeClick={onUpgradeClick}
onManageClick={onManageClick} onManageClick={onManageClick}
loginEnabled={loginEnabled}
/> />
))} ))}
</div> </div>
@@ -15,9 +15,10 @@ interface PlanCardProps {
currentTier?: 'free' | 'server' | 'enterprise' | null; currentTier?: 'free' | 'server' | 'enterprise' | null;
onUpgradeClick: (planGroup: PlanTierGroup) => void; onUpgradeClick: (planGroup: PlanTierGroup) => void;
onManageClick?: () => void; onManageClick?: () => void;
loginEnabled?: boolean;
} }
const PlanCard: React.FC<PlanCardProps> = ({ planGroup, isCurrentTier, isDowngrade, currentLicenseInfo, currentTier, onUpgradeClick, onManageClick }) => { const PlanCard: React.FC<PlanCardProps> = ({ planGroup, isCurrentTier, isDowngrade, currentLicenseInfo, currentTier, onUpgradeClick, onManageClick, loginEnabled = true }) => {
const { t } = useTranslation(); const { t } = useTranslation();
// Render Free plan // Render Free plan
@@ -174,7 +175,7 @@ const PlanCard: React.FC<PlanCardProps> = ({ planGroup, isCurrentTier, isDowngra
variant={isCurrentTier ? 'filled' : isDowngrade ? 'filled' : isEnterpriseBlockedForFree ? 'light' : 'filled'} variant={isCurrentTier ? 'filled' : isDowngrade ? 'filled' : isEnterpriseBlockedForFree ? 'light' : 'filled'}
fullWidth fullWidth
onClick={() => isCurrentTier && onManageClick ? onManageClick() : onUpgradeClick(planGroup)} onClick={() => isCurrentTier && onManageClick ? onManageClick() : onUpgradeClick(planGroup)}
disabled={isDowngrade || isEnterpriseBlockedForFree} disabled={!loginEnabled || isDowngrade || isEnterpriseBlockedForFree}
> >
{isCurrentTier {isCurrentTier
? t('plan.manage', 'Manage') ? t('plan.manage', 'Manage')