From b4a5393290b2e8f08a8c31896ceec41a60d5308e Mon Sep 17 00:00:00 2001 From: Jonathan Haas Date: Sun, 6 Apr 2025 14:42:42 +0000 Subject: [PATCH] [react] feat: Improved error handling when pattern drafting fails (#205) If drafting a part fails, the logs store contains an array [message, stacktrace] instead of a simple string. Handle this in the LogView. Also display an error message in the DraftView when there were errors during drafting. ![image](/attachments/62cdebe3-7825-4382-88fd-395337cda58b) after clicking the button: ![image](/attachments/f909887b-b9bd-467e-9e13-f850c38d76b3) (could also display the stack traces, but they're quite unhelpful and you can find them in the browser console anyway, the code for that is commented out) Closes #163 Reviewed-on: https://codeberg.org/freesewing/freesewing/pulls/205 Reviewed-by: Joost De Cock Co-authored-by: Jonathan Haas Co-committed-by: Jonathan Haas --- .../components/views/DraftErrorHandler.mjs | 59 +++++++++++++++++++ .../Editor/components/views/DraftView.mjs | 7 ++- .../Editor/components/views/InspectView.mjs | 4 +- .../Editor/components/views/LayoutView.mjs | 21 ++++--- .../Editor/components/views/LogEntry.mjs | 14 +++++ .../Editor/components/views/LogView.mjs | 6 +- 6 files changed, 96 insertions(+), 15 deletions(-) create mode 100644 packages/react/components/Editor/components/views/DraftErrorHandler.mjs create mode 100644 packages/react/components/Editor/components/views/LogEntry.mjs diff --git a/packages/react/components/Editor/components/views/DraftErrorHandler.mjs b/packages/react/components/Editor/components/views/DraftErrorHandler.mjs new file mode 100644 index 00000000000..f7047f3b130 --- /dev/null +++ b/packages/react/components/Editor/components/views/DraftErrorHandler.mjs @@ -0,0 +1,59 @@ +// Dependencies +import React, { useState } from 'react' +import { Popout } from '../../../Popout/index.mjs' +import { CloseIcon, ExpandIcon } from '../../../Icon/index.mjs' +import { Link } from '../../../Link/index.mjs' +import { LogEntry } from './LogEntry.mjs' + +export const DraftErrorHandler = ({ failure, errors }) => { + const [expanded, setExpanded] = useState(false) + const [hidden, setHidden] = useState(false) + + if (hidden || (!failure && errors.length === 0)) { + return null + } + + return ( + +

+ Sorry, there were problems drafting your pattern with the given measurements and options. +

+

+ Please double check your measurements according to{' '} + our documentation. Also check{' '} + + our common measurement issues FAQ entry + + . +

+

+ If you believe your measurements are correct and/or if you'd like further assistance, you + can ask for help on our forum,{' '} + our Discord server, or{' '} + report an issue. +

+ +
+ + +
+ {expanded ? ( +
+ {failure ? : null} + {errors.map((line, i) => ( + + ))} +
+ ) : null} +
+ ) +} diff --git a/packages/react/components/Editor/components/views/DraftView.mjs b/packages/react/components/Editor/components/views/DraftView.mjs index d26e80d30f8..fe399378225 100644 --- a/packages/react/components/Editor/components/views/DraftView.mjs +++ b/packages/react/components/Editor/components/views/DraftView.mjs @@ -1,10 +1,11 @@ // Dependencies import React from 'react' -import { draft, missingMeasurements, bundlePatternTranslations } from '../../lib/index.mjs' +import { bundlePatternTranslations, draft, missingMeasurements } from '../../lib/index.mjs' // Components import { Null } from '@freesewing/react/components/Null' import { ZoomablePattern } from '../ZoomablePattern.mjs' import { PatternLayout } from '../PatternLayout.mjs' +import { DraftErrorHandler } from './DraftErrorHandler.mjs' /** * The draft view allows users to tweak their pattern @@ -38,7 +39,7 @@ export const DraftView = ({ Design, state, update, config, plugins = [], PluginO /* * First, attempt to draft */ - const { pattern } = draft(Design, state.settings, plugins) + const { pattern, failure, errors } = draft(Design, state.settings, plugins) /* * Create object holding strings for translation @@ -52,6 +53,7 @@ export const DraftView = ({ Design, state, update, config, plugins = [], PluginO const __html = pattern.render() output = ( <> +
@@ -65,6 +67,7 @@ export const DraftView = ({ Design, state, update, config, plugins = [], PluginO renderProps = pattern.getRenderProps() output = ( <> + { /* * First, attempt to draft */ - const { pattern } = draft(Design, state.settings) + const { pattern, failure, errors } = draft(Design, state.settings, plugins) /* * Create object holding strings for translation @@ -57,6 +58,7 @@ export const InspectView = ({ Design, state, update, config }) => { const renderProps = pattern.getRenderProps() const output = ( <> + { const { config, state, update, Design } = props @@ -23,17 +24,19 @@ export const LayoutView = (props) => { * Now draft the pattern */ const { pattern, failure, errors } = draft(Design, settings, [tilerPlugin(pageSettings)]) - if (failure) return

Draft failed. FIXME: Handle this gracefully.

const output = ( - + <> + + + ) return diff --git a/packages/react/components/Editor/components/views/LogEntry.mjs b/packages/react/components/Editor/components/views/LogEntry.mjs new file mode 100644 index 00000000000..a5b01980e8f --- /dev/null +++ b/packages/react/components/Editor/components/views/LogEntry.mjs @@ -0,0 +1,14 @@ +import React from 'react' +import Markdown from 'react-markdown' + +export const LogEntry = ({ logEntry }) => ( + <> +
+ {Array.isArray(logEntry) ? logEntry[0] : logEntry} +
+ {/* uncomment to enable stacktrace view + Array.isArray(logEntry) && logEntry[1].stack ? ( + + ) : null */} + +) diff --git a/packages/react/components/Editor/components/views/LogView.mjs b/packages/react/components/Editor/components/views/LogView.mjs index a0fa4d50d3c..2c0dd2a46c3 100644 --- a/packages/react/components/Editor/components/views/LogView.mjs +++ b/packages/react/components/Editor/components/views/LogView.mjs @@ -3,10 +3,10 @@ import { draft } from '../../lib/index.mjs' // Hooks import React from 'react' // Components -import Markdown from 'react-markdown' import { H1, H3 } from '@freesewing/react/components/Heading' import { HeaderMenu } from '../HeaderMenu.mjs' import { Tabs, Tab } from '@freesewing/react/components/Tab' +import { LogEntry } from './LogEntry.mjs' // The log levels const levels = ['error', 'warn', 'info', 'debug'] @@ -36,7 +36,7 @@ export const LogView = (props) => { <>

{level}

{pattern.setStores[0].logs[level].map((line, i) => ( - {line} + ))} ) : null} @@ -50,7 +50,7 @@ export const LogView = (props) => { <>

{level}

{pattern.store.logs[level].map((line, i) => ( - {line} + ))} ) : null}