Add Retry for failed results
A failed plan item gets a Retry button on its card and in the preview. It asks the engine for a fresh quote for that item and starts it again, while the engine still allows another attempt (three per item). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -8,7 +8,7 @@ import { Empty, PageHead, SkeletonCards, SkeletonLines, SkeletonRows } from '../
|
||||
import { toast } from '../components/Toast'
|
||||
import { describeRule, kindLabel, type ContentKind } from '../express/parse'
|
||||
import {
|
||||
decideMaterial, download, exportPptx, fileName, isWorking, printPdf, listOf, listResults, materialContent, materialKey, questionsOf, reviewSource, slidesOf, textOf,
|
||||
decideMaterial, download, exportPptx, fileName, isWorking, printPdf, listOf, listResults, materialContent, materialKey, questionsOf, retryMaterial, reviewSource, slidesOf, textOf,
|
||||
titleOf, toMarkdown, type FoundSource, type Material, type MaterialContent, type MaterialStatus,
|
||||
} from '../express/results'
|
||||
import { MASTERY, scormPackage } from '../express/scorm'
|
||||
@@ -132,13 +132,34 @@ function ContentCard({ material: m, onOpen }: { material: Material; onOpen: () =
|
||||
<span title={describeRule(m.rule)}>{describeRule(m.rule)}</span>
|
||||
{working
|
||||
? <span>{m.video ? t('res.videoProgress', { n: m.video.progress }) : t('res.working')}</span>
|
||||
: <button className="text-btn" onClick={onOpen}>{t('res.open')} <Icon name="arrow-right" /></button>}
|
||||
: <div className="content-card-actions">
|
||||
{m.retry && <RetryButton material={m} className="text-btn" />}
|
||||
<button className="text-btn" onClick={onOpen}>{t('res.open')} <Icon name="arrow-right" /></button>
|
||||
</div>}
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
)
|
||||
}
|
||||
|
||||
// A failed plan item is produced again with a fresh quote; the result then shows as preparing.
|
||||
function RetryButton({ material: m, className }: { material: Material; className: string }) {
|
||||
const queryClient = useQueryClient()
|
||||
const retry = useMutation({
|
||||
mutationFn: () => retryMaterial(m),
|
||||
onSuccess: () => {
|
||||
toast(t('res.retried'))
|
||||
queryClient.invalidateQueries({ queryKey: ['results'] })
|
||||
},
|
||||
onError: e => toast(e.message, { error: true }),
|
||||
})
|
||||
return (
|
||||
<button className={className} disabled={retry.isPending} onClick={() => retry.mutate()}>
|
||||
{retry.isPending ? t('res.retrying') : t('res.retry')}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
function Preview({ material: m, onClose }: { material: Material; onClose: () => void }) {
|
||||
const ref = useRef<HTMLDialogElement>(null)
|
||||
const queryClient = useQueryClient()
|
||||
@@ -190,6 +211,7 @@ function Preview({ material: m, onClose }: { material: Material; onClose: () =>
|
||||
<div className="modal-foot">
|
||||
{decide.error ? <small className="error">{decide.error.message}</small> : <small>{t('res.footNote')}</small>}
|
||||
{m.composeSetId && <a className="btn" href={`${STUDIO_URL}/compose/${m.composeSetId}`} target="_blank" rel="noreferrer">{t('res.openStudio')}</a>}
|
||||
{m.retry && <RetryButton material={m} className="btn primary" />}
|
||||
{content.data?.content && <DownloadMenu material={m} content={content.data.content} outputId={content.data.outputId} />}
|
||||
{m.status === 'ready' && (
|
||||
<>
|
||||
|
||||
Reference in New Issue
Block a user