feat: update ESLint and TypeScript configurations across frontend and backend; enhance component structure and improve data handling in various modules

This commit is contained in:
2025-12-02 16:36:02 -03:00
parent f48d28067c
commit d79e6959c3
215 changed files with 29474 additions and 28173 deletions

View File

@@ -1,183 +1,177 @@
import { browser } from "$app/environment";
import { goto } from "$app/navigation";
import type { Id } from "@sgse-app/backend/convex/betterAuth/_generated/dataModel";
import { browser } from '$app/environment';
import { goto } from '$app/navigation';
import type { Id } from '@sgse-app/backend/convex/betterAuth/_generated/dataModel';
interface Usuario {
_id: string;
matricula: string;
nome: string;
email: string;
funcionarioId?: Id<"funcionarios">;
role: {
_id: string;
nome: string;
nivel: number;
setor?: string;
};
primeiroAcesso: boolean;
avatar?: string;
fotoPerfil?: string;
fotoPerfilUrl?: string | null;
_id: string;
matricula: string;
nome: string;
email: string;
funcionarioId?: Id<'funcionarios'>;
role: {
_id: string;
nome: string;
nivel: number;
setor?: string;
};
primeiroAcesso: boolean;
avatar?: string;
fotoPerfil?: string;
fotoPerfilUrl?: string | null;
}
interface AuthState {
usuario: Usuario | null;
token: string | null;
carregando: boolean;
usuario: Usuario | null;
token: string | null;
carregando: boolean;
}
class AuthStore {
private state = $state<AuthState>({
usuario: null,
token: null,
carregando: true,
});
private state = $state<AuthState>({
usuario: null,
token: null,
carregando: true
});
constructor() {
if (browser) {
this.carregarDoLocalStorage();
}
}
constructor() {
if (browser) {
this.carregarDoLocalStorage();
}
}
get usuario() {
return this.state.usuario;
}
get usuario() {
return this.state.usuario;
}
get token() {
return this.state.token;
}
get token() {
return this.state.token;
}
get carregando() {
return this.state.carregando;
}
get carregando() {
return this.state.carregando;
}
get autenticado() {
return !!this.state.usuario && !!this.state.token;
}
get autenticado() {
return !!this.state.usuario && !!this.state.token;
}
get isAdmin() {
return this.state.usuario?.role.nivel === 0;
}
get isAdmin() {
return this.state.usuario?.role.nivel === 0;
}
get isTI() {
return this.state.usuario?.role.nome === "ti" || this.isAdmin;
}
get isTI() {
return this.state.usuario?.role.nome === 'ti' || this.isAdmin;
}
get isRH() {
return this.state.usuario?.role.nome === "rh" || this.isAdmin;
}
get isRH() {
return this.state.usuario?.role.nome === 'rh' || this.isAdmin;
}
/**
* FASE 2: Login dual - suporta tanto sistema customizado quanto Better Auth
* Por enquanto, mantém sistema customizado. Better Auth será adicionado depois.
*/
login(usuario: Usuario, token: string) {
this.state.usuario = usuario;
this.state.token = token;
this.state.carregando = false;
/**
* FASE 2: Login dual - suporta tanto sistema customizado quanto Better Auth
* Por enquanto, mantém sistema customizado. Better Auth será adicionado depois.
*/
login(usuario: Usuario, token: string) {
this.state.usuario = usuario;
this.state.token = token;
this.state.carregando = false;
if (browser) {
localStorage.setItem("auth_token", token);
localStorage.setItem("auth_usuario", JSON.stringify(usuario));
// FASE 2: Preparar para Better Auth (ainda não ativo)
// Quando Better Auth estiver configurado, também salvaremos sessão do Better Auth aqui
if (import.meta.env.DEV) {
console.log("✅ [AuthStore] Login realizado:", {
usuario: usuario.nome,
email: usuario.email,
sistema: "customizado" // Será "better-auth" quando migrado
});
}
}
}
/**
* FASE 2: Login via Better Auth (preparado para futuro)
* Por enquanto não implementado, será usado quando Better Auth estiver completo
*/
async loginWithBetterAuth(email: string, senha: string) {
// TODO: Implementar quando Better Auth estiver pronto
// const { authClient } = await import("$lib/auth");
// const result = await authClient.signIn.email({ email, password: senha });
// if (result.data) {
// // Obter perfil do usuário do Convex
// // this.login(usuario, result.data.session.token);
// }
throw new Error("Better Auth ainda não configurado. Use login customizado.");
}
if (browser) {
localStorage.setItem('auth_token', token);
localStorage.setItem('auth_usuario', JSON.stringify(usuario));
logout() {
this.state.usuario = null;
this.state.token = null;
this.state.carregando = false;
// FASE 2: Preparar para Better Auth (ainda não ativo)
// Quando Better Auth estiver configurado, também salvaremos sessão do Better Auth aqui
if (import.meta.env.DEV) {
console.log('✅ [AuthStore] Login realizado:', {
usuario: usuario.nome,
email: usuario.email,
sistema: 'customizado' // Será "better-auth" quando migrado
});
}
}
}
if (browser) {
localStorage.removeItem("auth_token");
localStorage.removeItem("auth_usuario");
goto("/");
}
}
/**
* FASE 2: Login via Better Auth (preparado para futuro)
* Por enquanto não implementado, será usado quando Better Auth estiver completo
*/
async loginWithBetterAuth(email: string, senha: string) {
// TODO: Implementar quando Better Auth estiver pronto
// const { authClient } = await import("$lib/auth");
// const result = await authClient.signIn.email({ email, password: senha });
// if (result.data) {
// // Obter perfil do usuário do Convex
// // this.login(usuario, result.data.session.token);
// }
throw new Error('Better Auth ainda não configurado. Use login customizado.');
}
setCarregando(carregando: boolean) {
this.state.carregando = carregando;
}
logout() {
this.state.usuario = null;
this.state.token = null;
this.state.carregando = false;
async refresh() {
if (!browser || !this.state.token) return;
if (browser) {
localStorage.removeItem('auth_token');
localStorage.removeItem('auth_usuario');
goto('/');
}
}
try {
// Importação dinâmica do convex para evitar problemas de SSR
const { ConvexHttpClient } = await import("convex/browser");
const { api } = await import("@sgse-app/backend/convex/_generated/api");
setCarregando(carregando: boolean) {
this.state.carregando = carregando;
}
const client = new ConvexHttpClient(import.meta.env.VITE_CONVEX_URL);
client.setAuth(this.state.token);
async refresh() {
if (!browser || !this.state.token) return;
const usuarioAtualizado = await client.query(
api.usuarios.obterPerfil,
{}
);
try {
// Importação dinâmica do convex para evitar problemas de SSR
const { ConvexHttpClient } = await import('convex/browser');
const { api } = await import('@sgse-app/backend/convex/_generated/api');
if (usuarioAtualizado) {
// Preservar role e primeiroAcesso do estado atual
this.state.usuario = {
...usuarioAtualizado,
role: this.state.usuario?.role || {
_id: "",
nome: "Usuário",
nivel: 999,
},
primeiroAcesso: this.state.usuario?.primeiroAcesso ?? false,
};
const client = new ConvexHttpClient(import.meta.env.VITE_CONVEX_URL);
client.setAuth(this.state.token);
localStorage.setItem(
"auth_usuario",
JSON.stringify(this.state.usuario)
);
}
} catch (error) {
console.error("Erro ao atualizar perfil:", error);
}
}
const usuarioAtualizado = await client.query(api.usuarios.obterPerfil, {});
private carregarDoLocalStorage() {
const token = localStorage.getItem("auth_token");
const usuarioStr = localStorage.getItem("auth_usuario");
if (usuarioAtualizado) {
// Preservar role e primeiroAcesso do estado atual
this.state.usuario = {
...usuarioAtualizado,
role: this.state.usuario?.role || {
_id: '',
nome: 'Usuário',
nivel: 999
},
primeiroAcesso: this.state.usuario?.primeiroAcesso ?? false
};
if (token && usuarioStr) {
try {
const usuario = JSON.parse(usuarioStr);
this.state.usuario = usuario;
this.state.token = token;
} catch (error) {
console.error("Erro ao carregar usuário do localStorage:", error);
this.logout();
}
}
localStorage.setItem('auth_usuario', JSON.stringify(this.state.usuario));
}
} catch (error) {
console.error('Erro ao atualizar perfil:', error);
}
}
this.state.carregando = false;
}
private carregarDoLocalStorage() {
const token = localStorage.getItem('auth_token');
const usuarioStr = localStorage.getItem('auth_usuario');
if (token && usuarioStr) {
try {
const usuario = JSON.parse(usuarioStr);
this.state.usuario = usuario;
this.state.token = token;
} catch (error) {
console.error('Erro ao carregar usuário do localStorage:', error);
this.logout();
}
}
this.state.carregando = false;
}
}
export const authStore = new AuthStore();

View File

@@ -62,22 +62,13 @@ const estadoInicial: EstadoChamada = {
export const callState = writable<EstadoChamada>(estadoInicial);
// Store para indicar se há chamada ativa
export const chamadaAtiva = derived(
callState,
($state) => $state.chamadaId !== null
);
export const chamadaAtiva = derived(callState, ($state) => $state.chamadaId !== null);
// Store para indicar se está conectado
export const estaConectado = derived(
callState,
($state) => $state.estaConectado
);
export const estaConectado = derived(callState, ($state) => $state.estaConectado);
// Store para indicar se está gravando
export const gravando = derived(
callState,
($state) => $state.gravando
);
export const gravando = derived(callState, ($state) => $state.gravando);
// Funções para atualizar o estado
@@ -109,12 +100,12 @@ export function inicializarChamada(
*/
export function finalizarChamada(): void {
const estadoAtual = get(callState);
// Liberar recursos
if (estadoAtual.streamLocal) {
estadoAtual.streamLocal.getTracks().forEach((track) => track.stop());
}
callState.set(estadoInicial);
}
@@ -184,14 +175,12 @@ export function atualizarParticipantes(participantes: ParticipanteChamada[]): vo
export function adicionarParticipante(participante: ParticipanteChamada): void {
callState.update((state) => {
// Verificar se já existe
const existe = state.participantes.some(
(p) => p.usuarioId === participante.usuarioId
);
const existe = state.participantes.some((p) => p.usuarioId === participante.usuarioId);
if (existe) {
return state;
}
return {
...state,
participantes: [...state.participantes, participante]
@@ -205,9 +194,7 @@ export function adicionarParticipante(participante: ParticipanteChamada): void {
export function removerParticipante(usuarioId: Id<'usuarios'>): void {
callState.update((state) => ({
...state,
participantes: state.participantes.filter(
(p) => p.usuarioId !== usuarioId
)
participantes: state.participantes.filter((p) => p.usuarioId !== usuarioId)
}));
}
@@ -299,7 +286,7 @@ export function setStreamLocal(stream: MediaStream | null): void {
if (state.streamLocal) {
state.streamLocal.getTracks().forEach((track) => track.stop());
}
return {
...state,
streamLocal: stream
@@ -320,4 +307,3 @@ export function obterEstadoAtual(): EstadoChamada {
export function resetarEstado(): void {
finalizarChamada();
}

View File

@@ -1,53 +1,52 @@
import { writable } from "svelte/store";
import type { Doc, Id } from "@sgse-app/backend/convex/_generated/dataModel";
import { writable } from 'svelte/store';
import type { Doc, Id } from '@sgse-app/backend/convex/_generated/dataModel';
export type TicketDetalhe = {
ticket: Doc<"tickets">;
interactions: Doc<"ticketInteractions">[];
ticket: Doc<'tickets'>;
interactions: Doc<'ticketInteractions'>[];
};
function createChamadosStore() {
const tickets = writable<Array<Doc<"tickets">>>([]);
const detalhes = writable<Record<string, TicketDetalhe>>({});
const carregando = writable(false);
const tickets = writable<Array<Doc<'tickets'>>>([]);
const detalhes = writable<Record<string, TicketDetalhe>>({});
const carregando = writable(false);
function setTickets(lista: Array<Doc<"tickets">>) {
tickets.set(lista);
}
function setTickets(lista: Array<Doc<'tickets'>>) {
tickets.set(lista);
}
function upsertTicket(ticket: Doc<"tickets">) {
tickets.update((current) => {
const existente = current.findIndex((t) => t._id === ticket._id);
if (existente >= 0) {
const copia = [...current];
copia[existente] = ticket;
return copia;
}
return [ticket, ...current];
});
}
function upsertTicket(ticket: Doc<'tickets'>) {
tickets.update((current) => {
const existente = current.findIndex((t) => t._id === ticket._id);
if (existente >= 0) {
const copia = [...current];
copia[existente] = ticket;
return copia;
}
return [ticket, ...current];
});
}
function setDetalhe(ticketId: Id<"tickets">, detalhe: TicketDetalhe) {
detalhes.update((mapa) => ({
...mapa,
[ticketId]: detalhe,
}));
}
function setDetalhe(ticketId: Id<'tickets'>, detalhe: TicketDetalhe) {
detalhes.update((mapa) => ({
...mapa,
[ticketId]: detalhe
}));
}
function setCarregando(flag: boolean) {
carregando.set(flag);
}
function setCarregando(flag: boolean) {
carregando.set(flag);
}
return {
tickets,
detalhes,
carregando,
setTickets,
upsertTicket,
setDetalhe,
setCarregando,
};
return {
tickets,
detalhes,
carregando,
setTickets,
upsertTicket,
setDetalhe,
setCarregando
};
}
export const chamadosStore = createChamadosStore();

View File

@@ -2,7 +2,7 @@ import { writable, derived } from 'svelte/store';
import type { Id } from '@sgse-app/backend/convex/_generated/dataModel';
// Store para a conversa ativa
export const conversaAtiva = writable<Id<"conversas"> | null>(null);
export const conversaAtiva = writable<Id<'conversas'> | null>(null);
// Store para o estado do chat (aberto/minimizado/fechado)
export const chatAberto = writable<boolean>(false);
@@ -13,30 +13,29 @@ export const notificacoesCount = writable<number>(0);
// Funções auxiliares
export function abrirChat() {
chatAberto.set(true);
chatMinimizado.set(false);
chatAberto.set(true);
chatMinimizado.set(false);
}
export function fecharChat() {
chatAberto.set(false);
chatMinimizado.set(false);
conversaAtiva.set(null);
chatAberto.set(false);
chatMinimizado.set(false);
conversaAtiva.set(null);
}
export function minimizarChat() {
chatMinimizado.set(true);
chatMinimizado.set(true);
}
export function maximizarChat() {
chatMinimizado.set(false);
chatMinimizado.set(false);
}
export function abrirConversa(conversaId: Id<"conversas">) {
conversaAtiva.set(conversaId);
abrirChat();
export function abrirConversa(conversaId: Id<'conversas'>) {
conversaAtiva.set(conversaId);
abrirChat();
}
export function voltarParaLista() {
conversaAtiva.set(null);
conversaAtiva.set(null);
}

View File

@@ -1,13 +1,13 @@
/**
* Helper para garantir que o token seja passado para todas requisições Convex
*
*
* Este store reativa garante que quando o token mudar no authStore,
* todos os clientes Convex sejam atualizados automaticamente.
*/
import { authStore } from "./auth.svelte";
import { browser } from "$app/environment";
import { PUBLIC_CONVEX_URL } from "$env/static/public";
import { authStore } from './auth.svelte';
import { browser } from '$app/environment';
import { PUBLIC_CONVEX_URL } from '$env/static/public';
let convexClients = new Set<any>();
@@ -15,50 +15,49 @@ let convexClients = new Set<any>();
* Registrar um cliente Convex para receber atualizações de token
*/
export function registerConvexClient(client: any) {
if (!browser) return;
convexClients.add(client);
// Configurar token inicial
if (authStore.token && client.setAuth) {
client.setAuth(authStore.token);
}
// Retornar função de limpeza
return () => {
convexClients.delete(client);
};
if (!browser) return;
convexClients.add(client);
// Configurar token inicial
if (authStore.token && client.setAuth) {
client.setAuth(authStore.token);
}
// Retornar função de limpeza
return () => {
convexClients.delete(client);
};
}
/**
* Atualizar token em todos clientes registrados
*/
function updateAllClients() {
if (!browser) return;
const token = authStore.token;
convexClients.forEach((client) => {
if (client && typeof client.setAuth === "function") {
if (token) {
client.setAuth(token);
} else {
client.clearAuth?.();
}
}
});
if (!browser) return;
const token = authStore.token;
convexClients.forEach((client) => {
if (client && typeof client.setAuth === 'function') {
if (token) {
client.setAuth(token);
} else {
client.clearAuth?.();
}
}
});
}
// Observar mudanças no token e atualizar clientes
if (browser) {
// Usar uma abordagem reativa simples
let lastToken: string | null = null;
setInterval(() => {
const currentToken = authStore.token;
if (currentToken !== lastToken) {
lastToken = currentToken;
updateAllClients();
}
}, 500); // Verificar a cada 500ms
}
// Usar uma abordagem reativa simples
let lastToken: string | null = null;
setInterval(() => {
const currentToken = authStore.token;
if (currentToken !== lastToken) {
lastToken = currentToken;
updateAllClients();
}
}, 500); // Verificar a cada 500ms
}

View File

@@ -1,22 +1,21 @@
import { browser } from "$app/environment";
import { browser } from '$app/environment';
/**
* Store global para controlar o modal de login
*/
class LoginModalStore {
showModal = $state(false);
redirectAfterLogin = $state<string | null>(null);
showModal = $state(false);
redirectAfterLogin = $state<string | null>(null);
open(redirectTo?: string) {
this.showModal = true;
this.redirectAfterLogin = redirectTo || null;
}
open(redirectTo?: string) {
this.showModal = true;
this.redirectAfterLogin = redirectTo || null;
}
close() {
this.showModal = false;
this.redirectAfterLogin = null;
}
close() {
this.showModal = false;
this.redirectAfterLogin = null;
}
}
export const loginModalStore = new LoginModalStore();