diff --git a/backend/routers/content_reports.py b/backend/routers/content_reports.py
index 454e444..a922996 100644
--- a/backend/routers/content_reports.py
+++ b/backend/routers/content_reports.py
@@ -716,7 +716,7 @@ def patch_content_report(
with get_db() as conn:
cur = get_cursor(conn)
cur.execute(
- "SELECT id, status, target_type, target_id FROM content_reports WHERE id = %s",
+ "SELECT id, status, resolution_note, target_type, target_id FROM content_reports WHERE id = %s",
(report_id,),
)
row = cur.fetchone()
@@ -724,6 +724,7 @@ def patch_content_report(
raise HTTPException(status_code=404, detail="Meldung nicht gefunden")
old_report = r2d(row)
old_status = old_report["status"]
+ old_note = (old_report.get("resolution_note") or "").strip()
updates = ["updated_at = NOW()"]
params = []
@@ -735,6 +736,10 @@ def patch_content_report(
updates.append("reviewed_by_profile_id = %s")
updates.append("reviewed_at = NOW()")
params.append(pid)
+ elif body.status == "submitted":
+ # Wieder öffnen: Prüferfelder zurücksetzen
+ updates.append("reviewed_by_profile_id = NULL")
+ updates.append("reviewed_at = NULL")
if body.resolution_note is not None:
updates.append("resolution_note = %s")
@@ -755,21 +760,34 @@ def patch_content_report(
updated = r2d(cur.fetchone())
new_status = updated["status"]
- # Audit-Log-Eintrag bei Status-Aenderung auf media_asset-Meldungen
- if body.status is not None and old_status != new_status and old_report["target_type"] == "media_asset":
- resolution = (body.resolution_note or "").strip() or None
- write_audit_log_entry(
- cur,
- int(old_report["target_id"]),
- pid,
- "content_report_filed",
- {"status": STATUS_LABELS_DE.get(old_status, old_status)},
- {
- "content_report_id": report_id,
- "status": STATUS_LABELS_DE.get(new_status, new_status),
- **({"begründung": resolution} if resolution else {}),
- },
- )
+ is_media = old_report["target_type"] == "media_asset"
+ if is_media:
+ new_note = (body.resolution_note or "").strip() if body.resolution_note is not None else old_note
+
+ # Audit-Log: Statuswechsel
+ if body.status is not None and old_status != new_status:
+ write_audit_log_entry(
+ cur,
+ int(old_report["target_id"]),
+ pid,
+ "content_report_filed",
+ {"status": STATUS_LABELS_DE.get(old_status, old_status)},
+ {
+ "content_report_id": report_id,
+ "status": STATUS_LABELS_DE.get(new_status, new_status),
+ **({"begründung": new_note} if new_note else {}),
+ },
+ )
+ # Audit-Log: reine Notizänderung (ohne Statuswechsel)
+ elif body.resolution_note is not None and new_note != old_note:
+ write_audit_log_entry(
+ cur,
+ int(old_report["target_id"]),
+ pid,
+ "content_report_filed",
+ {"content_report_id": report_id, "begründung": old_note or None},
+ {"content_report_id": report_id, "begründung": new_note or None},
+ )
conn.commit()
diff --git a/backend/version.py b/backend/version.py
index b3b3770..720f0cc 100644
--- a/backend/version.py
+++ b/backend/version.py
@@ -1,6 +1,6 @@
# Shinkan Jinkendo Version Information
-APP_VERSION = "0.8.91"
+APP_VERSION = "0.8.92"
BUILD_DATE = "2026-05-11"
DB_SCHEMA_VERSION = "20260511053"
@@ -30,10 +30,22 @@ MODULE_VERSIONS = {
"membership": "1.0.0",
"catalogs": "1.5.0", # Updated: Trainer Contexts API (Migration 012)
"maturity_models": "1.4.0", # matrix_stack_bundle: vollständiger Katalog+Modelle+Bindings Export/Import
- "content_reports": "1.3.0", # P-13: Club-Admin-Zugriff; Audit-Log; E-Mail (Admin + Melder); target_name-Fix
+ "content_reports": "1.4.0", # P-13: Workflow-Reset (wieder öffnen), Kommentar-Audit-Log, PATCH-Verbesserungen
}
CHANGELOG = [
+ {
+ "version": "0.8.92",
+ "date": "2026-05-11",
+ "changes": [
+ "Fix P-13: Badge in Medienbibliothek aktualisiert sich sofort nach Einreichen einer Meldung.",
+ "Fix P-13: Inbox zeigt Fehlermeldung statt leerem Bereich wenn Backend-Fehler auftritt.",
+ "Fix P-13: Meldungen lassen sich nach Abschluss wieder öffnen (Wieder-öffnen-Button).",
+ "Fix P-13: Bearbeitungskommentare werden separat im Audit-Log protokolliert.",
+ "Fix P-13: Reviewer-Felder werden beim Wieder-öffnen einer Meldung zurückgesetzt.",
+ "Fix P-13: Workflow-Balken im Meldungs-Detail zeigt aktuellen Bearbeitungsstand.",
+ ],
+ },
{
"version": "0.8.91",
"date": "2026-05-11",
diff --git a/frontend/src/components/ReportContentModal.jsx b/frontend/src/components/ReportContentModal.jsx
index 0476560..a01b1bd 100644
--- a/frontend/src/components/ReportContentModal.jsx
+++ b/frontend/src/components/ReportContentModal.jsx
@@ -22,7 +22,7 @@ const REASON_OPTIONS = [
{ value: 'other', label: 'Sonstiges' },
]
-export default function ReportContentModal({ targetType, targetId, targetLabel, onClose }) {
+export default function ReportContentModal({ targetType, targetId, targetLabel, onClose, onSuccess }) {
const { user } = useAuth()
const [reason, setReason] = useState('')
@@ -55,6 +55,7 @@ export default function ReportContentModal({ targetType, targetId, targetLabel,
good_faith_confirmed: true,
})
setSuccess(true)
+ if (onSuccess) onSuccess()
} catch (err) {
setError(err.message || String(err))
} finally {
diff --git a/frontend/src/context/OrgInboxContext.jsx b/frontend/src/context/OrgInboxContext.jsx
index 59e82de..84e994a 100644
--- a/frontend/src/context/OrgInboxContext.jsx
+++ b/frontend/src/context/OrgInboxContext.jsx
@@ -30,6 +30,7 @@ export function notifyOrgInboxChanged() {
export function OrgInboxProvider({ user, children }) {
const [items, setItems] = useState([])
const [contentReports, setContentReports] = useState([])
+ const [contentReportsError, setContentReportsError] = useState(null)
const canAccess = useMemo(() => canAccessOrgInbox(user), [user])
const canAccessReports = useMemo(() => canSeeContentReports(user), [user])
@@ -47,12 +48,15 @@ export function OrgInboxProvider({ user, children }) {
if (!canAccessReports) {
setContentReports([])
+ setContentReportsError(null)
} else {
try {
const data = await api.getInboxContentReports()
setContentReports(Array.isArray(data) ? data : [])
- } catch {
+ setContentReportsError(null)
+ } catch (err) {
setContentReports([])
+ setContentReportsError(err?.message || String(err))
}
}
}, [canAccess, canAccessReports])
@@ -61,6 +65,7 @@ export function OrgInboxProvider({ user, children }) {
if (!canAccess && !canAccessReports) {
setItems([])
setContentReports([])
+ setContentReportsError(null)
return undefined
}
let cancelled = false
@@ -76,9 +81,15 @@ export function OrgInboxProvider({ user, children }) {
if (canAccessReports) {
try {
const data = await api.getInboxContentReports()
- if (!cancelled) setContentReports(Array.isArray(data) ? data : [])
- } catch {
- if (!cancelled) setContentReports([])
+ if (!cancelled) {
+ setContentReports(Array.isArray(data) ? data : [])
+ setContentReportsError(null)
+ }
+ } catch (err) {
+ if (!cancelled) {
+ setContentReports([])
+ setContentReportsError(err?.message || String(err))
+ }
}
}
})()
@@ -99,12 +110,13 @@ export function OrgInboxProvider({ user, children }) {
inboxCount: items.length,
contentReports,
contentReportCount: contentReports.filter((r) => r.status === 'submitted').length,
+ contentReportsError,
refreshOrgInbox: refresh,
canAccessOrgInbox: canAccess,
canAccessContentReports: canAccessReports,
isSuperadmin: user?.role === 'superadmin',
}),
- [items, contentReports, refresh, canAccess, canAccessReports, user?.role]
+ [items, contentReports, contentReportsError, refresh, canAccess, canAccessReports, user?.role]
)
return
- Diese Meldung ist bereits abgeschlossen. -
+ {isClosed && ( ++ Fehler beim Laden: {contentReportsError} +
+Keine Inhaltsmeldungen.