The prompt props (see MigrationPromptProps).
The rendered migration prompt modal.
<MigrationPrompt
open={pending.length > 0}
steps={pending}
onApply={handleApply}
onCancel={handleCancel}
/>
export function MigrationPrompt({
open,
steps,
busy = false,
error,
onApply,
onCancel,
}: MigrationPromptProps) {
return (
<Modal
data-testid="migration-modal"
open={open}
aria-labelledby="migration-title"
aria-describedby="migration-body"
>
<AboutModalBox className={styles['migration-box']}>
<Typography
id="migration-title"
variant="h6"
component="h2"
className={styles['migration-title']}
gutterBottom
>
{i18n('migration_title')}
</Typography>
<Typography id="migration-body" variant="body2" gutterBottom>
{i18n('migration_body')}
</Typography>
<Stack className={styles['migration-steps']} spacing={0.5}>
{steps.map((step) => (
<Typography key={`${step.from}-${step.to}`} variant="caption" color="text.secondary">
{i18n('migration_step', [step.from, step.to, step.description])}
</Typography>
))}
</Stack>
{error && (
<Alert severity="error" sx={{ mt: 1 }}>
{error}
</Alert>
)}
<Stack
direction="row"
spacing={1}
justifyContent="flex-end"
className={styles['migration-actions']}
>
<Button data-testid="migration-cancel" onClick={onCancel} disabled={busy} color="inherit">
{i18n('migration_cancel')}
</Button>
<Button
data-testid="migration-apply"
onClick={onApply}
disabled={busy}
variant="contained"
>
{i18n('migration_apply')}
</Button>
</Stack>
</AboutModalBox>
</Modal>
);
}
Startup modal shown when the cached data was written by an older app version. Lists each pending migration (
from → to: description) and offers two choices: Apply Updates (run the migrations in place) or Cancel (clear the cache and start fresh). It is intentionally non-dismissable — there is no backdrop close — because the app defers loading cached data until the user decides.