diff --git a/frontend/public/locales/en-GB/translation.json b/frontend/public/locales/en-GB/translation.json
index ee542e2dd8..1e8ea95d48 100644
--- a/frontend/public/locales/en-GB/translation.json
+++ b/frontend/public/locales/en-GB/translation.json
@@ -5104,7 +5104,8 @@
"planOverview": {
"adminTitle": "Admin Overview",
"userTitle": "Plan Overview",
- "adminBody": "As an admin, you can manage users, configure settings, and monitor server health. The first {{freeTierLimit}} people on your server get to use Stirling free of charge.",
+ "adminBodyLoginEnabled": "As an admin, you can manage users, configure settings, and monitor server health. The first {{freeTierLimit}} people on your server get to use Stirling free of charge.",
+ "adminBodyLoginDisabled": "Once you enable login mode, you can manage users, configure settings, and monitor server health. The first {{freeTierLimit}} people on your server get to use Stirling free of charge.",
"userBody": "Invite teammates, assign roles, and keep your documents organized in one secure workspace. Enable login mode whenever you're ready to grow beyond solo use."
},
"securityCheck": {
diff --git a/frontend/public/locales/en-US/translation.json b/frontend/public/locales/en-US/translation.json
index ceb0cb2adf..c0390af772 100644
--- a/frontend/public/locales/en-US/translation.json
+++ b/frontend/public/locales/en-US/translation.json
@@ -4914,7 +4914,8 @@
"planOverview": {
"adminTitle": "Admin Overview",
"userTitle": "Plan Overview",
- "adminBody": "As an admin, you can manage users, configure settings, and monitor server health. The first {{freeTierLimit}} people on your server get to use Stirling free of charge.",
+ "adminBodyLoginEnabled": "As an admin, you can manage users, configure settings, and monitor server health. The first {{freeTierLimit}} people on your server get to use Stirling free of charge.",
+ "adminBodyLoginDisabled": "Once you enable login mode, you can manage users, configure settings, and monitor server health. The first {{freeTierLimit}} people on your server get to use Stirling free of charge.",
"userBody": "Invite teammates, assign roles, and keep your documents organized in one secure workspace. Enable login mode whenever you're ready to grow beyond solo use."
},
"securityCheck": {
diff --git a/frontend/src/core/components/onboarding/InitialOnboardingModal/useInitialOnboardingState.ts b/frontend/src/core/components/onboarding/InitialOnboardingModal/useInitialOnboardingState.ts
index c57b0990b0..a7ffb426f5 100644
--- a/frontend/src/core/components/onboarding/InitialOnboardingModal/useInitialOnboardingState.ts
+++ b/frontend/src/core/components/onboarding/InitialOnboardingModal/useInitialOnboardingState.ts
@@ -42,6 +42,7 @@ export function useInitialOnboardingState({
hasPaidLicense,
scenarioKey,
setSelfReportedAdmin,
+ isNewServer,
} = useServerExperience();
const osType = useOs();
const navigate = useNavigate();
@@ -93,6 +94,13 @@ export function useInitialOnboardingState({
const effectiveEnableLogin = enableLogin;
const effectiveIsAdmin = isAdmin;
+ const shouldAssumeAdminForNewServer = Boolean(isNewServer) && !effectiveEnableLogin;
+
+ useEffect(() => {
+ if (shouldAssumeAdminForNewServer && !state.selfReportedAdmin) {
+ handleRoleSelect('admin');
+ }
+ }, [handleRoleSelect, shouldAssumeAdminForNewServer, state.selfReportedAdmin]);
const shouldUseServerCount =
(effectiveEnableLogin && effectiveIsAdmin) || !effectiveEnableLogin;
@@ -127,11 +135,19 @@ export function useInitialOnboardingState({
return options.filter(opt => opt.url);
}, []);
- const { ids: flowSlideIds, type: flowType } = resolveFlow(
- effectiveEnableLogin,
- effectiveIsAdmin,
- state.selfReportedAdmin,
+ const resolvedFlow = useMemo(
+ () => resolveFlow(effectiveEnableLogin, effectiveIsAdmin, state.selfReportedAdmin),
+ [effectiveEnableLogin, effectiveIsAdmin, state.selfReportedAdmin],
);
+ const shouldSkipSecurityCheck = shouldAssumeAdminForNewServer;
+ const flowSlideIds = useMemo(
+ () =>
+ shouldSkipSecurityCheck
+ ? resolvedFlow.ids.filter((id) => id !== 'security-check')
+ : resolvedFlow.ids,
+ [resolvedFlow.ids, shouldSkipSecurityCheck],
+ );
+ const flowType = resolvedFlow.type;
const totalSteps = flowSlideIds.length;
const maxIndex = Math.max(totalSteps - 1, 0);
@@ -212,6 +228,7 @@ export function useInitialOnboardingState({
selectedRole: state.selectedRole,
onRoleSelect: handleRoleSelect,
licenseNotice,
+ loginEnabled: effectiveEnableLogin,
});
const goNext = useCallback(() => {
diff --git a/frontend/src/core/components/onboarding/onboardingFlowConfig.ts b/frontend/src/core/components/onboarding/onboardingFlowConfig.ts
index a7617a1755..30967691dc 100644
--- a/frontend/src/core/components/onboarding/onboardingFlowConfig.ts
+++ b/frontend/src/core/components/onboarding/onboardingFlowConfig.ts
@@ -45,6 +45,7 @@ export interface SlideFactoryParams {
selectedRole: 'admin' | 'user' | null;
onRoleSelect: (role: 'admin' | 'user' | null) => void;
licenseNotice?: LicenseNotice;
+ loginEnabled?: boolean;
}
export interface HeroDefinition {
@@ -142,7 +143,8 @@ export const SLIDE_DEFINITIONS: Record = {
},
'admin-overview': {
id: 'admin-overview',
- createSlide: ({ licenseNotice }) => PlanOverviewSlide({ isAdmin: true, licenseNotice }),
+ createSlide: ({ licenseNotice, loginEnabled }) =>
+ PlanOverviewSlide({ isAdmin: true, licenseNotice, loginEnabled }),
hero: { type: 'diamond' },
buttons: [
{
diff --git a/frontend/src/core/components/onboarding/slides/PlanOverviewSlide.tsx b/frontend/src/core/components/onboarding/slides/PlanOverviewSlide.tsx
index 39287c8b51..e777aeff87 100644
--- a/frontend/src/core/components/onboarding/slides/PlanOverviewSlide.tsx
+++ b/frontend/src/core/components/onboarding/slides/PlanOverviewSlide.tsx
@@ -1,36 +1,58 @@
import React from 'react';
-import { useTranslation, Trans } from 'react-i18next';
+import { useTranslation } from 'react-i18next';
import { SlideConfig, LicenseNotice } from '../../../types/types';
import { UNIFIED_CIRCLE_CONFIG } from './unifiedBackgroundConfig';
interface PlanOverviewSlideProps {
isAdmin: boolean;
licenseNotice?: LicenseNotice;
+ loginEnabled?: boolean;
}
const DEFAULT_FREE_TIER_LIMIT = 5;
-export default function PlanOverviewSlide({ isAdmin, licenseNotice }: PlanOverviewSlideProps): SlideConfig {
+export default function PlanOverviewSlide({
+ isAdmin,
+ licenseNotice,
+ loginEnabled = false,
+}: PlanOverviewSlideProps): SlideConfig {
const { t } = useTranslation();
const freeTierLimit = licenseNotice?.freeTierLimit ?? DEFAULT_FREE_TIER_LIMIT;
- const adminBody = (
-
- }}
- values={{ freeTierLimit }}
- defaults="As an admin, you can manage users, configure settings, and monitor server health. The first {{freeTierLimit}} people on your server get to use Stirling free of charge."
- />
-
- );
+ const adminBodyKey = loginEnabled
+ ? 'onboarding.planOverview.adminBodyLoginEnabled'
+ : 'onboarding.planOverview.adminBodyLoginDisabled';
+ const adminBodyTemplate = t(adminBodyKey, {
+ freeTierLimit: '{{freeTierLimit}}',
+ defaultValue: loginEnabled
+ ? 'As an admin, you can manage users, configure settings, and monitor server health. The first {{freeTierLimit}} people on your server get to use Stirling free of charge.'
+ : 'Once you enable login mode, you can manage users, configure settings, and monitor server health. The first {{freeTierLimit}} people on your server get to use Stirling free of charge.',
+ });
+
+ const renderAdminBody = () => {
+ const [before, after] = adminBodyTemplate.split('{{freeTierLimit}}');
+ if (after !== undefined) {
+ return (
+
+ {before}
+ {freeTierLimit}
+ {after}
+
+ );
+ }
+ return (
+
+ {adminBodyTemplate.replace('{{freeTierLimit}}', String(freeTierLimit))}
+
+ );
+ };
return {
key: isAdmin ? 'admin-overview' : 'plan-overview',
title: isAdmin
? t('onboarding.planOverview.adminTitle', 'Admin Overview')
: t('onboarding.planOverview.userTitle', 'Plan Overview'),
- body: isAdmin ? adminBody : (
+ body: isAdmin ? renderAdminBody() : (
{t('onboarding.planOverview.userBody', 'Invite teammates, assign roles, and keep your documents organized in one secure workspace. Enable login mode whenever you\'re ready to grow beyond solo use.')}
diff --git a/frontend/src/core/contexts/CookieConsentContext.tsx b/frontend/src/core/contexts/CookieConsentContext.tsx
index 4d46bcd013..530f99a2a3 100644
--- a/frontend/src/core/contexts/CookieConsentContext.tsx
+++ b/frontend/src/core/contexts/CookieConsentContext.tsx
@@ -13,7 +13,7 @@ const CookieConsentContext = createContext = ({ children }) => {
const { config } = useAppConfig();
- const analyticsEnabled = config?.enableAnalytics === true;
+ const analyticsEnabled = config ? config.enableAnalytics !== false : false;
const {
showCookieConsent,
showCookiePreferences,
diff --git a/frontend/src/core/pages/HomePage.tsx b/frontend/src/core/pages/HomePage.tsx
index c77e352963..77fc519086 100644
--- a/frontend/src/core/pages/HomePage.tsx
+++ b/frontend/src/core/pages/HomePage.tsx
@@ -9,6 +9,7 @@ import { useBaseUrl } from "@app/hooks/useBaseUrl";
import { useIsMobile } from "@app/hooks/useIsMobile";
import { useAppConfig } from "@app/contexts/AppConfigContext";
import { useLogoPath } from "@app/hooks/useLogoPath";
+import { useCookieConsentContext } from "@app/contexts/CookieConsentContext";
import AppsIcon from '@mui/icons-material/AppsRounded';
import ToolPanel from "@app/components/tools/ToolPanel";
@@ -45,6 +46,7 @@ export default function HomePage() {
const { openFilesModal } = useFilesModalContext();
const { colorScheme } = useMantineColorScheme();
const { config } = useAppConfig();
+ const { hasResponded: cookieConsentResponded } = useCookieConsentContext();
const isMobile = useIsMobile();
const sliderRef = useRef(null);
const [activeMobileView, setActiveMobileView] = useState("tools");
@@ -54,10 +56,10 @@ export default function HomePage() {
// Show admin analytics choice modal if analytics settings not configured
useEffect(() => {
- if (config && config.enableAnalytics === null) {
+ if (config && config.enableAnalytics === null && cookieConsentResponded) {
setShowAnalyticsModal(true);
}
- }, [config]);
+ }, [config, cookieConsentResponded]);
const brandAltText = t("home.mobile.brandAlt", "Stirling PDF logo");
const brandIconSrc = useLogoPath();
diff --git a/frontend/src/proprietary/contexts/ServerExperienceContext.tsx b/frontend/src/proprietary/contexts/ServerExperienceContext.tsx
index 272238bce7..338dc159a4 100644
--- a/frontend/src/proprietary/contexts/ServerExperienceContext.tsx
+++ b/frontend/src/proprietary/contexts/ServerExperienceContext.tsx
@@ -158,6 +158,15 @@ export function ServerExperienceProvider({ children }: { children: ReactNode })
return () => window.removeEventListener('storage', handleStorage);
}, []);
+ useEffect(() => {
+ if (!config) {
+ return;
+ }
+ if (config.isNewServer && !loginEnabled && !selfReportedAdmin) {
+ setSelfReportedAdmin(true);
+ }
+ }, [config, loginEnabled, selfReportedAdmin, setSelfReportedAdmin]);
+
const fetchUserCounts = useCallback(async () => {
if (!config) {
return;