Files
Work/Frontend/src/components/bootstrap/BootstrapWizard/BootstrapWizardDialog.tsx
T
2026-06-29 03:06:18 +01:00

235 lines
8.5 KiB
TypeScript

// Path: Frontend/src/components/bootstrap/BootstrapWizard/BootstrapWizardDialog.tsx
import { Show, type JSX } from "solid-js";
import { Portal } from "solid-js/web";
import {
type AdminForm,
type BootstrapPersona,
bootstrapPersonaDefinitions,
type BootstrapStepDefinition,
type BootstrapStepKey,
type InstanceForm,
type ModeForm,
type StructureForm,
} from "./BootstrapWizard.data";
import { type BootstrapSubmissionState, type FieldTooltipState, type MaterializationState } from "./BootstrapWizard.hook";
import {
BootstrapAdminStep,
BootstrapFinishingState,
BootstrapInstanceStep,
BootstrapModeStep,
BootstrapPersonaStep,
BootstrapStructureStep,
BootstrapWizardProgress,
} from "./BootstrapWizard.parts";
import styles from "./BootstrapWizard.module.scss";
export type BootstrapWizardDialogProps = {
isOpen: boolean;
currentStep: BootstrapStepDefinition;
currentStepState: BootstrapSubmissionState;
activeWizardSteps: readonly BootstrapStepDefinition[];
currentWizardStepIndex: number;
stepState: Record<BootstrapStepKey, BootstrapSubmissionState>;
bootstrapStepCount: number;
wizardProgressFillWidth: string;
showBootstrapFinishingState: boolean;
materializationState: MaterializationState;
materializationStatusLabel: string;
materializationMessage: string;
isMaterializationInFlight: boolean;
hasMaterializationFailed: boolean;
instanceForm: InstanceForm;
modeForm: ModeForm;
adminForm: AdminForm;
structureForm: StructureForm;
selectedPersona: BootstrapPersona;
hasChosenPersona: boolean;
selectedPersonaIsAvailable: boolean;
usesCondensedBootstrapFlow: boolean;
bootstrapNamePlaceholder: string;
isFirstStep: boolean;
canDismissWizard: boolean;
fieldTooltip: FieldTooltipState | null;
onClose: () => void;
onCloseFinishingState: () => void;
onSubmit: JSX.EventHandlerUnion<HTMLFormElement, SubmitEvent>;
onSelectPersona: (persona: BootstrapPersona) => void;
onProtocolChange: (value: InstanceForm["protocol"]) => void;
onAccessChange: (value: InstanceForm["access"]) => void;
onHostChange: (value: string) => void;
onModeNameChange: (value: string) => void;
onProjectNameChange: (value: string) => void;
onTeamNameChange: (value: string) => void;
onAdminDisplayNameChange: (value: string) => void;
onAdminEmailChange: (value: string) => void;
onAdminPasswordChange: (value: string) => void;
onDepartmentNameChange: (value: string) => void;
onShowTooltip: (target: HTMLElement, text: string) => void;
onHideTooltip: () => void;
statusLabel: (state: BootstrapSubmissionState) => string;
stepStatusLabel: (step: BootstrapStepDefinition) => string;
onNavigateBack: () => void;
onSelectStep: (index: number) => void;
};
export const BootstrapWizardDialog = (props: BootstrapWizardDialogProps): JSX.Element => (
<Show when={props.isOpen}>
<Portal>
<div class={styles.wizardLayer} data-ui="bootstrap-wizard" data-step={props.currentStep.id}>
<div class={styles.wizardBackdrop} aria-hidden="true" />
<section class={styles.wizardPanel} role="dialog" aria-modal="true" aria-labelledby="bootstrap-wizard-title" data-slot="bootstrap-wizard-panel">
<header class={styles.wizardHeader} data-slot="bootstrap-wizard-header">
<div class={styles.wizardHeaderCopy}>
<h2 id="bootstrap-wizard-title" class={styles.wizardTitle}>
Bootstrap Server
</h2>
</div>
<Show when={props.canDismissWizard}>
<button type="button" class={styles.wizardCloseButton} onClick={props.onClose}>
Close
</button>
</Show>
</header>
<Show
when={!props.showBootstrapFinishingState}
fallback={
<BootstrapFinishingState
materializationState={props.materializationState}
statusLabel={props.materializationStatusLabel}
message={props.materializationMessage}
isInFlight={props.isMaterializationInFlight}
hasFailed={props.hasMaterializationFailed}
onClose={props.onCloseFinishingState}
/>
}
>
<div class={styles.wizardBody}>
<Show when={props.currentStep.id !== "persona"}>
<BootstrapWizardProgress
steps={props.activeWizardSteps}
currentStepId={props.currentStep.id}
currentWizardStepIndex={props.currentWizardStepIndex}
stepState={props.stepState}
bootstrapStepCount={props.bootstrapStepCount}
wizardProgressFillWidth={props.wizardProgressFillWidth}
stepStatusLabel={props.stepStatusLabel}
onSelectStep={props.onSelectStep}
/>
</Show>
<div class={styles.wizardStepPanel} data-slot="bootstrap-wizard-step-panel">
<Show when={props.currentStep.id !== "persona" || props.statusLabel(props.currentStepState)}>
<div class={styles.sectionHeader}>
<Show when={props.currentStep.id !== "persona"}>
<div>
<span class={styles.wizardStepEyebrow}>{`Step ${props.currentWizardStepIndex + 1} of ${props.bootstrapStepCount}`}</span>
</div>
</Show>
<Show when={props.statusLabel(props.currentStepState)}>
<div class={styles.statusBadge} data-status={props.currentStepState.status}>
{props.statusLabel(props.currentStepState)}
</div>
</Show>
</div>
</Show>
<form class={styles.form} onSubmit={props.onSubmit}>
<Show when={props.currentStep.id === "persona"}>
<BootstrapPersonaStep
personas={bootstrapPersonaDefinitions}
hasChosenPersona={props.hasChosenPersona}
selectedPersona={props.selectedPersona}
selectedPersonaIsAvailable={props.selectedPersonaIsAvailable}
onSelectPersona={props.onSelectPersona}
/>
</Show>
<Show when={props.currentStep.id === "instance"}>
<BootstrapInstanceStep
instanceForm={props.instanceForm}
onProtocolChange={props.onProtocolChange}
onAccessChange={props.onAccessChange}
onHostChange={props.onHostChange}
onShowTooltip={props.onShowTooltip}
onHideTooltip={props.onHideTooltip}
/>
</Show>
<Show when={props.currentStep.id === "mode"}>
<BootstrapModeStep
modeForm={props.modeForm}
structureForm={props.structureForm}
usesCondensedBootstrapFlow={props.usesCondensedBootstrapFlow}
selectedPersona={props.selectedPersona}
namePlaceholder={props.bootstrapNamePlaceholder}
onNameChange={props.onModeNameChange}
onProjectNameChange={props.onProjectNameChange}
onTeamNameChange={props.onTeamNameChange}
onShowTooltip={props.onShowTooltip}
onHideTooltip={props.onHideTooltip}
/>
</Show>
<Show when={props.currentStep.id === "admin"}>
<BootstrapAdminStep
adminForm={props.adminForm}
onDisplayNameChange={props.onAdminDisplayNameChange}
onEmailChange={props.onAdminEmailChange}
onPasswordChange={props.onAdminPasswordChange}
/>
</Show>
<Show when={props.currentStep.id === "structure"}>
<BootstrapStructureStep
mode={props.modeForm.mode}
structureForm={props.structureForm}
onDepartmentNameChange={props.onDepartmentNameChange}
onTeamNameChange={props.onTeamNameChange}
onProjectNameChange={props.onProjectNameChange}
onShowTooltip={props.onShowTooltip}
onHideTooltip={props.onHideTooltip}
/>
</Show>
<Show when={props.currentStep.id !== "persona"}>
<div class={styles.wizardFormActions}>
<button type="button" class={styles.secondaryButton} disabled={props.isFirstStep} onClick={props.onNavigateBack}>
Back
</button>
<button type="submit" class={styles.primaryButton} disabled={props.currentStepState.status === "submitting"}>
{props.currentStep.buttonLabel}
</button>
</div>
</Show>
</form>
<Show when={props.currentStepState.error}>
<p class={styles.errorText}>{props.currentStepState.error}</p>
</Show>
</div>
</div>
</Show>
</section>
<Show when={props.fieldTooltip}>
{(tooltip): JSX.Element => (
<div
class={styles.fieldTooltip}
data-placement={tooltip().placement}
style={{
left: `${tooltip().left}px`,
top: `${tooltip().top}px`,
}}
>
<div class={styles.fieldTooltipBubble}>{tooltip().text}</div>
</div>
)}
</Show>
</div>
</Portal>
</Show>
);