Analyse: Einschalten im WOMS nur mit Datum und Weg der Zustimmung in Textform

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-10-01 19:02:05 +00:00
1 parent 72f07410fe
commit 52968b3017
4 files changed
+183 -21

No files matched your search

+62 -13
View File
@@ -1,6 +1,6 @@
import { useState, useEffect } from 'react'
import { fetchAnalyse, setAnalyse } from '../lib/analyseApi'
import { analyseStatus, avvLabel } from '../lib/analyseView'
import { analyseStatus, textformLabel, canSubmitTextform, channelLabel, legalLinks, chatTemplate, privacyHint } from '../lib/analyseView'
const COLORS = { green: '#10b981', red: '#ef4444', grey: '#a0aec0' }
@@ -10,6 +10,9 @@ export default function AnalyseRow({ projectId }) {
const [loadError, setLoadError] = useState('')
const [mode, setMode] = useState(null) // null | 'on' | 'off'
const [confirmed, setConfirmed] = useState(false)
const [date, setDate] = useState('')
const [channel, setChannel] = useState('')
const [copied, setCopied] = useState(false)
const [busy, setBusy] = useState(false)
const [error, setError] = useState('')
@@ -25,10 +28,12 @@ export default function AnalyseRow({ projectId }) {
setBusy(true)
setError('')
try {
await setAnalyse(projectId, { on, avvConfirmedVerbally: on })
await setAnalyse(projectId, { on, textformDate: date, textformChannel: channel, audienceConfirmed: on })
setData(await fetchAnalyse(projectId))
setMode(null)
setConfirmed(false)
setDate('')
setChannel('')
} catch (err) {
setError(err.message)
} finally {
@@ -36,7 +41,18 @@ export default function AnalyseRow({ projectId }) {
}
}
const cancel = () => { setMode(null); setConfirmed(false); setError('') }
const cancel = () => { setMode(null); setConfirmed(false); setDate(''); setChannel(''); setError('') }
const today = new Date().toLocaleDateString('sv-SE', { timeZone: 'Europe/Berlin' })
const minDate = data?.avvVersion || ''
const privacyBlocked = data?.privacy?.ok === false
const canSubmit = !privacyBlocked && canSubmitTextform({ confirmed, date, channel, today, minDate })
const copyTemplate = async () => {
try {
await navigator.clipboard.writeText(chatTemplate(data.avvVersion))
setCopied(true)
setTimeout(() => setCopied(false), 2000)
} catch { /* Kopieren nicht möglich: Text bleibt markierbar */ }
}
const status = analyseStatus(data)
return (
@@ -59,16 +75,49 @@ export default function AnalyseRow({ projectId }) {
)}
</div>
{mode === 'on' && data && (
<div style={{ marginTop: 8, display: 'flex', gap: 8, flexWrap: 'wrap', alignItems: 'center' }}>
<label style={{ display: 'inline-flex', gap: 6, alignItems: 'center' }}>
<input type="checkbox" checked={confirmed} onChange={(e) => setConfirmed(e.target.checked)} />
{avvLabel(data.avvVersion)}
</label>
<button type="button" className="btn btn-sm" disabled={!confirmed || busy} onClick={() => apply(true)}>Einschalten</button>
<button type="button" className="btn btn-sm" disabled={busy} onClick={cancel}>Abbrechen</button>
</div>
)}
{mode === 'on' && data && (() => {
const label = textformLabel(data.avvVersion)
const links = legalLinks(data.avvVersion)
return (
<div style={{ marginTop: 8, display: 'flex', flexDirection: 'column', gap: 8 }}>
<label style={{ display: 'inline-flex', gap: 6, alignItems: 'flex-start' }}>
<input type="checkbox" checked={confirmed} onChange={(e) => setConfirmed(e.target.checked)} style={{ marginTop: 3 }} />
<span>
{label.before}
<a href={links.avv} target="_blank" rel="noopener noreferrer">{label.avv}</a>
{label.between}
<a href={links.lb} target="_blank" rel="noopener noreferrer">{label.lb}</a>
{label.after}
</span>
</label>
<div style={{ display: 'flex', gap: 12, flexWrap: 'wrap', alignItems: 'center' }}>
<label>
Datum der Zustimmung:{' '}
<input type="date" required value={date} min={minDate} max={today} onChange={(e) => setDate(e.target.value)} />
</label>
<span role="radiogroup" aria-label="Weg der Zustimmung" style={{ display: 'inline-flex', gap: 10, alignItems: 'center' }}>
<span>Weg:</span>
{['chat', 'email'].map((c) => (
<label key={c} style={{ display: 'inline-flex', gap: 4, alignItems: 'center' }}>
<input type="radio" name={`analyse-weg-${projectId}`} value={c} checked={channel === c} onChange={() => setChannel(c)} />
{channelLabel(c)}
</label>
))}
</span>
</div>
{privacyBlocked && <div style={{ color: COLORS.red }}>{privacyHint(data.privacy)}</div>}
<details>
<summary style={{ cursor: 'pointer' }}>Textvorlage für den Chat</summary>
<p style={{ margin: '6px 0', whiteSpace: 'pre-wrap' }}>{chatTemplate(data.avvVersion)}</p>
<button type="button" className="btn btn-sm" onClick={copyTemplate}>{copied ? 'Kopiert' : 'Kopieren'}</button>
</details>
<div style={{ display: 'flex', gap: 8 }}>
<button type="button" className="btn btn-sm" disabled={!canSubmit || busy} onClick={() => apply(true)}>Einschalten</button>
<button type="button" className="btn btn-sm" disabled={busy} onClick={cancel}>Abbrechen</button>
</div>
</div>
)
})()}
{mode === 'off' && (
<div style={{ marginTop: 8, display: 'flex', gap: 8, flexWrap: 'wrap', alignItems: 'center' }}>