Files
tickte-system/src/pages/StorageTasksPage.jsx
T

100 lines
5.2 KiB
React
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useCallback, useEffect, useState } from 'react'
import { Link } from 'react-router-dom'
import { FaSpinner, FaRotateRight } from 'react-icons/fa6'
import { PageHeader, Card, Badge, Button } from '../components/ui'
import { listStorageTasks, markStorageDone } from '../lib/teamApi'
import { fmtDate, fmtDateTime } from '../lib/format'
import { changeText, failedText, isOverdue, orderedKind, partialText, scopeText, summaryText, taskText } from '../lib/storageFormat'
import '../styles/admin-areas.css'
// E-Mail-Speicher (Kunden -> E-Mail-Speicher): Zusatzspeicher-Aufgaben in konsoleH wie im frueheren Admin-Panel
export default function StorageTasksPage() {
const [data, setData] = useState(null)
const [error, setError] = useState(null)
const [busyId, setBusyId] = useState('')
const load = useCallback(() => {
setError(null)
listStorageTasks()
.then(setData)
.catch((err) => setError(err.message || 'Speicher-Aufgaben konnten nicht geladen werden.'))
}, [])
useEffect(() => { load() }, [load])
const done = async (task) => {
if (task.partial && !window.confirm('Stripe-Menge von Hand korrigiert?')) return
setBusyId(task.id)
try {
await markStorageDone(task.id)
load()
} catch (err) {
window.alert(`Aufgabe konnte nicht als erledigt gespeichert werden: ${err.message}`)
} finally {
setBusyId('')
}
}
const summary = data ? summaryText(data) : null
return (
<div className="page">
<PageHeader title="E-Mail-Speicher" subtitle="Zusatzspeicher der Abos in konsoleH einrichten und senken" />
{error && (
<div className="area-error area-inline">
{error} <Button size="sm" variant="ghost" onClick={load}><FaRotateRight /> Erneut versuchen</Button>
</div>
)}
{!data && !error && <FaSpinner className="spinner" />}
{data && (
<>
<div className={`area-summary ${summary.warn ? 'warn' : ''}`}>{summary.text}</div>
<Card pad={false}>
<div className="area-rows">
<div className="area-row storage-row head"><span>Fällig</span><span>Kunde</span><span>Aufgabe in konsoleH</span><span>Bestellt</span><span /></div>
{data.tasks.length === 0 && <div className="area-row"><span className="area-muted">Keine offenen Speicher-Aufgaben.</span></div>}
{data.tasks.map((t) => (
<div key={t.id} className="area-row storage-row">
<span>
{t.kind === 'einrichten'
? <><Badge tone="warn">jetzt einrichten</Badge><br /><span className="area-muted">bestellt {fmtDateTime(t.createdAt)}</span></>
: <><Badge tone={isOverdue(t) ? 'warn' : 'muted'}>zum Periodenende</Badge><br />{fmtDate(t.dueAt)}</>}
</span>
<span>
<Link to={`/customers/${t.customerId}`}><strong>{t.customerName}</strong></Link>
{t.customerCode && <><br /><span className="area-muted">Kd.-Nr. {t.customerCode}</span></>}
{t.email && <><br /><span className="area-muted">{t.email}</span></>}
<br /><span className="area-muted">{t.productLabel}</span>
</span>
<span>
<strong>{taskText(t)}</strong>
<br /><span className="area-muted">{scopeText(t)}</span>
<br /><span className="area-muted">{changeText(t)}</span>
{t.partial && <><br /><span className="area-danger">{partialText(t)}</span></>}
</span>
<span>{fmtDateTime(t.createdAt)}<br /><span className="area-muted">{orderedKind(t)}</span></span>
<span>
<Button size="sm" variant="primary" data-storage-done={t.id} onClick={() => done(t)} disabled={busyId === t.id}>
{busyId === t.id ? <FaSpinner className="spinner" /> : 'Erledigt'}
</Button>
</span>
</div>
))}
</div>
</Card>
{data.failed.length > 0 && (
<Card title="Gescheiterte Änderungen (letzte 30 Tage)">
{data.failed.map((f) => (
<div key={f.id} style={{ marginBottom: 6 }}>
{fmtDateTime(f.createdAt)} · <strong>{f.customerName}</strong> · <span className="area-muted">{failedText(f)}</span>
</div>
))}
</Card>
)}
<p className="area-muted" style={{ marginTop: 12 }}>
Jedes Abo bringt 5 GB E-Mail-Speicher mit, jedes Zusatzpaket 5 GB dazu. „Einrichten“: Postfachgrenze in konsoleH jetzt auf den Zielwert setzen. „Zum Periodenende“: erst am genannten Tag auf den Zielwert senken. Die Grenzen gelten für den Gesamtspeicher des Kunden (5 GB + 5 GB je Paket); er verteilt ihn nach seiner Vorgabe auf seine Postfächer (§ 3 Abs. 4). Angezeigt wird die Domain der Kontakt-Adresse, eine eigene Mail-Domain ist im Portal nicht gespeichert. „Vergeben“ zählt, was konsoleH gerade hält: eine Verringerung zählt erst nach „Erledigt“. Danach „Erledigt“ drücken – der Kunde hat seine Bestätigung schon bekommen.
</p>
</>
)}
</div>
)
}