Offene Vorschauen sichtbar machen und doppelt aufnehmen verhindern
Der Abgleich bucht bewusst nichts, sondern legt eine Vorschau zur Bestaetigung an. Nur fuehrte von dort kein Weg weiter: die Import-Seite zeigte allein den Datei-Upload, und die Rueckmeldung sagte lediglich "abgeglichen". Die gefundenen Pakete lagen damit unsichtbar herum und der Knopf wirkte, als tue er nichts. Die Import-Seite listet jetzt alle Importe, die noch auf Bestaetigung warten, und oeffnet sie in derselben Vorschau wie einen frischen Upload. Die Meldung nach dem Abgleich nennt die Zahl der Funde und den naechsten Schritt. Ausserdem beruecksichtigt der Abgleich jetzt offene Vorschauen: bisher pruefte er nur gegen bereits gebuchte Pakete. Ein zweiter Lauf legte dieselben Zeilen deshalb ein weiteres Mal an, und beim Buchen kollidierten beide Importe an der eindeutigen Bestell-ID - der zweite schlug komplett fehl.
This commit is contained in:
@@ -7,7 +7,13 @@ import Notice from '../components/Notice';
|
||||
import { ApiError, api } from '../lib/api';
|
||||
import { KIND_LABEL, REASON_LABEL, SOURCE_LABEL, formatAmount, formatDate } from '../lib/format';
|
||||
import { useSession } from '../lib/session';
|
||||
import type { Character, CreditKind, LedgerEntry, PlayerDetail as Detail } from '../lib/types';
|
||||
import type {
|
||||
Character,
|
||||
CreditKind,
|
||||
LedgerEntry,
|
||||
PlayerDetail as Detail,
|
||||
ScrapeSummary,
|
||||
} from '../lib/types';
|
||||
|
||||
type DialogState =
|
||||
| { type: 'none' }
|
||||
@@ -61,6 +67,45 @@ export default function PlayerDetail() {
|
||||
}
|
||||
};
|
||||
|
||||
// Der Abgleich bucht nichts, er legt eine Vorschau an. Das muss die Meldung
|
||||
// auch sagen, sonst wirkt der Knopf wirkungslos.
|
||||
const syncFromGextension = async () => {
|
||||
setBusy(true);
|
||||
setError(null);
|
||||
try {
|
||||
const result = await api<ScrapeSummary>(`/api/scrape/player/${playerId}`, {
|
||||
method: 'POST',
|
||||
body: {},
|
||||
});
|
||||
await load();
|
||||
|
||||
if (result.newRows > 0) {
|
||||
setFlash(
|
||||
`${result.newRows} ${result.newRows === 1 ? 'Paket' : 'Pakete'} gefunden. ` +
|
||||
'Unter Import bestätigen, damit die Guthaben entstehen.',
|
||||
);
|
||||
} else if (result.awaitingConfirmation > 0) {
|
||||
setFlash(
|
||||
`${result.awaitingConfirmation} ${result.awaitingConfirmation === 1 ? 'Paket wartet' : 'Pakete warten'} ` +
|
||||
'bereits unter Import auf Bestätigung.',
|
||||
);
|
||||
} else if (result.unclear > 0) {
|
||||
setFlash(`${result.unclear} unklare Einträge — siehe Kasten oben.`);
|
||||
} else if (result.duplicates > 0) {
|
||||
setFlash('Nichts Neues — alle Pakete sind bereits gebucht.');
|
||||
} else if (result.failedPlayers.length > 0) {
|
||||
setError(`Profil nicht abrufbar: ${result.failedPlayers[0]?.reason ?? 'unbekannt'}`);
|
||||
} else {
|
||||
setFlash('Keine CC-relevanten Pakete im Profil gefunden.');
|
||||
}
|
||||
window.setTimeout(() => setFlash(null), 8000);
|
||||
} catch (err) {
|
||||
setError(err instanceof ApiError ? err.message : 'Abgleich fehlgeschlagen');
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) return <p className="muted">Lade …</p>;
|
||||
if (!detail) return <Notice kind="error">{error ?? 'Spieler nicht gefunden'}</Notice>;
|
||||
|
||||
@@ -162,15 +207,7 @@ export default function PlayerDetail() {
|
||||
className="btn"
|
||||
disabled={busy}
|
||||
title="Pakete dieses Spielers von GExtension abrufen"
|
||||
onClick={() =>
|
||||
void run(async () => {
|
||||
const result = await api<{ newRows: number; unclear: number }>(
|
||||
`/api/scrape/player/${playerId}`,
|
||||
{ method: 'POST', body: {} },
|
||||
);
|
||||
return result;
|
||||
}, 'GExtension abgeglichen.')
|
||||
}
|
||||
onClick={() => void syncFromGextension()}
|
||||
>
|
||||
GExtension abgleichen
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user