Files
unibus/web/src/Login.tsx
T
agent caf005f04b feat(web): frontend v1 — login (handle+contraseña), sidebar rooms+buscador, chat estilo Element
SPA React 19 + Vite + Mantine v9 en modo oscuro (acento índigo), datos mock para
iterar el diseño antes de cablear el gateway. Login con identidad + contraseña
(la contraseña desbloqueará la identidad Ed25519 cifrada en el dispositivo).
Sidebar: avatar de usuario, buscador (rooms/usuarios/mensajes) y lista de rooms
con candado E2E / hash cleartext / badges de no leídos. Panel de chat estilo
Element (avatar+nombre+hora+texto) con composer interactivo.
2026-06-07 17:57:50 +02:00

65 lines
1.9 KiB
TypeScript

import { useState } from "react";
import {
Button,
Card,
Center,
PasswordInput,
Stack,
Text,
TextInput,
ThemeIcon,
Title,
} from "@mantine/core";
import { IconShieldLock, IconKey } from "@tabler/icons-react";
import type { User } from "./types";
export function Login({ onLogin }: { onLogin: (u: User) => void }) {
const [handle, setHandle] = useState("");
const [password, setPassword] = useState("");
const ready = handle.trim().length > 0 && password.length > 0;
const connect = () => {
const h = handle.trim();
if (ready) onLogin({ id: h, handle: h });
};
return (
<Center h="100vh" bg="dark.9">
<Card w={380} p="xl" radius="lg" withBorder bg="dark.7">
<Stack align="center" gap="lg">
<ThemeIcon size={60} radius="xl" variant="light" color="brand">
<IconShieldLock size={32} />
</ThemeIcon>
<Stack gap={2} align="center">
<Title order={2}>unibus</Title>
<Text c="dimmed" size="sm">
Mensajería cifrada de extremo a extremo
</Text>
</Stack>
<TextInput
w="100%"
label="Identidad"
placeholder="tu-handle"
value={handle}
onChange={(e) => setHandle(e.currentTarget.value)}
onKeyDown={(e) => e.key === "Enter" && connect()}
data-autofocus
/>
<PasswordInput
w="100%"
label="Contraseña"
description="Desbloquea tu identidad cifrada en este dispositivo"
placeholder="••••••••"
leftSection={<IconKey size={16} />}
value={password}
onChange={(e) => setPassword(e.currentTarget.value)}
onKeyDown={(e) => e.key === "Enter" && connect()}
/>
<Button w="100%" size="md" onClick={connect} disabled={!ready}>
Conectar
</Button>
</Stack>
</Card>
</Center>
);
}