Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c23b2690d1 |
@@ -36,6 +36,7 @@ export default function DashboardPage() {
|
||||
const { data: session, isPending } = useSession();
|
||||
const [rooms, setRooms] = useState<Room[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [deleting, setDeleting] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isPending && !session) {
|
||||
@@ -63,6 +64,24 @@ export default function DashboardPage() {
|
||||
}
|
||||
}, [session]);
|
||||
|
||||
async function handleDelete(e: React.MouseEvent, room: Room) {
|
||||
e.preventDefault(); // prevent Link navigation
|
||||
e.stopPropagation();
|
||||
if (room.status === "ACTIVE") return;
|
||||
if (!confirm(`Удалить комнату "${room.name}"? Это действие необратимо.`)) return;
|
||||
setDeleting(room.id);
|
||||
try {
|
||||
const res = await fetch(`/api/rooms/${room.id}`, { method: "DELETE" });
|
||||
if (res.ok) {
|
||||
setRooms((prev) => prev.filter((r) => r.id !== room.id));
|
||||
}
|
||||
} catch {
|
||||
// silent
|
||||
} finally {
|
||||
setDeleting(null);
|
||||
}
|
||||
}
|
||||
|
||||
if (isPending) {
|
||||
return (
|
||||
<main className="min-h-screen bg-surface-0 flex items-center justify-center">
|
||||
@@ -157,15 +176,33 @@ export default function DashboardPage() {
|
||||
<Link
|
||||
key={room.id}
|
||||
href={`/room/${room.code}`}
|
||||
className="block bg-surface-1 border border-border-subtle rounded-xl p-5 hover:border-border-default transition-colors cursor-pointer group"
|
||||
className="block bg-surface-1 border border-border-subtle rounded-xl p-5 hover:border-border-default transition-colors cursor-pointer group relative"
|
||||
>
|
||||
<div className="flex items-start justify-between mb-4">
|
||||
<h2 className="font-semibold text-base truncate pr-3 group-hover:text-white transition-colors">
|
||||
{room.name}
|
||||
</h2>
|
||||
<div className={`flex items-center gap-1.5 shrink-0 text-xs font-medium ${statusTextColors[room.status]}`}>
|
||||
<span className={`h-2 w-2 rounded-full ${statusDotColors[room.status]}`} />
|
||||
{statusLabels[room.status]}
|
||||
<div className="flex items-center gap-2 shrink-0">
|
||||
<div className={`flex items-center gap-1.5 text-xs font-medium ${statusTextColors[room.status]}`}>
|
||||
<span className={`h-2 w-2 rounded-full ${statusDotColors[room.status]}`} />
|
||||
{statusLabels[room.status]}
|
||||
</div>
|
||||
{room.status !== "ACTIVE" && (
|
||||
<button
|
||||
onClick={(e) => handleDelete(e, room)}
|
||||
disabled={deleting === room.id}
|
||||
className="opacity-0 group-hover:opacity-100 p-1.5 rounded-lg text-text-muted hover:text-danger hover:bg-danger/10 transition-all disabled:opacity-50"
|
||||
title="Удалить комнату"
|
||||
>
|
||||
{deleting === room.id ? (
|
||||
<div className="w-4 h-4 border-2 border-danger border-t-transparent rounded-full animate-spin" />
|
||||
) : (
|
||||
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" />
|
||||
</svg>
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 text-sm">
|
||||
|
||||
@@ -153,13 +153,14 @@ export default function RoomPage() {
|
||||
serverUrl={LIVEKIT_URL}
|
||||
token={token}
|
||||
connect={true}
|
||||
className="h-screen flex flex-col bg-surface-0"
|
||||
data-lk-theme="default"
|
||||
className="h-screen max-h-screen flex flex-col bg-surface-0 overflow-hidden"
|
||||
onDisconnected={() => router.push(isHost ? "/dashboard" : "/")}
|
||||
>
|
||||
<RoomAudioRenderer />
|
||||
|
||||
{/* Top bar — minimal, just room info */}
|
||||
<header className="flex items-center px-4 h-12 bg-surface-1/80 backdrop-blur-sm border-b border-border-subtle shrink-0">
|
||||
<header className="flex items-center px-4 h-12 bg-surface-1/80 backdrop-blur-sm border-b border-border-subtle shrink-0 z-10">
|
||||
<h1 className="text-white font-semibold text-sm">{room?.name ?? "Комната"}</h1>
|
||||
{room && (
|
||||
<span className="text-[11px] text-text-muted font-mono bg-surface-2 px-2 py-0.5 rounded-md border border-border-subtle ml-3">
|
||||
@@ -168,28 +169,28 @@ export default function RoomPage() {
|
||||
)}
|
||||
</header>
|
||||
|
||||
{/* Main content area */}
|
||||
<div className="flex flex-1 min-h-0">
|
||||
{/* Main content area — fixed between header and footer */}
|
||||
<div className="flex flex-1 min-h-0 overflow-hidden">
|
||||
{/* Video area */}
|
||||
<div className="flex-1 min-w-0 p-2">
|
||||
<div className="flex-1 min-w-0 min-h-0 relative" style={{ contain: "strict" }}>
|
||||
<VideoArea />
|
||||
</div>
|
||||
|
||||
{/* Sidebar */}
|
||||
{sidebarOpen && (
|
||||
<aside className="w-80 shrink-0 border-l border-border-default flex flex-col bg-surface-1">
|
||||
<aside className="w-80 shrink-0 border-l border-border-default flex flex-col bg-surface-1 overflow-hidden">
|
||||
{isHost && showLobby && room && (
|
||||
<div className="border-b border-border-default">
|
||||
<div className="border-b border-border-default shrink-0">
|
||||
<LobbyManager roomId={room.id} />
|
||||
</div>
|
||||
)}
|
||||
{isHost && showModeration && room && (
|
||||
<div className="border-b border-border-default">
|
||||
<div className="border-b border-border-default shrink-0">
|
||||
<ModerationPanel roomId={room.id} />
|
||||
</div>
|
||||
)}
|
||||
{showChat && room && (
|
||||
<div className="flex-1 min-h-0">
|
||||
<div className="flex-1 min-h-0 overflow-hidden">
|
||||
<ChatPanel
|
||||
roomId={room.id}
|
||||
sessionId={userId ?? "anonymous"}
|
||||
@@ -202,7 +203,7 @@ export default function RoomPage() {
|
||||
</div>
|
||||
|
||||
{/* Bottom control bar — Google Meet style */}
|
||||
<footer className="flex items-center justify-between px-4 h-16 bg-surface-1 border-t border-border-default shrink-0">
|
||||
<footer className="flex items-center justify-between px-4 h-16 bg-surface-1 border-t border-border-default shrink-0 z-10">
|
||||
{/* Left: room time or empty */}
|
||||
<div className="w-48" />
|
||||
|
||||
@@ -298,7 +299,7 @@ function VideoArea() {
|
||||
// If someone is screen sharing, use focus layout
|
||||
if (screenShareTracks.length > 0) {
|
||||
return (
|
||||
<FocusLayoutContainer className="h-full">
|
||||
<FocusLayoutContainer className="absolute inset-0">
|
||||
<CarouselLayout tracks={cameraTracks} className="h-24">
|
||||
<ParticipantTile />
|
||||
</CarouselLayout>
|
||||
@@ -308,7 +309,7 @@ function VideoArea() {
|
||||
}
|
||||
|
||||
return (
|
||||
<GridLayout tracks={cameraTracks} className="h-full">
|
||||
<GridLayout tracks={cameraTracks} className="absolute inset-0">
|
||||
<ParticipantTile />
|
||||
</GridLayout>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user