Files
bot-pontos/web/app/(painel)/usuarios/[id]/page.tsx
2026-10-23 22:31:02 -03:00

138 lines
5.2 KiB
TypeScript

import Link from "next/link";
import { apiGet } from "@/lib/server-api";
import { badgeClass, dayStatusLabel, fmtDateTime, pauseReasonLabel, userStatusLabel } from "@/lib/format";
import { isRestricted } from "@/lib/access";
import type { Me, UserDetail } from "@/lib/types";
import { ActionButton } from "@/components/ActionButton";
import { CredentialsForm, ScheduleForm, TodayPanel } from "./forms";
export default async function UserPage({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
const [detail, me] = await Promise.all([apiGet<UserDetail>(`/users/${id}`), apiGet<Me>("/auth/me")]);
const { user, credential, today } = detail;
const restricted = isRestricted(me.account);
const canAdmin = me.account.role !== "viewer";
// Conta de leitura ligada a este usuário também altera as próprias configurações.
const canEdit = canAdmin || me.account.user_id === user.id;
const base = `/users/${user.id}`;
return (
<>
{!restricted && (
<p>
<Link href="/usuarios">← Usuários</Link>
</p>
)}
<h1>
{user.display_name} <span className={badgeClass(user.status)}>{userStatusLabel[user.status]}</span>
</h1>
<p className="muted">
ID {user.id} · chat {user.telegram_chat_id} · cadastrado em {fmtDateTime(user.created_at)}
</p>
<section className="card">
<h2>Cadastro e lembretes</h2>
<dl className="kv">
<dt>Lembretes</dt>
<dd>
{user.notifications_enabled
? "ligados"
: user.paused_reason
? pauseReasonLabel[user.paused_reason]
: "desligados"}
</dd>
<dt>Admin do bot</dt>
<dd>{user.is_bot_admin ? "sim" : "não"}</dd>
<dt>Hoje ({today.day})</dt>
<dd>
<span className={badgeClass(today.status)}>{dayStatusLabel[today.status]}</span>{" "}
<span className="muted">
{today.last_point_count} ponto(s) na última consulta · próxima consulta: {fmtDateTime(today.next_check_at)}
</span>
</dd>
</dl>
{canEdit && (
<div className="actions">
{canAdmin && user.status !== "active" && <ActionButton path={`${base}/activate`} label="Ativar" variant="primary" />}
{canAdmin && user.status !== "disabled" && (
<ActionButton
path={`${base}/disable`}
label="Desativar"
variant="danger"
confirm="Desativar este usuário? Os lembretes agendados serão cancelados."
/>
)}
{user.status === "active" &&
(user.notifications_enabled ? (
<ActionButton path={`${base}/notifications/disable`} label="Pausar lembretes" />
) : (
<ActionButton path={`${base}/notifications/enable`} label="Ligar lembretes" variant="primary" />
))}
{canAdmin && (
<ActionButton
path={`${base}/bot-admin`}
method="PUT"
body={{ enabled: !user.is_bot_admin }}
label={user.is_bot_admin ? "Remover admin do bot" : "Tornar admin do bot"}
/>
)}
{canAdmin && (today.status === "no_points" || today.status === "not_working") && (
<ActionButton path={`${base}/today/resume`} label="Retomar consultas de hoje" />
)}
</div>
)}
</section>
<section className="card">
<h2>Credencial PontoMais</h2>
{credential ? (
<dl className="kv">
<dt>E-mail</dt>
<dd>{credential.api_email}</dd>
<dt>Situação</dt>
<dd>
{credential.validated ? (
<span className="badge ok">validada em {fmtDateTime(credential.last_validated_at)}</span>
) : (
<span className="badge warn">não validada</span>
)}
</dd>
<dt>Falhas seguidas</dt>
<dd>{credential.consecutive_failures}</dd>
{credential.last_error && (
<>
<dt>Último erro</dt>
<dd className="error-text">{credential.last_error}</dd>
</>
)}
</dl>
) : (
<p className="muted">Nenhuma credencial configurada. Sem ela o usuário não pode ser ativado.</p>
)}
{canEdit && <CredentialsForm userId={user.id} />}
</section>
<section className="card">
<h2>Jornada e janela de consulta</h2>
<ScheduleForm
userId={user.id}
schedule={detail.schedule}
timezone={user.timezone}
effectiveWindow={detail.effective_window}
disabled={!canEdit}
/>
</section>
<section className="card">
<h2>Pontos de hoje</h2>
<TodayPanel userId={user.id} canTest={canEdit} />
</section>
<p>
<Link href={`/notificacoes?user_id=${user.id}`}>Lembretes deste usuário</Link> ·{" "}
{!restricted && <Link href={`/auditoria?target_user_id=${user.id}`}>Auditoria deste usuário</Link>}
</p>
</>
);
}