feat(telegram): integrate Telegram bot for notifications and add related models

This commit is contained in:
Jose Selesan
2026-06-17 08:42:13 -03:00
parent c0c8bf0945
commit b8d8f16f4e
14 changed files with 493 additions and 3 deletions

View File

@@ -1,8 +1,13 @@
import { createFileRoute } from "@tanstack/react-router";
import { Check, Copy, RefreshCw } from "lucide-react";
import { useState } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { useAuth } from "@/lib/auth-context";
import {
useGenerateTelegramCode,
useTelegramStatus,
} from "@/lib/queries";
export const Route = createFileRoute("/_authenticated/settings")({
component: SettingsPage,
@@ -48,7 +53,7 @@ function SettingsPage() {
}
return (
<div className="mx-auto max-w-md space-y-6">
<div className="mx-auto max-w-md space-y-8">
<div>
<h1 className="text-2xl font-semibold tracking-tight">Configuración</h1>
<p className="text-sm text-muted-foreground">Cambiar contraseña</p>
@@ -118,6 +123,124 @@ function SettingsPage() {
{loading ? "Guardando..." : "Cambiar contraseña"}
</Button>
</form>
<hr className="border-border" />
<TelegramSection />
</div>
);
}
function TelegramSection() {
const { data: status, isLoading } = useTelegramStatus();
const {
mutate: generateCode,
data: newCode,
isPending: generating,
reset: resetCode,
} = useGenerateTelegramCode();
const [copied, setCopied] = useState(false);
const code = newCode?.code;
async function handleCopy() {
if (!code) return;
try {
await navigator.clipboard.writeText(code);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
} catch {
// fallback
const el = document.createElement("textarea");
el.value = code;
document.body.appendChild(el);
el.select();
document.execCommand("copy");
document.body.removeChild(el);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
}
}
function handleGenerate() {
resetCode();
generateCode();
}
return (
<div className="space-y-4">
<div>
<h2 className="text-lg font-semibold tracking-tight">Telegram</h2>
<p className="text-sm text-muted-foreground">
Conectá tu cuenta de Telegram para recibir notificaciones
</p>
</div>
{isLoading ? (
<p className="text-sm text-muted-foreground">Cargando...</p>
) : status?.connected ? (
<div className="rounded-lg border border-emerald-200 bg-emerald-50 p-4 dark:border-emerald-800 dark:bg-emerald-950/30">
<p className="text-sm font-medium text-emerald-700 dark:text-emerald-400">
Conectado a Telegram
</p>
<p className="mt-1 text-xs text-emerald-600 dark:text-emerald-500">
Recibirás notificaciones de BELO aquí.
</p>
</div>
) : (
<div className="space-y-3">
<p className="text-sm text-muted-foreground">
{status?.botUsername ? (
<>
Enviá el código a{" "}
<strong className="font-medium text-foreground">
@{status.botUsername}
</strong>{" "}
en Telegram para conectar.
</>
) : (
"El bot no está disponible."
)}
</p>
{code ? (
<div className="flex items-center gap-2">
<code className="flex-1 rounded-md border bg-muted px-3 py-2 text-sm font-mono tracking-widest">
{code}
</code>
<Button
type="button"
variant="outline"
size="icon"
onClick={handleCopy}
className="shrink-0"
>
{copied ? (
<Check className="h-4 w-4 text-emerald-500" />
) : (
<Copy className="h-4 w-4" />
)}
</Button>
</div>
) : (
<Button
type="button"
onClick={handleGenerate}
disabled={generating || !status?.botUsername}
className="w-full"
>
{generating ? (
<>
<RefreshCw className="mr-2 h-4 w-4 animate-spin" />
Generando...
</>
) : (
"Generar código"
)}
</Button>
)}
</div>
)}
</div>
);
}