Funktionen: Abrechnung der Einrichtung wählen, Abo-Fehler anzeigen
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
8f75276c54
commit
06166221ec
2 files changed
+27
-5
No files matched your search
@@ -7,6 +7,7 @@ import {
|
||||
FUNKTIONEN, ZUSTAND, AKTION_LABEL, funktionOf, aktionen, mergeFunktion, siteKeyOf, abgeschaltetText,
|
||||
hindernisText, panelModell, anleitungOffen, textformNoetig, textformAnsicht, textformBereit, speicherStatus,
|
||||
anfrageZeilen, anfrageStatus, stripeText, stripeTon, shopHatEintrag, begruendungOk, BEGRUENDUNG_MAX, entscheidungStatus,
|
||||
ABRECHNUNG_OPTIONEN, ABRECHNUNG_HINWEIS, fragtAbrechnung, abrechnungBereit, featureFehlerText,
|
||||
} from '../../lib/funktionenView'
|
||||
import { FUNKTION_CHANNELS, channelLabel } from '../../lib/analyseView'
|
||||
import { berlinYmd, fmtDateTime } from '../../lib/format'
|
||||
@@ -264,6 +265,22 @@ function UnterkunftPanel({ customerId, eintrag, onDecided }) {
|
||||
)
|
||||
}
|
||||
|
||||
// Wahl beim Aktivieren, wohin die Einrichtungszeit geht (V9): Segment-Schalter ohne Vorauswahl, dahinter die Erklaerung (ohne Preis)
|
||||
function AbrechnungWahl({ value, onChange, disabled }) {
|
||||
return (
|
||||
<>
|
||||
<div className="seg" role="group" aria-label="Abrechnung der Einrichtung">
|
||||
{ABRECHNUNG_OPTIONEN.map((o) => (
|
||||
<button key={o.value} type="button" className={value === o.value ? 'on' : ''} aria-pressed={value === o.value} disabled={disabled} onClick={() => onChange(o.value)}>
|
||||
{o.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<span className="muted small">{ABRECHNUNG_HINWEIS}</span>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const LEERE_TEXTFORM = { confirmed: false, date: '', channel: '' }
|
||||
|
||||
export default function CustomerFunktionen({ customerId }) {
|
||||
@@ -273,6 +290,7 @@ export default function CustomerFunktionen({ customerId }) {
|
||||
const [error, setError] = useState(null)
|
||||
const [confirm, setConfirm] = useState(null) // { projectId, key, action }
|
||||
const [note, setNote] = useState('')
|
||||
const [abrechnung, setAbrechnung] = useState('') // Wahl beim Aktivieren: '' (noch keine) | 'abo' | 'projekt'; nie vorbelegt
|
||||
const [busy, setBusy] = useState(false)
|
||||
const [status, setStatus] = useState(null) // { ok, text }
|
||||
const [panel, setPanel] = useState({}) // projectId -> true/false (Knopf "Anleitung"); ohne Eintrag entscheidet die Adresse (?anleitung=<projectId>)
|
||||
@@ -309,9 +327,11 @@ export default function CustomerFunktionen({ customerId }) {
|
||||
setStatus(null)
|
||||
try {
|
||||
const mitTextform = textformNoetig(key, action, felder[key])
|
||||
const mitAbrechnung = fragtAbrechnung(key, action, funktionOf(projects?.find((p) => p.projectId === projectId), key))
|
||||
const antwort = await setFeature(customerId, key, {
|
||||
projectId, action, note: note.trim(),
|
||||
...(mitTextform ? { textformDate: textform.date, textformChannel: textform.channel } : {}),
|
||||
...(mitAbrechnung ? { abrechnung } : {}),
|
||||
})
|
||||
// Die Antwort enthaelt das ganze Projekt; uebernommen wird nur die geaenderte Funktion
|
||||
const updated = antwort?.projects?.[0]
|
||||
@@ -323,7 +343,7 @@ export default function CustomerFunktionen({ customerId }) {
|
||||
setStatus(key === 'zeiten' && action === 'disable' ? abschaltMeldung(antwort?.ersatzTicket) : speicherStatus(antwort, key))
|
||||
} catch (err) {
|
||||
setConfirm(null)
|
||||
setStatus({ ok: false, text: err.message })
|
||||
setStatus({ ok: false, text: featureFehlerText(err, key) })
|
||||
} finally {
|
||||
setBusy(false)
|
||||
// Version und Hindernis der Anleitung haengen vom Zustand ab: Panel dieser Zeile neu laden
|
||||
@@ -334,7 +354,7 @@ export default function CustomerFunktionen({ customerId }) {
|
||||
}
|
||||
|
||||
const teamGespeichert = (projectId, antwort) => setProjects((list) => mergeTeam(list, projectId, antwort))
|
||||
const ask = (projectId, key, action) => { setNote(''); setTextform(LEERE_TEXTFORM); setConfirm({ projectId, key, action }) }
|
||||
const ask = (projectId, key, action) => { setNote(''); setTextform(LEERE_TEXTFORM); setAbrechnung(''); setConfirm({ projectId, key, action }) }
|
||||
const heute = berlinYmd(new Date())
|
||||
const panelOffen = (projectId) => (Object.prototype.hasOwnProperty.call(panel, projectId) ? panel[projectId] : anleitungOffen(window.location.search, projectId))
|
||||
const togglePanel = (projectId) => setPanel((p) => ({ ...p, [projectId]: !panelOffen(projectId) }))
|
||||
@@ -417,8 +437,9 @@ export default function CustomerFunktionen({ customerId }) {
|
||||
<td colSpan={4}>
|
||||
<div className="flex items-center gap-2 wrap">
|
||||
<span>{f.bestaetigen[confirm.action]}</span>
|
||||
{fragtAbrechnung(f.key, confirm.action, feature) && <AbrechnungWahl value={abrechnung} onChange={setAbrechnung} disabled={busy} />}
|
||||
<input className="input funktionen-note" placeholder="Notiz (optional)" maxLength={120} aria-label="Notiz (optional)" value={note} onChange={(e) => setNote(e.target.value)} />
|
||||
<Button type="button" variant="primary" onClick={save} disabled={busy || textformFehlt}>{busy ? 'Speichert …' : 'Ja'}</Button>
|
||||
<Button type="button" variant="primary" onClick={save} disabled={busy || textformFehlt || !abrechnungBereit({ key: f.key, action: confirm.action, feature, abrechnung })}>{busy ? 'Speichert …' : 'Ja'}</Button>
|
||||
<Button type="button" onClick={() => setConfirm(null)} disabled={busy}>Abbrechen</Button>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
+3
-2
@@ -98,9 +98,10 @@ export const getFunktionen = (customerId) => request(`/funktionen/${enc(customer
|
||||
/**
|
||||
* Aktivieren (ohne Zahlung) oder Abschalten einer Funktion (key: news | termine | shop | zeiten | unterkunft); Antwort { projects: [das eine Projekt] }.
|
||||
* Terminplaner und Webshop im Modus on: beim Aktivieren zusaetzlich textformDate (JJJJ-MM-TT) und textformChannel (chat | email).
|
||||
* abrechnung ('abo' | 'projekt', nur beim Aktivieren): wohin die Einrichtungszeit geht; 409 abo_fehlt bringt err.code und err.grund mit (featureFehlerText).
|
||||
*/
|
||||
export const setFeature = (customerId, key, { projectId, action, note, textformDate, textformChannel }) =>
|
||||
request(`/funktionen/${enc(customerId)}/${enc(key)}`, { method: 'POST', body: featureBody({ projectId, action, note, textformDate, textformChannel }) })
|
||||
export const setFeature = (customerId, key, { projectId, action, note, textformDate, textformChannel, abrechnung }) =>
|
||||
request(`/funktionen/${enc(customerId)}/${enc(key)}`, { method: 'POST', body: featureBody({ projectId, action, note, textformDate, textformChannel, abrechnung }) })
|
||||
export const setNewsFeature = (customerId, body) => setFeature(customerId, 'news', body)
|
||||
/** Einbauanleitung des Terminplaners: { stand, warnungen, abschnitte, text, state, ticketWoid, ticketVersion, hindernis } (Aufbereitung: panelModell). */
|
||||
export const getTermineAnleitung = (customerId, projectId) => request(anleitungPfad(customerId, projectId))
|
||||
|
||||
Reference in new issue
Block a user