"use client";
import { useRouter } from "next/navigation";
import { useState, type ChangeEvent, type FormEvent } from "react";
import { WindowFields } from "@/components/WindowFields";
import { apiFetch, errorMessage } from "@/lib/client-api";
import { dayStatusLabel, fmtClock, fmtMinutes } from "@/lib/format";
import type { QueryWindow, Schedule, Today } from "@/lib/types";
/** Grava e valida a credencial na PontoMais; a anterior é mantida se a validação falhar. */
export function CredentialsForm({ userId }: { userId: number }) {
const router = useRouter();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [message, setMessage] = useState<{ ok: boolean; text: string } | null>(null);
const [busy, setBusy] = useState(false);
async function submit(e: FormEvent) {
e.preventDefault();
setBusy(true);
setMessage(null);
try {
await apiFetch(`/users/${userId}/credentials`, { method: "PUT", body: { email, password } });
setPassword("");
setMessage({ ok: true, text: "Credencial validada na PontoMais e gravada." });
router.refresh();
} catch (err) {
setMessage({ ok: false, text: errorMessage(err) });
} finally {
setBusy(false);
}
}
return (
);
}
export function ScheduleForm({
userId,
schedule,
timezone,
effectiveWindow,
disabled,
}: {
userId: number;
schedule: Schedule;
timezone: string;
effectiveWindow: QueryWindow;
disabled: boolean;
}) {
const router = useRouter();
const [form, setForm] = useState({
lunch_minutes: schedule.lunch_minutes,
daily_work_minutes: schedule.daily_work_minutes,
entrada2_lead_minutes: schedule.entrada2_lead_minutes,
saida2_lead_minutes: schedule.saida2_lead_minutes,
timezone,
});
const [useGlobal, setUseGlobal] = useState(schedule.window === null);
const [windowValue, setWindowValue] = useState(schedule.window ?? effectiveWindow);
const [message, setMessage] = useState<{ ok: boolean; text: string } | null>(null);
const [busy, setBusy] = useState(false);
function number(key: keyof typeof form) {
return (e: ChangeEvent) => setForm({ ...form, [key]: Number(e.target.value) });
}
async function submit(e: FormEvent) {
e.preventDefault();
setBusy(true);
setMessage(null);
try {
await apiFetch(`/users/${userId}/schedule`, {
method: "PUT",
body: { ...form, window: useGlobal ? null : windowValue },
});
setMessage({ ok: true, text: "Jornada salva. Vale a partir da próxima consulta." });
router.refresh();
} catch (err) {
setMessage({ ok: false, text: errorMessage(err) });
} finally {
setBusy(false);
}
}
return (
);
}
/** Consulta ao vivo na PontoMais (não é automática para não gastar consultas à toa). */
export function TodayPanel({ userId, canTest }: { userId: number; canTest: boolean }) {
const [today, setToday] = useState(null);
const [error, setError] = useState("");
const [busy, setBusy] = useState(false);
async function load(test: boolean) {
setBusy(true);
setError("");
try {
const path = test ? `/users/${userId}/credentials/test` : `/users/${userId}/today`;
setToday(await apiFetch(path, { method: test ? "POST" : "GET" }));
} catch (err) {
setError(errorMessage(err));
} finally {
setBusy(false);
}
}
return (
{canTest && (
)}
{error &&
{error}
}
{today && (
- Pontos
- {today.points.length ? today.points.map((p) => fmtClock(p, today.timezone)).join(" · ") : "nenhum"}
{today.invalid && (
<>
- Atenção
- pontos repetidos ou fora de ordem
>
)}
{today.overtime && (
<>
- Atenção
- mais de quatro pontos (hora extra, fora do escopo)
>
)}
- Retorno previsto
- {fmtClock(today.entrada2_at, today.timezone)}
- Saída prevista
- {today.saida2_error ? {today.saida2_error} : fmtClock(today.saida2_at, today.timezone)}
- Situação do dia
- {dayStatusLabel[today.day_state.status]}
)}
);
}