ultisuite-client/components/meet/meet-lobby.tsx
R3D347HR4Y ad1370ea7e
Some checks are pending
E2E / Playwright e2e (push) Waiting to run
feat: enhance configuration and add new demo layouts
- Introduced turbopack alias for canvas in next.config.mjs.
- Updated package.json scripts for development and branding tasks.
- Added new dependencies for Tiptap extensions.
- Implemented new demo layouts for agenda, contacts, drive, and mail applications.
- Enhanced globals.css for improved theming and splash screen animations.
- Added OAuth callback handling for drive mounts.
- Updated layout components to integrate new demo shells and improve structure.
2026-06-12 19:10:24 +02:00

182 lines
6.0 KiB
TypeScript

"use client"
import { useState } from "react"
import { useRouter } from "next/navigation"
import { Loader2, Video } from "lucide-react"
import { toast } from "sonner"
import { MeetHeader } from "@/components/meet/meet-header"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import {
useCreateMeetRoom,
useMeetConfig,
useMeetRoomToken,
} from "@/lib/api/hooks/use-meet-queries"
import { meetRoomPath } from "@/lib/meet/meet-url"
export function MeetLobby() {
const router = useRouter()
const { data: config, isLoading } = useMeetConfig()
const createRoom = useCreateMeetRoom()
const roomToken = useMeetRoomToken()
const [roomName, setRoomName] = useState("")
const [joinRoom, setJoinRoom] = useState("")
const pending = createRoom.isPending || roomToken.isPending
const startInstantMeeting = async () => {
try {
const token = await createRoom.mutateAsync(undefined)
router.push(meetRoomPath(token.room, token.token))
} catch {
toast.error("Impossible de créer la réunion")
}
}
const startNamedMeeting = async () => {
const name = roomName.trim()
if (!name) {
toast.message("Indiquez un nom de salle")
return
}
try {
const token = await createRoom.mutateAsync({ name })
router.push(meetRoomPath(token.room, token.token))
} catch {
toast.error("Impossible de créer la réunion")
}
}
const joinExistingRoom = async () => {
const room = joinRoom.trim()
if (!room) {
toast.message("Indiquez le code ou le nom de la salle")
return
}
try {
const token = await roomToken.mutateAsync(room)
router.push(meetRoomPath(token.room, token.token))
} catch {
toast.error("Impossible de rejoindre la salle")
}
}
if (isLoading) {
return (
<div className="flex h-dvh flex-col">
<MeetHeader />
<div className="flex flex-1 items-center justify-center text-sm text-muted-foreground">
<Loader2 className="mr-2 size-4 animate-spin" aria-hidden />
Chargement UltiMeet
</div>
</div>
)
}
if (!config?.enabled) {
return (
<div className="flex h-dvh flex-col">
<MeetHeader />
<div className="flex flex-1 flex-col items-center justify-center gap-3 px-6 text-center">
<Video className="size-10 text-muted-foreground" aria-hidden />
<p className="max-w-md text-sm text-muted-foreground">
UltiMeet n&apos;est pas activé sur cette instance. Activez Jitsi dans
l&apos;administration ou contactez votre administrateur.
</p>
</div>
</div>
)
}
return (
<div className="flex h-dvh flex-col">
<MeetHeader />
<main className="mx-auto flex w-full max-w-lg flex-1 flex-col justify-center gap-8 px-4 py-8">
<div className="space-y-2 text-center">
<h1 className="text-2xl font-medium tracking-tight">Visioconférence sécurisée</h1>
<p className="text-sm leading-relaxed text-muted-foreground">
Réunions chiffrées hébergées sur votre infrastructure, intégrées à
l&apos;Agenda et à la suite Ulti.
</p>
</div>
<div className="space-y-4 rounded-2xl border border-border/70 bg-card p-5 shadow-sm">
<Button
type="button"
className="h-11 w-full rounded-full text-base"
disabled={pending}
onClick={() => void startInstantMeeting()}
>
{createRoom.isPending ? (
<Loader2 className="mr-2 size-4 animate-spin" aria-hidden />
) : (
<Video className="mr-2 size-4" aria-hidden />
)}
Nouvelle réunion instantanée
</Button>
<div className="space-y-2">
<Label htmlFor="meet-room-name">Réunion planifiée (nom de salle)</Label>
<div className="flex gap-2">
<Input
id="meet-room-name"
value={roomName}
onChange={(e) => setRoomName(e.target.value)}
placeholder="equipe-produit"
autoComplete="off"
disabled={pending}
onKeyDown={(e) => {
if (e.key === "Enter") void startNamedMeeting()
}}
/>
<Button
type="button"
variant="outline"
className="shrink-0 rounded-full"
disabled={pending}
onClick={() => void startNamedMeeting()}
>
Créer
</Button>
</div>
</div>
<div className="border-t border-border/60 pt-4">
<div className="space-y-2">
<Label htmlFor="meet-join-room">Rejoindre avec un code</Label>
<div className="flex gap-2">
<Input
id="meet-join-room"
value={joinRoom}
onChange={(e) => setJoinRoom(e.target.value)}
placeholder="abc12345"
autoComplete="off"
disabled={pending}
onKeyDown={(e) => {
if (e.key === "Enter") void joinExistingRoom()
}}
/>
<Button
type="button"
variant="secondary"
className="shrink-0 rounded-full"
disabled={pending}
onClick={() => void joinExistingRoom()}
>
Rejoindre
</Button>
</div>
</div>
</div>
</div>
<p className="text-center text-xs text-muted-foreground">
{config.transcription_enabled
? `Transcription ${config.transcription_mode === "queued" ? "différée" : "en direct"} disponible.`
: "Les liens Agenda ouvrent directement la salle correspondante."}
</p>
</main>
</div>
)
}