chore(version): update version and changelog for release 0.8.126
All checks were successful
Deploy Development / deploy (push) Successful in 39s
Test Suite / pytest-backend (push) Successful in 41s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 12s
Test Suite / k6 /health Baseline (push) Successful in 33s
Test Suite / playwright-tests (push) Successful in 1m30s
All checks were successful
Deploy Development / deploy (push) Successful in 39s
Test Suite / pytest-backend (push) Successful in 41s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 12s
Test Suite / k6 /health Baseline (push) Successful in 33s
Test Suite / playwright-tests (push) Successful in 1m30s
- Bumped APP_VERSION to 0.8.126 and updated the changelog to reflect recent changes. - Added the TrainingPlanningFrameworkImportModal component to the TrainingPlanningPage for improved training session management. - Implemented a new Playwright test to verify the functionality of the framework import dialog in the training planning page.
This commit is contained in:
parent
300d916fad
commit
e4e362b0a9
|
|
@ -1,6 +1,6 @@
|
||||||
# Shinkan Jinkendo Version Information
|
# Shinkan Jinkendo Version Information
|
||||||
|
|
||||||
APP_VERSION = "0.8.125"
|
APP_VERSION = "0.8.126"
|
||||||
BUILD_DATE = "2026-05-12"
|
BUILD_DATE = "2026-05-12"
|
||||||
DB_SCHEMA_VERSION = "20260514062"
|
DB_SCHEMA_VERSION = "20260514062"
|
||||||
|
|
||||||
|
|
@ -36,6 +36,13 @@ MODULE_VERSIONS = {
|
||||||
}
|
}
|
||||||
|
|
||||||
CHANGELOG = [
|
CHANGELOG = [
|
||||||
|
{
|
||||||
|
"version": "0.8.126",
|
||||||
|
"date": "2026-05-13",
|
||||||
|
"changes": [
|
||||||
|
"Frontend Phase 3: TrainingPlanningFrameworkImportModal aus Trainingsplanungsseite; Playwright-Test 13 (Rahmen-Dialog, skip ohne Gruppe).",
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "0.8.125",
|
"version": "0.8.125",
|
||||||
"date": "2026-05-13",
|
"date": "2026-05-13",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,210 @@
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modal: geplante Einheiten aus einem Trainingsrahmenprogramm (Blueprint-Slots) erzeugen.
|
||||||
|
*/
|
||||||
|
export default function TrainingPlanningFrameworkImportModal({
|
||||||
|
open,
|
||||||
|
frameworkProgramsList,
|
||||||
|
fwImportProgramId,
|
||||||
|
onProgramChange,
|
||||||
|
fwImportLoading,
|
||||||
|
fwImportDetail,
|
||||||
|
fwImportSelectedSlots,
|
||||||
|
onToggleSlot,
|
||||||
|
fwImportSlotDates,
|
||||||
|
onSlotDateChange,
|
||||||
|
fwImportStartDate,
|
||||||
|
onFwImportStartDateChange,
|
||||||
|
fwImportIntervalDays,
|
||||||
|
onFwImportIntervalDaysChange,
|
||||||
|
fwImportSubmitting,
|
||||||
|
onApplyDateSuggestions,
|
||||||
|
onSubmit,
|
||||||
|
onClose,
|
||||||
|
}) {
|
||||||
|
if (!open) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-testid="planning-framework-import-modal"
|
||||||
|
style={{
|
||||||
|
position: 'fixed',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
background: 'rgba(0,0,0,0.5)',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
zIndex: 1010,
|
||||||
|
padding: '1rem',
|
||||||
|
overflowY: 'auto',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
background: 'var(--surface)',
|
||||||
|
borderRadius: '12px',
|
||||||
|
padding: 'clamp(14px, 3vw, 1.75rem)',
|
||||||
|
maxWidth: 'min(620px, 100%)',
|
||||||
|
width: '100%',
|
||||||
|
maxHeight: '90vh',
|
||||||
|
overflowY: 'auto',
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
minWidth: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<h2 style={{ marginBottom: '0.65rem' }}>Sessions aus Rahmen übernehmen</h2>
|
||||||
|
<p style={{ color: 'var(--text2)', fontSize: '0.9rem', marginBottom: '1rem', lineHeight: 1.5 }}>
|
||||||
|
Wähle ein Trainingsrahmenprogramm und eine oder mehrere Sessions. Pro Session entsteht eine{' '}
|
||||||
|
<strong>eigene geplante Einheit</strong> in der aktuellen Gruppe (Kopie des Ablaufs). Die{' '}
|
||||||
|
<strong>Verknüpfung zum Rahmen-Slot</strong> wird gespeichert, damit die Herkunft sichtbar bleibt.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="form-row">
|
||||||
|
<label className="form-label">Rahmenprogramm</label>
|
||||||
|
<select
|
||||||
|
className="form-input"
|
||||||
|
value={fwImportProgramId}
|
||||||
|
onChange={(e) => onProgramChange(e.target.value)}
|
||||||
|
disabled={fwImportLoading || fwImportSubmitting}
|
||||||
|
>
|
||||||
|
<option value="">Bitte wählen…</option>
|
||||||
|
{frameworkProgramsList.map((fp) => (
|
||||||
|
<option key={fp.id} value={String(fp.id)}>
|
||||||
|
{(fp.title || '').trim() || `Rahmen #${fp.id}`}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{fwImportLoading ? (
|
||||||
|
<p style={{ color: 'var(--text2)', marginTop: '1rem' }}>Laden der Sessions…</p>
|
||||||
|
) : fwImportDetail?.slots?.length ? (
|
||||||
|
<>
|
||||||
|
<fieldset style={{ border: 'none', margin: '1rem 0', padding: 0 }}>
|
||||||
|
<legend className="form-label" style={{ padding: 0, marginBottom: '0.5rem' }}>
|
||||||
|
Sessions (mit Ablauf)
|
||||||
|
</legend>
|
||||||
|
<ul style={{ listStyle: 'none', padding: 0, margin: 0 }}>
|
||||||
|
{[...fwImportDetail.slots]
|
||||||
|
.sort((a, b) => (a.sort_order ?? 0) - (b.sort_order ?? 0))
|
||||||
|
.map((slot) => {
|
||||||
|
const hasBp = !!slot.blueprint_training_unit_id
|
||||||
|
const checked = fwImportSelectedSlots.has(slot.id)
|
||||||
|
const label =
|
||||||
|
(slot.title || '').trim() || `Session ${(slot.sort_order ?? 0) + 1}`
|
||||||
|
return (
|
||||||
|
<li key={slot.id} style={{ marginBottom: '10px' }}>
|
||||||
|
<label
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
gap: '10px',
|
||||||
|
alignItems: 'flex-start',
|
||||||
|
cursor: hasBp ? 'pointer' : 'not-allowed',
|
||||||
|
opacity: hasBp ? 1 : 0.55,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={checked}
|
||||||
|
disabled={!hasBp || fwImportSubmitting}
|
||||||
|
onChange={() => onToggleSlot(slot)}
|
||||||
|
style={{ marginTop: '0.2rem', flexShrink: 0 }}
|
||||||
|
/>
|
||||||
|
<span style={{ flex: 1, minWidth: 0 }}>
|
||||||
|
<strong>{label}</strong>
|
||||||
|
{!hasBp ? (
|
||||||
|
<span style={{ display: 'block', fontSize: '0.82rem', color: 'var(--danger)' }}>
|
||||||
|
Ohne Session-Ablauf — Übernahme nicht möglich.
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
|
{hasBp && checked ? (
|
||||||
|
<span style={{ display: 'block', marginTop: '6px' }}>
|
||||||
|
<span className="form-label" style={{ fontSize: '0.78rem' }}>
|
||||||
|
Termin (Datum)
|
||||||
|
</span>
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
className="form-input"
|
||||||
|
style={{ maxWidth: '200px', marginTop: '4px' }}
|
||||||
|
value={fwImportSlotDates[String(slot.id)] || ''}
|
||||||
|
onChange={(e) => onSlotDateChange(String(slot.id), e.target.value)}
|
||||||
|
disabled={fwImportSubmitting}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className="responsive-grid-3"
|
||||||
|
style={{
|
||||||
|
marginBottom: '0.75rem',
|
||||||
|
padding: '12px',
|
||||||
|
background: 'var(--surface2)',
|
||||||
|
borderRadius: '8px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="form-row">
|
||||||
|
<label className="form-label">Startdatum (Vorschlag)</label>
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
className="form-input"
|
||||||
|
value={fwImportStartDate}
|
||||||
|
onChange={(e) => onFwImportStartDateChange(e.target.value)}
|
||||||
|
disabled={fwImportSubmitting}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="form-row">
|
||||||
|
<label className="form-label">Abstand (Tage)</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
min={0}
|
||||||
|
className="form-input"
|
||||||
|
value={fwImportIntervalDays}
|
||||||
|
onChange={(e) => onFwImportIntervalDaysChange(parseInt(e.target.value, 10) || 0)}
|
||||||
|
disabled={fwImportSubmitting}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="form-row" style={{ alignSelf: 'end' }}>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-secondary"
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
disabled={fwImportSubmitting}
|
||||||
|
onClick={onApplyDateSuggestions}
|
||||||
|
>
|
||||||
|
Datumsvorschläge setzen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : fwImportProgramId ? (
|
||||||
|
<p style={{ color: 'var(--text2)', marginTop: '0.75rem' }}>Keine Sessions in diesem Programm.</p>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.5rem', marginTop: '1.25rem' }}>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-primary"
|
||||||
|
disabled={fwImportSubmitting || !fwImportDetail}
|
||||||
|
onClick={onSubmit}
|
||||||
|
>
|
||||||
|
{fwImportSubmitting ? 'Übernehmen…' : 'In Planung übernehmen'}
|
||||||
|
</button>
|
||||||
|
<button type="button" className="btn btn-secondary" disabled={fwImportSubmitting} onClick={onClose}>
|
||||||
|
Abbrechen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -9,6 +9,7 @@ import ExercisePeekModal from '../components/ExercisePeekModal'
|
||||||
import TrainingUnitSectionsEditor from '../components/TrainingUnitSectionsEditor'
|
import TrainingUnitSectionsEditor from '../components/TrainingUnitSectionsEditor'
|
||||||
import TrainingPlanExerciseVisibilityPanel from '../components/TrainingPlanExerciseVisibilityPanel'
|
import TrainingPlanExerciseVisibilityPanel from '../components/TrainingPlanExerciseVisibilityPanel'
|
||||||
import PageSectionNav from '../components/PageSectionNav'
|
import PageSectionNav from '../components/PageSectionNav'
|
||||||
|
import TrainingPlanningFrameworkImportModal from '../components/planning/TrainingPlanningFrameworkImportModal'
|
||||||
import {
|
import {
|
||||||
defaultSection,
|
defaultSection,
|
||||||
normalizeUnitToForm,
|
normalizeUnitToForm,
|
||||||
|
|
@ -2287,198 +2288,28 @@ function TrainingPlanningPage() {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{frameworkImportOpen && (
|
<TrainingPlanningFrameworkImportModal
|
||||||
<div
|
open={frameworkImportOpen}
|
||||||
style={{
|
frameworkProgramsList={frameworkProgramsList}
|
||||||
position: 'fixed',
|
fwImportProgramId={fwImportProgramId}
|
||||||
top: 0,
|
onProgramChange={onFwImportProgramChange}
|
||||||
left: 0,
|
fwImportLoading={fwImportLoading}
|
||||||
right: 0,
|
fwImportDetail={fwImportDetail}
|
||||||
bottom: 0,
|
fwImportSelectedSlots={fwImportSelectedSlots}
|
||||||
background: 'rgba(0,0,0,0.5)',
|
onToggleSlot={toggleFwImportSlot}
|
||||||
display: 'flex',
|
fwImportSlotDates={fwImportSlotDates}
|
||||||
alignItems: 'center',
|
onSlotDateChange={(slotId, value) =>
|
||||||
justifyContent: 'center',
|
setFwImportSlotDates((prev) => ({ ...prev, [slotId]: value }))
|
||||||
zIndex: 1010,
|
}
|
||||||
padding: '1rem',
|
fwImportStartDate={fwImportStartDate}
|
||||||
overflowY: 'auto',
|
onFwImportStartDateChange={setFwImportStartDate}
|
||||||
}}
|
fwImportIntervalDays={fwImportIntervalDays}
|
||||||
>
|
onFwImportIntervalDaysChange={setFwImportIntervalDays}
|
||||||
<div
|
fwImportSubmitting={fwImportSubmitting}
|
||||||
style={{
|
onApplyDateSuggestions={applyFwImportDateSuggestions}
|
||||||
background: 'var(--surface)',
|
onSubmit={submitFrameworkImport}
|
||||||
borderRadius: '12px',
|
onClose={() => setFrameworkImportOpen(false)}
|
||||||
padding: 'clamp(14px, 3vw, 1.75rem)',
|
/>
|
||||||
maxWidth: 'min(620px, 100%)',
|
|
||||||
width: '100%',
|
|
||||||
maxHeight: '90vh',
|
|
||||||
overflowY: 'auto',
|
|
||||||
boxSizing: 'border-box',
|
|
||||||
minWidth: 0,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<h2 style={{ marginBottom: '0.65rem' }}>Sessions aus Rahmen übernehmen</h2>
|
|
||||||
<p style={{ color: 'var(--text2)', fontSize: '0.9rem', marginBottom: '1rem', lineHeight: 1.5 }}>
|
|
||||||
Wähle ein Trainingsrahmenprogramm und eine oder mehrere Sessions. Pro Session entsteht eine{' '}
|
|
||||||
<strong>eigene geplante Einheit</strong> in der aktuellen Gruppe (Kopie des Ablaufs). Die{' '}
|
|
||||||
<strong>Verknüpfung zum Rahmen-Slot</strong> wird gespeichert, damit die Herkunft sichtbar bleibt.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="form-row">
|
|
||||||
<label className="form-label">Rahmenprogramm</label>
|
|
||||||
<select
|
|
||||||
className="form-input"
|
|
||||||
value={fwImportProgramId}
|
|
||||||
onChange={(e) => onFwImportProgramChange(e.target.value)}
|
|
||||||
disabled={fwImportLoading || fwImportSubmitting}
|
|
||||||
>
|
|
||||||
<option value="">Bitte wählen…</option>
|
|
||||||
{frameworkProgramsList.map((fp) => (
|
|
||||||
<option key={fp.id} value={String(fp.id)}>
|
|
||||||
{(fp.title || '').trim() || `Rahmen #${fp.id}`}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{fwImportLoading ? (
|
|
||||||
<p style={{ color: 'var(--text2)', marginTop: '1rem' }}>Laden der Sessions…</p>
|
|
||||||
) : fwImportDetail?.slots?.length ? (
|
|
||||||
<>
|
|
||||||
<fieldset style={{ border: 'none', margin: '1rem 0', padding: 0 }}>
|
|
||||||
<legend className="form-label" style={{ padding: 0, marginBottom: '0.5rem' }}>
|
|
||||||
Sessions (mit Ablauf)
|
|
||||||
</legend>
|
|
||||||
<ul style={{ listStyle: 'none', padding: 0, margin: 0 }}>
|
|
||||||
{[...fwImportDetail.slots]
|
|
||||||
.sort((a, b) => (a.sort_order ?? 0) - (b.sort_order ?? 0))
|
|
||||||
.map((slot) => {
|
|
||||||
const hasBp = !!slot.blueprint_training_unit_id
|
|
||||||
const checked = fwImportSelectedSlots.has(slot.id)
|
|
||||||
const label =
|
|
||||||
(slot.title || '').trim() ||
|
|
||||||
`Session ${(slot.sort_order ?? 0) + 1}`
|
|
||||||
return (
|
|
||||||
<li key={slot.id} style={{ marginBottom: '10px' }}>
|
|
||||||
<label
|
|
||||||
style={{
|
|
||||||
display: 'flex',
|
|
||||||
gap: '10px',
|
|
||||||
alignItems: 'flex-start',
|
|
||||||
cursor: hasBp ? 'pointer' : 'not-allowed',
|
|
||||||
opacity: hasBp ? 1 : 0.55,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={checked}
|
|
||||||
disabled={!hasBp || fwImportSubmitting}
|
|
||||||
onChange={() => toggleFwImportSlot(slot)}
|
|
||||||
style={{ marginTop: '0.2rem', flexShrink: 0 }}
|
|
||||||
/>
|
|
||||||
<span style={{ flex: 1, minWidth: 0 }}>
|
|
||||||
<strong>{label}</strong>
|
|
||||||
{!hasBp ? (
|
|
||||||
<span style={{ display: 'block', fontSize: '0.82rem', color: 'var(--danger)' }}>
|
|
||||||
Ohne Session-Ablauf — Übernahme nicht möglich.
|
|
||||||
</span>
|
|
||||||
) : null}
|
|
||||||
{hasBp && checked ? (
|
|
||||||
<span style={{ display: 'block', marginTop: '6px' }}>
|
|
||||||
<span className="form-label" style={{ fontSize: '0.78rem' }}>
|
|
||||||
Termin (Datum)
|
|
||||||
</span>
|
|
||||||
<input
|
|
||||||
type="date"
|
|
||||||
className="form-input"
|
|
||||||
style={{ maxWidth: '200px', marginTop: '4px' }}
|
|
||||||
value={fwImportSlotDates[String(slot.id)] || ''}
|
|
||||||
onChange={(e) =>
|
|
||||||
setFwImportSlotDates((prev) => ({
|
|
||||||
...prev,
|
|
||||||
[String(slot.id)]: e.target.value,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
disabled={fwImportSubmitting}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
) : null}
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
</li>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</ul>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<div
|
|
||||||
className="responsive-grid-3"
|
|
||||||
style={{
|
|
||||||
marginBottom: '0.75rem',
|
|
||||||
padding: '12px',
|
|
||||||
background: 'var(--surface2)',
|
|
||||||
borderRadius: '8px',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="form-row">
|
|
||||||
<label className="form-label">Startdatum (Vorschlag)</label>
|
|
||||||
<input
|
|
||||||
type="date"
|
|
||||||
className="form-input"
|
|
||||||
value={fwImportStartDate}
|
|
||||||
onChange={(e) => setFwImportStartDate(e.target.value)}
|
|
||||||
disabled={fwImportSubmitting}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="form-row">
|
|
||||||
<label className="form-label">Abstand (Tage)</label>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
min={0}
|
|
||||||
className="form-input"
|
|
||||||
value={fwImportIntervalDays}
|
|
||||||
onChange={(e) => setFwImportIntervalDays(parseInt(e.target.value, 10) || 0)}
|
|
||||||
disabled={fwImportSubmitting}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="form-row" style={{ alignSelf: 'end' }}>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="btn btn-secondary"
|
|
||||||
style={{ width: '100%' }}
|
|
||||||
disabled={fwImportSubmitting}
|
|
||||||
onClick={applyFwImportDateSuggestions}
|
|
||||||
>
|
|
||||||
Datumsvorschläge setzen
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
) : fwImportProgramId ? (
|
|
||||||
<p style={{ color: 'var(--text2)', marginTop: '0.75rem' }}>Keine Sessions in diesem Programm.</p>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.5rem', marginTop: '1.25rem' }}>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="btn btn-primary"
|
|
||||||
disabled={fwImportSubmitting || !fwImportDetail}
|
|
||||||
onClick={submitFrameworkImport}
|
|
||||||
>
|
|
||||||
{fwImportSubmitting ? 'Übernehmen…' : 'In Planung übernehmen'}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="btn btn-secondary"
|
|
||||||
disabled={fwImportSubmitting}
|
|
||||||
onClick={() => setFrameworkImportOpen(false)}
|
|
||||||
>
|
|
||||||
Abbrechen
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{showModal && (
|
{showModal && (
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -283,6 +283,28 @@ test('12. Trainingsplanung: Seite lädt mit Überschrift', async ({ page }) => {
|
||||||
console.log('✓ Trainingsplanung: Grundansicht');
|
console.log('✓ Trainingsplanung: Grundansicht');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('13. Trainingsplanung: Rahmen-Import-Dialog öffnet und schließt', async ({ page }, testInfo) => {
|
||||||
|
await login(page);
|
||||||
|
await page.goto('/planning', { waitUntil: 'networkidle' });
|
||||||
|
const main = page.locator('.app-main');
|
||||||
|
await expect(main.locator('.spinner')).toHaveCount(0, { timeout: 25000 });
|
||||||
|
await expect(main.getByRole('heading', { level: 1, name: 'Trainingsplanung' })).toBeVisible({
|
||||||
|
timeout: 20000,
|
||||||
|
});
|
||||||
|
const openBtn = main.getByRole('button', { name: /Aus Rahmen übernehmen/i });
|
||||||
|
if (await openBtn.isDisabled()) {
|
||||||
|
testInfo.skip(true, 'Keine Trainingsgruppe — Button bleibt deaktiviert');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await openBtn.click();
|
||||||
|
const dlg = page.getByTestId('planning-framework-import-modal');
|
||||||
|
await expect(dlg).toBeVisible({ timeout: 10000 });
|
||||||
|
await expect(dlg.getByRole('heading', { name: /Sessions aus Rahmen übernehmen/i })).toBeVisible();
|
||||||
|
await dlg.getByRole('button', { name: 'Abbrechen' }).click();
|
||||||
|
await expect(dlg).toHaveCount(0);
|
||||||
|
console.log('✓ Trainingsplanung: Rahmen-Import-Dialog Smoke');
|
||||||
|
});
|
||||||
|
|
||||||
test('P-12: sessionStorage wird bei Logout bereinigt (sj_coach_* Schlüssel)', async ({ page }) => {
|
test('P-12: sessionStorage wird bei Logout bereinigt (sj_coach_* Schlüssel)', async ({ page }) => {
|
||||||
await page.setViewportSize({ width: 1280, height: 800 });
|
await page.setViewportSize({ width: 1280, height: 800 });
|
||||||
await login(page);
|
await login(page);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user