ultisuite-client/components/landing/product/product-demos/ultimeet-room-demo.tsx
R3D347HR4Y efaaf16f60
Some checks are pending
E2E / Playwright e2e (push) Waiting to run
feat: update metadata and layout for new product pages
- Refactored metadata for contacts, administration, and Ulticards pages to utilize dynamic app names and descriptions.
- Introduced new product pages for Ultiai, Ultical, Ulticards, Ultidrive, Ultimail, and Ultimeet with appropriate metadata.
- Enhanced layout components to ensure consistent styling and functionality across new product sections.
- Updated various components to replace hardcoded labels with dynamic references to improve maintainability and consistency.
2026-06-19 22:11:42 +02:00

161 lines
6.1 KiB
TypeScript

"use client"
import { Icon } from "@iconify/react"
import { cn } from "@/lib/utils"
const ACCENT = "#34A853"
type Participant = {
name: string
color: string
muted: boolean
cameraOff?: boolean
speaking?: boolean
}
const PARTICIPANTS: Participant[] = [
{ name: "Léa Fontaine", color: "#34A853", muted: false, speaking: true },
{ name: "Vincent Morel", color: "#4285F4", muted: true },
{ name: "Thomas Giraud", color: "#EA4335", muted: false, cameraOff: true },
{ name: "Camille Visiteur", color: "#FBBC04", muted: true },
{ name: "Sofia Rossi", color: "#A142F4", muted: false },
{ name: "Karim Benali", color: "#00ACC1", muted: true, cameraOff: true },
]
function initials(name: string) {
return name
.split(" ")
.map((part) => part.charAt(0))
.slice(0, 2)
.join("")
}
function ParticipantTile({ participant }: { participant: Participant }) {
return (
<div
className={cn(
"relative flex items-center justify-center overflow-hidden rounded-xl bg-[var(--landing-chip)]/50",
participant.speaking && "ring-2"
)}
style={
participant.speaking
? ({ boxShadow: `inset 0 0 0 2px ${ACCENT}` } as React.CSSProperties)
: undefined
}
>
{participant.cameraOff ? (
<span
className="flex size-12 items-center justify-center rounded-full text-base font-semibold text-white"
style={{ backgroundColor: participant.color }}
aria-hidden
>
{initials(participant.name)}
</span>
) : (
<div
className="absolute inset-0"
style={{
background: `radial-gradient(circle at 50% 35%, ${participant.color}33, transparent 70%)`,
}}
aria-hidden
>
<span
className="absolute left-1/2 top-[38%] flex size-12 -translate-x-1/2 items-center justify-center rounded-full text-base font-semibold text-white"
style={{ backgroundColor: participant.color }}
>
{initials(participant.name)}
</span>
</div>
)}
<div className="absolute bottom-1.5 left-1.5 flex items-center gap-1 rounded-md bg-black/55 px-1.5 py-0.5 text-[11px] font-medium text-white">
<Icon
icon={participant.muted ? "mdi:microphone-off" : "mdi:microphone"}
className={cn("size-3", participant.muted && "text-[#EA4335]")}
aria-hidden
/>
<span className="max-w-[7rem] truncate">{participant.name}</span>
</div>
{participant.speaking ? (
<span
className="absolute right-1.5 top-1.5 flex size-5 items-center justify-center rounded-full"
style={{ backgroundColor: ACCENT }}
aria-hidden
>
<Icon icon="mdi:volume-high" className="size-3 text-white" />
</span>
) : null}
</div>
)
}
const CONTROLS: { icon: string; label: string; primary?: boolean }[] = [
{ icon: "mdi:microphone", label: "Micro" },
{ icon: "mdi:video", label: "Caméra" },
{ icon: "mdi:monitor-share", label: "Partager", primary: true },
{ icon: "mdi:hand-back-left-outline", label: "Main" },
{ icon: "mdi:message-outline", label: "Chat" },
]
/** Aperçu statique d'une salle de réunion UltiMeet — grille de participants et contrôles. */
export function UltimeetRoomDemo() {
return (
<div className="flex flex-col gap-3">
<div className="landing-glass-strong overflow-hidden rounded-2xl shadow-[0_32px_70px_-36px_rgba(30,40,90,0.45)]">
<div className="flex items-center gap-2.5 border-b border-[var(--landing-line)] px-4 py-3">
<img src="/ultimeet-mark.svg" alt="" className="size-5 object-contain" aria-hidden />
<span className="text-sm font-semibold tracking-tight">Atelier Nord point hebdo</span>
<span className="ml-auto flex items-center gap-1 text-[11px] font-medium text-[var(--landing-muted)]">
<Icon icon="mdi:lock-check" className="size-3.5" style={{ color: ACCENT }} aria-hidden />
Chiffré
</span>
<span className="flex items-center gap-1 text-[11px] font-medium tabular-nums text-[var(--landing-muted)]">
<Icon icon="mdi:clock-outline" className="size-3.5" aria-hidden />
12:47
</span>
</div>
<div className="grid grid-cols-2 gap-2 bg-[var(--landing-bg)] p-3 [grid-auto-rows:9rem] sm:grid-cols-3 sm:[grid-auto-rows:9.5rem]">
{PARTICIPANTS.map((participant) => (
<ParticipantTile key={participant.name} participant={participant} />
))}
</div>
<div className="flex items-center justify-center gap-2 border-t border-[var(--landing-line)] bg-[var(--landing-chip)]/30 px-4 py-3">
{CONTROLS.map((control) => (
<button
key={control.label}
type="button"
tabIndex={-1}
aria-label={control.label}
className={cn(
"flex size-10 items-center justify-center rounded-full border transition-colors",
control.primary
? "border-transparent text-white"
: "border-[var(--landing-line)] text-[var(--landing-fg)] hover:bg-[var(--landing-chip)]"
)}
style={control.primary ? { backgroundColor: ACCENT } : undefined}
>
<Icon icon={control.icon} className="size-5" aria-hidden />
</button>
))}
<button
type="button"
tabIndex={-1}
aria-label="Quitter"
className="ml-1 flex h-10 items-center gap-1.5 rounded-full bg-[#EA4335] px-4 text-sm font-semibold text-white"
>
<Icon icon="mdi:phone-hangup" className="size-5" aria-hidden />
<span className="hidden sm:inline">Quitter</span>
</button>
</div>
</div>
<p className="flex items-start gap-2 text-sm text-[var(--landing-muted)]">
<Icon icon="mdi:incognito" className="mt-0.5 size-4 shrink-0" aria-hidden />
Aperçu statique visio WebRTC chiffrée, rien n&apos;est diffusé.
</p>
</div>
)
}