mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2026-02-01 20:10:35 +01:00
Remove "Download for Desktop" and "Security Check" slides from desktop app onboarding (#5012)
# Description of Changes - Title^ + I changed the text for set as default to make it more visible --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing) for more details. --------- Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com> Co-authored-by: Connor Yoh <connor@stirlingpdf.com>
This commit is contained in:
parent
f8386843d4
commit
d74856f675
@ -71,8 +71,10 @@ export default function ServerLicenseModal({
|
||||
slideKey={slide.key}
|
||||
/>
|
||||
<div className={styles.heroLogo}>
|
||||
<div className={styles.heroLogoCircle}>
|
||||
<img src={`${BASE_PATH}/branding/StirlingPDFLogoNoTextLightHC.svg`} alt="Stirling logo" />
|
||||
<div className={styles.heroIconsContainer}>
|
||||
<div className={styles.iconWrapper}>
|
||||
<img src={`${BASE_PATH}/modern-logo/logo512.png`} alt="Stirling icon" className={styles.downloadIcon} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -175,7 +175,7 @@ export const SLIDE_DEFINITIONS: Record<SlideId, SlideDefinition> = {
|
||||
'server-license': {
|
||||
id: 'server-license',
|
||||
createSlide: ({ licenseNotice }) => ServerLicenseSlide({ licenseNotice }),
|
||||
hero: { type: 'logo' },
|
||||
hero: { type: 'dual-icon' },
|
||||
buttons: [
|
||||
{
|
||||
key: 'license-close',
|
||||
|
||||
@ -130,6 +130,11 @@ export const InfoBanner: React.FC<InfoBannerProps> = ({
|
||||
onClick={onButtonClick}
|
||||
loading={loading}
|
||||
leftSection={<LocalIcon icon={buttonIcon} width="0.9rem" height="0.9rem" />}
|
||||
styles={{
|
||||
label: {
|
||||
color: textColor ?? toneStyle.text,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{buttonText}
|
||||
</Button>
|
||||
|
||||
@ -0,0 +1,390 @@
|
||||
/**
|
||||
* Desktop override of useInitialOnboardingState.
|
||||
*
|
||||
* Key difference: Handles the simplified onboarding flow where desktop-install
|
||||
* and security-check slides are removed. When welcome is the last slide,
|
||||
* clicking Next will launch the tools tour instead of doing nothing.
|
||||
*/
|
||||
|
||||
import { useCallback, useEffect, useMemo, useState, useRef } from 'react';
|
||||
import { usePreferences } from '@app/contexts/PreferencesContext';
|
||||
import { useOnboarding } from '@app/contexts/OnboardingContext';
|
||||
import { useOs } from '@app/hooks/useOs';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import {
|
||||
SLIDE_DEFINITIONS,
|
||||
type ButtonAction,
|
||||
type FlowState,
|
||||
type SlideId,
|
||||
} from '@app/components/onboarding/onboardingFlowConfig';
|
||||
import type { LicenseNotice } from '@app/types/types';
|
||||
import { resolveFlow } from '@app/components/onboarding/InitialOnboardingModal/flowResolver';
|
||||
import { useServerExperience } from '@app/hooks/useServerExperience';
|
||||
import { DEFAULT_STATE, type InitialOnboardingModalProps, type OnboardingState } from '@app/components/onboarding/InitialOnboardingModal/types';
|
||||
import { DOWNLOAD_URLS } from '@app/constants/downloads';
|
||||
|
||||
interface UseInitialOnboardingStateResult {
|
||||
state: OnboardingState;
|
||||
totalSteps: number;
|
||||
slideDefinition: (typeof SLIDE_DEFINITIONS)[SlideId];
|
||||
currentSlide: ReturnType<(typeof SLIDE_DEFINITIONS)[SlideId]['createSlide']>;
|
||||
licenseNotice: LicenseNotice;
|
||||
flowState: FlowState;
|
||||
closeAndMarkSeen: () => void;
|
||||
handleButtonAction: (action: ButtonAction) => void;
|
||||
}
|
||||
|
||||
export function useInitialOnboardingState({
|
||||
opened,
|
||||
onClose,
|
||||
onRequestServerLicense,
|
||||
onLicenseNoticeUpdate,
|
||||
}: InitialOnboardingModalProps): UseInitialOnboardingStateResult | null {
|
||||
const { preferences, updatePreference } = usePreferences();
|
||||
const { startTour } = useOnboarding();
|
||||
const {
|
||||
loginEnabled: loginEnabledFromServer,
|
||||
configIsAdmin,
|
||||
totalUsers: serverTotalUsers,
|
||||
userCountResolved: serverUserCountResolved,
|
||||
freeTierLimit,
|
||||
hasPaidLicense,
|
||||
scenarioKey,
|
||||
setSelfReportedAdmin,
|
||||
isNewServer,
|
||||
} = useServerExperience();
|
||||
const osType = useOs();
|
||||
const navigate = useNavigate();
|
||||
const selectedDownloadUrlRef = useRef<string>('');
|
||||
|
||||
const [state, setState] = useState<OnboardingState>(DEFAULT_STATE);
|
||||
|
||||
const resetState = useCallback(() => {
|
||||
setState(DEFAULT_STATE);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!opened) {
|
||||
resetState();
|
||||
}
|
||||
}, [opened, resetState]);
|
||||
|
||||
const handleRoleSelect = useCallback(
|
||||
(role: 'admin' | 'user' | null) => {
|
||||
const isAdminSelection = role === 'admin';
|
||||
setState((prev) => ({
|
||||
...prev,
|
||||
selectedRole: role,
|
||||
selfReportedAdmin: isAdminSelection,
|
||||
}));
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
if (isAdminSelection) {
|
||||
window.localStorage.setItem('stirling-self-reported-admin', 'true');
|
||||
} else {
|
||||
window.localStorage.removeItem('stirling-self-reported-admin');
|
||||
}
|
||||
}
|
||||
|
||||
setSelfReportedAdmin(isAdminSelection);
|
||||
},
|
||||
[setSelfReportedAdmin],
|
||||
);
|
||||
|
||||
const closeAndMarkSeen = useCallback(() => {
|
||||
if (!preferences.hasSeenIntroOnboarding) {
|
||||
updatePreference('hasSeenIntroOnboarding', true);
|
||||
}
|
||||
onClose();
|
||||
}, [onClose, preferences.hasSeenIntroOnboarding, updatePreference]);
|
||||
|
||||
const isAdmin = configIsAdmin;
|
||||
const enableLogin = loginEnabledFromServer;
|
||||
|
||||
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;
|
||||
const licenseUserCountFromServer =
|
||||
shouldUseServerCount && serverUserCountResolved ? serverTotalUsers : null;
|
||||
|
||||
const effectiveLicenseUserCount = licenseUserCountFromServer ?? null;
|
||||
|
||||
const os = useMemo(() => {
|
||||
switch (osType) {
|
||||
case 'windows':
|
||||
return { label: 'Windows', url: DOWNLOAD_URLS.WINDOWS };
|
||||
case 'mac-apple':
|
||||
return { label: 'Mac (Apple Silicon)', url: DOWNLOAD_URLS.MAC_APPLE_SILICON };
|
||||
case 'mac-intel':
|
||||
return { label: 'Mac (Intel)', url: DOWNLOAD_URLS.MAC_INTEL };
|
||||
case 'linux-x64':
|
||||
case 'linux-arm64':
|
||||
return { label: 'Linux', url: DOWNLOAD_URLS.LINUX_DOCS };
|
||||
default:
|
||||
return { label: '', url: '' };
|
||||
}
|
||||
}, [osType]);
|
||||
|
||||
const osOptions = useMemo(() => {
|
||||
const options = [
|
||||
{ label: 'Windows', url: DOWNLOAD_URLS.WINDOWS, value: 'windows' },
|
||||
{ label: 'Mac (Apple Silicon)', url: DOWNLOAD_URLS.MAC_APPLE_SILICON, value: 'mac-apple' },
|
||||
{ label: 'Mac (Intel)', url: DOWNLOAD_URLS.MAC_INTEL, value: 'mac-intel' },
|
||||
{ label: 'Linux', url: DOWNLOAD_URLS.LINUX_DOCS, value: 'linux' },
|
||||
];
|
||||
return options.filter(opt => opt.url);
|
||||
}, []);
|
||||
|
||||
const resolvedFlow = useMemo(
|
||||
() => resolveFlow(effectiveEnableLogin, effectiveIsAdmin, state.selfReportedAdmin),
|
||||
[effectiveEnableLogin, effectiveIsAdmin, state.selfReportedAdmin],
|
||||
);
|
||||
// Desktop: No security-check slide, so no need to filter it out
|
||||
const flowSlideIds = resolvedFlow.ids;
|
||||
const flowType = resolvedFlow.type;
|
||||
const totalSteps = flowSlideIds.length;
|
||||
const maxIndex = Math.max(totalSteps - 1, 0);
|
||||
|
||||
useEffect(() => {
|
||||
if (state.step >= flowSlideIds.length) {
|
||||
setState((prev) => ({
|
||||
...prev,
|
||||
step: Math.max(flowSlideIds.length - 1, 0),
|
||||
}));
|
||||
}
|
||||
}, [flowSlideIds.length, state.step]);
|
||||
|
||||
const currentSlideId = flowSlideIds[state.step] ?? flowSlideIds[flowSlideIds.length - 1];
|
||||
const slideDefinition = SLIDE_DEFINITIONS[currentSlideId];
|
||||
|
||||
if (!slideDefinition) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const scenarioProvidesInfo =
|
||||
scenarioKey && scenarioKey !== 'unknown' && scenarioKey !== 'licensed';
|
||||
const scenarioIndicatesAdmin = scenarioProvidesInfo
|
||||
? scenarioKey!.includes('admin')
|
||||
: state.selfReportedAdmin || effectiveIsAdmin;
|
||||
const scenarioIndicatesOverLimit = scenarioProvidesInfo
|
||||
? scenarioKey!.includes('over-limit')
|
||||
: effectiveLicenseUserCount != null && effectiveLicenseUserCount > freeTierLimit;
|
||||
const scenarioRequiresLicense =
|
||||
scenarioKey === 'licensed' ? false : scenarioKey === 'unknown' ? !hasPaidLicense : true;
|
||||
|
||||
const shouldShowServerLicenseInfo = scenarioIndicatesAdmin && scenarioRequiresLicense;
|
||||
|
||||
const licenseNotice = useMemo<LicenseNotice>(
|
||||
() => ({
|
||||
totalUsers: effectiveLicenseUserCount,
|
||||
freeTierLimit,
|
||||
isOverLimit: scenarioIndicatesOverLimit,
|
||||
requiresLicense: shouldShowServerLicenseInfo,
|
||||
}),
|
||||
[
|
||||
effectiveLicenseUserCount,
|
||||
freeTierLimit,
|
||||
scenarioIndicatesOverLimit,
|
||||
shouldShowServerLicenseInfo,
|
||||
],
|
||||
);
|
||||
|
||||
const requestServerLicenseIfNeeded = useCallback(
|
||||
(options?: { deferUntilTourComplete?: boolean; selfReportedAdmin?: boolean }) => {
|
||||
if (!shouldShowServerLicenseInfo) {
|
||||
return;
|
||||
}
|
||||
onRequestServerLicense?.(options);
|
||||
},
|
||||
[onRequestServerLicense, shouldShowServerLicenseInfo],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
onLicenseNoticeUpdate?.(licenseNotice);
|
||||
}, [licenseNotice, onLicenseNoticeUpdate]);
|
||||
|
||||
// Initialize ref with default URL
|
||||
useEffect(() => {
|
||||
if (!selectedDownloadUrlRef.current && os.url) {
|
||||
selectedDownloadUrlRef.current = os.url;
|
||||
}
|
||||
}, [os.url]);
|
||||
|
||||
const handleDownloadUrlChange = useCallback((url: string) => {
|
||||
selectedDownloadUrlRef.current = url;
|
||||
}, []);
|
||||
|
||||
const currentSlide = slideDefinition.createSlide({
|
||||
osLabel: os.label,
|
||||
osUrl: os.url,
|
||||
osOptions,
|
||||
onDownloadUrlChange: handleDownloadUrlChange,
|
||||
selectedRole: state.selectedRole,
|
||||
onRoleSelect: handleRoleSelect,
|
||||
licenseNotice,
|
||||
loginEnabled: effectiveEnableLogin,
|
||||
});
|
||||
|
||||
const goNext = useCallback(() => {
|
||||
setState((prev) => ({
|
||||
...prev,
|
||||
step: Math.min(prev.step + 1, maxIndex),
|
||||
}));
|
||||
}, [maxIndex]);
|
||||
|
||||
const goPrev = useCallback(() => {
|
||||
setState((prev) => ({
|
||||
...prev,
|
||||
step: Math.max(prev.step - 1, 0),
|
||||
}));
|
||||
}, []);
|
||||
|
||||
const launchTour = useCallback(
|
||||
(mode: 'admin' | 'tools', options?: { closeOnboardingSlides?: boolean }) => {
|
||||
if (options?.closeOnboardingSlides) {
|
||||
closeAndMarkSeen();
|
||||
}
|
||||
|
||||
startTour(mode, {
|
||||
source: 'initial-onboarding-modal',
|
||||
metadata: {
|
||||
hasCompletedOnboarding: preferences.hasCompletedOnboarding,
|
||||
toolPanelModePromptSeen: preferences.toolPanelModePromptSeen,
|
||||
selfReportedAdmin: state.selfReportedAdmin,
|
||||
},
|
||||
});
|
||||
},
|
||||
[closeAndMarkSeen, preferences.hasCompletedOnboarding, preferences.toolPanelModePromptSeen, startTour, state.selfReportedAdmin],
|
||||
);
|
||||
|
||||
const handleButtonAction = useCallback(
|
||||
(action: ButtonAction) => {
|
||||
const isOnLastSlide = state.step >= maxIndex;
|
||||
|
||||
// Desktop: For login-user and no-login flows, when on the last slide (welcome),
|
||||
// clicking Next should launch the tools tour
|
||||
const shouldAutoLaunchToolsTour =
|
||||
(flowType === 'login-user' || flowType === 'no-login') && isOnLastSlide;
|
||||
|
||||
switch (action) {
|
||||
case 'next':
|
||||
if (shouldAutoLaunchToolsTour) {
|
||||
launchTour('tools', { closeOnboardingSlides: true });
|
||||
return;
|
||||
}
|
||||
goNext();
|
||||
return;
|
||||
case 'prev':
|
||||
goPrev();
|
||||
return;
|
||||
case 'close':
|
||||
closeAndMarkSeen();
|
||||
return;
|
||||
case 'download-selected': {
|
||||
const downloadUrl = selectedDownloadUrlRef.current || os.url || currentSlide.downloadUrl;
|
||||
if (downloadUrl) {
|
||||
window.open(downloadUrl, '_blank', 'noopener');
|
||||
}
|
||||
if (shouldAutoLaunchToolsTour) {
|
||||
launchTour('tools', { closeOnboardingSlides: true });
|
||||
return;
|
||||
}
|
||||
goNext();
|
||||
return;
|
||||
}
|
||||
case 'complete-close':
|
||||
updatePreference('hasCompletedOnboarding', true);
|
||||
closeAndMarkSeen();
|
||||
return;
|
||||
case 'security-next':
|
||||
// Desktop: security-check slide is removed, but keep this for completeness
|
||||
if (!state.selectedRole) {
|
||||
return;
|
||||
}
|
||||
if (state.selectedRole === 'admin') {
|
||||
goNext();
|
||||
} else {
|
||||
launchTour('tools', { closeOnboardingSlides: true });
|
||||
}
|
||||
return;
|
||||
case 'launch-admin':
|
||||
requestServerLicenseIfNeeded({
|
||||
deferUntilTourComplete: true,
|
||||
selfReportedAdmin: state.selfReportedAdmin || effectiveIsAdmin,
|
||||
});
|
||||
launchTour('admin', { closeOnboardingSlides: true });
|
||||
return;
|
||||
case 'launch-tools':
|
||||
launchTour('tools', { closeOnboardingSlides: true });
|
||||
return;
|
||||
case 'launch-auto': {
|
||||
const launchMode = state.selfReportedAdmin || effectiveIsAdmin ? 'admin' : 'tools';
|
||||
if (launchMode === 'admin') {
|
||||
requestServerLicenseIfNeeded({
|
||||
deferUntilTourComplete: true,
|
||||
selfReportedAdmin: state.selfReportedAdmin || effectiveIsAdmin,
|
||||
});
|
||||
}
|
||||
launchTour(launchMode, { closeOnboardingSlides: true });
|
||||
return;
|
||||
}
|
||||
case 'skip-to-license':
|
||||
updatePreference('hasCompletedOnboarding', true);
|
||||
requestServerLicenseIfNeeded({
|
||||
deferUntilTourComplete: false,
|
||||
selfReportedAdmin: state.selfReportedAdmin || effectiveIsAdmin,
|
||||
});
|
||||
closeAndMarkSeen();
|
||||
return;
|
||||
case 'see-plans':
|
||||
closeAndMarkSeen();
|
||||
navigate('/settings/adminPlan');
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
},
|
||||
[
|
||||
closeAndMarkSeen,
|
||||
currentSlide,
|
||||
currentSlideId,
|
||||
effectiveIsAdmin,
|
||||
flowType,
|
||||
goNext,
|
||||
goPrev,
|
||||
launchTour,
|
||||
maxIndex,
|
||||
navigate,
|
||||
requestServerLicenseIfNeeded,
|
||||
onRequestServerLicense,
|
||||
os.url,
|
||||
state.selectedRole,
|
||||
state.selfReportedAdmin,
|
||||
state.step,
|
||||
updatePreference,
|
||||
],
|
||||
);
|
||||
|
||||
const flowState: FlowState = { selectedRole: state.selectedRole };
|
||||
|
||||
return {
|
||||
state,
|
||||
totalSteps,
|
||||
slideDefinition,
|
||||
currentSlide,
|
||||
licenseNotice,
|
||||
flowState,
|
||||
closeAndMarkSeen,
|
||||
handleButtonAction,
|
||||
};
|
||||
}
|
||||
|
||||
@ -0,0 +1,178 @@
|
||||
/**
|
||||
* Desktop override of onboarding flow config.
|
||||
*
|
||||
* This version removes the desktop-install and security-check slides
|
||||
* since they're not relevant when running as a desktop app.
|
||||
*
|
||||
* The SlideId type still includes all values for type compatibility,
|
||||
* but the actual FLOW_SEQUENCES don't use these slides.
|
||||
*/
|
||||
|
||||
import WelcomeSlide from '@app/components/onboarding/slides/WelcomeSlide';
|
||||
import PlanOverviewSlide from '@app/components/onboarding/slides/PlanOverviewSlide';
|
||||
import ServerLicenseSlide from '@app/components/onboarding/slides/ServerLicenseSlide';
|
||||
import { SlideConfig, LicenseNotice } from '@app/types/types';
|
||||
|
||||
// Keep the full type for compatibility, but these slides won't be used
|
||||
export type SlideId =
|
||||
| 'welcome'
|
||||
| 'desktop-install'
|
||||
| 'security-check'
|
||||
| 'admin-overview'
|
||||
| 'server-license';
|
||||
|
||||
export type HeroType = 'rocket' | 'dual-icon' | 'shield' | 'diamond' | 'logo';
|
||||
|
||||
export type ButtonAction =
|
||||
| 'next'
|
||||
| 'prev'
|
||||
| 'close'
|
||||
| 'complete-close'
|
||||
| 'download-selected'
|
||||
| 'security-next'
|
||||
| 'launch-admin'
|
||||
| 'launch-tools'
|
||||
| 'launch-auto'
|
||||
| 'see-plans'
|
||||
| 'skip-to-license';
|
||||
|
||||
export interface FlowState {
|
||||
selectedRole: 'admin' | 'user' | null;
|
||||
}
|
||||
|
||||
export interface OSOption {
|
||||
label: string;
|
||||
url: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface SlideFactoryParams {
|
||||
osLabel: string;
|
||||
osUrl: string;
|
||||
osOptions?: OSOption[];
|
||||
onDownloadUrlChange?: (url: string) => void;
|
||||
selectedRole: 'admin' | 'user' | null;
|
||||
onRoleSelect: (role: 'admin' | 'user' | null) => void;
|
||||
licenseNotice?: LicenseNotice;
|
||||
loginEnabled?: boolean;
|
||||
}
|
||||
|
||||
export interface HeroDefinition {
|
||||
type: HeroType;
|
||||
}
|
||||
|
||||
export interface ButtonDefinition {
|
||||
key: string;
|
||||
type: 'button' | 'icon';
|
||||
label?: string;
|
||||
icon?: 'chevron-left';
|
||||
variant?: 'primary' | 'secondary' | 'default';
|
||||
group: 'left' | 'right';
|
||||
action: ButtonAction;
|
||||
disabledWhen?: (state: FlowState) => boolean;
|
||||
}
|
||||
|
||||
export interface SlideDefinition {
|
||||
id: SlideId;
|
||||
createSlide: (params: SlideFactoryParams) => SlideConfig;
|
||||
hero: HeroDefinition;
|
||||
buttons: ButtonDefinition[];
|
||||
}
|
||||
|
||||
export const SLIDE_DEFINITIONS: Record<SlideId, SlideDefinition> = {
|
||||
'welcome': {
|
||||
id: 'welcome',
|
||||
createSlide: () => WelcomeSlide(),
|
||||
hero: { type: 'rocket' },
|
||||
buttons: [
|
||||
{
|
||||
key: 'welcome-next',
|
||||
type: 'button',
|
||||
label: 'onboarding.buttons.next',
|
||||
variant: 'primary',
|
||||
group: 'right',
|
||||
action: 'next',
|
||||
},
|
||||
],
|
||||
},
|
||||
// Stub definitions for desktop-install and security-check - not used on desktop
|
||||
// but kept for type compatibility with core code
|
||||
'desktop-install': {
|
||||
id: 'desktop-install',
|
||||
createSlide: () => WelcomeSlide(), // Placeholder - never used
|
||||
hero: { type: 'dual-icon' },
|
||||
buttons: [],
|
||||
},
|
||||
'security-check': {
|
||||
id: 'security-check',
|
||||
createSlide: () => WelcomeSlide(), // Placeholder - never used
|
||||
hero: { type: 'shield' },
|
||||
buttons: [],
|
||||
},
|
||||
'admin-overview': {
|
||||
id: 'admin-overview',
|
||||
createSlide: ({ licenseNotice, loginEnabled }) =>
|
||||
PlanOverviewSlide({ isAdmin: true, licenseNotice, loginEnabled }),
|
||||
hero: { type: 'diamond' },
|
||||
buttons: [
|
||||
{
|
||||
key: 'admin-back',
|
||||
type: 'icon',
|
||||
icon: 'chevron-left',
|
||||
group: 'left',
|
||||
action: 'prev',
|
||||
},
|
||||
{
|
||||
key: 'admin-show',
|
||||
type: 'button',
|
||||
label: 'onboarding.buttons.showMeAround',
|
||||
variant: 'primary',
|
||||
group: 'right',
|
||||
action: 'launch-admin',
|
||||
},
|
||||
{
|
||||
key: 'admin-skip',
|
||||
type: 'button',
|
||||
label: 'onboarding.buttons.skipTheTour',
|
||||
variant: 'secondary',
|
||||
group: 'left',
|
||||
action: 'skip-to-license',
|
||||
},
|
||||
],
|
||||
},
|
||||
'server-license': {
|
||||
id: 'server-license',
|
||||
createSlide: ({ licenseNotice }) => ServerLicenseSlide({ licenseNotice }),
|
||||
hero: { type: 'dual-icon' },
|
||||
buttons: [
|
||||
{
|
||||
key: 'license-close',
|
||||
type: 'button',
|
||||
label: 'onboarding.buttons.skipForNow',
|
||||
variant: 'secondary',
|
||||
group: 'left',
|
||||
action: 'close',
|
||||
},
|
||||
{
|
||||
key: 'license-see-plans',
|
||||
type: 'button',
|
||||
label: 'onboarding.serverLicense.seePlans',
|
||||
variant: 'primary',
|
||||
group: 'right',
|
||||
action: 'see-plans',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Desktop flow sequences - simplified without desktop-install and security-check slides
|
||||
* since users are already on desktop and security check is not needed.
|
||||
*/
|
||||
export const FLOW_SEQUENCES = {
|
||||
loginAdmin: ['welcome', 'admin-overview'] as SlideId[],
|
||||
loginUser: ['welcome'] as SlideId[],
|
||||
noLoginBase: ['welcome'] as SlideId[],
|
||||
noLoginAdmin: ['admin-overview'] as SlideId[],
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user