Projeto bot pontos
This commit is contained in:
112
web/app/(painel)/notificacoes/page.tsx
Normal file
112
web/app/(painel)/notificacoes/page.tsx
Normal file
@@ -0,0 +1,112 @@
|
||||
import Link from "next/link";
|
||||
import { ActionButton } from "@/components/ActionButton";
|
||||
import { apiGet } from "@/lib/server-api";
|
||||
import { badgeClass, fmtClock, fmtDay, notificationStatusLabel } from "@/lib/format";
|
||||
import type { Me, Notification } from "@/lib/types";
|
||||
|
||||
type Search = Promise<Record<string, string | string[] | undefined>>;
|
||||
|
||||
export default async function NotificationsPage({ searchParams }: { searchParams: Search }) {
|
||||
const sp = await searchParams;
|
||||
const params = new URLSearchParams();
|
||||
for (const key of ["day", "status", "type", "user_id"]) {
|
||||
const v = sp[key];
|
||||
if (typeof v === "string" && v) params.set(key, v);
|
||||
}
|
||||
const [{ notifications }, me] = await Promise.all([
|
||||
apiGet<{ notifications: Notification[] }>(`/notifications?${params}`),
|
||||
apiGet<Me>("/auth/me"),
|
||||
]);
|
||||
const canEdit = me.account.role !== "viewer";
|
||||
const value = (key: string) => (typeof sp[key] === "string" ? (sp[key] as string) : "");
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>Lembretes</h1>
|
||||
<form className="filters" method="get">
|
||||
<input type="date" name="day" defaultValue={value("day")} />
|
||||
<select name="status" defaultValue={value("status")}>
|
||||
<option value="">Todos os status</option>
|
||||
{Object.entries(notificationStatusLabel).map(([k, label]) => (
|
||||
<option key={k} value={k}>
|
||||
{label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<select name="type" defaultValue={value("type")}>
|
||||
<option value="">Todos os tipos</option>
|
||||
<option value="entrada2">Retorno do almoço</option>
|
||||
<option value="saida2">Saída</option>
|
||||
</select>
|
||||
{value("user_id") && <input type="hidden" name="user_id" value={value("user_id")} />}
|
||||
<button className="btn secondary" type="submit">
|
||||
Filtrar
|
||||
</button>
|
||||
<Link href="/notificacoes">Limpar</Link>
|
||||
</form>
|
||||
|
||||
<p className="muted">
|
||||
<strong>expirado</strong> = chegou tarde demais (mais de 10 min de atraso) e não foi enviado;{" "}
|
||||
<strong>falhou</strong> = erro no envio pelo Telegram. Só lembretes com falha do dia atual podem ser reenviados.
|
||||
</p>
|
||||
|
||||
<div className="table-wrap">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Dia</th>
|
||||
<th>Usuário</th>
|
||||
<th>Tipo</th>
|
||||
<th>Aviso</th>
|
||||
<th>Previsto</th>
|
||||
<th>Status</th>
|
||||
<th>Detalhe</th>
|
||||
{canEdit && <th></th>}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{notifications.length === 0 && (
|
||||
<tr>
|
||||
<td colSpan={canEdit ? 8 : 7} className="muted">
|
||||
Nenhum lembrete encontrado.
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{notifications.map((n) => (
|
||||
<tr key={n.id}>
|
||||
<td>{fmtDay(n.reference_date)}</td>
|
||||
<td>
|
||||
<Link href={`/usuarios/${n.user_id}`}>{n.user_name}</Link>
|
||||
</td>
|
||||
<td>{n.type === "entrada2" ? "retorno" : "saída"}</td>
|
||||
<td>{fmtClock(n.scheduled_at)}</td>
|
||||
<td>{fmtClock(n.predicted_at)}</td>
|
||||
<td>
|
||||
<span className={badgeClass(n.status)}>{notificationStatusLabel[n.status]}</span>
|
||||
</td>
|
||||
<td className="muted small">{n.error_message ?? (n.sent_at ? `enviado às ${fmtClock(n.sent_at)}` : "")}</td>
|
||||
{canEdit && (
|
||||
<td>
|
||||
{n.status === "scheduled" && (
|
||||
<ActionButton path={`/notifications/${n.id}/cancel`} label="Cancelar" confirm="Cancelar este lembrete?" />
|
||||
)}
|
||||
{n.status === "failed" && (
|
||||
<ActionButton
|
||||
path={`/notifications/${n.id}/resend`}
|
||||
label="Reenviar"
|
||||
variant="primary"
|
||||
confirm={
|
||||
"Reenviar este lembrete agora?\n\nSe a falha foi um travamento durante o envio, o usuário pode já ter recebido a mensagem original."
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
)}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user