refactor: integrate current user data across components

- Replaced instances of `authStore` with `currentUser` to streamline user authentication handling.
- Updated permission checks and user-related data retrieval to utilize the new `useQuery` for better performance and clarity.
- Cleaned up component structures and improved formatting for consistency and readability.
- Enhanced error handling and user feedback mechanisms in various components to improve user experience.
This commit is contained in:
2025-11-08 10:52:33 -03:00
parent 01138b3e1c
commit 9a5f2b294d
28 changed files with 2312 additions and 1235 deletions

View File

@@ -2,10 +2,19 @@
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";
import { MessageSquare, User, Users, Video, X, Search, ChevronRight, Plus, UserX } from "lucide-svelte";
import {
MessageSquare,
User,
Users,
Video,
X,
Search,
ChevronRight,
Plus,
UserX,
} from "lucide-svelte";
interface Props {
onClose: () => void;
@@ -16,6 +25,8 @@
const client = useConvexClient();
const usuarios = useQuery(api.usuarios.listarParaChat, {});
const meuPerfil = useQuery(api.usuarios.obterPerfil, {});
// Usuário atual
const currentUser = useQuery(api.auth.getCurrentUser, {});
let activeTab = $state<"individual" | "grupo" | "sala_reuniao">("individual");
let searchQuery = $state("");
@@ -26,27 +37,36 @@
const usuariosFiltrados = $derived(() => {
if (!usuarios?.data) return [];
// Filtrar o próprio usuário
const meuId = authStore.usuario?._id || meuPerfil?.data?._id;
const meuId = currentUser?.data?._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)
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;
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 || "");
});
@@ -99,7 +119,8 @@
onClose();
} catch (error: any) {
console.error("Erro ao criar grupo:", error);
const mensagem = error?.message || error?.data || "Erro desconhecido ao criar grupo";
const mensagem =
error?.message || error?.data || "Erro desconhecido ao criar grupo";
alert(`Erro ao criar grupo: ${mensagem}`);
} finally {
loading = false;
@@ -127,7 +148,10 @@
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";
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;
@@ -135,10 +159,18 @@
}
</script>
<dialog class="modal modal-open" onclick={(e) => e.target === e.currentTarget && onClose()}>
<div class="modal-box max-w-2xl max-h-[85vh] flex flex-col p-0" onclick={(e) => e.stopPropagation()}>
<dialog
class="modal modal-open"
onclick={(e) => e.target === e.currentTarget && onClose()}
>
<div
class="modal-box max-w-2xl max-h-[85vh] flex flex-col p-0"
onclick={(e) => e.stopPropagation()}
>
<!-- Header -->
<div class="flex items-center justify-between px-6 py-4 border-b border-base-300">
<div
class="flex items-center justify-between px-6 py-4 border-b border-base-300"
>
<h2 class="text-2xl font-bold flex items-center gap-2">
<MessageSquare class="w-6 h-6 text-primary" />
Nova Conversa
@@ -158,8 +190,8 @@
<button
type="button"
class={`tab flex items-center gap-2 transition-all duration-200 ${
activeTab === "individual"
? "tab-active bg-primary text-primary-content font-semibold"
activeTab === "individual"
? "tab-active bg-primary text-primary-content font-semibold"
: "hover:bg-base-300"
}`}
onclick={() => {
@@ -174,8 +206,8 @@
<button
type="button"
class={`tab flex items-center gap-2 transition-all duration-200 ${
activeTab === "grupo"
? "tab-active bg-primary text-primary-content font-semibold"
activeTab === "grupo"
? "tab-active bg-primary text-primary-content font-semibold"
: "hover:bg-base-300"
}`}
onclick={() => {
@@ -190,8 +222,8 @@
<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"
activeTab === "sala_reuniao"
? "tab-active bg-primary text-primary-content font-semibold"
: "hover:bg-base-300"
}`}
onclick={() => {
@@ -225,7 +257,9 @@
<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' : ''})` : ""}
Participantes {selectedUsers.length > 0
? `(${selectedUsers.length} selecionado${selectedUsers.length > 1 ? "s" : ""})`
: ""}
</span>
</label>
</div>
@@ -233,8 +267,11 @@
<!-- 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>
<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"
@@ -248,7 +285,9 @@
<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' : ''})` : ""}
Participantes {selectedUsers.length > 0
? `(${selectedUsers.length} selecionado${selectedUsers.length > 1 ? "s" : ""})`
: ""}
</span>
</label>
</div>
@@ -262,7 +301,9 @@
class="input input-bordered w-full pl-10 focus:input-primary transition-colors"
bind:value={searchQuery}
/>
<Search class="w-5 h-5 absolute left-3 top-1/2 -translate-y-1/2 text-base-content/40" />
<Search
class="w-5 h-5 absolute left-3 top-1/2 -translate-y-1/2 text-base-content/40"
/>
</div>
<!-- Lista de usuários -->
@@ -288,29 +329,37 @@
disabled={loading}
>
<!-- Avatar -->
<div class="relative flex-shrink-0">
<UserAvatar
<div class="relative shrink-0">
<UserAvatar
avatar={usuario.avatar}
fotoPerfilUrl={usuario.fotoPerfilUrl}
nome={usuario.nome}
size="md"
/>
<div class="absolute -bottom-1 -right-1">
<UserStatusBadge status={usuario.statusPresenca || "offline"} size="sm" />
<UserStatusBadge
status={usuario.statusPresenca || "offline"}
size="sm"
/>
</div>
</div>
<!-- Info -->
<div class="flex-1 min-w-0">
<p class="font-semibold 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.matricula || "Sem informações"}
{usuario.setor ||
usuario.email ||
usuario.matricula ||
"Sem informações"}
</p>
</div>
<!-- Checkbox melhorado (para grupo e sala de reunião) -->
{#if activeTab === "grupo" || activeTab === "sala_reuniao"}
<div class="flex-shrink-0">
<div class="shrink-0">
<input
type="checkbox"
class="checkbox checkbox-primary checkbox-lg"
@@ -326,17 +375,24 @@
{/each}
{: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>
<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="flex flex-col items-center justify-center py-12 text-center">
<div
class="flex flex-col items-center justify-center py-12 text-center"
>
<UserX class="w-16 h-16 text-base-content/30 mb-4" />
<p class="text-base-content/70 font-medium">
{searchQuery.trim() ? "Nenhum usuário encontrado" : "Nenhum usuário disponível"}
{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>
<p class="text-sm text-base-content/50 mt-2">
Tente buscar por nome, email ou matrícula
</p>
{/if}
</div>
{/if}
@@ -361,7 +417,9 @@
{/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>
<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"}
@@ -370,7 +428,9 @@
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()}
disabled={loading ||
selectedUsers.length < 1 ||
!salaReuniaoName.trim()}
>
{#if loading}
<span class="loading loading-spinner"></span>
@@ -381,7 +441,9 @@
{/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>
<p class="text-xs text-base-content/50 text-center mt-2">
Selecione pelo menos 1 participante
</p>
{/if}
</div>
{/if}
@@ -390,4 +452,3 @@
<button type="button" onclick={onClose}>fechar</button>
</form>
</dialog>