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.
This commit is contained in:
agent
2026-06-07 17:57:50 +02:00
parent 9787c218ac
commit caf005f04b
19 changed files with 2166 additions and 0 deletions
+43
View File
@@ -0,0 +1,43 @@
import { useState } from "react";
import { Flex, Box } from "@mantine/core";
import { Sidebar } from "./Sidebar";
import { ChatPanel } from "./ChatPanel";
import { MOCK_ROOMS } from "./mock";
import type { User } from "./types";
export function ChatShell({
user,
onLogout,
}: {
user: User;
onLogout: () => void;
}) {
const [rooms] = useState(MOCK_ROOMS);
const [activeId, setActiveId] = useState<string>(rooms[0]?.id ?? "");
const active = rooms.find((r) => r.id === activeId);
return (
<Flex h="100vh" w="100vw" style={{ overflow: "hidden" }}>
<Box
w={320}
h="100%"
bg="dark.8"
style={{
borderRight: "1px solid var(--mantine-color-dark-4)",
flexShrink: 0,
}}
>
<Sidebar
user={user}
rooms={rooms}
activeId={activeId}
onSelect={setActiveId}
onLogout={onLogout}
/>
</Box>
<Box flex={1} h="100%" bg="dark.7" style={{ minWidth: 0 }}>
<ChatPanel room={active} user={user} />
</Box>
</Flex>
);
}