shinkan-jinkendo/frontend/src/components/planning/FrameworkProgramListCard.jsx
Lars a4548f5587
Some checks failed
Test Suite / playwright-tests (push) Waiting to run
Deploy Development / deploy (push) Successful in 40s
Test Suite / pytest-backend (push) Successful in 36s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 14s
Test Suite / k6 /health Baseline (push) Has been cancelled
Refactor Training Framework Programs List Page with Enhanced Styling and New Utility Functions
- Updated the TrainingFrameworkProgramsListPage to utilize new CSS classes for improved layout and styling.
- Removed deprecated components and functions, streamlining the codebase for better maintainability.
- Introduced utility functions for splitting aggregated strings, enhancing data handling for framework program attributes.
- Enhanced the user interface with loading and empty state indicators, improving overall user experience.
2026-05-20 16:21:16 +02:00

135 lines
5.0 KiB
JavaScript

import React from 'react'
import NavStateLink from '../NavStateLink'
import {
frameworkSessionDurationLabel,
splitFrameworkCommaAgg,
splitFrameworkGoalsAgg,
frameworkProgramHasCatalogMeta,
} from '../../utils/frameworkProgramListHelpers'
function CatalogGroup({ label, items, variant }) {
if (!items.length) return null
return (
<div className={`fw-prog-card__catalog-group fw-prog-card__catalog-group--${variant}`}>
<span className="fw-prog-card__catalog-label">{label}</span>
<ul className="fw-prog-card__chip-list" aria-label={label}>
{items.map((name) => (
<li key={name} className="fw-prog-card__chip">
{name}
</li>
))}
</ul>
</div>
)
}
/**
* Einzelkarte für die Rahmenprogramm-Bibliothek.
*/
export default function FrameworkProgramListCard({ row, returnContext, onDelete }) {
const title = (row.title || '').trim() || `Rahmen #${row.id}`
const description = (row.description || '').trim()
const durationLabel = frameworkSessionDurationLabel(row)
const hasDuration = durationLabel !== 'Dauer nicht angegeben'
const goals = splitFrameworkGoalsAgg(row.goal_titles_agg)
const focusAreas = splitFrameworkCommaAgg(row.focus_area_names_agg)
const styleDirs = splitFrameworkCommaAgg(
row.style_direction_names_agg || row.style_direction_name
)
const trainingTypes = splitFrameworkCommaAgg(row.training_type_names_agg)
const targetGroups = splitFrameworkCommaAgg(row.target_group_names_agg)
const goalsCount = Number(row.goals_count)
const slotsCount = Number(row.slots_count)
const showCatalog = frameworkProgramHasCatalogMeta(row)
return (
<article className="fw-prog-card card">
<div className="fw-prog-card__accent" aria-hidden="true" />
<div className="fw-prog-card__inner">
<header className="fw-prog-card__head">
<div className="fw-prog-card__title-block">
<h2 className="fw-prog-card__title">
<NavStateLink
to={`/planning/framework-programs/${row.id}`}
returnContext={returnContext}
className="fw-prog-card__title-link"
>
{title}
</NavStateLink>
</h2>
{description ? (
<p className="fw-prog-card__desc">{description}</p>
) : null}
</div>
<ul className="fw-prog-card__stats" aria-label="Kennzahlen">
<li
className={
'fw-prog-card__stat' +
(hasDuration ? ' fw-prog-card__stat--duration' : ' fw-prog-card__stat--muted')
}
>
<span className="fw-prog-card__stat-label">Session</span>
<span className="fw-prog-card__stat-value">{durationLabel}</span>
</li>
<li className="fw-prog-card__stat">
<span className="fw-prog-card__stat-label">Ziele</span>
<span className="fw-prog-card__stat-value">
{Number.isFinite(goalsCount) ? goalsCount : '—'}
</span>
</li>
<li className="fw-prog-card__stat">
<span className="fw-prog-card__stat-label">Sessions</span>
<span className="fw-prog-card__stat-value">
{Number.isFinite(slotsCount) ? slotsCount : '—'}
</span>
</li>
</ul>
</header>
{goals.length > 0 ? (
<section className="fw-prog-card__section fw-prog-card__section--goals">
<h3 className="fw-prog-card__section-title">Entwicklungsziele</h3>
<ul className="fw-prog-card__goal-list" aria-label="Entwicklungsziele">
{goals.map((g) => (
<li key={g} className="fw-prog-card__goal">
{g}
</li>
))}
</ul>
</section>
) : null}
{showCatalog ? (
<section className="fw-prog-card__section fw-prog-card__section--catalog">
<h3 className="fw-prog-card__section-title">Einordnung</h3>
<div className="fw-prog-card__catalog">
<CatalogGroup label="Fokus" items={focusAreas} variant="focus" />
<CatalogGroup label="Stil" items={styleDirs} variant="style" />
<CatalogGroup label="Trainingsart" items={trainingTypes} variant="type" />
<CatalogGroup label="Zielgruppe" items={targetGroups} variant="target" />
</div>
</section>
) : null}
<footer className="fw-prog-card__actions">
<NavStateLink
to={`/planning/framework-programs/${row.id}`}
returnContext={returnContext}
className="btn btn-primary fw-prog-card__btn-primary"
>
Öffnen
</NavStateLink>
<button
type="button"
className="btn btn-secondary fw-prog-card__btn-danger"
onClick={() => onDelete(row.id, row.title)}
>
Löschen
</button>
</footer>
</div>
</article>
)
}