mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-11-16 01:21:16 +01:00
Add first run UI and fix errors
This commit is contained in:
parent
fb704cc901
commit
17e038e206
@ -5281,5 +5281,139 @@
|
|||||||
"offline": "Backend Offline",
|
"offline": "Backend Offline",
|
||||||
"starting": "Backend starting up...",
|
"starting": "Backend starting up...",
|
||||||
"wait": "Please wait for the backend to finish launching and try again."
|
"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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,44 @@
|
|||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
import { AppProviders as ProprietaryAppProviders } from "@proprietary/components/AppProviders";
|
import { AppProviders as ProprietaryAppProviders } from "@proprietary/components/AppProviders";
|
||||||
import { DesktopConfigSync } from '@app/components/DesktopConfigSync';
|
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';
|
import { DESKTOP_DEFAULT_APP_CONFIG } from '@app/config/defaultAppConfig';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Desktop application providers
|
* Desktop application providers
|
||||||
* Wraps proprietary providers and adds desktop-specific configuration
|
* Wraps proprietary providers and adds desktop-specific configuration
|
||||||
* - Enables retry logic for app config (needed for Tauri mode when backend is starting)
|
* - 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 }) {
|
export function AppProviders({ children }: { children: ReactNode }) {
|
||||||
|
const { isFirstLaunch, setupComplete } = useAppInitialization();
|
||||||
|
|
||||||
|
// Show setup wizard on first launch
|
||||||
|
if (isFirstLaunch && !setupComplete) {
|
||||||
|
return (
|
||||||
|
<ProprietaryAppProviders
|
||||||
|
appConfigRetryOptions={{
|
||||||
|
maxRetries: 5,
|
||||||
|
initialDelay: 1000,
|
||||||
|
}}
|
||||||
|
appConfigProviderProps={{
|
||||||
|
initialConfig: DESKTOP_DEFAULT_APP_CONFIG,
|
||||||
|
bootstrapMode: 'non-blocking',
|
||||||
|
autoFetch: false,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SetupWizard
|
||||||
|
onComplete={() => {
|
||||||
|
// Reload the page to reinitialize with new connection config
|
||||||
|
window.location.reload();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ProprietaryAppProviders>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normal app flow
|
||||||
return (
|
return (
|
||||||
<ProprietaryAppProviders
|
<ProprietaryAppProviders
|
||||||
appConfigRetryOptions={{
|
appConfigRetryOptions={{
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Stack, Button, Text } from '@mantine/core';
|
import { Stack, Button, Text } from '@mantine/core';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { IconCloud, IconDeviceDesktop } from '@tabler/icons-react';
|
import CloudIcon from '@mui/icons-material/Cloud';
|
||||||
|
import ComputerIcon from '@mui/icons-material/Computer';
|
||||||
|
|
||||||
interface ModeSelectionProps {
|
interface ModeSelectionProps {
|
||||||
onSelect: (mode: 'offline' | 'server') => void;
|
onSelect: (mode: 'offline' | 'server') => void;
|
||||||
@ -18,7 +19,7 @@ export const ModeSelection: React.FC<ModeSelectionProps> = ({ onSelect, loading
|
|||||||
variant="light"
|
variant="light"
|
||||||
onClick={() => onSelect('offline')}
|
onClick={() => onSelect('offline')}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
leftSection={<IconDeviceDesktop size={24} />}
|
leftSection={<ComputerIcon />}
|
||||||
>
|
>
|
||||||
<div style={{ textAlign: 'left', flex: 1 }}>
|
<div style={{ textAlign: 'left', flex: 1 }}>
|
||||||
<Text fw={600}>{t('setup.mode.offline.title', 'Use Offline')}</Text>
|
<Text fw={600}>{t('setup.mode.offline.title', 'Use Offline')}</Text>
|
||||||
@ -33,7 +34,7 @@ export const ModeSelection: React.FC<ModeSelectionProps> = ({ onSelect, loading
|
|||||||
variant="light"
|
variant="light"
|
||||||
onClick={() => onSelect('server')}
|
onClick={() => onSelect('server')}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
leftSection={<IconCloud size={24} />}
|
leftSection={<CloudIcon />}
|
||||||
>
|
>
|
||||||
<div style={{ textAlign: 'left', flex: 1 }}>
|
<div style={{ textAlign: 'left', flex: 1 }}>
|
||||||
<Text fw={600}>{t('setup.mode.server.title', 'Connect to Server')}</Text>
|
<Text fw={600}>{t('setup.mode.server.title', 'Connect to Server')}</Text>
|
||||||
|
|||||||
@ -117,7 +117,7 @@ export const SetupWizard: React.FC<SetupWizardProps> = ({ onComplete }) => {
|
|||||||
{t('setup.description', 'Get started by choosing how you want to use Stirling PDF')}
|
{t('setup.description', 'Get started by choosing how you want to use Stirling PDF')}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Stepper active={activeStep} breakpoint="sm">
|
<Stepper active={activeStep}>
|
||||||
<Stepper.Step
|
<Stepper.Step
|
||||||
label={t('setup.step1.label', 'Choose Mode')}
|
label={t('setup.step1.label', 'Choose Mode')}
|
||||||
description={t('setup.step1.description', 'Offline or Server')}
|
description={t('setup.step1.description', 'Offline or Server')}
|
||||||
|
|||||||
@ -0,0 +1,30 @@
|
|||||||
|
import { createConfigNavSections as createProprietaryConfigNavSections } from '@proprietary/components/shared/config/configNavSections';
|
||||||
|
import { ConfigNavSection } from '@core/components/shared/config/configNavSections';
|
||||||
|
import { ConnectionSettings } from '@app/components/ConnectionSettings';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Desktop extension of createConfigNavSections that adds connection settings
|
||||||
|
*/
|
||||||
|
export const createConfigNavSections = (
|
||||||
|
isAdmin: boolean = false,
|
||||||
|
runningEE: boolean = false,
|
||||||
|
loginEnabled: boolean = false
|
||||||
|
): ConfigNavSection[] => {
|
||||||
|
// 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: <ConnectionSettings />,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
return sections;
|
||||||
|
};
|
||||||
8
frontend/src/desktop/components/shared/config/types.ts
Normal file
8
frontend/src/desktop/components/shared/config/types.ts
Normal file
@ -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];
|
||||||
Loading…
Reference in New Issue
Block a user