Merge pull request #27 from jhbruhn/fix/error-badge-auto-popup

feature: Add error badge with auto-opening popover for machine errors
This commit is contained in:
Jan-Henrik Bruhn 2025-12-25 21:54:38 +01:00 committed by GitHub
commit 213be4670c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 203 additions and 72 deletions

View file

@ -1,3 +1,4 @@
import { useState, useEffect, useRef } from "react";
import { useShallow } from "zustand/react/shallow"; import { useShallow } from "zustand/react/shallow";
import { useMachineStore } from "../stores/useMachineStore"; import { useMachineStore } from "../stores/useMachineStore";
import { useUIStore } from "../stores/useUIStore"; import { useUIStore } from "../stores/useUIStore";
@ -7,6 +8,7 @@ import {
getStateVisualInfo, getStateVisualInfo,
getStatusIndicatorState, getStatusIndicatorState,
} from "../utils/machineStateHelpers"; } from "../utils/machineStateHelpers";
import { hasError, getErrorDetails } from "../utils/errorCodeHelpers";
import { import {
CheckCircleIcon, CheckCircleIcon,
BoltIcon, BoltIcon,
@ -58,6 +60,15 @@ export function AppHeader() {
})), })),
); );
// State management for error popover auto-open/close
const [errorPopoverOpen, setErrorPopoverOpen] = useState(false);
const [dismissedErrorCode, setDismissedErrorCode] = useState<number | null>(
null,
);
const prevMachineErrorRef = useRef<number | undefined>(undefined);
const prevErrorMessageRef = useRef<string | null>(null);
const prevPyodideErrorRef = useRef<string | null>(null);
// Get state visual info for header status badge // Get state visual info for header status badge
const stateVisual = getStateVisualInfo(machineStatus); const stateVisual = getStateVisualInfo(machineStatus);
const stateIcons = { const stateIcons = {
@ -75,6 +86,66 @@ export function AppHeader() {
? getStatusIndicatorState(machineStatus) ? getStatusIndicatorState(machineStatus)
: "idle"; : "idle";
// Auto-open/close error popover based on error state changes
/* eslint-disable react-hooks/set-state-in-effect */
useEffect(() => {
const currentError = machineError;
const prevError = prevMachineErrorRef.current;
const currentErrorMessage = machineErrorMessage;
const prevErrorMessage = prevErrorMessageRef.current;
const currentPyodideError = pyodideError;
const prevPyodideError = prevPyodideErrorRef.current;
// Check if there's any error now
const hasAnyError =
machineErrorMessage || pyodideError || hasError(currentError);
// Check if there was any error before
const hadAnyError =
prevErrorMessage || prevPyodideError || hasError(prevError);
// Auto-open popover when new error appears
const isNewMachineError =
hasError(currentError) &&
currentError !== prevError &&
currentError !== dismissedErrorCode;
const isNewErrorMessage =
currentErrorMessage && currentErrorMessage !== prevErrorMessage;
const isNewPyodideError =
currentPyodideError && currentPyodideError !== prevPyodideError;
if (isNewMachineError || isNewErrorMessage || isNewPyodideError) {
setErrorPopoverOpen(true);
}
// Auto-close popover when all errors are cleared
if (!hasAnyError && hadAnyError) {
setErrorPopoverOpen(false);
setDismissedErrorCode(null); // Reset dismissed tracking
}
// Update refs for next comparison
prevMachineErrorRef.current = currentError;
prevErrorMessageRef.current = currentErrorMessage;
prevPyodideErrorRef.current = currentPyodideError;
}, [machineError, machineErrorMessage, pyodideError, dismissedErrorCode]);
/* eslint-enable react-hooks/set-state-in-effect */
// Handle manual popover dismiss
const handlePopoverOpenChange = (open: boolean) => {
setErrorPopoverOpen(open);
// If user manually closes it, remember the current error state to prevent reopening
if (!open) {
// For machine errors, track the error code
if (hasError(machineError)) {
setDismissedErrorCode(machineError);
}
// Update refs so we don't reopen for the same error message/pyodide error
prevErrorMessageRef.current = machineErrorMessage;
prevPyodideErrorRef.current = pyodideError;
}
};
return ( return (
<TooltipProvider> <TooltipProvider>
<header className="bg-gradient-to-r from-primary-600 via-primary-700 to-primary-800 dark:from-primary-700 dark:via-primary-800 dark:to-primary-900 px-4 sm:px-6 lg:px-8 py-3 shadow-lg border-b-2 border-primary-900/20 dark:border-primary-800/30 flex-shrink-0"> <header className="bg-gradient-to-r from-primary-600 via-primary-700 to-primary-800 dark:from-primary-700 dark:via-primary-800 dark:to-primary-900 px-4 sm:px-6 lg:px-8 py-3 shadow-lg border-b-2 border-primary-900/20 dark:border-primary-800/30 flex-shrink-0">
@ -166,22 +237,22 @@ export function AppHeader() {
)} )}
{/* Error indicator - always render to prevent layout shift */} {/* Error indicator - always render to prevent layout shift */}
<Popover> <Popover
open={errorPopoverOpen}
onOpenChange={handlePopoverOpenChange}
>
<PopoverTrigger asChild> <PopoverTrigger asChild>
<Button <button
size="sm"
variant="destructive"
className={cn( className={cn(
"gap-1.5 flex-shrink-0", "inline-flex items-center rounded-full border border-transparent bg-destructive text-white px-2.5 py-1.5 text-xs font-semibold gap-1.5 cursor-pointer hover:bg-destructive/90 transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-destructive focus-visible:ring-offset-2",
machineErrorMessage || pyodideError machineErrorMessage || pyodideError
? "animate-pulse hover:animate-none" ? "animate-pulse hover:animate-none"
: "invisible pointer-events-none", : "invisible pointer-events-none",
)} )}
aria-label="View error details" aria-label="View error details"
disabled={!(machineErrorMessage || pyodideError)}
> >
<ExclamationTriangleIcon className="w-3.5 h-3.5 flex-shrink-0" /> <ExclamationTriangleIcon className="w-3.5 h-3.5 flex-shrink-0" />
<span> <span className="font-semibold">
{(() => { {(() => {
if (pyodideError) return "Python Error"; if (pyodideError) return "Python Error";
if (isPairingError) return "Pairing Required"; if (isPairingError) return "Pairing Required";
@ -202,17 +273,19 @@ export function AppHeader() {
return "Pattern Error"; return "Pattern Error";
} }
if (machineError !== undefined) { if (machineError !== undefined) {
return `Machine Error`; // Get short name from error details
const errorDetails = getErrorDetails(machineError);
return errorDetails?.shortName || "Machine Error";
} }
// Default fallback // Default fallback
return "Error"; return "Error";
})()} })()}
</span> </span>
</Button> </button>
</PopoverTrigger> </PopoverTrigger>
{/* Error popover content */} {/* Error popover content - unchanged */}
{(machineErrorMessage || pyodideError) && ( {(machineErrorMessage || pyodideError) && (
<ErrorPopoverContent <ErrorPopoverContent
machineError={ machineError={

View file

@ -8,7 +8,6 @@ import {
ExclamationTriangleIcon, ExclamationTriangleIcon,
} from "@heroicons/react/24/solid"; } from "@heroicons/react/24/solid";
import { MachineStatus } from "../types/machine"; import { MachineStatus } from "../types/machine";
import { getErrorDetails, hasError } from "../utils/errorCodeHelpers";
interface Step { interface Step {
id: number; id: number;
@ -28,38 +27,7 @@ const steps: Step[] = [
]; ];
// Helper function to get guide content for a step // Helper function to get guide content for a step
function getGuideContent( function getGuideContent(stepId: number, machineStatus: MachineStatus) {
stepId: number,
machineStatus: MachineStatus,
hasError: boolean,
errorCode?: number,
errorMessage?: string,
) {
// Check for errors first
if (hasError) {
const errorDetails = getErrorDetails(errorCode);
if (errorDetails?.isInformational) {
return {
type: "info" as const,
title: errorDetails.title,
description: errorDetails.description,
items: errorDetails.solutions || [],
};
}
return {
type: "error" as const,
title: errorDetails?.title || "Error Occurred",
description:
errorDetails?.description ||
errorMessage ||
"An error occurred. Please check the machine and try again.",
items: errorDetails?.solutions || [],
errorCode,
};
}
// Return content based on step // Return content based on step
switch (stepId) { switch (stepId) {
case 1: case 1:
@ -273,17 +241,10 @@ function getCurrentStep(
export function WorkflowStepper() { export function WorkflowStepper() {
// Machine store // Machine store
const { const { machineStatus, isConnected } = useMachineStore(
machineStatus,
isConnected,
machineError,
error: errorMessage,
} = useMachineStore(
useShallow((state) => ({ useShallow((state) => ({
machineStatus: state.machineStatus, machineStatus: state.machineStatus,
isConnected: state.isConnected, isConnected: state.isConnected,
machineError: state.machineError,
error: state.error,
})), })),
); );
@ -297,7 +258,6 @@ export function WorkflowStepper() {
// Derived state: pattern is uploaded if machine has pattern info // Derived state: pattern is uploaded if machine has pattern info
const patternUploaded = usePatternUploaded(); const patternUploaded = usePatternUploaded();
const hasPattern = pesData !== null; const hasPattern = pesData !== null;
const hasErrorFlag = hasError(machineError);
const currentStep = getCurrentStep( const currentStep = getCurrentStep(
machineStatus, machineStatus,
isConnected, isConnected,
@ -443,13 +403,7 @@ export function WorkflowStepper() {
aria-label="Step guidance" aria-label="Step guidance"
> >
{(() => { {(() => {
const content = getGuideContent( const content = getGuideContent(popoverStep, machineStatus);
popoverStep,
machineStatus,
hasErrorFlag,
machineError,
errorMessage || undefined,
);
if (!content) return null; if (!content) return null;
const colorClasses = { const colorClasses = {
@ -497,7 +451,7 @@ export function WorkflowStepper() {
}; };
const Icon = const Icon =
content.type === "error" content.type === "warning"
? ExclamationTriangleIcon ? ExclamationTriangleIcon
: InformationCircleIcon; : InformationCircleIcon;
@ -538,18 +492,6 @@ export function WorkflowStepper() {
))} ))}
</ul> </ul>
)} )}
{content.type === "error" &&
content.errorCode !== undefined && (
<p
className={`text-xs ${descColorClasses[content.type]} mt-3 font-mono`}
>
Error Code: 0x
{content.errorCode
.toString(16)
.toUpperCase()
.padStart(2, "0")}
</p>
)}
</div> </div>
</div> </div>
</div> </div>

View file

@ -0,0 +1,84 @@
import { describe, it, expect } from "vitest";
import { getErrorDetails, SewingMachineError } from "./errorCodeHelpers";
describe("errorCodeHelpers", () => {
describe("shortName validation", () => {
it("should ensure all error shortNames are 15 characters or less", () => {
// Get all error codes except None (0xDD) and Unknown (0xEE) since they might not have details
const errorCodes = Object.values(SewingMachineError).filter(
(code) =>
code !== SewingMachineError.None &&
code !== SewingMachineError.Unknown &&
code !== SewingMachineError.OtherError,
);
const violations: Array<{
code: number;
shortName: string;
length: number;
}> = [];
errorCodes.forEach((code) => {
const details = getErrorDetails(code);
if (details?.shortName) {
const length = details.shortName.length;
if (length > 15) {
violations.push({
code,
shortName: details.shortName,
length,
});
}
}
});
// If there are violations, create a helpful error message
if (violations.length > 0) {
const violationMessages = violations
.map(
(v) =>
`Error code 0x${v.code.toString(16).toUpperCase()}: "${v.shortName}" (${v.length} chars)`,
)
.join("\n ");
expect.fail(
`The following error shortNames exceed 15 characters:\n ${violationMessages}`,
);
}
// Assertion to confirm test ran
expect(violations).toHaveLength(0);
});
it("should ensure all error details have a shortName", () => {
// Get all error codes except None (0xDD) and Unknown (0xEE)
const errorCodes = Object.values(SewingMachineError).filter(
(code) =>
code !== SewingMachineError.None &&
code !== SewingMachineError.Unknown &&
code !== SewingMachineError.OtherError,
);
const missing: number[] = [];
errorCodes.forEach((code) => {
const details = getErrorDetails(code);
if (details && !details.shortName) {
missing.push(code);
}
});
if (missing.length > 0) {
const missingCodes = missing
.map((code) => `0x${code.toString(16).toUpperCase()}`)
.join(", ");
expect.fail(
`The following error codes are missing shortName: ${missingCodes}`,
);
}
expect(missing).toHaveLength(0);
});
});
});

View file

@ -42,6 +42,8 @@ export const SewingMachineError = {
*/ */
interface ErrorInfo { interface ErrorInfo {
title: string; title: string;
/** Short name for badge display (max 15 characters) */
shortName: string;
description: string; description: string;
solutions: string[]; solutions: string[];
/** If true, this "error" is really just an informational step, not a real error */ /** If true, this "error" is really just an informational step, not a real error */
@ -55,12 +57,14 @@ interface ErrorInfo {
const ERROR_DETAILS: Record<number, ErrorInfo> = { const ERROR_DETAILS: Record<number, ErrorInfo> = {
[SewingMachineError.NeedlePositionError]: { [SewingMachineError.NeedlePositionError]: {
title: "The Needle is Down", title: "The Needle is Down",
shortName: "Needle Down",
description: description:
"The needle is in the down position and needs to be raised before continuing.", "The needle is in the down position and needs to be raised before continuing.",
solutions: ["Press the needle position switch to raise the needle"], solutions: ["Press the needle position switch to raise the needle"],
}, },
[SewingMachineError.SafetyError]: { [SewingMachineError.SafetyError]: {
title: "Safety Error", title: "Safety Error",
shortName: "Safety Error",
description: "The machine is sensing an operational issue.", description: "The machine is sensing an operational issue.",
solutions: [ solutions: [
"Remove the thread on the top of the fabric and then remove the needle", "Remove the thread on the top of the fabric and then remove the needle",
@ -72,21 +76,25 @@ const ERROR_DETAILS: Record<number, ErrorInfo> = {
}, },
[SewingMachineError.LowerThreadSafetyError]: { [SewingMachineError.LowerThreadSafetyError]: {
title: "Lower Thread Safety Error", title: "Lower Thread Safety Error",
shortName: "Lower Thread",
description: "The bobbin winder safety device is activated.", description: "The bobbin winder safety device is activated.",
solutions: ["Check if the thread is tangled"], solutions: ["Check if the thread is tangled"],
}, },
[SewingMachineError.LowerThreadFreeError]: { [SewingMachineError.LowerThreadFreeError]: {
title: "Lower Thread Free Error", title: "Lower Thread Free Error",
shortName: "Lower Thread",
description: "Problem with lower thread.", description: "Problem with lower thread.",
solutions: ["Slide the bobbin winder shaft toward the front"], solutions: ["Slide the bobbin winder shaft toward the front"],
}, },
[SewingMachineError.RestartError10]: { [SewingMachineError.RestartError10]: {
title: "Restart Required", title: "Restart Required",
shortName: "Restart Needed",
description: "A malfunction occurred.", description: "A malfunction occurred.",
solutions: ["Turn the machine off, then on again"], solutions: ["Turn the machine off, then on again"],
}, },
[SewingMachineError.RestartError11]: { [SewingMachineError.RestartError11]: {
title: "Restart Required (M519411)", title: "Restart Required (M519411)",
shortName: "Restart Needed",
description: "A malfunction occurred. Error code: M519411", description: "A malfunction occurred. Error code: M519411",
solutions: [ solutions: [
"Turn the machine off, then on again", "Turn the machine off, then on again",
@ -95,6 +103,7 @@ const ERROR_DETAILS: Record<number, ErrorInfo> = {
}, },
[SewingMachineError.RestartError12]: { [SewingMachineError.RestartError12]: {
title: "Restart Required (M519412)", title: "Restart Required (M519412)",
shortName: "Restart Needed",
description: "A malfunction occurred. Error code: M519412", description: "A malfunction occurred. Error code: M519412",
solutions: [ solutions: [
"Turn the machine off, then on again", "Turn the machine off, then on again",
@ -103,6 +112,7 @@ const ERROR_DETAILS: Record<number, ErrorInfo> = {
}, },
[SewingMachineError.RestartError13]: { [SewingMachineError.RestartError13]: {
title: "Restart Required (M519413)", title: "Restart Required (M519413)",
shortName: "Restart Needed",
description: "A malfunction occurred. Error code: M519413", description: "A malfunction occurred. Error code: M519413",
solutions: [ solutions: [
"Turn the machine off, then on again", "Turn the machine off, then on again",
@ -111,6 +121,7 @@ const ERROR_DETAILS: Record<number, ErrorInfo> = {
}, },
[SewingMachineError.RestartError14]: { [SewingMachineError.RestartError14]: {
title: "Restart Required (M519414)", title: "Restart Required (M519414)",
shortName: "Restart Needed",
description: "A malfunction occurred. Error code: M519414", description: "A malfunction occurred. Error code: M519414",
solutions: [ solutions: [
"Turn the machine off, then on again", "Turn the machine off, then on again",
@ -119,6 +130,7 @@ const ERROR_DETAILS: Record<number, ErrorInfo> = {
}, },
[SewingMachineError.RestartError15]: { [SewingMachineError.RestartError15]: {
title: "Restart Required (M519415)", title: "Restart Required (M519415)",
shortName: "Restart Needed",
description: "A malfunction occurred. Error code: M519415", description: "A malfunction occurred. Error code: M519415",
solutions: [ solutions: [
"Turn the machine off, then on again", "Turn the machine off, then on again",
@ -127,6 +139,7 @@ const ERROR_DETAILS: Record<number, ErrorInfo> = {
}, },
[SewingMachineError.RestartError16]: { [SewingMachineError.RestartError16]: {
title: "Restart Required (M519416)", title: "Restart Required (M519416)",
shortName: "Restart Needed",
description: "A malfunction occurred. Error code: M519416", description: "A malfunction occurred. Error code: M519416",
solutions: [ solutions: [
"Turn the machine off, then on again", "Turn the machine off, then on again",
@ -135,6 +148,7 @@ const ERROR_DETAILS: Record<number, ErrorInfo> = {
}, },
[SewingMachineError.RestartError17]: { [SewingMachineError.RestartError17]: {
title: "Restart Required (M519417)", title: "Restart Required (M519417)",
shortName: "Restart Needed",
description: "A malfunction occurred. Error code: M519417", description: "A malfunction occurred. Error code: M519417",
solutions: [ solutions: [
"Turn the machine off, then on again", "Turn the machine off, then on again",
@ -143,6 +157,7 @@ const ERROR_DETAILS: Record<number, ErrorInfo> = {
}, },
[SewingMachineError.RestartError18]: { [SewingMachineError.RestartError18]: {
title: "Restart Required (M519418)", title: "Restart Required (M519418)",
shortName: "Restart Needed",
description: "A malfunction occurred. Error code: M519418", description: "A malfunction occurred. Error code: M519418",
solutions: [ solutions: [
"Turn the machine off, then on again", "Turn the machine off, then on again",
@ -151,6 +166,7 @@ const ERROR_DETAILS: Record<number, ErrorInfo> = {
}, },
[SewingMachineError.RestartError19]: { [SewingMachineError.RestartError19]: {
title: "Restart Required (M519419)", title: "Restart Required (M519419)",
shortName: "Restart Needed",
description: "A malfunction occurred. Error code: M519419", description: "A malfunction occurred. Error code: M519419",
solutions: [ solutions: [
"Turn the machine off, then on again", "Turn the machine off, then on again",
@ -159,6 +175,7 @@ const ERROR_DETAILS: Record<number, ErrorInfo> = {
}, },
[SewingMachineError.RestartError1A]: { [SewingMachineError.RestartError1A]: {
title: "Restart Required (M51941A)", title: "Restart Required (M51941A)",
shortName: "Restart Needed",
description: "A malfunction occurred. Error code: M51941A", description: "A malfunction occurred. Error code: M51941A",
solutions: [ solutions: [
"Turn the machine off, then on again", "Turn the machine off, then on again",
@ -167,6 +184,7 @@ const ERROR_DETAILS: Record<number, ErrorInfo> = {
}, },
[SewingMachineError.RestartError1B]: { [SewingMachineError.RestartError1B]: {
title: "Restart Required (M51941B)", title: "Restart Required (M51941B)",
shortName: "Restart Needed",
description: "A malfunction occurred. Error code: M51941B", description: "A malfunction occurred. Error code: M51941B",
solutions: [ solutions: [
"Turn the machine off, then on again", "Turn the machine off, then on again",
@ -175,6 +193,7 @@ const ERROR_DETAILS: Record<number, ErrorInfo> = {
}, },
[SewingMachineError.RestartError1C]: { [SewingMachineError.RestartError1C]: {
title: "Restart Required (M51941C)", title: "Restart Required (M51941C)",
shortName: "Restart Needed",
description: "A malfunction occurred. Error code: M51941C", description: "A malfunction occurred. Error code: M51941C",
solutions: [ solutions: [
"Turn the machine off, then on again", "Turn the machine off, then on again",
@ -183,6 +202,7 @@ const ERROR_DETAILS: Record<number, ErrorInfo> = {
}, },
[SewingMachineError.NeedlePlateError]: { [SewingMachineError.NeedlePlateError]: {
title: "Needle Plate Error", title: "Needle Plate Error",
shortName: "Needle Plate",
description: "Check the needle plate cover.", description: "Check the needle plate cover.",
solutions: [ solutions: [
"Reattach the needle plate cover", "Reattach the needle plate cover",
@ -191,11 +211,13 @@ const ERROR_DETAILS: Record<number, ErrorInfo> = {
}, },
[SewingMachineError.ThreadLeverError]: { [SewingMachineError.ThreadLeverError]: {
title: "Thread Lever Error", title: "Thread Lever Error",
shortName: "Thread Lever",
description: "The needle threading lever is not in its original position.", description: "The needle threading lever is not in its original position.",
solutions: ["Return the needle threading lever to its original position"], solutions: ["Return the needle threading lever to its original position"],
}, },
[SewingMachineError.UpperThreadError]: { [SewingMachineError.UpperThreadError]: {
title: "Upper Thread Error", title: "Upper Thread Error",
shortName: "Upper Thread",
description: "Check and rethread the upper thread.", description: "Check and rethread the upper thread.",
solutions: [ solutions: [
"Check the upper thread and rethread it", "Check the upper thread and rethread it",
@ -204,6 +226,7 @@ const ERROR_DETAILS: Record<number, ErrorInfo> = {
}, },
[SewingMachineError.LowerThreadError]: { [SewingMachineError.LowerThreadError]: {
title: "Lower Thread Error", title: "Lower Thread Error",
shortName: "Lower Thread",
description: "The bobbin thread is almost empty.", description: "The bobbin thread is almost empty.",
solutions: [ solutions: [
"Replace the bobbin thread", "Replace the bobbin thread",
@ -212,6 +235,7 @@ const ERROR_DETAILS: Record<number, ErrorInfo> = {
}, },
[SewingMachineError.UpperThreadSewingStartError]: { [SewingMachineError.UpperThreadSewingStartError]: {
title: "Upper Thread Error at Sewing Start", title: "Upper Thread Error at Sewing Start",
shortName: "Upper Thread",
description: "Check and rethread the upper thread.", description: "Check and rethread the upper thread.",
solutions: [ solutions: [
"Press the Accept button to resolve the error", "Press the Accept button to resolve the error",
@ -221,21 +245,25 @@ const ERROR_DETAILS: Record<number, ErrorInfo> = {
}, },
[SewingMachineError.PRWiperError]: { [SewingMachineError.PRWiperError]: {
title: "PR Wiper Error", title: "PR Wiper Error",
shortName: "PR Wiper",
description: "PR Wiper Error.", description: "PR Wiper Error.",
solutions: ["Press the Accept button to resolve the error"], solutions: ["Press the Accept button to resolve the error"],
}, },
[SewingMachineError.HoopError]: { [SewingMachineError.HoopError]: {
title: "Hoop Error", title: "Hoop Error",
shortName: "Hoop Error",
description: "This embroidery frame cannot be used.", description: "This embroidery frame cannot be used.",
solutions: ["Use another frame that fits the pattern"], solutions: ["Use another frame that fits the pattern"],
}, },
[SewingMachineError.NoHoopError]: { [SewingMachineError.NoHoopError]: {
title: "No Hoop Detected", title: "No Hoop Detected",
shortName: "No Hoop",
description: "No hoop attached.", description: "No hoop attached.",
solutions: ["Attach the embroidery hoop"], solutions: ["Attach the embroidery hoop"],
}, },
[SewingMachineError.InitialHoopError]: { [SewingMachineError.InitialHoopError]: {
title: "Machine Initialization Required", title: "Machine Initialization Required",
shortName: "Init Required",
description: "An initial homing procedure must be performed.", description: "An initial homing procedure must be performed.",
solutions: [ solutions: [
"Remove the embroidery hoop from the machine completely", "Remove the embroidery hoop from the machine completely",
@ -248,12 +276,14 @@ const ERROR_DETAILS: Record<number, ErrorInfo> = {
}, },
[SewingMachineError.RegularInspectionError]: { [SewingMachineError.RegularInspectionError]: {
title: "Regular Inspection Required", title: "Regular Inspection Required",
shortName: "Inspection Due",
description: description:
"Preventive maintenance is recommended. This message is displayed when maintenance is due.", "Preventive maintenance is recommended. This message is displayed when maintenance is due.",
solutions: ["Please contact the service center"], solutions: ["Please contact the service center"],
}, },
[SewingMachineError.Setting]: { [SewingMachineError.Setting]: {
title: "Settings Error", title: "Settings Error",
shortName: "Settings Error",
description: "Stitch count cannot be changed.", description: "Stitch count cannot be changed.",
solutions: ["This setting cannot be modified at this time"], solutions: ["This setting cannot be modified at this time"],
}, },
@ -358,6 +388,7 @@ export function getErrorDetails(
if (errorTitle) { if (errorTitle) {
return { return {
title: errorTitle, title: errorTitle,
shortName: errorTitle.length > 15 ? "Machine Error" : errorTitle,
description: "Please check the machine display for more information.", description: "Please check the machine display for more information.",
solutions: [ solutions: [
"Consult your machine manual for specific troubleshooting steps", "Consult your machine manual for specific troubleshooting steps",
@ -370,6 +401,7 @@ export function getErrorDetails(
// Unknown error code // Unknown error code
return { return {
title: `Machine Error 0x${errorCode.toString(16).toUpperCase().padStart(2, "0")}`, title: `Machine Error 0x${errorCode.toString(16).toUpperCase().padStart(2, "0")}`,
shortName: "Machine Error",
description: description:
"The machine has reported an error code that is not recognized.", "The machine has reported an error code that is not recognized.",
solutions: [ solutions: [