import { Fragment, FunctionComponent, useState, useEffect } from 'react';
import Message from '@patternfly/chatbot/dist/dynamic/Message';
import userAvatar from './user_avatar.svg';
import patternflyAvatar from '../Messages/patternfly_avatar.jpg';
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionToggle,
Alert,
Badge,
Button,
ButtonVariant,
Card,
CardBody,
CardExpandableContent,
CardFooter,
CardHeader,
CardTitle,
CodeBlock,
CodeBlockCode,
Content,
ContentVariants,
DescriptionList,
DescriptionListDescription,
DescriptionListGroup,
DescriptionListTerm,
Flex,
FlexItem,
HelperText,
HelperTextItem,
Icon,
Progress,
ProgressMeasureLocation,
ExpandableSection,
ExpandableSectionToggle,
Label,
Tab,
Tabs,
TabTitleText,
Spinner
} from '@patternfly/react-core';
import { ArrowCircleDownIcon, ArrowRightIcon, CheckCircleIcon, CubeIcon, CubesIcon } from '@patternfly/react-icons';
const UserActionEndContent = () => {
// eslint-disable-next-line no-console
const onClick = () => console.log('custom button click');
return (
End content button
);
};
const CardInformationAfterMainContent = () => (
This is content card after main content
Body
Footer
);
const BeforeMainContent = () => (
7
24
);
interface Stage {
id: string;
name: string;
startProgress: number;
endProgress: number;
}
const LiveProgressSummaryCard = () => {
const [isCardExpanded, setIsCardExpanded] = useState(false);
const [isAccordionExpanded, setIsAccordionExpanded] = useState('installing-toggle');
const [progress, setProgress] = useState(15);
const [isSimulationRunning, setIsSimulationRunning] = useState(false);
const [userManuallyExpandedAccordion, setUserManuallyExpandedAccordion] = useState(false);
const stages: Stage[] = [
{
id: 'installing-toggle',
name: 'Installing cluster bootstrap',
startProgress: 0,
endProgress: 25
},
{
id: 'setup-toggle',
name: 'Control plane setup',
startProgress: 25,
endProgress: 45
},
{
id: 'deploying-toggle',
name: 'Deploying cluster operators',
startProgress: 45,
endProgress: 85
},
{
id: 'finalizing-toggle',
name: 'Finalizing installation',
startProgress: 85,
endProgress: 100
}
];
const getCurrentStage = () =>
stages.find((stage) => progress >= stage.startProgress && progress < stage.endProgress) ||
stages[stages.length - 1];
const getTimeRemaining = () => {
const remainingProgress = 100 - progress;
const estimatedMinutes = Math.max(1, Math.round((remainingProgress / 100) * 30)); // 30 minutes total simulation
return `About ${estimatedMinutes} minute${estimatedMinutes !== 1 ? 's' : ''} remaining`;
};
const getCurrentStageName = () => {
const currentStage = getCurrentStage();
return currentStage.name;
};
const getStageStatus = (stage: Stage) => {
if (progress >= stage.endProgress) {
return 'completed';
}
if (progress >= stage.startProgress) {
return 'in-progress';
}
return 'pending';
};
const renderStageIcon = (stage: Stage) => {
const status = getStageStatus(stage);
if (status === 'completed') {
return ;
} else if (status === 'in-progress') {
return ;
} else {
return {/* Empty space for pending stages */}
;
}
};
// Auto-increment progress when simulation is running
useEffect(() => {
let interval;
if (isSimulationRunning && progress < 100) {
interval = setInterval(() => {
setProgress((prev) => {
const increment = Math.random() * 2 + 0.5; // Random increment between 0.5-2.5%
const newProgress = Math.min(prev + increment, 100);
// Stop simulation when complete
if (newProgress >= 100) {
setIsSimulationRunning(false);
setUserManuallyExpandedAccordion(false); // Reset manual override when simulation completes
}
return newProgress;
});
}, 800);
}
return () => {
if (interval) {
clearInterval(interval);
}
};
}, [isSimulationRunning, progress]);
// Auto-expand accordion to show current stage (only if user hasn't manually overridden)
useEffect(() => {
if (isSimulationRunning && !userManuallyExpandedAccordion) {
setIsAccordionExpanded(getCurrentStage().id);
}
}, [progress, isSimulationRunning, userManuallyExpandedAccordion]);
const onExpandCard = (_event: React.MouseEvent) => {
setIsCardExpanded(!isCardExpanded);
};
const onExpandAccordion = (id: string) => {
setUserManuallyExpandedAccordion(true);
if (id === isAccordionExpanded) {
setIsAccordionExpanded('');
} else {
setIsAccordionExpanded(id);
}
};
const getStageContent = (stage: Stage) => {
const status = getStageStatus(stage);
if (status === 'in-progress') {
switch (stage.id) {
case 'installing-toggle':
return `Installing bootstrap node...
Installing etcd cluster...
Installing control plane...`;
case 'setup-toggle':
return `Configuring cluster networking...
Setting up OpenShift API server...
Configuring authentication...
Setting up cluster operators...`;
case 'deploying-toggle':
return `Deploying openshift-apiserver operator...
Deploying openshift-sdn operator...
Deploying openshift-ingress operator...
`;
case 'finalizing-toggle':
return `Finalizing cluster configuration...
Running post-installation tasks...
Setting up cluster console...`;
}
}
if (status === 'pending') {
return 'Processing...';
}
return 'Complete!';
};
const renderCodeBlock = (stage: Stage) => (
{getStageContent(stage)}
);
return (
<>
{
setProgress(15);
setIsSimulationRunning(true);
setUserManuallyExpandedAccordion(false);
}}
isDisabled={isSimulationRunning}
>
{isSimulationRunning ? 'Simulation running...' : 'Start simulation of cluster installation progress'}
{
setIsSimulationRunning(false);
setProgress(15);
setIsAccordionExpanded('installing-toggle');
setUserManuallyExpandedAccordion(false);
}}
>
Reset
OpenShift cluster installation
{progress >= 100 ? 'Installation complete!' : getCurrentStageName()}
{/* Progress was getting announced on VoiceOver constantly - this helps avoid that */}
{progress >= 100 ? 'Completed' : getTimeRemaining()}
}
/>
{stages.map((stage) => (
{
onExpandAccordion(stage.id);
}}
id={stage.id}
>
{renderStageIcon(stage)}
{stage.name}
{renderCodeBlock(stage)}
))}
} iconPosition="end">
Open in console
>
);
};
const VersionSelectorCard = () => {
const [activeTabKey, setActiveTabKey] = useState(0);
const [isCardSelected, setIsCardSelected] = useState('');
const [isExpanded, setIsExpanded] = useState(false);
const id1 = '4.20';
const id2 = '4.19';
const id3 = '4.18';
const id4 = '4.17';
const onChange = (event: React.FormEvent) => {
setIsCardSelected(event.currentTarget.id);
};
const onToggleExpandableSection = (isExpanded: boolean) => {
setIsExpanded(isExpanded);
};
const handleTabClick = (_event: any, tabIndex: string | number) => {
setActiveTabKey(tabIndex);
};
const contentId = 'detached-expandable-section-content';
const toggleId = 'detached-expandable-section-toggle';
const generateTabContent = (title: string, subtitle: string) => (
{title}
{subtitle}
4.20.0-ec.3
Preview
Developer preview • Not for production
4.19.2
Latest
Newest features • 18-month support • Recommended
4.18.19
Previous stable • Full support
4.17.34
Maintenance
Maintenance support phase
{isExpanded ? 'Hide older versions' : 'Show older versions'}
);
return (
Single arch
}
>
{generateTabContent('x86_64 Intel/AMD only', 'Standard deployments • Most common choice')}
Multi arch
}
>
{generateTabContent('Multi arch', 'Standard deployments')}
Continue with selections
);
};
const DownloadCard = () => (
Your discovery ISO is ready
To begin adding hosts to your bare metal cluster, you first need to boot them with the generated Discovery
ISO. This allows the installation program to see and manage your hardware.
} isBlock>
Download Discovery ISO
1.2 GB • Expires in 24 hours
Next step: After downloading, boot your bare metal hosts from this ISO image.
);
export const UserMessageWithExtraContent: FunctionComponent = () => (
<>
,
afterMainContent: ,
endContent:
}}
/>
}}
/>
}}
/>
}}
/>
>
);