feat: enhance chat functionality with new conversation and meeting room features
- Added support for creating and managing group conversations and meeting rooms, allowing users to initiate discussions with multiple participants. - Implemented a modal for creating new conversations, including options for individual, group, and meeting room types. - Enhanced the chat list component to filter and display conversations based on type, improving user navigation. - Introduced admin functionalities for meeting rooms, enabling user management and role assignments within the chat interface. - Updated backend schema and API to accommodate new conversation types and related operations, ensuring robust data handling.
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
import { useQuery, useConvexClient } from "convex-svelte";
|
||||
import { api } from "@sgse-app/backend/convex/_generated/api";
|
||||
import { abrirConversa } from "$lib/stores/chatStore";
|
||||
import { authStore } from "$lib/stores/auth.svelte";
|
||||
import UserStatusBadge from "./UserStatusBadge.svelte";
|
||||
import UserAvatar from "./UserAvatar.svelte";
|
||||
|
||||
@@ -12,24 +13,42 @@
|
||||
let { onClose }: Props = $props();
|
||||
|
||||
const client = useConvexClient();
|
||||
const usuarios = useQuery(api.chat.listarTodosUsuarios, {});
|
||||
const usuarios = useQuery(api.usuarios.listarParaChat, {});
|
||||
const meuPerfil = useQuery(api.usuarios.obterPerfil, {});
|
||||
|
||||
let activeTab = $state<"individual" | "grupo">("individual");
|
||||
let activeTab = $state<"individual" | "grupo" | "sala_reuniao">("individual");
|
||||
let searchQuery = $state("");
|
||||
let selectedUsers = $state<string[]>([]);
|
||||
let groupName = $state("");
|
||||
let salaReuniaoName = $state("");
|
||||
let loading = $state(false);
|
||||
|
||||
const usuariosFiltrados = $derived(() => {
|
||||
if (!usuarios) return [];
|
||||
if (!searchQuery.trim()) return usuarios;
|
||||
|
||||
const query = searchQuery.toLowerCase();
|
||||
return usuarios.filter((u: any) =>
|
||||
u.nome.toLowerCase().includes(query) ||
|
||||
u.email.toLowerCase().includes(query) ||
|
||||
u.matricula.toLowerCase().includes(query)
|
||||
);
|
||||
if (!usuarios?.data) return [];
|
||||
|
||||
// Filtrar o próprio usuário
|
||||
const meuId = authStore.usuario?._id || meuPerfil?.data?._id;
|
||||
let lista = usuarios.data.filter((u: any) => u._id !== meuId);
|
||||
|
||||
// Aplicar busca
|
||||
if (searchQuery.trim()) {
|
||||
const query = searchQuery.toLowerCase();
|
||||
lista = lista.filter((u: any) =>
|
||||
u.nome?.toLowerCase().includes(query) ||
|
||||
u.email?.toLowerCase().includes(query) ||
|
||||
u.matricula?.toLowerCase().includes(query)
|
||||
);
|
||||
}
|
||||
|
||||
// Ordenar: online primeiro, depois por nome
|
||||
return lista.sort((a: any, b: any) => {
|
||||
const statusOrder = { online: 0, ausente: 1, externo: 2, em_reuniao: 3, offline: 4 };
|
||||
const statusA = statusOrder[a.statusPresenca as keyof typeof statusOrder] ?? 4;
|
||||
const statusB = statusOrder[b.statusPresenca as keyof typeof statusOrder] ?? 4;
|
||||
|
||||
if (statusA !== statusB) return statusA - statusB;
|
||||
return (a.nome || "").localeCompare(b.nome || "");
|
||||
});
|
||||
});
|
||||
|
||||
function toggleUserSelection(userId: string) {
|
||||
@@ -77,26 +96,63 @@
|
||||
});
|
||||
abrirConversa(conversaId);
|
||||
onClose();
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
console.error("Erro ao criar grupo:", error);
|
||||
alert("Erro ao criar grupo");
|
||||
const mensagem = error?.message || error?.data || "Erro desconhecido ao criar grupo";
|
||||
alert(`Erro ao criar grupo: ${mensagem}`);
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleCriarSalaReuniao() {
|
||||
if (selectedUsers.length < 1) {
|
||||
alert("Selecione pelo menos 1 participante");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!salaReuniaoName.trim()) {
|
||||
alert("Digite um nome para a sala de reunião");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
loading = true;
|
||||
const conversaId = await client.mutation(api.chat.criarSalaReuniao, {
|
||||
nome: salaReuniaoName.trim(),
|
||||
participantes: selectedUsers as any,
|
||||
});
|
||||
abrirConversa(conversaId);
|
||||
onClose();
|
||||
} catch (error: any) {
|
||||
console.error("Erro ao criar sala de reunião:", error);
|
||||
const mensagem = error?.message || error?.data || "Erro desconhecido ao criar sala de reunião";
|
||||
alert(`Erro ao criar sala de reunião: ${mensagem}`);
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="fixed inset-0 z-[100] flex items-center justify-center bg-black/50" onclick={onClose}>
|
||||
<div class="fixed inset-0 z-[100] flex items-center justify-center bg-black/60 backdrop-blur-sm" onclick={onClose}>
|
||||
<div
|
||||
class="bg-base-100 rounded-xl shadow-2xl w-full max-w-lg max-h-[80vh] flex flex-col m-4"
|
||||
class="bg-base-100 rounded-2xl shadow-2xl w-full max-w-2xl max-h-[85vh] flex flex-col m-4 border border-base-300 overflow-hidden"
|
||||
onclick={(e) => e.stopPropagation()}
|
||||
style="box-shadow: 0 20px 60px -15px rgba(0, 0, 0, 0.3);"
|
||||
>
|
||||
<!-- Header -->
|
||||
<div class="flex items-center justify-between px-6 py-4 border-b border-base-300">
|
||||
<h2 class="text-xl font-semibold">Nova Conversa</h2>
|
||||
<!-- Header com gradiente -->
|
||||
<div class="flex items-center justify-between px-6 py-5 border-b border-base-300 relative overflow-hidden"
|
||||
style="background: linear-gradient(135deg, #667eea 0%, #764ba2 50%, #f093fb 100%);">
|
||||
<div class="absolute inset-0 opacity-20" style="background: radial-gradient(circle at 20% 50%, rgba(255,255,255,0.3) 0%, transparent 50%);"></div>
|
||||
<h2 class="text-2xl font-bold text-white relative z-10 flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8.625 12a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0H8.25m4.125 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0H12m4.125 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0h-.375M21 12c0 4.556-4.03 8.25-9 8.25a9.764 9.764 0 0 1-2.555-.337A5.972 5.972 0 0 1 5.41 20.97a5.969 5.969 0 0 1-.474-.065 4.48 4.48 0 0 0 .978-2.025c.09-.457-.133-.901-.467-1.226C3.93 16.178 3 14.189 3 12c0-4.556 4.03-8.25 9-8.25s9 3.694 9 8.25Z" />
|
||||
</svg>
|
||||
Nova Conversa
|
||||
</h2>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-ghost btn-sm btn-circle"
|
||||
class="btn btn-ghost btn-sm btn-circle hover:bg-white/20 transition-all duration-200 relative z-10"
|
||||
onclick={onClose}
|
||||
aria-label="Fechar"
|
||||
>
|
||||
@@ -104,81 +160,156 @@
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke-width="2"
|
||||
stroke="currentColor"
|
||||
class="w-5 h-5"
|
||||
class="w-5 h-5 text-white"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Tabs -->
|
||||
<div class="tabs tabs-boxed p-4">
|
||||
<!-- Tabs melhoradas -->
|
||||
<div class="tabs tabs-boxed p-4 bg-base-200/50">
|
||||
<button
|
||||
type="button"
|
||||
class={`tab ${activeTab === "individual" ? "tab-active" : ""}`}
|
||||
onclick={() => (activeTab = "individual")}
|
||||
class={`tab flex items-center gap-2 transition-all duration-200 ${
|
||||
activeTab === "individual"
|
||||
? "tab-active bg-primary text-primary-content font-semibold"
|
||||
: "hover:bg-base-300"
|
||||
}`}
|
||||
onclick={() => {
|
||||
activeTab = "individual";
|
||||
selectedUsers = [];
|
||||
searchQuery = "";
|
||||
}}
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-4 h-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 6a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0ZM4.501 20.118a7.5 7.5 0 0 1 14.998 0A17.933 17.933 0 0 1 12 21.75c-2.676 0-5.216-.584-7.499-1.632Z" />
|
||||
</svg>
|
||||
Individual
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class={`tab ${activeTab === "grupo" ? "tab-active" : ""}`}
|
||||
onclick={() => (activeTab = "grupo")}
|
||||
class={`tab flex items-center gap-2 transition-all duration-200 ${
|
||||
activeTab === "grupo"
|
||||
? "tab-active bg-primary text-primary-content font-semibold"
|
||||
: "hover:bg-base-300"
|
||||
}`}
|
||||
onclick={() => {
|
||||
activeTab = "grupo";
|
||||
selectedUsers = [];
|
||||
searchQuery = "";
|
||||
}}
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-4 h-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M18 18.72a9.094 9.094 0 0 0 3.741-.479 3 3 0 0 0-4.682-2.72m.94 3.198.001.031c0 .225-.012.447-.037.666A11.944 11.944 0 0 1 12 21c-2.17 0-4.207-.576-5.963-1.584A6.062 6.062 0 0 1 6 18.719m12 0a5.971 5.971 0 0 0-.941-3.197m0 0A5.995 5.995 0 0 0 12 12.75a5.995 5.995 0 0 0-5.058 2.772m0 0a3 3 0 0 0-4.681 2.72 8.986 8.986 0 0 0 3.74.477m.94-3.197a5.971 5.971 0 0 0-.94 3.197M15 6.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Zm6 3a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Zm-13.5 0a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Z" />
|
||||
</svg>
|
||||
Grupo
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class={`tab flex items-center gap-2 transition-all duration-200 ${
|
||||
activeTab === "sala_reuniao"
|
||||
? "tab-active bg-primary text-primary-content font-semibold"
|
||||
: "hover:bg-base-300"
|
||||
}`}
|
||||
onclick={() => {
|
||||
activeTab = "sala_reuniao";
|
||||
selectedUsers = [];
|
||||
searchQuery = "";
|
||||
}}
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-4 h-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15 19.128a9.38 9.38 0 0 0 2.625.372 9.337 9.337 0 0 0 4.121-.952 4.125 4.125 0 0 0-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 0 1 8.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0 1 11.964-3.07M12 6.375a3.375 3.375 0 1 1-6.75 0 3.375 3.375 0 0 1 6.75 0Zm8.25 2.25a2.625 2.625 0 1 1-5.25 0 2.625 2.625 0 0 1 5.25 0Z" />
|
||||
</svg>
|
||||
Sala de Reunião
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="flex-1 overflow-y-auto px-6">
|
||||
<div class="flex-1 overflow-y-auto px-6 py-4">
|
||||
{#if activeTab === "grupo"}
|
||||
<!-- Criar Grupo -->
|
||||
<div class="mb-4">
|
||||
<label class="label">
|
||||
<span class="label-text">Nome do Grupo</span>
|
||||
<label class="label pb-2">
|
||||
<span class="label-text font-semibold">Nome do Grupo</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Digite o nome do grupo..."
|
||||
class="input input-bordered w-full"
|
||||
class="input input-bordered w-full focus:input-primary transition-colors"
|
||||
bind:value={groupName}
|
||||
maxlength="50"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<label class="label">
|
||||
<span class="label-text">
|
||||
Participantes {selectedUsers.length > 0 ? `(${selectedUsers.length})` : ""}
|
||||
<div class="mb-3">
|
||||
<label class="label pb-2">
|
||||
<span class="label-text font-semibold">
|
||||
Participantes {selectedUsers.length > 0 ? `(${selectedUsers.length} selecionado${selectedUsers.length > 1 ? 's' : ''})` : ""}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
{:else if activeTab === "sala_reuniao"}
|
||||
<!-- Criar Sala de Reunião -->
|
||||
<div class="mb-4">
|
||||
<label class="label pb-2">
|
||||
<span class="label-text font-semibold">Nome da Sala de Reunião</span>
|
||||
<span class="label-text-alt text-primary font-medium">👑 Você será o administrador</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Digite o nome da sala de reunião..."
|
||||
class="input input-bordered w-full focus:input-primary transition-colors"
|
||||
bind:value={salaReuniaoName}
|
||||
maxlength="50"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="label pb-2">
|
||||
<span class="label-text font-semibold">
|
||||
Participantes {selectedUsers.length > 0 ? `(${selectedUsers.length} selecionado${selectedUsers.length > 1 ? 's' : ''})` : ""}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Search -->
|
||||
<div class="mb-4">
|
||||
<!-- Search melhorado -->
|
||||
<div class="mb-4 relative">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Buscar usuários..."
|
||||
class="input input-bordered w-full"
|
||||
placeholder="🔍 Buscar usuários por nome, email ou matrícula..."
|
||||
class="input input-bordered w-full pl-10 focus:input-primary transition-colors"
|
||||
bind:value={searchQuery}
|
||||
/>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="2"
|
||||
stroke="currentColor"
|
||||
class="w-5 h-5 absolute left-3 top-1/2 -translate-y-1/2 text-base-content/40"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<!-- Lista de usuários -->
|
||||
<div class="space-y-2">
|
||||
{#if usuarios && usuariosFiltrados().length > 0}
|
||||
{#if usuarios?.data && usuariosFiltrados().length > 0}
|
||||
{#each usuariosFiltrados() as usuario (usuario._id)}
|
||||
{@const isSelected = selectedUsers.includes(usuario._id)}
|
||||
<button
|
||||
type="button"
|
||||
class={`w-full text-left px-4 py-3 rounded-lg border transition-colors flex items-center gap-3 ${
|
||||
activeTab === "grupo" && selectedUsers.includes(usuario._id)
|
||||
? "border-primary bg-primary/10"
|
||||
: "border-base-300 hover:bg-base-200"
|
||||
}`}
|
||||
class={`w-full text-left px-4 py-3 rounded-xl border-2 transition-all duration-200 flex items-center gap-3 ${
|
||||
isSelected
|
||||
? "border-primary bg-primary/10 shadow-md scale-[1.02]"
|
||||
: "border-base-300 hover:bg-base-200 hover:border-primary/30 hover:shadow-sm"
|
||||
} ${loading ? "opacity-50 cursor-not-allowed" : "cursor-pointer"}`}
|
||||
onclick={() => {
|
||||
if (loading) return;
|
||||
if (activeTab === "individual") {
|
||||
handleCriarIndividual(usuario._id);
|
||||
} else {
|
||||
@@ -191,62 +322,106 @@
|
||||
<div class="relative flex-shrink-0">
|
||||
<UserAvatar
|
||||
avatar={usuario.avatar}
|
||||
fotoPerfilUrl={usuario.fotoPerfil}
|
||||
fotoPerfilUrl={usuario.fotoPerfilUrl}
|
||||
nome={usuario.nome}
|
||||
size="sm"
|
||||
size="md"
|
||||
/>
|
||||
<div class="absolute bottom-0 right-0">
|
||||
<UserStatusBadge status={usuario.statusPresenca} size="sm" />
|
||||
<div class="absolute -bottom-1 -right-1">
|
||||
<UserStatusBadge status={usuario.statusPresenca || "offline"} size="sm" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Info -->
|
||||
<div class="flex-1 min-w-0">
|
||||
<p class="font-medium text-base-content truncate">{usuario.nome}</p>
|
||||
<p class="font-semibold text-base-content truncate">{usuario.nome}</p>
|
||||
<p class="text-sm text-base-content/60 truncate">
|
||||
{usuario.setor || usuario.email}
|
||||
{usuario.setor || usuario.email || usuario.matricula || "Sem informações"}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Checkbox (apenas para grupo) -->
|
||||
{#if activeTab === "grupo"}
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox checkbox-primary"
|
||||
checked={selectedUsers.includes(usuario._id)}
|
||||
readonly
|
||||
/>
|
||||
<!-- Checkbox melhorado (para grupo e sala de reunião) -->
|
||||
{#if activeTab === "grupo" || activeTab === "sala_reuniao"}
|
||||
<div class="flex-shrink-0">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox checkbox-primary checkbox-lg"
|
||||
checked={isSelected}
|
||||
readonly
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<!-- Ícone de seta para individual -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-5 h-5 text-base-content/40">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M13.5 4.5 21 12m0 0-7.5 7.5M21 12H3" />
|
||||
</svg>
|
||||
{/if}
|
||||
</button>
|
||||
{/each}
|
||||
{:else if !usuarios}
|
||||
<div class="flex items-center justify-center py-8">
|
||||
<span class="loading loading-spinner loading-lg"></span>
|
||||
{:else if !usuarios?.data}
|
||||
<div class="flex flex-col items-center justify-center py-12">
|
||||
<span class="loading loading-spinner loading-lg text-primary"></span>
|
||||
<p class="mt-4 text-base-content/60">Carregando usuários...</p>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="text-center py-8 text-base-content/50">
|
||||
Nenhum usuário encontrado
|
||||
<div class="flex flex-col items-center justify-center py-12 text-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-16 h-16 text-base-content/30 mb-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 6a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0ZM4.501 20.118a7.5 7.5 0 0 1 14.998 0A17.933 17.933 0 0 1 12 21.75c-2.676 0-5.216-.584-7.499-1.632Z" />
|
||||
</svg>
|
||||
<p class="text-base-content/70 font-medium">
|
||||
{searchQuery.trim() ? "Nenhum usuário encontrado" : "Nenhum usuário disponível"}
|
||||
</p>
|
||||
{#if searchQuery.trim()}
|
||||
<p class="text-sm text-base-content/50 mt-2">Tente buscar por nome, email ou matrícula</p>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer (apenas para grupo) -->
|
||||
<!-- Footer (para grupo e sala de reunião) -->
|
||||
{#if activeTab === "grupo"}
|
||||
<div class="px-6 py-4 border-t border-base-300">
|
||||
<div class="px-6 py-4 border-t border-base-300 bg-base-200/50">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary btn-block"
|
||||
class="btn btn-primary btn-block btn-lg font-semibold shadow-lg hover:shadow-xl transition-all duration-200"
|
||||
onclick={handleCriarGrupo}
|
||||
disabled={loading || selectedUsers.length < 2 || !groupName.trim()}
|
||||
>
|
||||
{#if loading}
|
||||
<span class="loading loading-spinner"></span>
|
||||
Criando...
|
||||
Criando grupo...
|
||||
{:else}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-5 h-5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
|
||||
</svg>
|
||||
Criar Grupo
|
||||
{/if}
|
||||
</button>
|
||||
{#if selectedUsers.length < 2 && activeTab === "grupo"}
|
||||
<p class="text-xs text-base-content/50 text-center mt-2">Selecione pelo menos 2 participantes</p>
|
||||
{/if}
|
||||
</div>
|
||||
{:else if activeTab === "sala_reuniao"}
|
||||
<div class="px-6 py-4 border-t border-base-300 bg-base-200/50">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary btn-block btn-lg font-semibold shadow-lg hover:shadow-xl transition-all duration-200"
|
||||
onclick={handleCriarSalaReuniao}
|
||||
disabled={loading || selectedUsers.length < 1 || !salaReuniaoName.trim()}
|
||||
>
|
||||
{#if loading}
|
||||
<span class="loading loading-spinner"></span>
|
||||
Criando sala...
|
||||
{:else}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-5 h-5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
|
||||
</svg>
|
||||
Criar Sala de Reunião
|
||||
{/if}
|
||||
</button>
|
||||
{#if selectedUsers.length < 1 && activeTab === "sala_reuniao"}
|
||||
<p class="text-xs text-base-content/50 text-center mt-2">Selecione pelo menos 1 participante</p>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user