feat(version): update app version to 0.8.80 and add changelog entries for recent fixes and enhancements
All checks were successful
Deploy Development / deploy (push) Successful in 37s
Test Suite / pytest-backend (push) Successful in 34s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 10s
Test Suite / playwright-tests (push) Successful in 54s

fix(RightsDeclarationDialog): change cancel button to icon for improved UI
feat(MediaLibraryPage): implement rights dialog for visibility promotions and enhance error handling
fix(version): update MediaLibraryPage version to 1.4.0 reflecting rights dialog changes
This commit is contained in:
Lars 2026-05-11 09:39:15 +02:00
parent fff30b49e1
commit 6586d3b68b
5 changed files with 91 additions and 31 deletions

View File

@ -1,6 +1,6 @@
# Shinkan Jinkendo Version Information
APP_VERSION = "0.8.78"
APP_VERSION = "0.8.80"
BUILD_DATE = "2026-05-11"
DB_SCHEMA_VERSION = "20260511048"
@ -31,6 +31,20 @@ MODULE_VERSIONS = {
}
CHANGELOG = [
{
"version": "0.8.80",
"date": "2026-05-11",
"changes": [
"Fix P-06: Bei Sichtbarkeits-Promotion (private→club, club→official) oeffnet das Frontend bei RIGHTS_SCOPE_INSUFFICIENT / LEGACY_REDECLARATION_REQUIRED automatisch den Einwilligungsdialog (promotion/redeclaration-Modus) statt eines alert()-Fehlers; PATCH wird nach Bestaetigung mit P-06-Feldern wiederholt.",
],
},
{
"version": "0.8.79",
"date": "2026-05-11",
"changes": [
"Fix: profiles.username -> profiles.name in Journal-Endpoint (500er behoben); RightsDeclarationDialog: doppelter Abbrechen-Button entfernt (Header-X statt Text); Playwright P-01: Platzhalter-Pruefung optional (echtes Impressum deployed); Playwright P-06c: Selector-Konflikt durch UI-Fix beseitigt.",
],
},
{
"version": "0.8.78",
"date": "2026-05-11",

View File

@ -103,8 +103,8 @@ export default function RightsDeclarationDialog({
<h3 id="rights-decl-title" className="admin-modal-sheet__title">
{titleMap[mode] || titleMap.upload}
</h3>
<button type="button" className="btn btn-secondary admin-modal-sheet__close" onClick={handleCancel}>
Abbrechen
<button type="button" className="admin-modal-sheet__close" onClick={handleCancel} aria-label="Schließen">
×
</button>
</div>

View File

@ -283,9 +283,13 @@ export default function MediaLibraryPage() {
const [uploadClubId, setUploadClubId] = useState('')
const [uploadBusy, setUploadBusy] = useState(false)
const [uploadSummary, setUploadSummary] = useState('')
// P-06: Rechte-Dialog
// P-06: Rechte-Dialog (Upload)
const [rightsDialogOpen, setRightsDialogOpen] = useState(false)
const [pendingUploadFiles, setPendingUploadFiles] = useState(null)
// P-06: Rechte-Dialog (Sichtbarkeits-Promotion)
const [rightsUpgradeDialogOpen, setRightsUpgradeDialogOpen] = useState(false)
const [rightsUpgradeMode, setRightsUpgradeMode] = useState('promotion')
const [pendingPatchBody, setPendingPatchBody] = useState(null)
const [journalModal, setJournalModal] = useState(null)
const [journalLoading, setJournalLoading] = useState(false)
const mediaListFetchSeqRef = useRef(0)
@ -418,32 +422,62 @@ export default function MediaLibraryPage() {
}
}
function parseApiErrorCode(msg) {
try {
const d = JSON.parse(msg)
if (d && d.code) return d
} catch { /* not JSON */ }
return null
}
const saveModal = async () => {
if (!modal || !modalDraft) return
const p = modal.permissions || {}
const body = {}
if (p.edit_metadata) {
body.original_filename = modalDraft.display_name
body.copyright_notice = modalDraft.copyright_notice
body.tags = parseTagsInput(modalDraft.tags_input)
}
if (p.change_visibility) {
body.visibility = modalDraft.visibility
if (modalDraft.visibility === 'club' || (modalDraft.visibility === 'private' && isPlatformAdmin)) {
const cid = Number(modalDraft.club_id)
if (!cid) {
alert('Bitte einen Verein wählen.')
return
}
body.club_id = cid
}
}
if (!Object.keys(body).length) return
setBusy(true)
try {
const body = {}
if (p.edit_metadata) {
body.original_filename = modalDraft.display_name
body.copyright_notice = modalDraft.copyright_notice
body.tags = parseTagsInput(modalDraft.tags_input)
}
if (p.change_visibility) {
body.visibility = modalDraft.visibility
if (modalDraft.visibility === 'club' || (modalDraft.visibility === 'private' && isPlatformAdmin)) {
const cid = Number(modalDraft.club_id)
if (!cid) {
alert('Bitte einen Verein wählen.')
setBusy(false)
return
}
body.club_id = cid
}
}
if (Object.keys(body).length) {
await api.patchMediaAsset(modal.id, body)
await api.patchMediaAsset(modal.id, body)
closeModal()
await loadItems()
} catch (e) {
const parsed = parseApiErrorCode(e.message)
if (parsed && (parsed.code === 'RIGHTS_SCOPE_INSUFFICIENT' || parsed.code === 'LEGACY_REDECLARATION_REQUIRED')) {
setPendingPatchBody(body)
setRightsUpgradeMode(parsed.code === 'LEGACY_REDECLARATION_REQUIRED' ? 'redeclaration' : 'promotion')
setRightsUpgradeDialogOpen(true)
return
}
alert(e.message || String(e))
} finally {
setBusy(false)
}
}
const doSaveWithRightsDecl = async (decl) => {
setRightsUpgradeDialogOpen(false)
if (!modal || !pendingPatchBody) return
const body = { ...pendingPatchBody, ...decl }
setPendingPatchBody(null)
setBusy(true)
try {
await api.patchMediaAsset(modal.id, body)
closeModal()
await loadItems()
} catch (e) {
@ -589,6 +623,14 @@ export default function MediaLibraryPage() {
targetVisibility={uploadVis}
mode="upload"
/>
<RightsDeclarationDialog
open={rightsUpgradeDialogOpen}
onCancel={() => { setRightsUpgradeDialogOpen(false); setPendingPatchBody(null) }}
onConfirm={doSaveWithRightsDecl}
targetVisibility={pendingPatchBody?.visibility || modal?.visibility || 'club'}
isPromotion={true}
mode={rightsUpgradeMode}
/>
<div className="media-library__container">
<header className="media-library__hero">
<div className="media-library__hero-row">

View File

@ -1,6 +1,6 @@
// Shinkan Jinkendo Frontend Version
export const APP_VERSION = "0.8.78"
export const APP_VERSION = "0.8.80"
export const BUILD_DATE = "2026-05-11"
export const PAGE_VERSIONS = {
@ -20,7 +20,7 @@ export const PAGE_VERSIONS = {
TrainingCoachPage: "1.0.0",
AdminCatalogsPage: "2.2.0",
TrainerContextsPage: "1.0.0",
MediaLibraryPage: "1.3.0", // P-06: Superadmin Medienjournal-Modal
MediaLibraryPage: "1.4.0", // P-06: Rechte-Dialog bei Sichtbarkeits-Promotion
ExerciseInlineFileMediaModal: "1.1.0", // P-06: RightsDeclarationDialog vor Upload
ExerciseInlineEmbedModal: "1.1.0", // P-06: RightsDeclarationDialog vor Embed-Upload
}

View File

@ -204,19 +204,23 @@ for (const route of LEGAL_ROUTES) {
// Seite ist erreichbar (kein Redirect zur Login-Seite)
expect(page.url()).toContain(route.path);
// Platzhalterhinweis sichtbar
const hinweis = await page.getByText('MUSTER / PLATZHALTER').first();
await expect(hinweis).toBeVisible();
// Seitentitel korrekt
await expect(page.getByRole('heading', { level: 1 })).toContainText(route.label);
// Platzhalterhinweis sichtbar nur wenn noch kein echtes Dokument veröffentlicht wurde
const hasPlaceholder = await page.getByText('MUSTER / PLATZHALTER').first().isVisible().catch(() => false);
if (hasPlaceholder) {
console.log(`✓ P-01: ${route.label} Platzhalter sichtbar`);
} else {
console.log(`✓ P-01: ${route.label} echtes Dokument veröffentlicht (kein Platzhalter)`);
}
// Reload funktioniert
await page.reload();
await page.waitForLoadState('networkidle');
expect(page.url()).toContain(route.path);
console.log(`✓ P-01: ${route.label} ohne Auth erreichbar, Platzhalter sichtbar, Reload OK`);
console.log(`✓ P-01: ${route.label} ohne Auth erreichbar, Reload OK`);
});
}