diff --git a/src/components/WorkflowStepper.tsx b/src/components/WorkflowStepper.tsx deleted file mode 100644 index 40c12fe..0000000 --- a/src/components/WorkflowStepper.tsx +++ /dev/null @@ -1,487 +0,0 @@ -import { useState, useRef } from "react"; -import { useClickOutside } from "@/hooks"; -import { useShallow } from "zustand/react/shallow"; -import { useMachineStore, usePatternUploaded } from "../stores/useMachineStore"; -import { usePatternStore } from "../stores/usePatternStore"; -import { - CheckCircleIcon, - InformationCircleIcon, - ExclamationTriangleIcon, -} from "@heroicons/react/24/solid"; -import { MachineStatus } from "../types/machine"; - -interface Step { - id: number; - label: string; - description: string; -} - -const steps: Step[] = [ - { id: 1, label: "Connect", description: "Connect to machine" }, - { id: 2, label: "Home Machine", description: "Initialize hoop position" }, - { id: 3, label: "Load Pattern", description: "Choose PES file" }, - { id: 4, label: "Upload", description: "Upload to machine" }, - { id: 5, label: "Mask Trace", description: "Trace pattern area" }, - { id: 6, label: "Start Sewing", description: "Begin embroidery" }, - { id: 7, label: "Monitor", description: "Watch progress" }, - { id: 8, label: "Complete", description: "Finish and remove" }, -]; - -// Helper function to get guide content for a step -function getGuideContent(stepId: number, machineStatus: MachineStatus) { - // Return content based on step - switch (stepId) { - case 1: - return { - type: "info" as const, - title: "Step 1: Connect to Machine", - description: - "To get started, connect to your Brother embroidery machine via Bluetooth.", - items: [ - "Make sure your machine is powered on", - "Enable Bluetooth on your machine", - 'Click the "Connect to Machine" button below', - ], - }; - - case 2: - return { - type: "info" as const, - title: "Step 2: Home Machine", - description: - "The hoop needs to be removed and an initial homing procedure must be performed.", - items: [ - "Remove the embroidery hoop from the machine completely", - "Press the Accept button on the machine", - "Wait for the machine to complete its initialization (homing)", - "Once initialization is complete, reattach the hoop", - "The machine should now recognize the hoop correctly", - ], - }; - - case 3: - return { - type: "info" as const, - title: "Step 3: Load Your Pattern", - description: - "Choose a PES embroidery file from your computer to preview and upload.", - items: [ - 'Click "Choose PES File" in the Pattern File section', - "Select your embroidery design (.pes file)", - "Review the pattern preview on the right", - "You can drag the pattern to adjust its position", - ], - }; - - case 4: - return { - type: "info" as const, - title: "Step 4: Upload Pattern to Machine", - description: - "Send your pattern to the embroidery machine to prepare for sewing.", - items: [ - "Review the pattern preview to ensure it's positioned correctly", - "Check the pattern size matches your hoop", - 'Click "Upload to Machine" when ready', - "Wait for the upload to complete (this may take a minute)", - ], - }; - - case 5: - // Check machine status for substates - if (machineStatus === MachineStatus.MASK_TRACE_LOCK_WAIT) { - return { - type: "warning" as const, - title: "Machine Action Required", - description: "The machine is ready to trace the pattern outline.", - items: [ - "Press the button on your machine to confirm and start the mask trace", - "Ensure the hoop is properly attached", - "Make sure the needle area is clear", - ], - }; - } - if (machineStatus === MachineStatus.MASK_TRACING) { - return { - type: "progress" as const, - title: "Mask Trace In Progress", - description: - "The machine is tracing the pattern boundary. Please wait...", - items: [ - "Watch the machine trace the outline", - "Verify the pattern fits within your hoop", - "Do not interrupt the machine", - ], - }; - } - return { - type: "info" as const, - title: "Step 5: Start Mask Trace", - description: - "The mask trace helps the machine understand the pattern boundaries.", - items: [ - 'Click "Start Mask Trace" button in the Sewing Progress section', - "The machine will trace the pattern outline", - "This ensures the hoop is positioned correctly", - ], - }; - - case 6: - return { - type: "success" as const, - title: "Step 6: Ready to Sew!", - description: "The machine is ready to begin embroidering your pattern.", - items: [ - "Verify your thread colors are correct", - "Ensure the fabric is properly hooped", - 'Click "Start Sewing" when ready', - ], - }; - - case 7: - // Check for substates - if (machineStatus === MachineStatus.COLOR_CHANGE_WAIT) { - return { - type: "warning" as const, - title: "Thread Change Required", - description: - "The machine needs a different thread color to continue.", - items: [ - "Check the color blocks section to see which thread is needed", - "Change to the correct thread color", - "Press the button on your machine to resume sewing", - ], - }; - } - if ( - machineStatus === MachineStatus.PAUSE || - machineStatus === MachineStatus.STOP || - machineStatus === MachineStatus.SEWING_INTERRUPTION - ) { - return { - type: "warning" as const, - title: "Sewing Paused", - description: "The embroidery has been paused or interrupted.", - items: [ - "Check if everything is okay with the machine", - 'Click "Resume Sewing" when ready to continue', - "The machine will pick up where it left off", - ], - }; - } - return { - type: "progress" as const, - title: "Step 7: Sewing In Progress", - description: - "Your embroidery is being stitched. Monitor the progress below.", - items: [ - "Watch the progress bar and current stitch count", - "The machine will pause when a color change is needed", - "Do not leave the machine unattended", - ], - }; - - case 8: - return { - type: "success" as const, - title: "Step 8: Embroidery Complete!", - description: "Your embroidery is finished. Great work!", - items: [ - "Remove the hoop from the machine", - "Press the Accept button on the machine", - "Carefully remove your finished embroidery", - "Trim any jump stitches or loose threads", - 'Click "Delete Pattern" to start a new project', - ], - }; - - default: - return null; - } -} - -function getCurrentStep( - machineStatus: MachineStatus, - isConnected: boolean, - hasPattern: boolean, - patternUploaded: boolean, -): number { - if (!isConnected) return 1; - - // Check if machine needs homing (Initial state) - if (machineStatus === MachineStatus.Initial) return 2; - - if (!hasPattern) return 3; - if (!patternUploaded) return 4; - - // After upload, determine step based on machine status - switch (machineStatus) { - case MachineStatus.IDLE: - case MachineStatus.MASK_TRACE_LOCK_WAIT: - case MachineStatus.MASK_TRACING: - return 5; - - case MachineStatus.MASK_TRACE_COMPLETE: - case MachineStatus.SEWING_WAIT: - return 6; - - case MachineStatus.SEWING: - case MachineStatus.COLOR_CHANGE_WAIT: - case MachineStatus.PAUSE: - case MachineStatus.STOP: - case MachineStatus.SEWING_INTERRUPTION: - return 7; - - case MachineStatus.SEWING_COMPLETE: - return 8; - - default: - return 5; - } -} - -export function WorkflowStepper() { - // Machine store - const { machineStatus, isConnected } = useMachineStore( - useShallow((state) => ({ - machineStatus: state.machineStatus, - isConnected: state.isConnected, - })), - ); - - // Pattern store - const { pesData } = usePatternStore( - useShallow((state) => ({ - pesData: state.pesData, - })), - ); - - // Derived state: pattern is uploaded if machine has pattern info - const patternUploaded = usePatternUploaded(); - const hasPattern = pesData !== null; - const currentStep = getCurrentStep( - machineStatus, - isConnected, - hasPattern, - patternUploaded, - ); - const [showPopover, setShowPopover] = useState(false); - const [popoverStep, setPopoverStep] = useState(null); - const popoverRef = useRef(null); - const stepRefs = useRef<{ [key: number]: HTMLDivElement | null }>({}); - - // Close popover when clicking outside (exclude step circles) - useClickOutside(popoverRef, () => setShowPopover(false), { - enabled: showPopover, - excludeRefs: [stepRefs], - }); - - const handleStepClick = (stepId: number) => { - // Only allow clicking on current step or earlier completed steps - if (stepId <= currentStep) { - if (showPopover && popoverStep === stepId) { - setShowPopover(false); - setPopoverStep(null); - } else { - setPopoverStep(stepId); - setShowPopover(true); - } - } - }; - - return ( -
- {/* Progress bar background */} -
- - {/* Progress bar fill */} -
- - {/* Steps */} -
- {steps.map((step) => { - const isComplete = step.id < currentStep; - const isCurrent = step.id === currentStep; - const isUpcoming = step.id > currentStep; - - return ( -
- {/* Step circle */} -
{ - stepRefs.current[step.id] = el; - }} - onClick={() => handleStepClick(step.id)} - className={` - w-8 h-8 lg:w-10 lg:h-10 rounded-full flex items-center justify-center font-bold text-xs transition-all duration-300 border-2 shadow-md - ${step.id <= currentStep ? "cursor-pointer hover:scale-110" : "cursor-not-allowed"} - ${isComplete ? "bg-success-500 dark:bg-success-600 border-success-400 dark:border-success-500 text-white shadow-success-500/30 dark:shadow-success-600/30" : ""} - ${isCurrent ? "bg-primary-600 dark:bg-primary-700 border-primary-500 dark:border-primary-600 text-white scale-105 lg:scale-110 shadow-primary-600/40 dark:shadow-primary-700/40 ring-2 ring-primary-300 dark:ring-primary-500 ring-offset-2 dark:ring-offset-gray-900" : ""} - ${isUpcoming ? "bg-primary-700 dark:bg-primary-800 border-primary-500/30 dark:border-primary-600/30 text-primary-200/70 dark:text-primary-300/70" : ""} - ${showPopover && popoverStep === step.id ? "ring-4 ring-white dark:ring-gray-800" : ""} - `} - aria-label={`${step.label}: ${isComplete ? "completed" : isCurrent ? "current" : "upcoming"}. Click for details.`} - role="button" - tabIndex={step.id <= currentStep ? 0 : -1} - > - {isComplete ? ( -
- - {/* Step label */} -
-
- {step.label} -
-
-
- ); - })} -
- - {/* Popover */} - {showPopover && popoverStep !== null && ( -
- {(() => { - const content = getGuideContent(popoverStep, machineStatus); - if (!content) return null; - - const colorClasses = { - info: "bg-info-50 dark:bg-info-900/95 border-info-600 dark:border-info-500", - success: - "bg-success-50 dark:bg-success-900/95 border-success-600 dark:border-success-500", - warning: - "bg-warning-50 dark:bg-warning-900/95 border-warning-600 dark:border-warning-500", - error: - "bg-danger-50 dark:bg-danger-900/95 border-danger-600 dark:border-danger-500", - progress: - "bg-info-50 dark:bg-info-900/95 border-info-600 dark:border-info-500", - }; - - const iconColorClasses = { - info: "text-info-600 dark:text-info-400", - success: "text-success-600 dark:text-success-400", - warning: "text-warning-600 dark:text-warning-400", - error: "text-danger-600 dark:text-danger-400", - progress: "text-info-600 dark:text-info-400", - }; - - const textColorClasses = { - info: "text-info-900 dark:text-info-200", - success: "text-success-900 dark:text-success-200", - warning: "text-warning-900 dark:text-warning-200", - error: "text-danger-900 dark:text-danger-200", - progress: "text-info-900 dark:text-info-200", - }; - - const descColorClasses = { - info: "text-info-800 dark:text-info-300", - success: "text-success-800 dark:text-success-300", - warning: "text-warning-800 dark:text-warning-300", - error: "text-danger-800 dark:text-danger-300", - progress: "text-info-800 dark:text-info-300", - }; - - const listColorClasses = { - info: "text-blue-700 dark:text-blue-300", - success: "text-green-700 dark:text-green-300", - warning: "text-yellow-700 dark:text-yellow-300", - error: "text-red-700 dark:text-red-300", - progress: "text-cyan-700 dark:text-cyan-300", - }; - - const Icon = - content.type === "warning" - ? ExclamationTriangleIcon - : InformationCircleIcon; - - return ( -
-
- -
-

- {content.title} -

-

- {content.description} -

- {content.items && content.items.length > 0 && ( -
    - {content.items.map((item, index) => ( -
  • $1", - ), - }} - /> - ))} -
- )} -
-
-
- ); - })()} -
- )} -
- ); -} diff --git a/src/components/WorkflowStepper/StepCircle.tsx b/src/components/WorkflowStepper/StepCircle.tsx new file mode 100644 index 0000000..4c9ab83 --- /dev/null +++ b/src/components/WorkflowStepper/StepCircle.tsx @@ -0,0 +1,54 @@ +/** + * StepCircle Component + * + * Renders a circular step indicator with number or checkmark icon + */ + +import { forwardRef } from "react"; +import { CheckCircleIcon } from "@heroicons/react/24/solid"; + +export interface StepCircleProps { + stepId: number; + label: string; + isComplete: boolean; + isCurrent: boolean; + isUpcoming: boolean; + showPopover: boolean; + onClick: () => void; +} + +export const StepCircle = forwardRef( + ( + { stepId, label, isComplete, isCurrent, isUpcoming, showPopover, onClick }, + ref, + ) => { + return ( +
+ {isComplete ? ( +
+ ); + }, +); + +StepCircle.displayName = "StepCircle"; diff --git a/src/components/WorkflowStepper/StepLabel.tsx b/src/components/WorkflowStepper/StepLabel.tsx new file mode 100644 index 0000000..617b676 --- /dev/null +++ b/src/components/WorkflowStepper/StepLabel.tsx @@ -0,0 +1,29 @@ +/** + * StepLabel Component + * + * Renders the text label below each step circle + */ + +export interface StepLabelProps { + label: string; + isCurrent: boolean; + isComplete: boolean; +} + +export function StepLabel({ label, isCurrent, isComplete }: StepLabelProps) { + return ( +
+
+ {label} +
+
+ ); +} diff --git a/src/components/WorkflowStepper/StepPopover.tsx b/src/components/WorkflowStepper/StepPopover.tsx new file mode 100644 index 0000000..711fe8a --- /dev/null +++ b/src/components/WorkflowStepper/StepPopover.tsx @@ -0,0 +1,123 @@ +/** + * StepPopover Component + * + * Renders the guidance popover with dynamic content based on step and machine status + */ + +import { forwardRef } from "react"; +import { + InformationCircleIcon, + ExclamationTriangleIcon, +} from "@heroicons/react/24/solid"; +import { MachineStatus } from "../../types/machine"; +import { getGuideContent } from "../../utils/workflowGuideContent"; + +export interface StepPopoverProps { + stepId: number; + machineStatus: MachineStatus; +} + +export const StepPopover = forwardRef( + ({ stepId, machineStatus }, ref) => { + const content = getGuideContent(stepId, machineStatus); + if (!content) return null; + + const colorClasses = { + info: "bg-info-50 dark:bg-info-900/95 border-info-600 dark:border-info-500", + success: + "bg-success-50 dark:bg-success-900/95 border-success-600 dark:border-success-500", + warning: + "bg-warning-50 dark:bg-warning-900/95 border-warning-600 dark:border-warning-500", + error: + "bg-danger-50 dark:bg-danger-900/95 border-danger-600 dark:border-danger-500", + progress: + "bg-info-50 dark:bg-info-900/95 border-info-600 dark:border-info-500", + }; + + const iconColorClasses = { + info: "text-info-600 dark:text-info-400", + success: "text-success-600 dark:text-success-400", + warning: "text-warning-600 dark:text-warning-400", + error: "text-danger-600 dark:text-danger-400", + progress: "text-info-600 dark:text-info-400", + }; + + const textColorClasses = { + info: "text-info-900 dark:text-info-200", + success: "text-success-900 dark:text-success-200", + warning: "text-warning-900 dark:text-warning-200", + error: "text-danger-900 dark:text-danger-200", + progress: "text-info-900 dark:text-info-200", + }; + + const descColorClasses = { + info: "text-info-800 dark:text-info-300", + success: "text-success-800 dark:text-success-300", + warning: "text-warning-800 dark:text-warning-300", + error: "text-danger-800 dark:text-danger-300", + progress: "text-info-800 dark:text-info-300", + }; + + const listColorClasses = { + info: "text-blue-700 dark:text-blue-300", + success: "text-green-700 dark:text-green-300", + warning: "text-yellow-700 dark:text-yellow-300", + error: "text-red-700 dark:text-red-300", + progress: "text-cyan-700 dark:text-cyan-300", + }; + + const Icon = + content.type === "warning" + ? ExclamationTriangleIcon + : InformationCircleIcon; + + return ( +
+
+
+ +
+

+ {content.title} +

+

+ {content.description} +

+ {content.items && content.items.length > 0 && ( +
    + {content.items.map((item, index) => ( +
  • $1", + ), + }} + /> + ))} +
+ )} +
+
+
+
+ ); + }, +); + +StepPopover.displayName = "StepPopover"; diff --git a/src/components/WorkflowStepper/WorkflowStepper.tsx b/src/components/WorkflowStepper/WorkflowStepper.tsx new file mode 100644 index 0000000..c065ced --- /dev/null +++ b/src/components/WorkflowStepper/WorkflowStepper.tsx @@ -0,0 +1,141 @@ +/** + * WorkflowStepper Component + * + * Displays the 8-step embroidery workflow with progress tracking and contextual guidance + */ + +import { useState, useRef } from "react"; +import { useClickOutside } from "@/hooks"; +import { useShallow } from "zustand/react/shallow"; +import { useMachineStore, usePatternUploaded } from "../../stores/useMachineStore"; +import { usePatternStore } from "../../stores/usePatternStore"; +import { WORKFLOW_STEPS } from "../../constants/workflowSteps"; +import { getCurrentStep } from "../../utils/workflowStepCalculation"; +import { StepCircle } from "./StepCircle"; +import { StepLabel } from "./StepLabel"; +import { StepPopover } from "./StepPopover"; + +export function WorkflowStepper() { + // Machine store + const { machineStatus, isConnected } = useMachineStore( + useShallow((state) => ({ + machineStatus: state.machineStatus, + isConnected: state.isConnected, + })), + ); + + // Pattern store + const { pesData } = usePatternStore( + useShallow((state) => ({ + pesData: state.pesData, + })), + ); + + // Derived state: pattern is uploaded if machine has pattern info + const patternUploaded = usePatternUploaded(); + const hasPattern = pesData !== null; + const currentStep = getCurrentStep( + machineStatus, + isConnected, + hasPattern, + patternUploaded, + ); + const [showPopover, setShowPopover] = useState(false); + const [popoverStep, setPopoverStep] = useState(null); + const popoverRef = useRef(null); + const stepRefs = useRef<{ [key: number]: HTMLDivElement | null }>({}); + + // Close popover when clicking outside (exclude step circles) + useClickOutside(popoverRef, () => setShowPopover(false), { + enabled: showPopover, + excludeRefs: [stepRefs], + }); + + const handleStepClick = (stepId: number) => { + // Only allow clicking on current step or earlier completed steps + if (stepId <= currentStep) { + if (showPopover && popoverStep === stepId) { + setShowPopover(false); + setPopoverStep(null); + } else { + setPopoverStep(stepId); + setShowPopover(true); + } + } + }; + + return ( +
+ {/* Progress bar background */} +
+ + {/* Progress bar fill */} +
+ + {/* Steps */} +
+ {WORKFLOW_STEPS.map((step) => { + const isComplete = step.id < currentStep; + const isCurrent = step.id === currentStep; + const isUpcoming = step.id > currentStep; + + return ( +
+ { + stepRefs.current[step.id] = el; + }} + stepId={step.id} + label={step.label} + isComplete={isComplete} + isCurrent={isCurrent} + isUpcoming={isUpcoming} + showPopover={showPopover && popoverStep === step.id} + onClick={() => handleStepClick(step.id)} + /> + + +
+ ); + })} +
+ + {/* Popover */} + {showPopover && popoverStep !== null && ( + + )} +
+ ); +} diff --git a/src/components/WorkflowStepper/index.ts b/src/components/WorkflowStepper/index.ts new file mode 100644 index 0000000..e1b594e --- /dev/null +++ b/src/components/WorkflowStepper/index.ts @@ -0,0 +1,5 @@ +/** + * WorkflowStepper component barrel export + */ + +export { WorkflowStepper } from "./WorkflowStepper"; diff --git a/src/constants/workflowSteps.ts b/src/constants/workflowSteps.ts new file mode 100644 index 0000000..63b7864 --- /dev/null +++ b/src/constants/workflowSteps.ts @@ -0,0 +1,20 @@ +/** + * Workflow step definitions for the embroidery process + */ + +export interface WorkflowStep { + id: number; + label: string; + description: string; +} + +export const WORKFLOW_STEPS: readonly WorkflowStep[] = [ + { id: 1, label: "Connect", description: "Connect to machine" }, + { id: 2, label: "Home Machine", description: "Initialize hoop position" }, + { id: 3, label: "Load Pattern", description: "Choose PES file" }, + { id: 4, label: "Upload", description: "Upload to machine" }, + { id: 5, label: "Mask Trace", description: "Trace pattern area" }, + { id: 6, label: "Start Sewing", description: "Begin embroidery" }, + { id: 7, label: "Monitor", description: "Watch progress" }, + { id: 8, label: "Complete", description: "Finish and remove" }, +] as const; diff --git a/src/utils/workflowGuideContent.ts b/src/utils/workflowGuideContent.ts new file mode 100644 index 0000000..e0d1b70 --- /dev/null +++ b/src/utils/workflowGuideContent.ts @@ -0,0 +1,195 @@ +/** + * Workflow step guide content + * + * Provides contextual guidance for each workflow step based on machine state + */ + +import { MachineStatus } from "../types/machine"; + +export interface GuideContent { + type: "info" | "warning" | "success" | "error" | "progress"; + title: string; + description: string; + items: string[]; +} + +/** + * Get guide content for a specific workflow step + * + * @param stepId - The workflow step ID (1-8) + * @param machineStatus - Current machine status for dynamic content + * @returns Guide content with type, title, description, and items + */ +export function getGuideContent( + stepId: number, + machineStatus: MachineStatus, +): GuideContent | null { + switch (stepId) { + case 1: + return { + type: "info", + title: "Step 1: Connect to Machine", + description: + "To get started, connect to your Brother embroidery machine via Bluetooth.", + items: [ + "Make sure your machine is powered on", + "Enable Bluetooth on your machine", + 'Click the "Connect to Machine" button below', + ], + }; + + case 2: + return { + type: "info", + title: "Step 2: Home Machine", + description: + "The hoop needs to be removed and an initial homing procedure must be performed.", + items: [ + "Remove the embroidery hoop from the machine completely", + "Press the Accept button on the machine", + "Wait for the machine to complete its initialization (homing)", + "Once initialization is complete, reattach the hoop", + "The machine should now recognize the hoop correctly", + ], + }; + + case 3: + return { + type: "info", + title: "Step 3: Load Your Pattern", + description: + "Choose a PES embroidery file from your computer to preview and upload.", + items: [ + 'Click "Choose PES File" in the Pattern File section', + "Select your embroidery design (.pes file)", + "Review the pattern preview on the right", + "You can drag the pattern to adjust its position", + ], + }; + + case 4: + return { + type: "info", + title: "Step 4: Upload Pattern to Machine", + description: + "Send your pattern to the embroidery machine to prepare for sewing.", + items: [ + "Review the pattern preview to ensure it's positioned correctly", + "Check the pattern size matches your hoop", + 'Click "Upload to Machine" when ready', + "Wait for the upload to complete (this may take a minute)", + ], + }; + + case 5: + // Check machine status for substates + if (machineStatus === MachineStatus.MASK_TRACE_LOCK_WAIT) { + return { + type: "warning", + title: "Machine Action Required", + description: "The machine is ready to trace the pattern outline.", + items: [ + "Press the button on your machine to confirm and start the mask trace", + "Ensure the hoop is properly attached", + "Make sure the needle area is clear", + ], + }; + } + if (machineStatus === MachineStatus.MASK_TRACING) { + return { + type: "progress", + title: "Mask Trace In Progress", + description: + "The machine is tracing the pattern boundary. Please wait...", + items: [ + "Watch the machine trace the outline", + "Verify the pattern fits within your hoop", + "Do not interrupt the machine", + ], + }; + } + return { + type: "info", + title: "Step 5: Start Mask Trace", + description: + "The mask trace helps the machine understand the pattern boundaries.", + items: [ + 'Click "Start Mask Trace" button in the Sewing Progress section', + "The machine will trace the pattern outline", + "This ensures the hoop is positioned correctly", + ], + }; + + case 6: + return { + type: "success", + title: "Step 6: Ready to Sew!", + description: "The machine is ready to begin embroidering your pattern.", + items: [ + "Verify your thread colors are correct", + "Ensure the fabric is properly hooped", + 'Click "Start Sewing" when ready', + ], + }; + + case 7: + // Check for substates + if (machineStatus === MachineStatus.COLOR_CHANGE_WAIT) { + return { + type: "warning", + title: "Thread Change Required", + description: + "The machine needs a different thread color to continue.", + items: [ + "Check the color blocks section to see which thread is needed", + "Change to the correct thread color", + "Press the button on your machine to resume sewing", + ], + }; + } + if ( + machineStatus === MachineStatus.PAUSE || + machineStatus === MachineStatus.STOP || + machineStatus === MachineStatus.SEWING_INTERRUPTION + ) { + return { + type: "warning", + title: "Sewing Paused", + description: "The embroidery has been paused or interrupted.", + items: [ + "Check if everything is okay with the machine", + 'Click "Resume Sewing" when ready to continue', + "The machine will pick up where it left off", + ], + }; + } + return { + type: "progress", + title: "Step 7: Sewing In Progress", + description: + "Your embroidery is being stitched. Monitor the progress below.", + items: [ + "Watch the progress bar and current stitch count", + "The machine will pause when a color change is needed", + "Do not leave the machine unattended", + ], + }; + + case 8: + return { + type: "success", + title: "Step 8: Embroidery Complete!", + description: "Your embroidery is finished. Great work!", + items: [ + "Remove the hoop from the machine", + "Press the Accept button on the machine", + "Carefully remove your finished embroidery", + "Trim any jump stitches or loose threads", + 'Click "Delete Pattern" to start a new project', + ], + }; + + default: + return null; + } +} diff --git a/src/utils/workflowStepCalculation.ts b/src/utils/workflowStepCalculation.ts new file mode 100644 index 0000000..42e9c6c --- /dev/null +++ b/src/utils/workflowStepCalculation.ts @@ -0,0 +1,56 @@ +/** + * Workflow step calculation utilities + * + * Determines the current workflow step based on machine state and pattern status + */ + +import { MachineStatus } from "../types/machine"; + +/** + * Calculate the current workflow step based on machine state + * + * @param machineStatus - Current machine status + * @param isConnected - Whether machine is connected + * @param hasPattern - Whether a pattern is loaded + * @param patternUploaded - Whether pattern has been uploaded to machine + * @returns Current step number (1-8) + */ +export function getCurrentStep( + machineStatus: MachineStatus, + isConnected: boolean, + hasPattern: boolean, + patternUploaded: boolean, +): number { + if (!isConnected) return 1; + + // Check if machine needs homing (Initial state) + if (machineStatus === MachineStatus.Initial) return 2; + + if (!hasPattern) return 3; + if (!patternUploaded) return 4; + + // After upload, determine step based on machine status + switch (machineStatus) { + case MachineStatus.IDLE: + case MachineStatus.MASK_TRACE_LOCK_WAIT: + case MachineStatus.MASK_TRACING: + return 5; + + case MachineStatus.MASK_TRACE_COMPLETE: + case MachineStatus.SEWING_WAIT: + return 6; + + case MachineStatus.SEWING: + case MachineStatus.COLOR_CHANGE_WAIT: + case MachineStatus.PAUSE: + case MachineStatus.STOP: + case MachineStatus.SEWING_INTERRUPTION: + return 7; + + case MachineStatus.SEWING_COMPLETE: + return 8; + + default: + return 5; + } +}