[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.  after clicking the button:  (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 <joostdecock@noreply.codeberg.org> Co-authored-by: Jonathan Haas <haasjona@gmail.com> Co-committed-by: Jonathan Haas <haasjona@gmail.com>
This commit is contained in:
parent
40eb75ce43
commit
b4a5393290
6 changed files with 96 additions and 15 deletions
|
@ -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 (
|
||||||
|
<Popout error>
|
||||||
|
<p>
|
||||||
|
Sorry, there were problems drafting your pattern with the given measurements and options.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Please double check your measurements according to{' '}
|
||||||
|
<Link href="/docs/measurements/">our documentation</Link>. Also check{' '}
|
||||||
|
<Link href="https://v4.freesewing.org/docs/about/faq/measurements-issues/">
|
||||||
|
our common measurement issues FAQ entry
|
||||||
|
</Link>
|
||||||
|
.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
If you believe your measurements are correct and/or if you'd like further assistance, you
|
||||||
|
can ask for help <Link href="https://forum.freesewing.eu">on our forum</Link>,{' '}
|
||||||
|
<Link href="https://discord.freesewing.org">our Discord server</Link>, or{' '}
|
||||||
|
<Link href="https://codeberg.org/freesewing/freesewing/issues">report an issue</Link>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className={'tw-mt-4'}>
|
||||||
|
<button
|
||||||
|
className={`tw-daisy-btn tw-daisy-btn-primary`}
|
||||||
|
onClick={() => setExpanded(!expanded)}
|
||||||
|
>
|
||||||
|
<ExpandIcon />
|
||||||
|
Show error details
|
||||||
|
</button>
|
||||||
|
<button className={`tw-daisy-btn tw-ml-4`} onClick={() => setHidden(true)}>
|
||||||
|
<CloseIcon />
|
||||||
|
Hide
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{expanded ? (
|
||||||
|
<div className={'tw-mt-8'}>
|
||||||
|
{failure ? <LogEntry key="failure" logEntry={failure} /> : null}
|
||||||
|
{errors.map((line, i) => (
|
||||||
|
<LogEntry key={i} logEntry={line} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</Popout>
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,10 +1,11 @@
|
||||||
// Dependencies
|
// Dependencies
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { draft, missingMeasurements, bundlePatternTranslations } from '../../lib/index.mjs'
|
import { bundlePatternTranslations, draft, missingMeasurements } from '../../lib/index.mjs'
|
||||||
// Components
|
// Components
|
||||||
import { Null } from '@freesewing/react/components/Null'
|
import { Null } from '@freesewing/react/components/Null'
|
||||||
import { ZoomablePattern } from '../ZoomablePattern.mjs'
|
import { ZoomablePattern } from '../ZoomablePattern.mjs'
|
||||||
import { PatternLayout } from '../PatternLayout.mjs'
|
import { PatternLayout } from '../PatternLayout.mjs'
|
||||||
|
import { DraftErrorHandler } from './DraftErrorHandler.mjs'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The draft view allows users to tweak their pattern
|
* 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
|
* 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
|
* Create object holding strings for translation
|
||||||
|
@ -52,6 +53,7 @@ export const DraftView = ({ Design, state, update, config, plugins = [], PluginO
|
||||||
const __html = pattern.render()
|
const __html = pattern.render()
|
||||||
output = (
|
output = (
|
||||||
<>
|
<>
|
||||||
|
<DraftErrorHandler {...{ failure, errors }} />
|
||||||
<PluginOutput {...{ pattern, Design, state, update, config }} />
|
<PluginOutput {...{ pattern, Design, state, update, config }} />
|
||||||
<ZoomablePattern update={update}>
|
<ZoomablePattern update={update}>
|
||||||
<div className="tw-w-full tw-h-full" dangerouslySetInnerHTML={{ __html }} />
|
<div className="tw-w-full tw-h-full" dangerouslySetInnerHTML={{ __html }} />
|
||||||
|
@ -65,6 +67,7 @@ export const DraftView = ({ Design, state, update, config, plugins = [], PluginO
|
||||||
renderProps = pattern.getRenderProps()
|
renderProps = pattern.getRenderProps()
|
||||||
output = (
|
output = (
|
||||||
<>
|
<>
|
||||||
|
<DraftErrorHandler {...{ failure, errors }} />
|
||||||
<PluginOutput {...{ pattern, Design, state, update, config, strings }} />
|
<PluginOutput {...{ pattern, Design, state, update, config, strings }} />
|
||||||
<ZoomablePattern
|
<ZoomablePattern
|
||||||
renderProps={renderProps}
|
renderProps={renderProps}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { Null } from '@freesewing/react/components/Null'
|
||||||
import { ZoomablePattern } from '../ZoomablePattern.mjs'
|
import { ZoomablePattern } from '../ZoomablePattern.mjs'
|
||||||
import { PatternLayout } from '../PatternLayout.mjs'
|
import { PatternLayout } from '../PatternLayout.mjs'
|
||||||
import { Xray } from '@freesewing/react/components/Xray'
|
import { Xray } from '@freesewing/react/components/Xray'
|
||||||
|
import { DraftErrorHandler } from './DraftErrorHandler.mjs'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The inspect view allows users to inspect their pattern
|
* The inspect view allows users to inspect their pattern
|
||||||
|
@ -47,7 +48,7 @@ export const InspectView = ({ Design, state, update, config }) => {
|
||||||
/*
|
/*
|
||||||
* First, attempt to draft
|
* 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
|
* Create object holding strings for translation
|
||||||
|
@ -57,6 +58,7 @@ export const InspectView = ({ Design, state, update, config }) => {
|
||||||
const renderProps = pattern.getRenderProps()
|
const renderProps = pattern.getRenderProps()
|
||||||
const output = (
|
const output = (
|
||||||
<>
|
<>
|
||||||
|
<DraftErrorHandler {...{ failure, errors }} />
|
||||||
<ZoomablePattern
|
<ZoomablePattern
|
||||||
renderProps={renderProps}
|
renderProps={renderProps}
|
||||||
patternLocale={state.locale || 'en'}
|
patternLocale={state.locale || 'en'}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { draft } from '../../lib/index.mjs'
|
||||||
// Components
|
// Components
|
||||||
import { PatternLayout } from '../PatternLayout.mjs'
|
import { PatternLayout } from '../PatternLayout.mjs'
|
||||||
import { MovablePattern } from '../MovablePattern.mjs'
|
import { MovablePattern } from '../MovablePattern.mjs'
|
||||||
|
import { DraftErrorHandler } from './DraftErrorHandler.mjs'
|
||||||
|
|
||||||
export const LayoutView = (props) => {
|
export const LayoutView = (props) => {
|
||||||
const { config, state, update, Design } = props
|
const { config, state, update, Design } = props
|
||||||
|
@ -23,9 +24,10 @@ export const LayoutView = (props) => {
|
||||||
* Now draft the pattern
|
* Now draft the pattern
|
||||||
*/
|
*/
|
||||||
const { pattern, failure, errors } = draft(Design, settings, [tilerPlugin(pageSettings)])
|
const { pattern, failure, errors } = draft(Design, settings, [tilerPlugin(pageSettings)])
|
||||||
if (failure) return <p>Draft failed. FIXME: Handle this gracefully.</p>
|
|
||||||
|
|
||||||
const output = (
|
const output = (
|
||||||
|
<>
|
||||||
|
<DraftErrorHandler {...{ failure, errors }} />
|
||||||
<MovablePattern
|
<MovablePattern
|
||||||
{...{
|
{...{
|
||||||
update,
|
update,
|
||||||
|
@ -34,6 +36,7 @@ export const LayoutView = (props) => {
|
||||||
state,
|
state,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
|
|
||||||
return <PatternLayout {...{ update, Design, output, state, pattern, config }} />
|
return <PatternLayout {...{ update, Design, output, state, pattern, config }} />
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
import React from 'react'
|
||||||
|
import Markdown from 'react-markdown'
|
||||||
|
|
||||||
|
export const LogEntry = ({ logEntry }) => (
|
||||||
|
<>
|
||||||
|
<div className="log-entry tw-mb-2">
|
||||||
|
<Markdown>{Array.isArray(logEntry) ? logEntry[0] : logEntry}</Markdown>
|
||||||
|
</div>
|
||||||
|
{/* uncomment to enable stacktrace view
|
||||||
|
Array.isArray(logEntry) && logEntry[1].stack ? (
|
||||||
|
<Highlight title="Stacktrace" raw={logEntry[1]?.stack} />
|
||||||
|
) : null */}
|
||||||
|
</>
|
||||||
|
)
|
|
@ -3,10 +3,10 @@ import { draft } from '../../lib/index.mjs'
|
||||||
// Hooks
|
// Hooks
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
// Components
|
// Components
|
||||||
import Markdown from 'react-markdown'
|
|
||||||
import { H1, H3 } from '@freesewing/react/components/Heading'
|
import { H1, H3 } from '@freesewing/react/components/Heading'
|
||||||
import { HeaderMenu } from '../HeaderMenu.mjs'
|
import { HeaderMenu } from '../HeaderMenu.mjs'
|
||||||
import { Tabs, Tab } from '@freesewing/react/components/Tab'
|
import { Tabs, Tab } from '@freesewing/react/components/Tab'
|
||||||
|
import { LogEntry } from './LogEntry.mjs'
|
||||||
|
|
||||||
// The log levels
|
// The log levels
|
||||||
const levels = ['error', 'warn', 'info', 'debug']
|
const levels = ['error', 'warn', 'info', 'debug']
|
||||||
|
@ -36,7 +36,7 @@ export const LogView = (props) => {
|
||||||
<>
|
<>
|
||||||
<H3>{level}</H3>
|
<H3>{level}</H3>
|
||||||
{pattern.setStores[0].logs[level].map((line, i) => (
|
{pattern.setStores[0].logs[level].map((line, i) => (
|
||||||
<Markdown key={i}>{line}</Markdown>
|
<LogEntry key={i} logEntry={line} />
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -50,7 +50,7 @@ export const LogView = (props) => {
|
||||||
<>
|
<>
|
||||||
<H3>{level}</H3>
|
<H3>{level}</H3>
|
||||||
{pattern.store.logs[level].map((line, i) => (
|
{pattern.store.logs[level].map((line, i) => (
|
||||||
<Markdown key={i}>{line}</Markdown>
|
<LogEntry key={i} logEntry={line} />
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue