/**
* Property On — Register «Aufgaben» des Personalblatts (§8).
*
* Die Liste zeigt jede Aufgabe des gewählten Mitarbeiters einzeln schaltbar.
* Geändert wird zuerst nur ein lokaler Entwurf: Zuschalten und Pausieren
* verändert, was ein digitaler Mitarbeiter im Alltag der Bewirtschaftung tut —
* das darf nicht beiläufig im Hintergrund passieren, sondern erst mit einer
* bewussten Freigabe über «Änderungen speichern» (§8.2).
*/
import { memo, useCallback, useMemo, useState } from 'react'
import { Box, Button, Switch, Typography } from '@mui/material'
import { Clock, Plug, RotateCcw, Save, ShieldCheck } from 'lucide-react'
import type { AgentTask, TeamAgent } from '../../domain/teamAgent'
import { AGENT_CHANNEL_LABELS, AGENT_SYSTEM_LABELS } from '../../lib/constants'
import { DS_BG, DS_BORDER, DS_SHADOW, DS_SURFACE, DS_TEXT } from '../../lib/ds'
import { GenericBadge } from '../shared/GenericBadge'
import { useSaveAgentTasks } from '../../hooks/useTeamAgents'
// ── Eine Aufgabenzeile ────────────────────────────────────────────────────────
interface TaskRowProps {
task: AgentTask
busy: boolean
onToggle: (id: string, enabled: boolean) => void
}
/**
* `React.memo` ist Pflicht: ein Mitarbeiter führt bis zu zwölf Aufgaben, und
* jeder Schalterklick verändert den Entwurf der ganzen Liste (CLAUDE.md §10.2).
*/
const TaskRow = memo(function TaskRow({ task, busy, onToggle }: TaskRowProps) {
const dependencies = useMemo(() => {
const out: string[] = []
if (task.dependsOnChannel) {
out.push(`Kanal: ${AGENT_CHANNEL_LABELS[task.dependsOnChannel] ?? task.dependsOnChannel}`)
}
if (task.dependsOnSystem) {
out.push(`Systemzugang: ${AGENT_SYSTEM_LABELS[task.dependsOnSystem] ?? task.dependsOnSystem}`)
}
return out
}, [task.dependsOnChannel, task.dependsOnSystem])
const handleChange = useCallback(
(_event: unknown, checked: boolean) => onToggle(task.id, checked),
[onToggle, task.id],
)
return (
{/* Schalter mit Zustand als Text — Farbe allein trägt keinen Status (§18) */}
{task.enabled ? 'Aktiv' : 'Pausiert'}
{task.title}
{task.description}
Zeitplan: {task.schedule}
{dependencies.map((dependency) => (
Setzt voraus — {dependency}
))}
{task.requiresApproval && (
}
tooltip="Das Ergebnis dieser Aufgabe wird erst nach Ihrer Freigabe wirksam."
/>
)}
)
})
// ── Register «Aufgaben» ───────────────────────────────────────────────────────
export function AgentTasksTab({ agent }: { agent: TeamAgent }) {
const [draft, setDraft] = useState(agent.tasks)
const saveTasks = useSaveAgentTasks()
/**
* Beim Wechsel des Mitarbeiters wird der Entwurf neu aufgesetzt — sonst stünde
* der Entwurf des zuvor gewählten Mitarbeiters in einem fremden Personalblatt.
* Bewusst nur an `agent.id` gebunden: liefe der Effekt auch bei jeder neuen
* Referenz von `agent.tasks`, würde ein Hintergrundabgleich unbestätigte
* Änderungen still verwerfen.
*/
// Kein Effekt nötig: `Personalverwaltung` gibt diesem Reiter die Mitarbeiter-ID
// als `key` mit, er wird beim Wechsel des Dossiers ohnehin neu aufgebaut.
/** Ein Durchlauf für beide Ableitungen — Anzahl aktiver Aufgaben und Abweichung (§10.4). */
const { activeCount, dirty } = useMemo(() => {
const saved = new Map(agent.tasks.map((task) => [task.id, task.enabled]))
let active = 0
let changed = draft.length !== agent.tasks.length
for (const task of draft) {
if (task.enabled) active += 1
if (saved.get(task.id) !== task.enabled) changed = true
}
return { activeCount: active, dirty: changed }
}, [draft, agent.tasks])
const handleToggle = useCallback((id: string, enabled: boolean) => {
setDraft((prev) => prev.map((task) => (task.id === id ? { ...task, enabled } : task)))
}, [])
const handleReset = useCallback(() => setDraft(agent.tasks), [agent.tasks])
const handleSave = useCallback(() => {
saveTasks.mutate({ id: agent.id, tasks: draft })
}, [saveTasks, agent.id, draft])
const busy = saveTasks.isPending
const focusRing = { '&:focus-visible': { outline: `2px solid ${DS_TEXT.brand}`, outlineOffset: 2 } }
return (
{activeCount} von {draft.length} Aufgaben aktiv
Jede Aufgabe lässt sich einzeln zuschalten oder pausieren. Änderungen werden erst mit dem
Speichern für {agent.name} wirksam.
{dirty && (
Nicht gespeicherte Änderungen
}
onClick={handleSave}
sx={{ textTransform: 'none', fontWeight: 600, ml: 'auto', ...focusRing }}
>
Änderungen speichern
}
onClick={handleReset}
sx={{ textTransform: 'none', fontWeight: 600, ...focusRing }}
>
Zurücksetzen
)}
{draft.length === 0 ? (
Für {agent.name} sind noch keine Aufgaben hinterlegt.
) : (
{draft.map((task) => (
))}
)}
)
}