diff --git a/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/Messages.md b/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/Messages.md index 981464fdf..493aefd92 100644 --- a/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/Messages.md +++ b/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/Messages.md @@ -30,6 +30,7 @@ propComponents: sortValue: 3 --- +import { ArrowRightIcon, CheckCircleIcon } from '@patternfly/react-icons'; import Message from '@patternfly/chatbot/dist/dynamic/Message'; import MessageDivider from '@patternfly/chatbot/dist/dynamic/MessageDivider'; import { rehypeCodeBlockToggle } from '@patternfly/chatbot/dist/esm/Message/Plugins/rehypeCodeBlockToggle'; diff --git a/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/UserMessageWithExtraContent.tsx b/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/UserMessageWithExtraContent.tsx index db5acf761..e0d608d43 100644 --- a/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/UserMessageWithExtraContent.tsx +++ b/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/UserMessageWithExtraContent.tsx @@ -1,8 +1,35 @@ -import { Fragment, FunctionComponent } from 'react'; - +import { Fragment, FunctionComponent, useState, useEffect } from 'react'; import Message from '@patternfly/chatbot/dist/dynamic/Message'; import userAvatar from './user_avatar.svg'; -import { Alert, Badge, Button, Card, CardBody, CardFooter, CardTitle } from '@patternfly/react-core'; +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionToggle, + Alert, + Badge, + Button, + Card, + CardBody, + CardFooter, + CodeBlock, + DescriptionList, + DescriptionListDescription, + DescriptionListGroup, + DescriptionListTerm, + HelperText, + HelperTextItem, + Progress, + ProgressMeasureLocation, + Flex, + FlexItem, + CardTitle, + CardHeader, + CardExpandableContent, + Spinner, + CodeBlockCode +} from '@patternfly/react-core'; +import { ArrowRightIcon, CheckCircleIcon } from '@patternfly/react-icons'; const UserActionEndContent = () => { // eslint-disable-next-line no-console @@ -36,6 +63,305 @@ const BeforeMainContent = () => ( ); +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 ( + <> + + + + + + + + + + + + + + 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)} + + ))} + +
+
+ + + +
+ + ); +}; + export const UserMessageWithExtraContent: FunctionComponent = () => ( <> ( endContent: }} /> + + }} + /> );