Kairo-Jinkendo/frontend/src/components/RecurringRhythmPanel.jsx
Lars 19e962d8ec
Some checks failed
Deploy Development / deploy (push) Successful in 49s
Test Suite / pytest-backend (push) Failing after 4m46s
Test Suite / k6 /api/health Baseline (push) Has been skipped
Test Suite / playwright-smoke (push) Has been skipped
Test Suite / lint-backend (push) Successful in 2s
Test Suite / compose-smoke (push) Has been skipped
fix(A1): Übungen an Stufen binden, Gates entkoppeln, Tages-Abschluss
Activity Set pro Stufe statt Gate-Titel-Recurring; Single-Active-Stufe bei Reopen; Complete heute schliesst Next Action; Heute-Panel fuer alle Uebungen der aktiven Stufe.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-28 13:00:06 +02:00

105 lines
3.6 KiB
JavaScript

import { Link } from 'react-router-dom'
import { RECURRING_STATUS_LABELS } from '../constants/status.js'
import { EmptyState } from './EmptyState.jsx'
import { scopedPath } from '../utils/routes.js'
function formatDueAt(iso) {
if (!iso) return null
try {
return new Date(iso).toLocaleString('de-DE')
} catch {
return iso
}
}
function activeMaturityStage(roadmapItems = []) {
return [...roadmapItems]
.filter((item) => item.item_type === 'maturity_stage' && item.status === 'active')
.sort((a, b) => (a.sort_order ?? 0) - (b.sort_order ?? 0))[0]
}
function practicesForStage(recurringItems = [], stageId) {
if (!stageId) {
return (recurringItems || []).filter((item) => item.roadmap_item_id)
}
return (recurringItems || []).filter((item) => item.roadmap_item_id === stageId)
}
export function RecurringRhythmPanel({
initiativeId,
roadmapItems = [],
recurringItems = [],
canManage = false,
embedded = true,
}) {
const stage = activeMaturityStage(roadmapItems)
const stagePractices = practicesForStage(recurringItems, stage?.id)
const active = stagePractices.filter((item) => item.status === 'active')
const paused = stagePractices.filter((item) => item.status === 'paused')
const body =
!stage ? (
<EmptyState message="Keine aktive Reifegrad-Stufe — Plan → Stufen prüfen." />
) : stagePractices.length === 0 ? (
<EmptyState message="Keine Übungen für diese Stufe — Starter-Kit oder Stufenwechsel prüfen." />
) : (
<>
<p className="recurring-rhythm-summary muted">
Stufe <strong>{stage.title}</strong> {active.length} aktiv
{paused.length > 0 ? `, ${paused.length} pausiert` : ''}.
</p>
<ul className="item-list recurring-rhythm-list">
{[...active, ...paused].map((item) => (
<li
key={item.id}
className={
'list-item card-list-item recurring-rhythm-item' +
(item.status === 'active' ? ' recurring-rhythm-item--active' : '') +
(item.today_completed ? ' recurring-rhythm-item--done-today' : '')
}
>
<div className="list-item-main">
<strong>{item.title}</strong>
<span className="muted list-item-sub">
{RECURRING_STATUS_LABELS[item.status] || item.status}
{item.today_completed ? ' · heute erledigt' : ''}
{!item.today_completed && item.next_due_at
? ` · Fällig: ${formatDueAt(item.next_due_at)}`
: ''}
</span>
{item.today_measurement_note && (
<span className="muted list-item-sub">Messwert: {item.today_measurement_note}</span>
)}
</div>
</li>
))}
</ul>
<p className="muted recurring-rhythm-hint">
{canManage
? 'Übungen anlegen, pausieren oder löschen unter '
: 'Rhythmen pflegen unter '}
<Link to={scopedPath('/control/journey', { initiativeId })} className="link-inline">
Kontrolle Journey Wiederkehrend
</Link>
.
</p>
</>
)
if (!embedded) return body
return (
<section className="card recurring-rhythm-panel">
<div className="section-header">
<div>
<h2>Übungen der Stufe</h2>
<p className="section-lead muted">
Activity Set der aktiven Reifegrad-Stufe nicht die Gates selbst (A1).
</p>
</div>
</div>
{body}
</section>
)
}