From 17e038e2061c3b22dfc546b608fc1f98aed9d5a4 Mon Sep 17 00:00:00 2001 From: James Brunton Date: Fri, 14 Nov 2025 17:27:49 +0000 Subject: [PATCH] Add first run UI and fix errors --- .../public/locales/en-GB/translation.json | 134 ++++++++++++++++++ .../src/desktop/components/AppProviders.tsx | 30 ++++ .../components/SetupWizard/ModeSelection.tsx | 7 +- .../desktop/components/SetupWizard/index.tsx | 2 +- .../shared/config/configNavSections.tsx | 30 ++++ .../desktop/components/shared/config/types.ts | 8 ++ 6 files changed, 207 insertions(+), 4 deletions(-) create mode 100644 frontend/src/desktop/components/shared/config/configNavSections.tsx create mode 100644 frontend/src/desktop/components/shared/config/types.ts diff --git a/frontend/public/locales/en-GB/translation.json b/frontend/public/locales/en-GB/translation.json index 8c93539c6..c1671bc90 100644 --- a/frontend/public/locales/en-GB/translation.json +++ b/frontend/public/locales/en-GB/translation.json @@ -5281,5 +5281,139 @@ "offline": "Backend Offline", "starting": "Backend starting up...", "wait": "Please wait for the backend to finish launching and try again." + }, + "setup": { + "welcome": "Welcome to Stirling PDF", + "description": "Get started by choosing how you want to use Stirling PDF", + "step1": { + "label": "Choose Mode", + "description": "Offline or Server" + }, + "step2": { + "label": "Select Server", + "description": "SaaS or Self-hosted" + }, + "step3": { + "label": "Login", + "description": "Enter credentials" + }, + "mode": { + "offline": { + "title": "Use Offline", + "description": "Run locally without an internet connection" + }, + "server": { + "title": "Connect to Server", + "description": "Connect to a remote Stirling PDF server" + } + }, + "server": { + "type": { + "saas": "Stirling PDF SaaS (stirling.com/app)", + "selfhosted": "Self-hosted server" + }, + "url": { + "label": "Server URL", + "description": "Enter the full URL of your self-hosted Stirling PDF server" + }, + "error": { + "emptyUrl": "Please enter a server URL", + "unreachable": "Could not connect to server", + "testFailed": "Connection test failed" + }, + "testing": "Testing connection..." + }, + "login": { + "connectingTo": "Connecting to:", + "username": { + "label": "Username", + "placeholder": "Enter your username" + }, + "password": { + "label": "Password", + "placeholder": "Enter your password" + }, + "error": { + "emptyUsername": "Please enter your username", + "emptyPassword": "Please enter your password" + }, + "submit": "Login" + } + }, + "settings": { + "connection": { + "title": "Connection Mode", + "mode": { + "offline": "Offline", + "server": "Server" + }, + "server": "Server", + "user": "Logged in as", + "switchToServer": "Connect to Server", + "switchToOffline": "Switch to Offline", + "logout": "Logout", + "selectServer": "Select Server", + "login": "Login" + }, + "general": { + "title": "General", + "description": "Configure general application preferences.", + "user": "User", + "logout": "Log out", + "enableFeatures": { + "dismiss": "Dismiss", + "title": "For System Administrators", + "intro": "Enable user authentication, team management, and workspace features for your organisation.", + "action": "Configure", + "and": "and", + "benefit": "Enables user roles, team collaboration, admin controls, and enterprise features.", + "learnMore": "Learn more in documentation" + }, + "defaultToolPickerMode": "Default tool picker mode", + "defaultToolPickerModeDescription": "Choose whether the tool picker opens in fullscreen or sidebar by default", + "mode": { + "sidebar": "Sidebar", + "fullscreen": "Fullscreen" + }, + "autoUnzipTooltip": "Automatically extract ZIP files returned from API operations. Disable to keep ZIP files intact. This does not affect automation workflows.", + "autoUnzip": "Auto-unzip API responses", + "autoUnzipDescription": "Automatically extract files from ZIP responses", + "autoUnzipFileLimitTooltip": "Only unzip if the ZIP contains this many files or fewer. Set higher to extract larger ZIPs.", + "autoUnzipFileLimit": "Auto-unzip file limit", + "autoUnzipFileLimitDescription": "Maximum number of files to extract from ZIP" + }, + "hotkeys": { + "errorConflict": "Shortcut already used by {{tool}}.", + "searchPlaceholder": "Search tools...", + "none": "Not assigned", + "customBadge": "Custom", + "defaultLabel": "Default: {{shortcut}}", + "capturing": "Press keys… (Esc to cancel)", + "change": "Change shortcut", + "reset": "Reset", + "shortcut": "Shortcut", + "noShortcut": "No shortcut set" + } + }, + "auth": { + "sessionExpired": "Session Expired", + "pleaseLoginAgain": "Please login again.", + "accessDenied": "Access Denied", + "insufficientPermissions": "You do not have permission to perform this action." + }, + "common": { + "loading": "Loading...", + "back": "Back", + "continue": "Continue", + "preview": "Preview", + "previous": "Previous", + "next": "Next", + "copied": "Copied!", + "copy": "Copy", + "expand": "Expand", + "collapse": "Collapse", + "retry": "Retry", + "refresh": "Refresh", + "cancel": "Cancel" } } diff --git a/frontend/src/desktop/components/AppProviders.tsx b/frontend/src/desktop/components/AppProviders.tsx index c04ede4f9..7e1bebdf9 100644 --- a/frontend/src/desktop/components/AppProviders.tsx +++ b/frontend/src/desktop/components/AppProviders.tsx @@ -1,14 +1,44 @@ import { ReactNode } from "react"; import { AppProviders as ProprietaryAppProviders } from "@proprietary/components/AppProviders"; import { DesktopConfigSync } from '@app/components/DesktopConfigSync'; +import { SetupWizard } from '@app/components/SetupWizard'; +import { useAppInitialization } from '@app/hooks/useAppInitialization'; import { DESKTOP_DEFAULT_APP_CONFIG } from '@app/config/defaultAppConfig'; /** * Desktop application providers * Wraps proprietary providers and adds desktop-specific configuration * - Enables retry logic for app config (needed for Tauri mode when backend is starting) + * - Shows setup wizard on first launch */ export function AppProviders({ children }: { children: ReactNode }) { + const { isFirstLaunch, setupComplete } = useAppInitialization(); + + // Show setup wizard on first launch + if (isFirstLaunch && !setupComplete) { + return ( + + { + // Reload the page to reinitialize with new connection config + window.location.reload(); + }} + /> + + ); + } + + // Normal app flow return ( void; @@ -18,7 +19,7 @@ export const ModeSelection: React.FC = ({ onSelect, loading variant="light" onClick={() => onSelect('offline')} disabled={loading} - leftSection={} + leftSection={} >
{t('setup.mode.offline.title', 'Use Offline')} @@ -33,7 +34,7 @@ export const ModeSelection: React.FC = ({ onSelect, loading variant="light" onClick={() => onSelect('server')} disabled={loading} - leftSection={} + leftSection={} >
{t('setup.mode.server.title', 'Connect to Server')} diff --git a/frontend/src/desktop/components/SetupWizard/index.tsx b/frontend/src/desktop/components/SetupWizard/index.tsx index c0fe2f019..3c7976262 100644 --- a/frontend/src/desktop/components/SetupWizard/index.tsx +++ b/frontend/src/desktop/components/SetupWizard/index.tsx @@ -117,7 +117,7 @@ export const SetupWizard: React.FC = ({ onComplete }) => { {t('setup.description', 'Get started by choosing how you want to use Stirling PDF')} - + { + // Get the proprietary sections (includes core Preferences + admin sections) + const sections = createProprietaryConfigNavSections(isAdmin, runningEE, loginEnabled); + + // Add Connection section at the beginning (after Preferences) + sections.splice(1, 0, { + title: 'Connection', + items: [ + { + key: 'connectionMode', + label: 'Connection Mode', + icon: 'cloud-rounded', + component: , + }, + ], + }); + + return sections; +}; diff --git a/frontend/src/desktop/components/shared/config/types.ts b/frontend/src/desktop/components/shared/config/types.ts new file mode 100644 index 000000000..d22056e01 --- /dev/null +++ b/frontend/src/desktop/components/shared/config/types.ts @@ -0,0 +1,8 @@ +import { VALID_NAV_KEYS as CORE_NAV_KEYS } from '@core/components/shared/config/types'; + +export const VALID_NAV_KEYS = [ + ...CORE_NAV_KEYS, + 'connectionMode', +] + +export type NavKey = typeof VALID_NAV_KEYS[number];