KI Übungen - MVP 0.8 #48

Merged
Lars merged 9 commits from develop into main 2026-05-22 19:51:54 +02:00
3 changed files with 84 additions and 32 deletions
Showing only changes of commit 9d880e2346 - Show all commits

View File

@ -3733,6 +3733,68 @@ html.modal-scroll-locked .app-main {
.exercises-page__title { .exercises-page__title {
margin: 0; margin: 0;
} }
.exercises-page__header-actions {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 10px;
}
.exercises-ai-assistant-toggle {
display: inline-flex;
align-items: center;
gap: 10px;
cursor: pointer;
user-select: none;
font-size: 14px;
color: var(--text2);
padding: 6px 10px;
border-radius: 8px;
border: 1px solid var(--border);
background: var(--surface2);
}
.exercises-ai-assistant-toggle:hover {
border-color: var(--accent-dark, rgba(29, 158, 117, 0.45));
}
.exercises-ai-assistant-toggle:has(input:checked) {
border-color: var(--accent);
background: var(--accent-light, rgba(29, 158, 117, 0.12));
color: var(--accent-dark);
font-weight: 600;
}
.exercises-ai-assistant-toggle input {
position: absolute;
opacity: 0;
width: 0;
height: 0;
pointer-events: none;
}
.exercises-ai-assistant-toggle__track {
position: relative;
flex-shrink: 0;
width: 40px;
height: 22px;
border-radius: 999px;
background: var(--border);
transition: background 0.15s ease;
}
.exercises-ai-assistant-toggle__track::after {
content: '';
position: absolute;
top: 2px;
left: 2px;
width: 18px;
height: 18px;
border-radius: 50%;
background: #fff;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
transition: transform 0.15s ease;
}
.exercises-ai-assistant-toggle:has(input:checked) .exercises-ai-assistant-toggle__track {
background: var(--accent);
}
.exercises-ai-assistant-toggle:has(input:checked) .exercises-ai-assistant-toggle__track::after {
transform: translateX(18px);
}
.exercises-page-toolbar-tabs { .exercises-page-toolbar-tabs {
margin-bottom: 14px; margin-bottom: 14px;
} }

View File

@ -14,8 +14,6 @@ export default function ExerciseListSearchBar({
exerciseCount, exerciseCount,
allOnPageSelected, allOnPageSelected,
onToggleSelectAllPage, onToggleSelectAllPage,
aiQuickCreateEnabled = false,
onToggleAiQuickCreate,
}) { }) {
return ( return (
<div className="card exercise-search-bar"> <div className="card exercise-search-bar">
@ -50,19 +48,6 @@ export default function ExerciseListSearchBar({
/> />
<div className="exercise-search-bar__actions exercise-search-bar__actions--split"> <div className="exercise-search-bar__actions exercise-search-bar__actions--split">
<div className="exercise-search-bar__actions-main"> <div className="exercise-search-bar__actions-main">
{typeof onToggleAiQuickCreate === 'function' ? (
<button
type="button"
className={
'btn btn-secondary exercise-mine-toggle' +
(aiQuickCreateEnabled ? ' exercise-mine-toggle--active' : '')
}
onClick={onToggleAiQuickCreate}
title="Neue Übung per KI aus dem Suchtext vorschlagen und anlegen"
>
KI-Anlage
</button>
) : null}
<button <button
type="button" type="button"
className={'btn btn-secondary exercise-mine-toggle' + (mineOnly ? ' exercise-mine-toggle--active' : '')} className={'btn btn-secondary exercise-mine-toggle' + (mineOnly ? ' exercise-mine-toggle--active' : '')}
@ -108,13 +93,6 @@ export default function ExerciseListSearchBar({
<p className="exercise-search-hint"> <p className="exercise-search-hint">
Trefferliste aktualisiert sich kurz nach Eingabe. Titel der aktuellen Liste erscheinen als Vorschläge (Pfeil im Trefferliste aktualisiert sich kurz nach Eingabe. Titel der aktuellen Liste erscheinen als Vorschläge (Pfeil im
Feld). Fachliche Filter über Filter zwischen Feldern UND, Auswahl mehrerer Werte je Feld mit ODER. Feld). Fachliche Filter über Filter zwischen Feldern UND, Auswahl mehrerer Werte je Feld mit ODER.
{aiQuickCreateEnabled ? (
<>
{' '}
<strong>KI-Anlage aktiv:</strong> Suchtext als Ausgang für einen KI-Entwurf ohne Treffer oder direkt über
Mit KI anlegen.
</>
) : null}
{exerciseCount > 0 ? ( {exerciseCount > 0 ? (
<> <>
{' '} {' '}

View File

@ -594,6 +594,19 @@ function ExercisesListPageRoot() {
<div className="exercises-page__header"> <div className="exercises-page__header">
<h1 className="page-title exercises-page__title">Übungen</h1> <h1 className="page-title exercises-page__title">Übungen</h1>
{pageTab === 'list' ? ( {pageTab === 'list' ? (
<div className="exercises-page__header-actions">
<label
className="exercises-ai-assistant-toggle"
title="Neue Übung per KI vorschlagen — Titel, optional Kurzbeschreibung, Fokusbereich"
>
<input
type="checkbox"
checked={aiQuickCreateEnabled}
onChange={(e) => setAiQuickCreateEnabled(e.target.checked)}
/>
<span className="exercises-ai-assistant-toggle__track" aria-hidden="true" />
<span>Neu mit KI-Assistent</span>
</label>
<NavStateLink <NavStateLink
to="/exercises/new" to="/exercises/new"
returnContext={exercisesModuleReturnContext} returnContext={exercisesModuleReturnContext}
@ -601,6 +614,7 @@ function ExercisesListPageRoot() {
> >
+ Neu + Neu
</NavStateLink> </NavStateLink>
</div>
) : ( ) : (
<span aria-hidden="true" /> <span aria-hidden="true" />
)} )}
@ -643,8 +657,6 @@ function ExercisesListPageRoot() {
exerciseCount={exercises.length} exerciseCount={exercises.length}
allOnPageSelected={allOnPageSelected} allOnPageSelected={allOnPageSelected}
onToggleSelectAllPage={toggleSelectAllPage} onToggleSelectAllPage={toggleSelectAllPage}
aiQuickCreateEnabled={aiQuickCreateEnabled}
onToggleAiQuickCreate={() => setAiQuickCreateEnabled((v) => !v)}
/> />
{showQuickCreateOffer ? ( {showQuickCreateOffer ? (
@ -663,7 +675,7 @@ function ExercisesListPageRoot() {
onRunAi={runQuickCreateAiSuggest} onRunAi={runQuickCreateAiSuggest}
hint={ hint={
aiQuickCreateEnabled aiQuickCreateEnabled
? 'KI-Anlage: Titel aus Suche oder manuell; Kurzbeschreibung optional — leer für freien KI-Vorschlag, ausgefüllt als Ausgangsidee.' ? 'Titel aus Suche oder manuell; Kurzbeschreibung optional — leer für freien KI-Vorschlag, ausgefüllt als deine Ausgangsidee.'
: undefined : undefined
} }
/> />