// Tryb "Sprawdź teraz" — manual skan wybranych WORDów, opcjonalna rezerwacja const { useState: uSC, useEffect: uEC, useMemo: uMC } = React; function CheckNowView({ toast }) { const [words, setWords] = uSC([]); const [wordsLoading, setWordsLoading] = uSC(true); const [selectedWords, setSelectedWords] = uSC(new Set()); const [selectedVoiv, setSelectedVoiv] = uSC(""); const [category, setCategory] = uSC("A"); const [examType, setExamType] = uSC("praktyka"); const [runId, setRunId] = uSC(null); const [run, setRun] = uSC(null); const [reservingId, setReservingId] = uSC(""); uEC(() => { window.ZlapApi.listWords() .then(setWords) .catch(e => toast(`Błąd listy WORDów: ${e.message}`, "err")) .finally(() => setWordsLoading(false)); }, []); uEC(() => { if (!runId) return; let cancelled = false; const tick = async () => { try { const r = await window.ZlapApi.getCheckRun(runId); if (!cancelled) { setRun(r); if (r.status === "done" || r.status === "failed") return; } } catch (e) {} if (!cancelled) setTimeout(tick, 2000); }; tick(); return () => { cancelled = true; }; }, [runId]); const voivodeships = uMC(() => { const s = new Set(words.map(w => w.voivodeship)); return ["", ...Array.from(s).sort()]; }, [words]); const wordsInVoiv = uMC(() => { return selectedVoiv ? words.filter(w => w.voivodeship === selectedVoiv) : words; }, [words, selectedVoiv]); const toggleWord = (id) => { setSelectedWords(s => { const ns = new Set(s); if (ns.has(id)) ns.delete(id); else ns.add(id); return ns; }); }; const selectAll = () => setSelectedWords(new Set(wordsInVoiv.map(w => w.id))); const clearAll = () => setSelectedWords(new Set()); const refreshWords = async () => { try { toast("Odświeżam listę WORDów — to może potrwać kilkanaście sekund…", "info"); const r = await window.ZlapApi.refreshWords(); const fresh = await window.ZlapApi.listWords(); setWords(fresh); toast(`Zaktualizowano: +${r.inserted} nowych, ${r.updated} odświeżonych`, "ok"); } catch (e) { toast(`Błąd refresh: ${e.message}`, "err"); } }; const runCheck = async () => { if (selectedWords.size === 0) { toast("Wybierz co najmniej jeden WORD", "warn"); return; } try { const r = await window.ZlapApi.checkNow({ word_ids: Array.from(selectedWords), category, exam_type: examType, }); setRunId(r.id); setRun(null); toast("Skan uruchomiony — czekaj na wyniki", "info"); } catch (e) { toast(`Błąd: ${e.message}`, "err"); } }; const reserve = async (row) => { if (!confirm( `Zarezerwować ${row.word_name} ${row.slot_date} ${row.slot_time}?\n\n` + `Bot przejdzie cały flow, wypełni dane kandydata i wyśle link do płatności na email.\n` + `MASZ 3 MINUTY na opłacenie po otrzymaniu linku.` )) return; setReservingId(`${row.word_id}-${row.slot_date}-${row.slot_time}`); try { const res = await window.ZlapApi.reserveFromCheck(runId, { word_id: row.word_id, slot_date: row.slot_date, slot_time: row.slot_time, category, exam_type: examType, }); if (res.status === "reserved") { toast(`ZAREZERWOWANO — link do płatności wysłany na email. Masz 3 minuty!`, "hit"); } else { toast(`Status: ${res.status} — ${res.error || "sprawdź Rezerwacje"}`, "warn"); } } catch (e) { toast(`Błąd rezerwacji: ${e.message}`, "err"); } finally { setReservingId(""); } }; return (
Sprawdź terminy
Manualny skan wybranych WORDów. Po kliknięciu „Zarezerwuj" bot przejdzie flow i wyśle link do płatności na email.
WORDy do sprawdzenia: {selectedWords.size} / {wordsInVoiv.length}
{wordsLoading ? (
Ładowanie listy…
) : wordsInVoiv.length === 0 ? (
Brak WORDów w bazie. Kliknij „Odśwież listę WORDów", żeby pobrać z info-car.pl.
) : (
{wordsInVoiv.map(w => ( ))}
)}
{run && (
Wyniki skanu
{run.status} {run.status === "running" && ( Skanowanie w toku… )}
{run.error && (
{run.error}
)} {run.results.length === 0 && } {run.results.map((r, i) => ( ))}
WojewództwoWORDNajbliższyInfo
Brak wyników (jeszcze)…
{r.voivodeship} {r.word_name} {r.available ? `${r.slot_date} ${r.slot_time}` : "—"} {r.message} {r.available && ( )}
)}
); } window.CheckNowView = CheckNowView;