stash/ui/v2.5/src/components/Setup/Setup.tsx
2024-11-07 13:16:28 +11:00

1075 lines
29 KiB
TypeScript

import React, { useState, useContext, useCallback } from "react";
import { FormattedMessage, useIntl } from "react-intl";
import {
Alert,
Button,
Card,
Container,
Form,
InputGroup,
} from "react-bootstrap";
import * as GQL from "src/core/generated-graphql";
import {
mutateSetup,
useConfigureUI,
useSystemStatus,
} from "src/core/StashService";
import { useHistory } from "react-router-dom";
import { ConfigurationContext } from "src/hooks/Config";
import StashConfiguration from "../Settings/StashConfiguration";
import { Icon } from "../Shared/Icon";
import { LoadingIndicator } from "../Shared/LoadingIndicator";
import { ModalComponent } from "../Shared/Modal";
import { FolderSelectDialog } from "../Shared/FolderSelect/FolderSelectDialog";
import {
faEllipsisH,
faExclamationTriangle,
faQuestionCircle,
} from "@fortawesome/free-solid-svg-icons";
import { releaseNotes } from "src/docs/en/ReleaseNotes";
import { ExternalLink } from "../Shared/ExternalLink";
interface ISetupContextState {
configuration: GQL.ConfigDataFragment;
systemStatus: GQL.SystemStatusQuery;
setupState: Partial<GQL.SetupInput>;
setupError: string | undefined;
pathJoin: (...paths: string[]) => string;
pathDir(path: string): string;
homeDir: string;
windows: boolean;
macApp: boolean;
homeDirPath: string;
pwd: string;
workingDir: string;
}
const SetupStateContext = React.createContext<ISetupContextState | null>(null);
const useSetupContext = () => {
const context = React.useContext(SetupStateContext);
if (context === null) {
throw new Error("useSettings must be used within a SettingsContext");
}
return context;
};
const SetupContext: React.FC<{
setupState: Partial<GQL.SetupInput>;
setupError: string | undefined;
systemStatus: GQL.SystemStatusQuery;
configuration: GQL.ConfigDataFragment;
}> = ({ setupState, setupError, systemStatus, configuration, children }) => {
const status = systemStatus?.systemStatus;
const windows = status?.os === "windows";
const pathSep = windows ? "\\" : "/";
const homeDir = windows ? "%USERPROFILE%" : "$HOME";
const pwd = windows ? "%CD%" : "$PWD";
const pathJoin = useCallback(
(...paths: string[]) => {
return paths.join(pathSep);
},
[pathSep]
);
// simply returns everything preceding the last path separator
function pathDir(path: string) {
const lastSep = path.lastIndexOf(pathSep);
if (lastSep === -1) return "";
return path.slice(0, lastSep);
}
const workingDir = status?.workingDir ?? ".";
// When running Stash.app, the working directory is (usually) set to /.
// Assume that the user doesn't want to set up in / (it's usually mounted read-only anyway),
// so in this situation disallow setting up in the working directory.
const macApp = status?.os === "darwin" && workingDir === "/";
const homeDirPath = pathJoin(status?.homeDir ?? homeDir, ".stash");
const state: ISetupContextState = {
systemStatus,
configuration,
windows,
macApp,
pathJoin,
pathDir,
homeDir,
homeDirPath,
pwd,
workingDir,
setupState,
setupError,
};
return (
<SetupStateContext.Provider value={state}>
{children}
</SetupStateContext.Provider>
);
};
interface IWizardStep {
next: (input?: Partial<GQL.SetupInput>) => void;
goBack: () => void;
}
const WelcomeSpecificConfig: React.FC<IWizardStep> = ({ next }) => {
const { systemStatus } = useSetupContext();
const status = systemStatus?.systemStatus;
const overrideConfig = status?.configPath;
function onNext() {
next({ configLocation: overrideConfig! });
}
return (
<>
<section>
<h2 className="mb-5">
<FormattedMessage id="setup.welcome_to_stash" />
</h2>
<p className="lead text-center">
<FormattedMessage id="setup.welcome_specific_config.unable_to_locate_specified_config" />
</p>
<p>
<FormattedMessage
id="setup.welcome_specific_config.config_path"
values={{
path: overrideConfig,
code: (chunks: string) => <code>{chunks}</code>,
}}
/>
</p>
<p>
<FormattedMessage id="setup.welcome_specific_config.next_step" />
</p>
</section>
<section className="mt-5">
<div className="d-flex justify-content-center">
<Button variant="primary mx-2 p-5" onClick={() => onNext()}>
<FormattedMessage id="actions.next_action" />
</Button>
</div>
</section>
</>
);
};
const DefaultWelcomeStep: React.FC<IWizardStep> = ({ next }) => {
const { pathJoin, homeDir, macApp, homeDirPath, pwd, workingDir } =
useSetupContext();
const fallbackStashDir = pathJoin(homeDir, ".stash");
const fallbackConfigPath = pathJoin(fallbackStashDir, "config.yml");
function onConfigLocationChosen(inWorkingDir: boolean) {
const configLocation = inWorkingDir ? "config.yml" : "";
next({ configLocation });
}
return (
<>
<section>
<h2 className="mb-5">
<FormattedMessage id="setup.welcome_to_stash" />
</h2>
<p className="lead text-center">
<FormattedMessage id="setup.welcome.unable_to_locate_config" />
</p>
<p>
<FormattedMessage
id="setup.welcome.config_path_logic_explained"
values={{
code: (chunks: string) => <code>{chunks}</code>,
fallback_path: fallbackConfigPath,
}}
/>
</p>
<Alert variant="info text-center">
<FormattedMessage
id="setup.welcome.unexpected_explained"
values={{
code: (chunks: string) => <code>{chunks}</code>,
}}
/>
</Alert>
<p>
<FormattedMessage id="setup.welcome.next_step" />
</p>
</section>
<section className="mt-5">
<h3 className="text-center mb-5">
<FormattedMessage id="setup.welcome.store_stash_config" />
</h3>
<div className="d-flex justify-content-center">
<Button
variant="secondary mx-2 p-5"
onClick={() => onConfigLocationChosen(false)}
>
<FormattedMessage
id="setup.welcome.in_current_stash_directory"
values={{
code: (chunks: string) => <code>{chunks}</code>,
path: fallbackStashDir,
}}
/>
<br />
<code>{homeDirPath}</code>
</Button>
<Button
variant="secondary mx-2 p-5"
onClick={() => onConfigLocationChosen(true)}
disabled={macApp}
>
{macApp ? (
<>
<FormattedMessage
id="setup.welcome.in_the_current_working_directory_disabled"
values={{
code: (chunks: string) => <code>{chunks}</code>,
path: pwd,
}}
/>
<br />
<b>
<FormattedMessage
id="setup.welcome.in_the_current_working_directory_disabled_macos"
values={{
code: (chunks: string) => <code>{chunks}</code>,
br: () => <br />,
}}
/>
</b>
</>
) : (
<>
<FormattedMessage
id="setup.welcome.in_the_current_working_directory"
values={{
code: (chunks: string) => <code>{chunks}</code>,
path: pwd,
}}
/>
<br />
<code>{workingDir}</code>
</>
)}
</Button>
</div>
</section>
</>
);
};
const WelcomeStep: React.FC<IWizardStep> = (props) => {
const { systemStatus } = useSetupContext();
const status = systemStatus?.systemStatus;
const overrideConfig = status?.configPath;
return overrideConfig ? (
<WelcomeSpecificConfig {...props} />
) : (
<DefaultWelcomeStep {...props} />
);
};
const StashAlert: React.FC<{ close: (confirm: boolean) => void }> = ({
close,
}) => {
const intl = useIntl();
return (
<ModalComponent
show
icon={faExclamationTriangle}
accept={{
text: intl.formatMessage({ id: "actions.confirm" }),
variant: "danger",
onClick: () => close(true),
}}
cancel={{ onClick: () => close(false) }}
>
<p>
<FormattedMessage id="setup.paths.stash_alert" />
</p>
</ModalComponent>
);
};
const DatabaseSection: React.FC<{
databaseFile: string;
setDatabaseFile: React.Dispatch<React.SetStateAction<string>>;
}> = ({ databaseFile, setDatabaseFile }) => {
const intl = useIntl();
return (
<Form.Group id="database">
<h3>
<FormattedMessage id="setup.paths.where_can_stash_store_its_database" />
</h3>
<p>
<FormattedMessage
id="setup.paths.where_can_stash_store_its_database_description"
values={{
code: (chunks: string) => <code>{chunks}</code>,
}}
/>
<br />
<FormattedMessage
id="setup.paths.where_can_stash_store_its_database_warning"
values={{
strong: (chunks: string) => <strong>{chunks}</strong>,
}}
/>
</p>
<Form.Control
className="text-input"
defaultValue={databaseFile}
placeholder={intl.formatMessage({
id: "setup.paths.database_filename_empty_for_default",
})}
onChange={(e) => setDatabaseFile(e.currentTarget.value)}
/>
</Form.Group>
);
};
const DirectorySelector: React.FC<{
value: string;
setValue: React.Dispatch<React.SetStateAction<string>>;
placeholder: string;
disabled?: boolean;
}> = ({ value, setValue, placeholder, disabled = false }) => {
const [showSelectDialog, setShowSelectDialog] = useState(false);
function onSelectClosed(dir?: string) {
if (dir) {
setValue(dir);
}
setShowSelectDialog(false);
}
return (
<>
{showSelectDialog ? (
<FolderSelectDialog onClose={onSelectClosed} />
) : null}
<InputGroup>
<Form.Control
className="text-input"
value={disabled ? "" : value}
placeholder={placeholder}
onChange={(e) => setValue(e.currentTarget.value)}
disabled={disabled}
/>
<InputGroup.Append>
<Button
variant="secondary"
className="text-input"
onClick={() => setShowSelectDialog(true)}
disabled={disabled}
>
<Icon icon={faEllipsisH} />
</Button>
</InputGroup.Append>
</InputGroup>
</>
);
};
const GeneratedSection: React.FC<{
generatedLocation: string;
setGeneratedLocation: React.Dispatch<React.SetStateAction<string>>;
}> = ({ generatedLocation, setGeneratedLocation }) => {
const intl = useIntl();
return (
<Form.Group id="generated">
<h3>
<FormattedMessage id="setup.paths.where_can_stash_store_its_generated_content" />
</h3>
<p>
<FormattedMessage
id="setup.paths.where_can_stash_store_its_generated_content_description"
values={{
code: (chunks: string) => <code>{chunks}</code>,
}}
/>
</p>
<DirectorySelector
value={generatedLocation}
setValue={setGeneratedLocation}
placeholder={intl.formatMessage({
id: "setup.paths.path_to_generated_directory_empty_for_default",
})}
/>
</Form.Group>
);
};
const CacheSection: React.FC<{
cacheLocation: string;
setCacheLocation: React.Dispatch<React.SetStateAction<string>>;
}> = ({ cacheLocation, setCacheLocation }) => {
const intl = useIntl();
return (
<Form.Group id="cache">
<h3>
<FormattedMessage id="setup.paths.where_can_stash_store_cache_files" />
</h3>
<p>
<FormattedMessage
id="setup.paths.where_can_stash_store_cache_files_description"
values={{
code: (chunks: string) => <code>{chunks}</code>,
}}
/>
</p>
<DirectorySelector
value={cacheLocation}
setValue={setCacheLocation}
placeholder={intl.formatMessage({
id: "setup.paths.path_to_cache_directory_empty_for_default",
})}
/>
</Form.Group>
);
};
const BlobsSection: React.FC<{
blobsLocation: string;
setBlobsLocation: React.Dispatch<React.SetStateAction<string>>;
storeBlobsInDatabase: boolean;
setStoreBlobsInDatabase: React.Dispatch<React.SetStateAction<boolean>>;
}> = ({
blobsLocation,
setBlobsLocation,
storeBlobsInDatabase,
setStoreBlobsInDatabase,
}) => {
const intl = useIntl();
return (
<Form.Group id="blobs">
<h3>
<FormattedMessage id="setup.paths.where_can_stash_store_blobs" />
</h3>
<p>
<FormattedMessage
id="setup.paths.where_can_stash_store_blobs_description"
values={{
code: (chunks: string) => <code>{chunks}</code>,
}}
/>
</p>
<p>
<FormattedMessage
id="setup.paths.where_can_stash_store_blobs_description_addendum"
values={{
code: (chunks: string) => <code>{chunks}</code>,
strong: (chunks: string) => <strong>{chunks}</strong>,
}}
/>
</p>
<div>
<Form.Check
id="store-blobs-in-database"
checked={storeBlobsInDatabase}
label={intl.formatMessage({
id: "setup.paths.store_blobs_in_database",
})}
onChange={() => setStoreBlobsInDatabase(!storeBlobsInDatabase)}
/>
</div>
<div>
<DirectorySelector
value={blobsLocation}
setValue={setBlobsLocation}
placeholder={intl.formatMessage({
id: "setup.paths.path_to_blobs_directory_empty_for_default",
})}
disabled={storeBlobsInDatabase}
/>
</div>
</Form.Group>
);
};
const SetPathsStep: React.FC<IWizardStep> = ({ goBack, next }) => {
const { configuration } = useSetupContext();
const [showStashAlert, setShowStashAlert] = useState(false);
const [stashes, setStashes] = useState<GQL.StashConfig[]>([]);
const [databaseFile, setDatabaseFile] = useState("");
const [generatedLocation, setGeneratedLocation] = useState("");
const [cacheLocation, setCacheLocation] = useState("");
const [storeBlobsInDatabase, setStoreBlobsInDatabase] = useState(false);
const [blobsLocation, setBlobsLocation] = useState("");
const overrideDatabase = configuration?.general.databasePath;
const overrideGenerated = configuration?.general.generatedPath;
const overrideCache = configuration?.general.cachePath;
const overrideBlobs = configuration?.general.blobsPath;
function preNext() {
if (stashes.length === 0) {
setShowStashAlert(true);
} else {
onNext();
}
}
function onNext() {
const input: Partial<GQL.SetupInput> = {
stashes,
databaseFile,
generatedLocation,
cacheLocation,
blobsLocation: storeBlobsInDatabase ? "" : blobsLocation,
storeBlobsInDatabase,
};
next(input);
}
return (
<>
{showStashAlert ? (
<StashAlert
close={(confirm) => {
setShowStashAlert(false);
if (confirm) {
onNext();
}
}}
/>
) : null}
<section>
<h2 className="mb-3">
<FormattedMessage id="setup.paths.set_up_your_paths" />
</h2>
<p>
<FormattedMessage id="setup.paths.description" />
</p>
</section>
<section>
<Form.Group id="stashes">
<h3>
<FormattedMessage id="setup.paths.where_is_your_porn_located" />
</h3>
<p>
<FormattedMessage id="setup.paths.where_is_your_porn_located_description" />
</p>
<Card>
<StashConfiguration
stashes={stashes}
setStashes={(s) => setStashes(s)}
/>
</Card>
</Form.Group>
{overrideDatabase ? null : (
<DatabaseSection
databaseFile={databaseFile}
setDatabaseFile={setDatabaseFile}
/>
)}
{overrideGenerated ? null : (
<GeneratedSection
generatedLocation={generatedLocation}
setGeneratedLocation={setGeneratedLocation}
/>
)}
{overrideCache ? null : (
<CacheSection
cacheLocation={cacheLocation}
setCacheLocation={setCacheLocation}
/>
)}
{overrideBlobs ? null : (
<BlobsSection
blobsLocation={blobsLocation}
setBlobsLocation={setBlobsLocation}
storeBlobsInDatabase={storeBlobsInDatabase}
setStoreBlobsInDatabase={setStoreBlobsInDatabase}
/>
)}
</section>
<section className="mt-5">
<div className="d-flex justify-content-center">
<Button variant="secondary mx-2 p-5" onClick={() => goBack()}>
<FormattedMessage id="actions.previous_action" />
</Button>
<Button variant="primary mx-2 p-5" onClick={() => preNext()}>
<FormattedMessage id="actions.next_action" />
</Button>
</div>
</section>
</>
);
};
const StashExclusions: React.FC<{ stash: GQL.StashConfig }> = ({ stash }) => {
if (!stash.excludeImage && !stash.excludeVideo) {
return null;
}
const excludes = [];
if (stash.excludeVideo) {
excludes.push("videos");
}
if (stash.excludeImage) {
excludes.push("images");
}
return <span>{`(excludes ${excludes.join(" and ")})`}</span>;
};
const ConfirmStep: React.FC<IWizardStep> = ({ goBack, next }) => {
const { configuration, pathDir, pathJoin, pwd, setupState } =
useSetupContext();
const cfgFile = setupState.configLocation
? setupState.configLocation
: pathJoin(pwd, "config.yml");
const cfgDir = pathDir(cfgFile);
const stashes = setupState.stashes ?? [];
const {
databaseFile,
generatedLocation,
cacheLocation,
blobsLocation,
storeBlobsInDatabase,
} = setupState;
const overrideDatabase = configuration?.general.databasePath;
const overrideGenerated = configuration?.general.generatedPath;
const overrideCache = configuration?.general.cachePath;
const overrideBlobs = configuration?.general.blobsPath;
function joinCfgDir(path: string) {
if (cfgDir) {
return pathJoin(cfgDir, path);
} else {
return path;
}
}
return (
<>
<section>
<h2 className="mb-3">
<FormattedMessage id="setup.confirm.nearly_there" />
</h2>
<p>
<FormattedMessage id="setup.confirm.almost_ready" />
</p>
<dl>
<dt>
<FormattedMessage id="setup.confirm.configuration_file_location" />
</dt>
<dd>
<code>{cfgFile}</code>
</dd>
</dl>
<dl>
<dt>
<FormattedMessage id="setup.confirm.stash_library_directories" />
</dt>
<dd>
<ul>
{stashes.map((s) => (
<li key={s.path}>
<code>{s.path} </code>
<StashExclusions stash={s} />
</li>
))}
</ul>
</dd>
</dl>
{!overrideDatabase && (
<dl>
<dt>
<FormattedMessage id="setup.confirm.database_file_path" />
</dt>
<dd>
<code>{databaseFile || joinCfgDir("stash-go.sqlite")}</code>
</dd>
</dl>
)}
{!overrideGenerated && (
<dl>
<dt>
<FormattedMessage id="setup.confirm.generated_directory" />
</dt>
<dd>
<code>{generatedLocation || joinCfgDir("generated")}</code>
</dd>
</dl>
)}
{!overrideCache && (
<dl>
<dt>
<FormattedMessage id="setup.confirm.cache_directory" />
</dt>
<dd>
<code>{cacheLocation || joinCfgDir("cache")}</code>
</dd>
</dl>
)}
{!overrideBlobs && (
<dl>
<dt>
<FormattedMessage id="setup.confirm.blobs_directory" />
</dt>
<dd>
<code>
{storeBlobsInDatabase ? (
<FormattedMessage id="setup.confirm.blobs_use_database" />
) : (
blobsLocation || joinCfgDir("blobs")
)}
</code>
</dd>
</dl>
)}
</section>
<section className="mt-5">
<div className="d-flex justify-content-center">
<Button variant="secondary mx-2 p-5" onClick={() => goBack()}>
<FormattedMessage id="actions.previous_action" />
</Button>
<Button variant="success mx-2 p-5" onClick={() => next()}>
<FormattedMessage id="actions.confirm" />
</Button>
</div>
</section>
</>
);
};
const DiscordLink = (
<ExternalLink href="https://discord.gg/2TsNFKt">Discord</ExternalLink>
);
const GithubLink = (
<ExternalLink href="https://github.com/stashapp/stash/issues">
<FormattedMessage id="setup.github_repository" />
</ExternalLink>
);
const ErrorStep: React.FC<{ error: string; goBack: () => void }> = ({
error,
goBack,
}) => {
return (
<>
<section>
<h2>
<FormattedMessage id="setup.errors.something_went_wrong" />
</h2>
<p>
<FormattedMessage
id="setup.errors.something_went_wrong_while_setting_up_your_system"
values={{ error: <pre>{error}</pre> }}
/>
</p>
<p>
<FormattedMessage
id="setup.errors.something_went_wrong_description"
values={{ GithubLink, DiscordLink }}
/>
</p>
</section>
<section className="mt-5">
<div className="d-flex justify-content-center">
<Button variant="secondary mx-2 p-5" onClick={goBack}>
<FormattedMessage id="actions.previous_action" />
</Button>
</div>
</section>
</>
);
};
const SuccessStep: React.FC<{}> = () => {
const intl = useIntl();
const history = useHistory();
const [mutateDownloadFFMpeg] = GQL.useDownloadFfMpegMutation();
const [downloadFFmpeg, setDownloadFFmpeg] = useState(true);
const { systemStatus } = useSetupContext();
const status = systemStatus?.systemStatus;
function onFinishClick() {
if ((!status?.ffmpegPath || !status?.ffprobePath) && downloadFFmpeg) {
mutateDownloadFFMpeg();
}
history.push("/settings?tab=library");
}
return (
<>
<section>
<h2>
<FormattedMessage id="setup.success.your_system_has_been_created" />
</h2>
<p>
<FormattedMessage id="setup.success.next_config_step_one" />
</p>
<p>
<FormattedMessage
id="setup.success.next_config_step_two"
values={{
code: (chunks: string) => <code>{chunks}</code>,
localized_task: intl.formatMessage({
id: "config.categories.tasks",
}),
localized_scan: intl.formatMessage({ id: "actions.scan" }),
}}
/>
</p>
{!status?.ffmpegPath || !status?.ffprobePath ? (
<>
<Alert variant="warning text-center">
<FormattedMessage
id="setup.success.missing_ffmpeg"
values={{
code: (chunks: string) => <code>{chunks}</code>,
}}
/>
</Alert>
<p>
<Form.Check
id="download-ffmpeg"
checked={downloadFFmpeg}
label={intl.formatMessage({
id: "setup.success.download_ffmpeg",
})}
onChange={() => setDownloadFFmpeg(!downloadFFmpeg)}
/>
</p>
</>
) : null}
</section>
<section>
<h3>
<FormattedMessage id="setup.success.getting_help" />
</h3>
<p>
<FormattedMessage
id="setup.success.in_app_manual_explained"
values={{ icon: <Icon icon={faQuestionCircle} /> }}
/>
</p>
<p>
<FormattedMessage
id="setup.success.help_links"
values={{ DiscordLink, GithubLink }}
/>
</p>
</section>
<section>
<h3>
<FormattedMessage id="setup.success.support_us" />
</h3>
<p>
<FormattedMessage
id="setup.success.open_collective"
values={{
open_collective_link: (
<ExternalLink href="https://opencollective.com/stashapp">
Open Collective
</ExternalLink>
),
}}
/>
</p>
<p>
<FormattedMessage id="setup.success.welcome_contrib" />
</p>
</section>
<section>
<p className="lead text-center">
<FormattedMessage id="setup.success.thanks_for_trying_stash" />
</p>
</section>
<section className="mt-5">
<div className="d-flex justify-content-center">
<Button variant="success mx-2 p-5" onClick={() => onFinishClick()}>
<FormattedMessage id="actions.finish" />
</Button>
</div>
</section>
</>
);
};
const FinishStep: React.FC<IWizardStep> = ({ goBack }) => {
const { setupError } = useSetupContext();
if (setupError !== undefined) {
return <ErrorStep error={setupError} goBack={goBack} />;
}
return <SuccessStep />;
};
export const Setup: React.FC = () => {
const intl = useIntl();
const { configuration, loading: configLoading } =
useContext(ConfigurationContext);
const [saveUI] = useConfigureUI();
const {
data: systemStatus,
loading: statusLoading,
error: statusError,
} = useSystemStatus();
const [step, setStep] = useState(0);
const [setupInput, setSetupInput] = useState<Partial<GQL.SetupInput>>({});
const [creating, setCreating] = useState(false);
const [setupError, setSetupError] = useState<string | undefined>(undefined);
const history = useHistory();
const steps: React.FC<IWizardStep>[] = [
WelcomeStep,
SetPathsStep,
ConfirmStep,
FinishStep,
];
const Step = steps[step];
async function createSystem() {
try {
setCreating(true);
setSetupError(undefined);
await mutateSetup(setupInput as GQL.SetupInput);
// Set lastNoteSeen to hide release notes dialog
await saveUI({
variables: {
input: {
...configuration?.ui,
lastNoteSeen: releaseNotes[0].date,
},
},
});
} catch (e) {
if (e instanceof Error && e.message) {
setSetupError(e.message);
} else {
setSetupError(String(e));
}
} finally {
setCreating(false);
setStep(step + 1);
}
}
function next(input?: Partial<GQL.SetupInput>) {
setSetupInput({ ...setupInput, ...input });
if (Step === ConfirmStep) {
// create the system
createSystem();
} else {
setStep(step + 1);
}
}
function goBack() {
if (Step === FinishStep) {
// go back to the step before ConfirmStep
setStep(step - 2);
} else {
setStep(step - 1);
}
}
if (configLoading || statusLoading) {
return <LoadingIndicator />;
}
if (
step === 0 &&
systemStatus &&
systemStatus.systemStatus.status !== GQL.SystemStatusEnum.Setup
) {
// redirect to main page
history.push("/");
return <LoadingIndicator />;
}
if (statusError) {
return (
<Container>
<Alert variant="danger">
<FormattedMessage
id="setup.errors.unable_to_retrieve_system_status"
values={{ error: statusError.message }}
/>
</Alert>
</Container>
);
}
if (!configuration || !systemStatus) {
return (
<Container>
<Alert variant="danger">
<FormattedMessage
id="setup.errors.unable_to_retrieve_configuration"
values={{ error: "configuration or systemStatus === undefined" }}
/>
</Alert>
</Container>
);
}
return (
<SetupContext
setupState={setupInput}
setupError={setupError}
configuration={configuration}
systemStatus={systemStatus}
>
<Container className="setup-wizard">
<h1 className="text-center">
<FormattedMessage id="setup.stash_setup_wizard" />
</h1>
<Card>
{creating ? (
<LoadingIndicator
message={intl.formatMessage({
id: "setup.creating.creating_your_system",
})}
/>
) : (
<Step next={next} goBack={goBack} />
)}
</Card>
</Container>
</SetupContext>
);
};
export default Setup;