shinkan-jinkendo/frontend/src/components/UnsavedChangesPrompt.jsx
Lars 49adb395dd
All checks were successful
Deploy Development / deploy (push) Successful in 41s
Test Suite / pytest-backend (push) Successful in 35s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 12s
Test Suite / playwright-tests (push) Successful in 1m15s
feat(version): bump to 0.8.110 and update project specifications
- Updated app version to 0.8.110 and database schema version to 20260512057, reflecting recent enhancements.
- Revised project status documentation to include new versioning and next steps for development.
- Enhanced the functional specification for training modules and combination exercises, detailing upcoming features and improvements.
- Improved technical specifications to align with the latest code changes, ensuring consistency across documentation.
- Introduced new UI elements for toast notifications and unsaved changes prompts to enhance user experience.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 16:34:38 +02:00

64 lines
1.9 KiB
JavaScript

import React from 'react'
import { createPortal } from 'react-dom'
/**
* Bei blocker.state === "blocked": Speichern, Abbrechen (auf Seite bleiben), Verwerfen (Navigation fortsetzen).
*/
export default function UnsavedChangesPrompt({
blocker,
isBusy,
onSave,
onDiscardWithoutSave,
title = 'Ungespeicherte Änderungen',
detail = 'Es gibt Änderungen, die noch nicht gespeichert sind. Was möchten Sie tun?',
}) {
if (!blocker || blocker.state !== 'blocked') return null
return createPortal(
<div
className="unsaved-prompt-backdrop"
role="dialog"
aria-modal="true"
aria-labelledby="unsaved-prompt-title"
onMouseDown={(e) => {
if (e.target === e.currentTarget && !isBusy) blocker.reset()
}}
>
<div className="unsaved-prompt-sheet card" onMouseDown={(e) => e.stopPropagation()}>
<h2 id="unsaved-prompt-title" className="unsaved-prompt-title">
{title}
</h2>
<p style={{ margin: '0 0 1rem', fontSize: '0.9375rem', color: 'var(--text2)', lineHeight: 1.55 }}>
{detail}
</p>
<div className="unsaved-prompt-actions">
<button
type="button"
className="btn btn-primary"
disabled={isBusy}
onClick={onSave}
>
Speichern
</button>
<button type="button" className="btn btn-secondary" disabled={isBusy} onClick={() => blocker.reset()}>
Abbrechen
</button>
<button
type="button"
className="btn"
disabled={isBusy}
style={{ borderColor: 'var(--danger)', color: 'var(--danger)' }}
onClick={() => {
onDiscardWithoutSave()
blocker.proceed()
}}
>
Nicht speichern
</button>
</div>
</div>
</div>,
document.body,
)
}