Remove AI suggestion preview dialog from ExerciseFormPageRoot component to streamline user interface and improve performance. This change eliminates unnecessary complexity in the component's rendering logic.
All checks were successful
Deploy Development / deploy (push) Successful in 42s
Test Suite / pytest-backend (push) Successful in 39s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 13s
Test Suite / k6 /health Baseline (push) Successful in 38s
Test Suite / playwright-tests (push) Successful in 1m30s
Test Suite / pytest-backend (pull_request) Successful in 35s
Test Suite / lint-backend (pull_request) Successful in 0s
Test Suite / build-frontend (pull_request) Successful in 12s
Test Suite / k6 /health Baseline (pull_request) Successful in 33s
Test Suite / playwright-tests (pull_request) Successful in 1m31s
All checks were successful
Deploy Development / deploy (push) Successful in 42s
Test Suite / pytest-backend (push) Successful in 39s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 13s
Test Suite / k6 /health Baseline (push) Successful in 38s
Test Suite / playwright-tests (push) Successful in 1m30s
Test Suite / pytest-backend (pull_request) Successful in 35s
Test Suite / lint-backend (pull_request) Successful in 0s
Test Suite / build-frontend (pull_request) Successful in 12s
Test Suite / k6 /health Baseline (pull_request) Successful in 33s
Test Suite / playwright-tests (pull_request) Successful in 1m31s
This commit is contained in:
parent
888d0bd009
commit
f9e295bce0
|
|
@ -2735,258 +2735,6 @@ function ExerciseFormPageRoot() {
|
|||
</div>
|
||||
</div>
|
||||
)}
|
||||
{aiSuggestionPreview &&
|
||||
(() => {
|
||||
const p = aiSuggestionPreview
|
||||
const summaryBoxSx = {
|
||||
padding: '10px 12px',
|
||||
borderRadius: '8px',
|
||||
border: '1px solid var(--border)',
|
||||
background: 'var(--surface2)',
|
||||
fontSize: '13px',
|
||||
lineHeight: 1.45,
|
||||
whiteSpace: 'pre-wrap',
|
||||
wordBreak: 'break-word',
|
||||
minHeight: '72px',
|
||||
}
|
||||
const canApplySomething =
|
||||
(p.applySummary && p.summaryAfterHtml) || p.skillChoices.some((c) => c.include)
|
||||
return (
|
||||
<div
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-label="KI-Vorschlag prüfen"
|
||||
style={{
|
||||
position: 'fixed',
|
||||
inset: 0,
|
||||
background: 'rgba(0,0,0,0.5)',
|
||||
zIndex: 1001,
|
||||
overflow: 'auto',
|
||||
padding: '16px',
|
||||
}}
|
||||
onClick={() => discardExerciseAiSuggestionPreview()}
|
||||
onKeyDown={(e) => e.key === 'Escape' && discardExerciseAiSuggestionPreview()}
|
||||
>
|
||||
<div
|
||||
className="card"
|
||||
style={{
|
||||
maxWidth: 760,
|
||||
margin: '3vh auto',
|
||||
maxHeight: '92vh',
|
||||
overflow: 'auto',
|
||||
position: 'relative',
|
||||
}}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<h3 style={{ marginTop: 0, fontSize: '1.1rem', marginBottom: '6px' }}>KI-Vorschlag übernehmen</h3>
|
||||
<p style={{ fontSize: '13px', color: 'var(--text3)', marginTop: 0, marginBottom: '16px' }}>
|
||||
Vergleichen und nur die gewünschten Teile übernehmen. Es werden keine Daten automatisch gespeichert.
|
||||
</p>
|
||||
|
||||
{p.hasSummaryProposal ? (
|
||||
<section style={{ marginBottom: '20px' }} aria-labelledby="ai-preview-summary-heading">
|
||||
<div
|
||||
id="ai-preview-summary-heading"
|
||||
style={{ fontWeight: 600, fontSize: '0.95rem', marginBottom: '10px' }}
|
||||
>
|
||||
Kurzfassung
|
||||
</div>
|
||||
<label
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px',
|
||||
marginBottom: '10px',
|
||||
cursor: 'pointer',
|
||||
fontSize: '14px',
|
||||
}}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={p.applySummary}
|
||||
onChange={(e) =>
|
||||
setAiSuggestionPreview((prev) =>
|
||||
prev ? { ...prev, applySummary: e.target.checked } : prev,
|
||||
)
|
||||
}
|
||||
/>
|
||||
Kurzfassung durch Vorschlag ersetzen (bestehende Kurzbeschreibung wird überschrieben)
|
||||
</label>
|
||||
<div
|
||||
style={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'minmax(0,1fr) minmax(0,1fr)',
|
||||
gap: '12px',
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<div style={{ fontSize: '12px', color: 'var(--text3)', marginBottom: '4px' }}>
|
||||
Aktuell (ohne Formatierung)
|
||||
</div>
|
||||
<div style={summaryBoxSx}>{p.summaryBeforePlain || '(leer)'}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ fontSize: '12px', color: 'var(--text3)', marginBottom: '4px' }}>KI-Vorschlag</div>
|
||||
<div style={{ ...summaryBoxSx, borderColor: 'var(--accent-dark, rgba(29,158,117,0.45))' }}>
|
||||
{p.summaryAfterPlain || '(leer)'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
) : null}
|
||||
|
||||
{p.skillsRequested ? (
|
||||
<section aria-labelledby="ai-preview-skills-heading">
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
gap: '8px',
|
||||
marginBottom: '10px',
|
||||
}}
|
||||
>
|
||||
<div id="ai-preview-skills-heading" style={{ fontWeight: 600, fontSize: '0.95rem' }}>
|
||||
Fähigkeiten ({p.skillChoices.length}
|
||||
{p.skillChoices.length === 1 ? ' Vorschlag' : ' Vorschläge'})
|
||||
</div>
|
||||
{p.skillChoices.length > 0 ? (
|
||||
<div style={{ display: 'flex', gap: '6px', flexWrap: 'wrap' }}>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-secondary"
|
||||
style={{ fontSize: '11px', padding: '4px 8px' }}
|
||||
onClick={() =>
|
||||
setAiSuggestionPreview((prev) =>
|
||||
prev
|
||||
? {
|
||||
...prev,
|
||||
skillChoices: prev.skillChoices.map((x) => ({ ...x, include: true })),
|
||||
}
|
||||
: prev,
|
||||
)
|
||||
}
|
||||
>
|
||||
Alle auswählen
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-secondary"
|
||||
style={{ fontSize: '11px', padding: '4px 8px' }}
|
||||
onClick={() =>
|
||||
setAiSuggestionPreview((prev) =>
|
||||
prev
|
||||
? {
|
||||
...prev,
|
||||
skillChoices: prev.skillChoices.map((x) => ({ ...x, include: false })),
|
||||
}
|
||||
: prev,
|
||||
)
|
||||
}
|
||||
>
|
||||
Alle abwählen
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
{p.skillChoices.length === 0 ? (
|
||||
<p style={{ fontSize: '13px', color: 'var(--text3)', margin: 0 }}>
|
||||
Keine passenden Fähigkeiten — der Katalog-Vorschlag war leer oder enthielt nur ungültige IDs.
|
||||
</p>
|
||||
) : (
|
||||
<ul style={{ listStyle: 'none', padding: 0, margin: 0 }}>
|
||||
{p.skillChoices.map((c) => (
|
||||
<li
|
||||
key={c.key}
|
||||
style={{
|
||||
border: '1px solid var(--border)',
|
||||
borderRadius: '8px',
|
||||
padding: '10px 12px',
|
||||
marginBottom: '10px',
|
||||
background: 'var(--surface)',
|
||||
}}
|
||||
>
|
||||
<label
|
||||
style={{
|
||||
display: 'flex',
|
||||
gap: '10px',
|
||||
alignItems: 'flex-start',
|
||||
cursor: 'pointer',
|
||||
margin: 0,
|
||||
}}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={c.include}
|
||||
onChange={() =>
|
||||
setAiSuggestionPreview((prev) =>
|
||||
prev
|
||||
? {
|
||||
...prev,
|
||||
skillChoices: prev.skillChoices.map((x) =>
|
||||
x.skill_id === c.skill_id ? { ...x, include: !x.include } : x,
|
||||
),
|
||||
}
|
||||
: prev,
|
||||
)
|
||||
}
|
||||
style={{ marginTop: '4px' }}
|
||||
/>
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<div style={{ fontWeight: 600, fontSize: '13px', marginBottom: '6px' }}>
|
||||
{c.kind === 'add' ? 'Neu hinzufügen' : 'Bestehende Zeile aktualisieren'}
|
||||
</div>
|
||||
{c.kind === 'update' && c.before ? (
|
||||
<div style={{ fontSize: '12px', lineHeight: 1.5 }}>
|
||||
<div style={{ color: 'var(--text3)', marginBottom: '2px' }}>Bisher</div>
|
||||
<div style={{ marginBottom: '8px' }}>
|
||||
{describeExerciseSkillRowForPreview(c.before, skillsCatalog)}
|
||||
</div>
|
||||
<div style={{ color: 'var(--text3)', marginBottom: '2px' }}>Nach KI-Vorschlag</div>
|
||||
<div>{describeExerciseSkillRowForPreview(c.after, skillsCatalog)}</div>
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ fontSize: '13px', lineHeight: 1.5 }}>
|
||||
{describeExerciseSkillRowForPreview(c.after, skillsCatalog)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</label>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</section>
|
||||
) : null}
|
||||
|
||||
<div
|
||||
style={{
|
||||
marginTop: '20px',
|
||||
paddingTop: '14px',
|
||||
borderTop: '1px solid var(--border)',
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
flexWrap: 'wrap',
|
||||
gap: '10px',
|
||||
}}
|
||||
>
|
||||
<button type="button" className="btn btn-secondary" onClick={discardExerciseAiSuggestionPreview}>
|
||||
Abbrechen
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-primary"
|
||||
disabled={!canApplySomething}
|
||||
onClick={() => applyExerciseAiSuggestionPreview()}
|
||||
>
|
||||
Ausgewähltes übernehmen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})()}
|
||||
{mediaPreview && (
|
||||
<MediaPreviewModal
|
||||
title={(mediaPreview.title || '').trim() || mediaPreview.original_filename || `Medium #${mediaPreview.id}`}
|
||||
|
|
@ -3017,6 +2765,259 @@ function ExerciseFormPageRoot() {
|
|||
</form>
|
||||
</div>
|
||||
|
||||
{aiSuggestionPreview &&
|
||||
(() => {
|
||||
const p = aiSuggestionPreview
|
||||
const summaryBoxSx = {
|
||||
padding: '10px 12px',
|
||||
borderRadius: '8px',
|
||||
border: '1px solid var(--border)',
|
||||
background: 'var(--surface2)',
|
||||
fontSize: '13px',
|
||||
lineHeight: 1.45,
|
||||
whiteSpace: 'pre-wrap',
|
||||
wordBreak: 'break-word',
|
||||
minHeight: '72px',
|
||||
}
|
||||
const canApplySomething =
|
||||
(p.applySummary && p.summaryAfterHtml) || p.skillChoices.some((c) => c.include)
|
||||
return (
|
||||
<div
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-label="KI-Vorschlag prüfen"
|
||||
style={{
|
||||
position: 'fixed',
|
||||
inset: 0,
|
||||
background: 'rgba(0,0,0,0.5)',
|
||||
zIndex: 1001,
|
||||
overflow: 'auto',
|
||||
padding: '16px',
|
||||
}}
|
||||
onClick={() => discardExerciseAiSuggestionPreview()}
|
||||
onKeyDown={(e) => e.key === 'Escape' && discardExerciseAiSuggestionPreview()}
|
||||
>
|
||||
<div
|
||||
className="card"
|
||||
style={{
|
||||
maxWidth: 760,
|
||||
margin: '3vh auto',
|
||||
maxHeight: '92vh',
|
||||
overflow: 'auto',
|
||||
position: 'relative',
|
||||
}}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<h3 style={{ marginTop: 0, fontSize: '1.1rem', marginBottom: '6px' }}>KI-Vorschlag übernehmen</h3>
|
||||
<p style={{ fontSize: '13px', color: 'var(--text3)', marginTop: 0, marginBottom: '16px' }}>
|
||||
Vergleichen und nur die gewünschten Teile übernehmen. Es werden keine Daten automatisch gespeichert.
|
||||
</p>
|
||||
|
||||
{p.hasSummaryProposal ? (
|
||||
<section style={{ marginBottom: '20px' }} aria-labelledby="ai-preview-summary-heading">
|
||||
<div
|
||||
id="ai-preview-summary-heading"
|
||||
style={{ fontWeight: 600, fontSize: '0.95rem', marginBottom: '10px' }}
|
||||
>
|
||||
Kurzfassung
|
||||
</div>
|
||||
<label
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px',
|
||||
marginBottom: '10px',
|
||||
cursor: 'pointer',
|
||||
fontSize: '14px',
|
||||
}}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={p.applySummary}
|
||||
onChange={(e) =>
|
||||
setAiSuggestionPreview((prev) =>
|
||||
prev ? { ...prev, applySummary: e.target.checked } : prev,
|
||||
)
|
||||
}
|
||||
/>
|
||||
Kurzfassung durch Vorschlag ersetzen (bestehende Kurzbeschreibung wird überschrieben)
|
||||
</label>
|
||||
<div
|
||||
style={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'minmax(0,1fr) minmax(0,1fr)',
|
||||
gap: '12px',
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<div style={{ fontSize: '12px', color: 'var(--text3)', marginBottom: '4px' }}>
|
||||
Aktuell (ohne Formatierung)
|
||||
</div>
|
||||
<div style={summaryBoxSx}>{p.summaryBeforePlain || '(leer)'}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ fontSize: '12px', color: 'var(--text3)', marginBottom: '4px' }}>KI-Vorschlag</div>
|
||||
<div style={{ ...summaryBoxSx, borderColor: 'var(--accent-dark, rgba(29,158,117,0.45))' }}>
|
||||
{p.summaryAfterPlain || '(leer)'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
) : null}
|
||||
|
||||
{p.skillsRequested ? (
|
||||
<section aria-labelledby="ai-preview-skills-heading">
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
gap: '8px',
|
||||
marginBottom: '10px',
|
||||
}}
|
||||
>
|
||||
<div id="ai-preview-skills-heading" style={{ fontWeight: 600, fontSize: '0.95rem' }}>
|
||||
Fähigkeiten ({p.skillChoices.length}
|
||||
{p.skillChoices.length === 1 ? ' Vorschlag' : ' Vorschläge'})
|
||||
</div>
|
||||
{p.skillChoices.length > 0 ? (
|
||||
<div style={{ display: 'flex', gap: '6px', flexWrap: 'wrap' }}>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-secondary"
|
||||
style={{ fontSize: '11px', padding: '4px 8px' }}
|
||||
onClick={() =>
|
||||
setAiSuggestionPreview((prev) =>
|
||||
prev
|
||||
? {
|
||||
...prev,
|
||||
skillChoices: prev.skillChoices.map((x) => ({ ...x, include: true })),
|
||||
}
|
||||
: prev,
|
||||
)
|
||||
}
|
||||
>
|
||||
Alle auswählen
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-secondary"
|
||||
style={{ fontSize: '11px', padding: '4px 8px' }}
|
||||
onClick={() =>
|
||||
setAiSuggestionPreview((prev) =>
|
||||
prev
|
||||
? {
|
||||
...prev,
|
||||
skillChoices: prev.skillChoices.map((x) => ({ ...x, include: false })),
|
||||
}
|
||||
: prev,
|
||||
)
|
||||
}
|
||||
>
|
||||
Alle abwählen
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
{p.skillChoices.length === 0 ? (
|
||||
<p style={{ fontSize: '13px', color: 'var(--text3)', margin: 0 }}>
|
||||
Keine passenden Fähigkeiten — der Katalog-Vorschlag war leer oder enthielt nur ungültige IDs.
|
||||
</p>
|
||||
) : (
|
||||
<ul style={{ listStyle: 'none', padding: 0, margin: 0 }}>
|
||||
{p.skillChoices.map((c) => (
|
||||
<li
|
||||
key={c.key}
|
||||
style={{
|
||||
border: '1px solid var(--border)',
|
||||
borderRadius: '8px',
|
||||
padding: '10px 12px',
|
||||
marginBottom: '10px',
|
||||
background: 'var(--surface)',
|
||||
}}
|
||||
>
|
||||
<label
|
||||
style={{
|
||||
display: 'flex',
|
||||
gap: '10px',
|
||||
alignItems: 'flex-start',
|
||||
cursor: 'pointer',
|
||||
margin: 0,
|
||||
}}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={c.include}
|
||||
onChange={() =>
|
||||
setAiSuggestionPreview((prev) =>
|
||||
prev
|
||||
? {
|
||||
...prev,
|
||||
skillChoices: prev.skillChoices.map((x) =>
|
||||
x.skill_id === c.skill_id ? { ...x, include: !x.include } : x,
|
||||
),
|
||||
}
|
||||
: prev,
|
||||
)
|
||||
}
|
||||
style={{ marginTop: '4px' }}
|
||||
/>
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<div style={{ fontWeight: 600, fontSize: '13px', marginBottom: '6px' }}>
|
||||
{c.kind === 'add' ? 'Neu hinzufügen' : 'Bestehende Zeile aktualisieren'}
|
||||
</div>
|
||||
{c.kind === 'update' && c.before ? (
|
||||
<div style={{ fontSize: '12px', lineHeight: 1.5 }}>
|
||||
<div style={{ color: 'var(--text3)', marginBottom: '2px' }}>Bisher</div>
|
||||
<div style={{ marginBottom: '8px' }}>
|
||||
{describeExerciseSkillRowForPreview(c.before, skillsCatalog)}
|
||||
</div>
|
||||
<div style={{ color: 'var(--text3)', marginBottom: '2px' }}>Nach KI-Vorschlag</div>
|
||||
<div>{describeExerciseSkillRowForPreview(c.after, skillsCatalog)}</div>
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ fontSize: '13px', lineHeight: 1.5 }}>
|
||||
{describeExerciseSkillRowForPreview(c.after, skillsCatalog)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</label>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</section>
|
||||
) : null}
|
||||
|
||||
<div
|
||||
style={{
|
||||
marginTop: '20px',
|
||||
paddingTop: '14px',
|
||||
borderTop: '1px solid var(--border)',
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
flexWrap: 'wrap',
|
||||
gap: '10px',
|
||||
}}
|
||||
>
|
||||
<button type="button" className="btn btn-secondary" onClick={discardExerciseAiSuggestionPreview}>
|
||||
Abbrechen
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-primary"
|
||||
disabled={!canApplySomething}
|
||||
onClick={() => applyExerciseAiSuggestionPreview()}
|
||||
>
|
||||
Ausgewähltes übernehmen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})()}
|
||||
|
||||
<ExercisePickerModal
|
||||
open={comboStationPickerIx !== null}
|
||||
onClose={() => setComboStationPickerIx(null)}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user