Fix usuarios page #6
@@ -5,18 +5,27 @@
|
|||||||
import { formatDistanceToNow } from "date-fns";
|
import { formatDistanceToNow } from "date-fns";
|
||||||
import { ptBR } from "date-fns/locale";
|
import { ptBR } from "date-fns/locale";
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
|
import { authStore } from "$lib/stores/auth.svelte";
|
||||||
|
|
||||||
// Queries e Client
|
// Queries e Client
|
||||||
const client = useConvexClient();
|
const client = useConvexClient();
|
||||||
const notificacoesQuery = useQuery(api.chat.obterNotificacoes, { apenasPendentes: true });
|
const notificacoesQuery = useQuery(api.chat.obterNotificacoes, {
|
||||||
|
apenasPendentes: true,
|
||||||
|
});
|
||||||
const countQuery = useQuery(api.chat.contarNotificacoesNaoLidas, {});
|
const countQuery = useQuery(api.chat.contarNotificacoesNaoLidas, {});
|
||||||
|
|
||||||
let dropdownOpen = $state(false);
|
let dropdownOpen = $state(false);
|
||||||
let notificacoesFerias = $state<any[]>([]);
|
let notificacoesFerias = $state<any[]>([]);
|
||||||
|
|
||||||
// Helpers para obter valores das queries
|
// Helpers para obter valores das queries
|
||||||
const count = $derived((typeof countQuery === 'number' ? countQuery : countQuery?.data) ?? 0);
|
const count = $derived(
|
||||||
const notificacoes = $derived((Array.isArray(notificacoesQuery) ? notificacoesQuery : notificacoesQuery?.data) ?? []);
|
(typeof countQuery === "number" ? countQuery : countQuery?.data) ?? 0
|
||||||
|
);
|
||||||
|
const notificacoes = $derived(
|
||||||
|
(Array.isArray(notificacoesQuery)
|
||||||
|
? notificacoesQuery
|
||||||
|
: notificacoesQuery?.data) ?? []
|
||||||
|
);
|
||||||
|
|
||||||
// Atualizar contador no store
|
// Atualizar contador no store
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
@@ -27,11 +36,15 @@
|
|||||||
// Buscar notificações de férias
|
// Buscar notificações de férias
|
||||||
async function buscarNotificacoesFerias() {
|
async function buscarNotificacoesFerias() {
|
||||||
try {
|
try {
|
||||||
const usuarioStore = await import("$lib/stores/auth.svelte").then(m => m.authStore);
|
const usuarioStore = authStore;
|
||||||
|
|
||||||
if (usuarioStore.usuario?._id) {
|
if (usuarioStore.usuario?._id) {
|
||||||
const notifsFerias = await client.query(api.ferias.obterNotificacoesNaoLidas, {
|
const notifsFerias = await client.query(
|
||||||
|
api.ferias.obterNotificacoesNaoLidas,
|
||||||
|
{
|
||||||
usuarioId: usuarioStore.usuario._id as any,
|
usuarioId: usuarioStore.usuario._id as any,
|
||||||
});
|
}
|
||||||
|
);
|
||||||
notificacoesFerias = notifsFerias || [];
|
notificacoesFerias = notifsFerias || [];
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -61,19 +74,25 @@
|
|||||||
await client.mutation(api.chat.marcarTodasNotificacoesLidas, {});
|
await client.mutation(api.chat.marcarTodasNotificacoesLidas, {});
|
||||||
// Marcar todas as notificações de férias como lidas
|
// Marcar todas as notificações de férias como lidas
|
||||||
for (const notif of notificacoesFerias) {
|
for (const notif of notificacoesFerias) {
|
||||||
await client.mutation(api.ferias.marcarComoLida, { notificacaoId: notif._id });
|
await client.mutation(api.ferias.marcarComoLida, {
|
||||||
|
notificacaoId: notif._id,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
dropdownOpen = false;
|
dropdownOpen = false;
|
||||||
await buscarNotificacoesFerias();
|
await buscarNotificacoesFerias();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleClickNotificacao(notificacaoId: string) {
|
async function handleClickNotificacao(notificacaoId: string) {
|
||||||
await client.mutation(api.chat.marcarNotificacaoLida, { notificacaoId: notificacaoId as any });
|
await client.mutation(api.chat.marcarNotificacaoLida, {
|
||||||
|
notificacaoId: notificacaoId as any,
|
||||||
|
});
|
||||||
dropdownOpen = false;
|
dropdownOpen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleClickNotificacaoFerias(notificacaoId: string) {
|
async function handleClickNotificacaoFerias(notificacaoId: string) {
|
||||||
await client.mutation(api.ferias.marcarComoLida, { notificacaoId: notificacaoId as any });
|
await client.mutation(api.ferias.marcarComoLida, {
|
||||||
|
notificacaoId: notificacaoId as any,
|
||||||
|
});
|
||||||
await buscarNotificacoesFerias();
|
await buscarNotificacoesFerias();
|
||||||
dropdownOpen = false;
|
dropdownOpen = false;
|
||||||
// Redirecionar para a página de férias
|
// Redirecionar para a página de férias
|
||||||
@@ -98,43 +117,6 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
|
||||||
@keyframes badge-bounce {
|
|
||||||
0%, 100% {
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
transform: scale(1.1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes pulse-ring-subtle {
|
|
||||||
0%, 100% {
|
|
||||||
opacity: 0.1;
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
opacity: 0.3;
|
|
||||||
transform: scale(1.05);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes bell-ring {
|
|
||||||
0%, 100% {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
10%, 30% {
|
|
||||||
transform: rotate(-10deg);
|
|
||||||
}
|
|
||||||
20%, 40% {
|
|
||||||
transform: rotate(10deg);
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="dropdown dropdown-end notification-bell">
|
<div class="dropdown dropdown-end notification-bell">
|
||||||
<!-- Botão de Notificação ULTRA MODERNO (igual ao perfil) -->
|
<!-- Botão de Notificação ULTRA MODERNO (igual ao perfil) -->
|
||||||
<button
|
<button
|
||||||
@@ -146,14 +128,21 @@
|
|||||||
aria-label="Notificações"
|
aria-label="Notificações"
|
||||||
>
|
>
|
||||||
<!-- Efeito de brilho no hover -->
|
<!-- Efeito de brilho no hover -->
|
||||||
<div class="absolute inset-0 bg-gradient-to-br from-white/0 to-white/20 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
|
<div
|
||||||
|
class="absolute inset-0 bg-gradient-to-br from-white/0 to-white/20 opacity-0 group-hover:opacity-100 transition-opacity duration-300"
|
||||||
|
></div>
|
||||||
|
|
||||||
<!-- Anel de pulso sutil -->
|
<!-- Anel de pulso sutil -->
|
||||||
<div class="absolute inset-0 rounded-2xl" style="animation: pulse-ring-subtle 3s cubic-bezier(0.4, 0, 0.6, 1) infinite;"></div>
|
<div
|
||||||
|
class="absolute inset-0 rounded-2xl"
|
||||||
|
style="animation: pulse-ring-subtle 3s cubic-bezier(0.4, 0, 0.6, 1) infinite;"
|
||||||
|
></div>
|
||||||
|
|
||||||
<!-- Glow effect quando tem notificações -->
|
<!-- Glow effect quando tem notificações -->
|
||||||
{#if count && count > 0}
|
{#if count && count > 0}
|
||||||
<div class="absolute inset-0 rounded-2xl bg-error/30 blur-lg animate-pulse"></div>
|
<div
|
||||||
|
class="absolute inset-0 rounded-2xl bg-error/30 blur-lg animate-pulse"
|
||||||
|
></div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<!-- Ícone do sino PREENCHIDO moderno -->
|
<!-- Ícone do sino PREENCHIDO moderno -->
|
||||||
@@ -162,9 +151,16 @@
|
|||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
class="w-7 h-7 text-white relative z-10 transition-all duration-300 group-hover:scale-110"
|
class="w-7 h-7 text-white relative z-10 transition-all duration-300 group-hover:scale-110"
|
||||||
style="filter: drop-shadow(0 2px 8px rgba(0,0,0,0.3)); animation: {count && count > 0 ? 'bell-ring 2s ease-in-out infinite' : 'none'};"
|
style="filter: drop-shadow(0 2px 8px rgba(0,0,0,0.3)); animation: {count &&
|
||||||
|
count > 0
|
||||||
|
? 'bell-ring 2s ease-in-out infinite'
|
||||||
|
: 'none'};"
|
||||||
>
|
>
|
||||||
<path fill-rule="evenodd" d="M5.25 9a6.75 6.75 0 0113.5 0v.75c0 2.123.8 4.057 2.118 5.52a.75.75 0 01-.297 1.206c-1.544.57-3.16.99-4.831 1.243a3.75 3.75 0 11-7.48 0 24.585 24.585 0 01-4.831-1.244.75.75 0 01-.298-1.205A8.217 8.217 0 005.25 9.75V9zm4.502 8.9a2.25 2.25 0 104.496 0 25.057 25.057 0 01-4.496 0z" clip-rule="evenodd" />
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M5.25 9a6.75 6.75 0 0113.5 0v.75c0 2.123.8 4.057 2.118 5.52a.75.75 0 01-.297 1.206c-1.544.57-3.16.99-4.831 1.243a3.75 3.75 0 11-7.48 0 24.585 24.585 0 01-4.831-1.244.75.75 0 01-.298-1.205A8.217 8.217 0 005.25 9.75V9zm4.502 8.9a2.25 2.25 0 104.496 0 25.057 25.057 0 01-4.496 0z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<!-- Badge premium MODERNO com gradiente -->
|
<!-- Badge premium MODERNO com gradiente -->
|
||||||
@@ -186,7 +182,9 @@
|
|||||||
class="dropdown-content z-50 mt-3 w-80 max-h-96 overflow-auto rounded-box bg-base-100 p-2 shadow-2xl border border-base-300"
|
class="dropdown-content z-50 mt-3 w-80 max-h-96 overflow-auto rounded-box bg-base-100 p-2 shadow-2xl border border-base-300"
|
||||||
>
|
>
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div class="flex items-center justify-between px-4 py-2 border-b border-base-300">
|
<div
|
||||||
|
class="flex items-center justify-between px-4 py-2 border-b border-base-300"
|
||||||
|
>
|
||||||
<h3 class="text-lg font-semibold">Notificações</h3>
|
<h3 class="text-lg font-semibold">Notificações</h3>
|
||||||
{#if count > 0}
|
{#if count > 0}
|
||||||
<button
|
<button
|
||||||
@@ -297,8 +295,19 @@
|
|||||||
<div class="flex items-start gap-3">
|
<div class="flex items-start gap-3">
|
||||||
<!-- Ícone -->
|
<!-- Ícone -->
|
||||||
<div class="flex-shrink-0 mt-1">
|
<div class="flex-shrink-0 mt-1">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-purple-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="w-5 h-5 text-purple-600"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -346,3 +355,44 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
@keyframes badge-bounce {
|
||||||
|
0%,
|
||||||
|
100% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse-ring-subtle {
|
||||||
|
0%,
|
||||||
|
100% {
|
||||||
|
opacity: 0.1;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 0.3;
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes bell-ring {
|
||||||
|
0%,
|
||||||
|
100% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
10%,
|
||||||
|
30% {
|
||||||
|
transform: rotate(-10deg);
|
||||||
|
}
|
||||||
|
20%,
|
||||||
|
40% {
|
||||||
|
transform: rotate(10deg);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import { browser } from "$app/environment";
|
import { browser } from "$app/environment";
|
||||||
import { goto } from "$app/navigation";
|
import { goto } from "$app/navigation";
|
||||||
|
import type { Id } from "@sgse-app/backend/convex/betterAuth/_generated/dataModel";
|
||||||
|
|
||||||
interface Usuario {
|
interface Usuario {
|
||||||
_id: string;
|
_id: string;
|
||||||
matricula: string;
|
matricula: string;
|
||||||
nome: string;
|
nome: string;
|
||||||
email: string;
|
email: string;
|
||||||
funcionarioId?: string;
|
funcionarioId?: Id<"funcionarios">;
|
||||||
role: {
|
role: {
|
||||||
_id: string;
|
_id: string;
|
||||||
nome: string;
|
nome: string;
|
||||||
@@ -104,7 +105,10 @@ class AuthStore {
|
|||||||
const client = new ConvexHttpClient(import.meta.env.VITE_CONVEX_URL);
|
const client = new ConvexHttpClient(import.meta.env.VITE_CONVEX_URL);
|
||||||
client.setAuth(this.state.token);
|
client.setAuth(this.state.token);
|
||||||
|
|
||||||
const usuarioAtualizado = await client.query(api.usuarios.obterPerfil, {});
|
const usuarioAtualizado = await client.query(
|
||||||
|
api.usuarios.obterPerfil,
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
if (usuarioAtualizado && this.state.usuario) {
|
if (usuarioAtualizado && this.state.usuario) {
|
||||||
this.state.usuario = {
|
this.state.usuario = {
|
||||||
@@ -112,7 +116,10 @@ class AuthStore {
|
|||||||
...usuarioAtualizado,
|
...usuarioAtualizado,
|
||||||
};
|
};
|
||||||
|
|
||||||
localStorage.setItem("auth_usuario", JSON.stringify(this.state.usuario));
|
localStorage.setItem(
|
||||||
|
"auth_usuario",
|
||||||
|
JSON.stringify(this.state.usuario)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Erro ao atualizar perfil:", error);
|
console.error("Erro ao atualizar perfil:", error);
|
||||||
@@ -139,4 +146,3 @@ class AuthStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const authStore = new AuthStore();
|
export const authStore = new AuthStore();
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -11,10 +11,14 @@
|
|||||||
const usuarios = useQuery(api.usuarios.listar, {});
|
const usuarios = useQuery(api.usuarios.listar, {});
|
||||||
|
|
||||||
let filtroNome = $state("");
|
let filtroNome = $state("");
|
||||||
let filtroStatus = $state<"todos" | "ativo" | "bloqueado" | "inativo">("todos");
|
let filtroStatus = $state<"todos" | "ativo" | "bloqueado" | "inativo">(
|
||||||
|
"todos"
|
||||||
|
);
|
||||||
let usuarioSelecionado = $state<any>(null);
|
let usuarioSelecionado = $state<any>(null);
|
||||||
let modalAberto = $state(false);
|
let modalAberto = $state(false);
|
||||||
let modalAcao = $state<"bloquear" | "desbloquear" | "reset" | "associar">("bloquear");
|
let modalAcao = $state<"bloquear" | "desbloquear" | "reset" | "associar">(
|
||||||
|
"bloquear"
|
||||||
|
);
|
||||||
let motivo = $state("");
|
let motivo = $state("");
|
||||||
let processando = $state(false);
|
let processando = $state(false);
|
||||||
|
|
||||||
@@ -25,19 +29,21 @@
|
|||||||
let buscaFuncionario = $state("");
|
let buscaFuncionario = $state("");
|
||||||
|
|
||||||
// Query de funcionários
|
// Query de funcionários
|
||||||
const funcionarios = useQuery(api.funcionarios.list, {});
|
const funcionarios = useQuery(api.funcionarios.getAll, {});
|
||||||
|
|
||||||
// Usuários filtrados
|
// Usuários filtrados
|
||||||
const usuariosFiltrados = $derived.by(() => {
|
const usuariosFiltrados = $derived.by(() => {
|
||||||
if (!usuarios?.data || !Array.isArray(usuarios.data)) return [];
|
if (!usuarios?.data || !Array.isArray(usuarios.data)) return [];
|
||||||
|
|
||||||
return usuarios.data.filter(u => {
|
return usuarios.data.filter((u) => {
|
||||||
const matchNome = !filtroNome ||
|
const matchNome =
|
||||||
|
!filtroNome ||
|
||||||
u.nome.toLowerCase().includes(filtroNome.toLowerCase()) ||
|
u.nome.toLowerCase().includes(filtroNome.toLowerCase()) ||
|
||||||
u.matricula.includes(filtroNome) ||
|
u.matricula.includes(filtroNome) ||
|
||||||
u.email?.toLowerCase().includes(filtroNome.toLowerCase());
|
u.email?.toLowerCase().includes(filtroNome.toLowerCase());
|
||||||
|
|
||||||
const matchStatus = filtroStatus === "todos" ||
|
const matchStatus =
|
||||||
|
filtroStatus === "todos" ||
|
||||||
(filtroStatus === "ativo" && u.ativo && !u.bloqueado) ||
|
(filtroStatus === "ativo" && u.ativo && !u.bloqueado) ||
|
||||||
(filtroStatus === "bloqueado" && u.bloqueado) ||
|
(filtroStatus === "bloqueado" && u.bloqueado) ||
|
||||||
(filtroStatus === "inativo" && !u.ativo);
|
(filtroStatus === "inativo" && !u.ativo);
|
||||||
@@ -50,24 +56,27 @@
|
|||||||
const funcionariosFiltrados = $derived.by(() => {
|
const funcionariosFiltrados = $derived.by(() => {
|
||||||
if (!funcionarios?.data || !Array.isArray(funcionarios.data)) return [];
|
if (!funcionarios?.data || !Array.isArray(funcionarios.data)) return [];
|
||||||
|
|
||||||
return funcionarios.data.filter(f => {
|
return funcionarios.data
|
||||||
|
.filter((f) => {
|
||||||
// Filtro por busca
|
// Filtro por busca
|
||||||
const matchBusca = !buscaFuncionario ||
|
const matchBusca =
|
||||||
|
!buscaFuncionario ||
|
||||||
f.nome.toLowerCase().includes(buscaFuncionario.toLowerCase()) ||
|
f.nome.toLowerCase().includes(buscaFuncionario.toLowerCase()) ||
|
||||||
f.cpf?.includes(buscaFuncionario) ||
|
f.cpf?.includes(buscaFuncionario) ||
|
||||||
f.matricula?.includes(buscaFuncionario);
|
f.matricula?.includes(buscaFuncionario);
|
||||||
|
|
||||||
return matchBusca;
|
return matchBusca;
|
||||||
}).sort((a, b) => a.nome.localeCompare(b.nome));
|
})
|
||||||
|
.sort((a, b) => a.nome.localeCompare(b.nome));
|
||||||
});
|
});
|
||||||
|
|
||||||
const stats = $derived.by(() => {
|
const stats = $derived.by(() => {
|
||||||
if (!usuarios?.data || !Array.isArray(usuarios.data)) return null;
|
if (!usuarios?.data || !Array.isArray(usuarios.data)) return null;
|
||||||
return {
|
return {
|
||||||
total: usuarios.data.length,
|
total: usuarios.data.length,
|
||||||
ativos: usuarios.data.filter(u => u.ativo && !u.bloqueado).length,
|
ativos: usuarios.data.filter((u) => u.ativo && !u.bloqueado).length,
|
||||||
bloqueados: usuarios.data.filter(u => u.bloqueado).length,
|
bloqueados: usuarios.data.filter((u) => u.bloqueado).length,
|
||||||
inativos: usuarios.data.filter(u => !u.ativo).length
|
inativos: usuarios.data.filter((u) => !u.ativo).length,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -105,7 +114,7 @@
|
|||||||
try {
|
try {
|
||||||
await client.mutation(api.usuarios.associarFuncionario, {
|
await client.mutation(api.usuarios.associarFuncionario, {
|
||||||
usuarioId: usuarioParaAssociar._id as Id<"usuarios">,
|
usuarioId: usuarioParaAssociar._id as Id<"usuarios">,
|
||||||
funcionarioId: funcionarioSelecionadoId as Id<"funcionarios">
|
funcionarioId: funcionarioSelecionadoId as Id<"funcionarios">,
|
||||||
});
|
});
|
||||||
|
|
||||||
alert("Funcionário associado com sucesso!");
|
alert("Funcionário associado com sucesso!");
|
||||||
@@ -120,12 +129,13 @@
|
|||||||
async function desassociarFuncionario() {
|
async function desassociarFuncionario() {
|
||||||
if (!usuarioParaAssociar) return;
|
if (!usuarioParaAssociar) return;
|
||||||
|
|
||||||
if (!confirm("Deseja realmente desassociar o funcionário deste usuário?")) return;
|
if (!confirm("Deseja realmente desassociar o funcionário deste usuário?"))
|
||||||
|
return;
|
||||||
|
|
||||||
processando = true;
|
processando = true;
|
||||||
try {
|
try {
|
||||||
await client.mutation(api.usuarios.desassociarFuncionario, {
|
await client.mutation(api.usuarios.desassociarFuncionario, {
|
||||||
usuarioId: usuarioParaAssociar._id as Id<"usuarios">
|
usuarioId: usuarioParaAssociar._id as Id<"usuarios">,
|
||||||
});
|
});
|
||||||
|
|
||||||
alert("Funcionário desassociado com sucesso!");
|
alert("Funcionário desassociado com sucesso!");
|
||||||
@@ -151,17 +161,17 @@
|
|||||||
await client.mutation(api.usuarios.bloquearUsuario, {
|
await client.mutation(api.usuarios.bloquearUsuario, {
|
||||||
usuarioId: usuarioSelecionado._id as Id<"usuarios">,
|
usuarioId: usuarioSelecionado._id as Id<"usuarios">,
|
||||||
motivo,
|
motivo,
|
||||||
bloqueadoPorId: authStore.usuario._id as Id<"usuarios">
|
bloqueadoPorId: authStore.usuario._id as Id<"usuarios">,
|
||||||
});
|
});
|
||||||
} else if (modalAcao === "desbloquear") {
|
} else if (modalAcao === "desbloquear") {
|
||||||
await client.mutation(api.usuarios.desbloquearUsuario, {
|
await client.mutation(api.usuarios.desbloquearUsuario, {
|
||||||
usuarioId: usuarioSelecionado._id as Id<"usuarios">,
|
usuarioId: usuarioSelecionado._id as Id<"usuarios">,
|
||||||
desbloqueadoPorId: authStore.usuario._id as Id<"usuarios">
|
desbloqueadoPorId: authStore.usuario._id as Id<"usuarios">,
|
||||||
});
|
});
|
||||||
} else if (modalAcao === "reset") {
|
} else if (modalAcao === "reset") {
|
||||||
await client.mutation(api.usuarios.resetarSenhaUsuario, {
|
await client.mutation(api.usuarios.resetarSenhaUsuario, {
|
||||||
usuarioId: usuarioSelecionado._id as Id<"usuarios">,
|
usuarioId: usuarioSelecionado._id as Id<"usuarios">,
|
||||||
resetadoPorId: authStore.usuario._id as Id<"usuarios">
|
resetadoPorId: authStore.usuario._id as Id<"usuarios">,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,8 +193,19 @@
|
|||||||
<p class="text-base-content/60 mt-1">Gerenciar usuários do sistema</p>
|
<p class="text-base-content/60 mt-1">Gerenciar usuários do sistema</p>
|
||||||
</div>
|
</div>
|
||||||
<a href="/ti/usuarios/criar" class="btn btn-primary">
|
<a href="/ti/usuarios/criar" class="btn btn-primary">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-5 w-5"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M12 4v16m8-8H4"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
Criar Usuário
|
Criar Usuário
|
||||||
</a>
|
</a>
|
||||||
@@ -233,7 +254,11 @@
|
|||||||
<label class="label" for="filtro-status-select">
|
<label class="label" for="filtro-status-select">
|
||||||
<span class="label-text">Filtrar por status</span>
|
<span class="label-text">Filtrar por status</span>
|
||||||
</label>
|
</label>
|
||||||
<select id="filtro-status-select" bind:value={filtroStatus} class="select select-bordered">
|
<select
|
||||||
|
id="filtro-status-select"
|
||||||
|
bind:value={filtroStatus}
|
||||||
|
class="select select-bordered"
|
||||||
|
>
|
||||||
<option value="todos">Todos</option>
|
<option value="todos">Todos</option>
|
||||||
<option value="ativo">Ativos</option>
|
<option value="ativo">Ativos</option>
|
||||||
<option value="bloqueado">Bloqueados</option>
|
<option value="bloqueado">Bloqueados</option>
|
||||||
@@ -270,24 +295,49 @@
|
|||||||
<td>{usuario.nome}</td>
|
<td>{usuario.nome}</td>
|
||||||
<td>{usuario.email || "-"}</td>
|
<td>{usuario.email || "-"}</td>
|
||||||
<td>
|
<td>
|
||||||
{#if usuario.funcionarioId}
|
{#if usuario.funcionario?._id}
|
||||||
<div class="badge badge-success gap-2">
|
<div class="badge badge-success gap-2">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-3 w-3"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M5 13l4 4L19 7"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
Associado
|
Associado
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="badge badge-warning gap-2">
|
<div class="badge badge-warning gap-2">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-3 w-3"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
Não associado
|
Não associado
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<UserStatusBadge ativo={usuario.ativo} bloqueado={usuario.bloqueado} />
|
<UserStatusBadge
|
||||||
|
ativo={usuario.ativo}
|
||||||
|
bloqueado={usuario.bloqueado}
|
||||||
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="flex gap-2 flex-wrap">
|
<div class="flex gap-2 flex-wrap">
|
||||||
@@ -295,12 +345,25 @@
|
|||||||
<button
|
<button
|
||||||
class="btn btn-sm btn-info"
|
class="btn btn-sm btn-info"
|
||||||
onclick={() => abrirModalAssociar(usuario)}
|
onclick={() => abrirModalAssociar(usuario)}
|
||||||
title={usuario.funcionarioId ? "Alterar funcionário associado" : "Associar funcionário"}
|
title={usuario.funcionario?._id
|
||||||
|
? "Alterar funcionário associado"
|
||||||
|
: "Associar funcionário"}
|
||||||
>
|
>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-4 w-4"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
{usuario.funcionarioId ? "Alterar" : "Associar"}
|
{usuario.funcionario?._id ? "Alterar" : "Associar"}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{#if usuario.bloqueado}
|
{#if usuario.bloqueado}
|
||||||
@@ -308,8 +371,19 @@
|
|||||||
class="btn btn-sm btn-success"
|
class="btn btn-sm btn-success"
|
||||||
onclick={() => abrirModal(usuario, "desbloquear")}
|
onclick={() => abrirModal(usuario, "desbloquear")}
|
||||||
>
|
>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 11V7a4 4 0 118 0m-4 8v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2z" />
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-4 w-4"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M8 11V7a4 4 0 118 0m-4 8v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2z"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
Desbloquear
|
Desbloquear
|
||||||
</button>
|
</button>
|
||||||
@@ -318,8 +392,19 @@
|
|||||||
class="btn btn-sm btn-error"
|
class="btn btn-sm btn-error"
|
||||||
onclick={() => abrirModal(usuario, "bloquear")}
|
onclick={() => abrirModal(usuario, "bloquear")}
|
||||||
>
|
>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-4 w-4"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
Bloquear
|
Bloquear
|
||||||
</button>
|
</button>
|
||||||
@@ -329,8 +414,19 @@
|
|||||||
class="btn btn-sm btn-warning"
|
class="btn btn-sm btn-warning"
|
||||||
onclick={() => abrirModal(usuario, "reset")}
|
onclick={() => abrirModal(usuario, "reset")}
|
||||||
>
|
>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z" />
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-4 w-4"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
Reset Senha
|
Reset Senha
|
||||||
</button>
|
</button>
|
||||||
@@ -356,14 +452,17 @@
|
|||||||
<div class="modal modal-open">
|
<div class="modal modal-open">
|
||||||
<div class="modal-box">
|
<div class="modal-box">
|
||||||
<h3 class="font-bold text-lg mb-4">
|
<h3 class="font-bold text-lg mb-4">
|
||||||
{modalAcao === "bloquear" ? "Bloquear Usuário" :
|
{modalAcao === "bloquear"
|
||||||
modalAcao === "desbloquear" ? "Desbloquear Usuário" :
|
? "Bloquear Usuário"
|
||||||
"Resetar Senha"}
|
: modalAcao === "desbloquear"
|
||||||
|
? "Desbloquear Usuário"
|
||||||
|
: "Resetar Senha"}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<p class="text-base-content/80">
|
<p class="text-base-content/80">
|
||||||
<strong>Usuário:</strong> {usuarioSelecionado?.nome} ({usuarioSelecionado?.matricula})
|
<strong>Usuário:</strong>
|
||||||
|
{usuarioSelecionado?.nome} ({usuarioSelecionado?.matricula})
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -384,8 +483,18 @@
|
|||||||
|
|
||||||
{#if modalAcao === "reset"}
|
{#if modalAcao === "reset"}
|
||||||
<div class="alert alert-info mb-4">
|
<div class="alert alert-info mb-4">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="stroke-current shrink-0 w-6 h-6">
|
<svg
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
class="stroke-current shrink-0 w-6 h-6"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||||
|
></path>
|
||||||
</svg>
|
</svg>
|
||||||
<span>Uma senha temporária será gerada automaticamente.</span>
|
<span>Uma senha temporária será gerada automaticamente.</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -421,21 +530,33 @@
|
|||||||
{#if modalAssociarAberto && usuarioParaAssociar}
|
{#if modalAssociarAberto && usuarioParaAssociar}
|
||||||
<div class="modal modal-open">
|
<div class="modal modal-open">
|
||||||
<div class="modal-box max-w-2xl">
|
<div class="modal-box max-w-2xl">
|
||||||
<h3 class="font-bold text-lg mb-4">
|
<h3 class="font-bold text-lg mb-4">Associar Funcionário ao Usuário</h3>
|
||||||
Associar Funcionário ao Usuário
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<div class="mb-6">
|
<div class="mb-6">
|
||||||
<p class="text-base-content/80 mb-2">
|
<p class="text-base-content/80 mb-2">
|
||||||
<strong>Usuário:</strong> {usuarioParaAssociar.nome} ({usuarioParaAssociar.matricula})
|
<strong>Usuário:</strong>
|
||||||
|
{usuarioParaAssociar.nome} ({usuarioParaAssociar.matricula})
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{#if usuarioParaAssociar.funcionarioId}
|
{#if usuarioParaAssociar.funcionario?._id}
|
||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="stroke-current shrink-0 w-6 h-6">
|
<svg
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
class="stroke-current shrink-0 w-6 h-6"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||||
|
></path>
|
||||||
</svg>
|
</svg>
|
||||||
<span>Este usuário já possui um funcionário associado. Você pode alterá-lo ou desassociá-lo.</span>
|
<span
|
||||||
|
>Este usuário já possui um funcionário associado. Você pode
|
||||||
|
alterá-lo ou desassociá-lo.</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
@@ -463,11 +584,15 @@
|
|||||||
<div class="border rounded-lg max-h-96 overflow-y-auto">
|
<div class="border rounded-lg max-h-96 overflow-y-auto">
|
||||||
{#if funcionariosFiltrados.length === 0}
|
{#if funcionariosFiltrados.length === 0}
|
||||||
<div class="p-4 text-center text-base-content/60">
|
<div class="p-4 text-center text-base-content/60">
|
||||||
{buscaFuncionario ? "Nenhum funcionário encontrado com esse critério" : "Carregando funcionários..."}
|
{buscaFuncionario
|
||||||
|
? "Nenhum funcionário encontrado com esse critério"
|
||||||
|
: "Carregando funcionários..."}
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
{#each funcionariosFiltrados as func}
|
{#each funcionariosFiltrados as func}
|
||||||
<label class="flex items-center gap-3 p-3 hover:bg-base-200 cursor-pointer border-b last:border-b-0">
|
<label
|
||||||
|
class="flex items-center gap-3 p-3 hover:bg-base-200 cursor-pointer border-b last:border-b-0"
|
||||||
|
>
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
name="funcionario"
|
name="funcionario"
|
||||||
@@ -484,7 +609,9 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{#if func.descricaoCargo}
|
{#if func.descricaoCargo}
|
||||||
<div class="text-xs text-base-content/60">{func.descricaoCargo}</div>
|
<div class="text-xs text-base-content/60">
|
||||||
|
{func.descricaoCargo}
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
@@ -533,4 +660,3 @@
|
|||||||
<div class="modal-backdrop" onclick={fecharModalAssociar}></div>
|
<div class="modal-backdrop" onclick={fecharModalAssociar}></div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|||||||
1
bun.lock
1
bun.lock
@@ -8,6 +8,7 @@
|
|||||||
"chart.js": "^4.5.1",
|
"chart.js": "^4.5.1",
|
||||||
"lucide-svelte": "^0.548.0",
|
"lucide-svelte": "^0.548.0",
|
||||||
"svelte-chartjs": "^3.1.5",
|
"svelte-chartjs": "^3.1.5",
|
||||||
|
"svelte-sonner": "^1.0.5",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "^2.3.2",
|
"@biomejs/biome": "^2.3.2",
|
||||||
|
|||||||
@@ -24,7 +24,8 @@
|
|||||||
"@tanstack/svelte-form": "^1.23.8",
|
"@tanstack/svelte-form": "^1.23.8",
|
||||||
"chart.js": "^4.5.1",
|
"chart.js": "^4.5.1",
|
||||||
"lucide-svelte": "^0.548.0",
|
"lucide-svelte": "^0.548.0",
|
||||||
"svelte-chartjs": "^3.1.5"
|
"svelte-chartjs": "^3.1.5",
|
||||||
|
"svelte-sonner": "^1.0.5"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@rollup/rollup-win32-x64-msvc": "^4.52.5"
|
"@rollup/rollup-win32-x64-msvc": "^4.52.5"
|
||||||
|
|||||||
@@ -24,7 +24,9 @@ export const listarTodas = query({
|
|||||||
// Buscar time do funcionário
|
// Buscar time do funcionário
|
||||||
const membroTime = await ctx.db
|
const membroTime = await ctx.db
|
||||||
.query("timesMembros")
|
.query("timesMembros")
|
||||||
.withIndex("by_funcionario", (q) => q.eq("funcionarioId", s.funcionarioId))
|
.withIndex("by_funcionario", (q) =>
|
||||||
|
q.eq("funcionarioId", s.funcionarioId)
|
||||||
|
)
|
||||||
.filter((q) => q.eq(q.field("ativo"), true))
|
.filter((q) => q.eq(q.field("ativo"), true))
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
@@ -41,7 +43,9 @@ export const listarTodas = query({
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
return solicitacoesComDetalhes.sort((a, b) => b._creationTime - a._creationTime);
|
return solicitacoesComDetalhes.sort(
|
||||||
|
(a, b) => b._creationTime - a._creationTime
|
||||||
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -52,7 +56,9 @@ export const listarMinhasSolicitacoes = query({
|
|||||||
handler: async (ctx, args) => {
|
handler: async (ctx, args) => {
|
||||||
return await ctx.db
|
return await ctx.db
|
||||||
.query("solicitacoesFerias")
|
.query("solicitacoesFerias")
|
||||||
.withIndex("by_funcionario", (q) => q.eq("funcionarioId", args.funcionarioId))
|
.withIndex("by_funcionario", (q) =>
|
||||||
|
q.eq("funcionarioId", args.funcionarioId)
|
||||||
|
)
|
||||||
.order("desc")
|
.order("desc")
|
||||||
.collect();
|
.collect();
|
||||||
},
|
},
|
||||||
@@ -76,14 +82,18 @@ export const listarSolicitacoesSubordinados = query({
|
|||||||
// Buscar membros do time
|
// Buscar membros do time
|
||||||
const membros = await ctx.db
|
const membros = await ctx.db
|
||||||
.query("timesMembros")
|
.query("timesMembros")
|
||||||
.withIndex("by_time_and_ativo", (q) => q.eq("timeId", time._id).eq("ativo", true))
|
.withIndex("by_time_and_ativo", (q) =>
|
||||||
|
q.eq("timeId", time._id).eq("ativo", true)
|
||||||
|
)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// Buscar solicitações de cada membro
|
// Buscar solicitações de cada membro
|
||||||
for (const membro of membros) {
|
for (const membro of membros) {
|
||||||
const solic = await ctx.db
|
const solic = await ctx.db
|
||||||
.query("solicitacoesFerias")
|
.query("solicitacoesFerias")
|
||||||
.withIndex("by_funcionario", (q) => q.eq("funcionarioId", membro.funcionarioId))
|
.withIndex("by_funcionario", (q) =>
|
||||||
|
q.eq("funcionarioId", membro.funcionarioId)
|
||||||
|
)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// Adicionar info do funcionário
|
// Adicionar info do funcionário
|
||||||
@@ -157,7 +167,9 @@ export const criarSolicitacao = mutation({
|
|||||||
// Buscar usuário que está criando (pode não ser o próprio funcionário)
|
// Buscar usuário que está criando (pode não ser o próprio funcionário)
|
||||||
const usuario = await ctx.db
|
const usuario = await ctx.db
|
||||||
.query("usuarios")
|
.query("usuarios")
|
||||||
.withIndex("by_funcionarioId", (q) => q.eq("funcionarioId", args.funcionarioId))
|
.withIndex("by_funcionarioId", (q) =>
|
||||||
|
q.eq("funcionarioId", args.funcionarioId)
|
||||||
|
)
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
const solicitacaoId = await ctx.db.insert("solicitacoesFerias", {
|
const solicitacaoId = await ctx.db.insert("solicitacoesFerias", {
|
||||||
@@ -166,11 +178,13 @@ export const criarSolicitacao = mutation({
|
|||||||
status: "aguardando_aprovacao",
|
status: "aguardando_aprovacao",
|
||||||
periodos: args.periodos,
|
periodos: args.periodos,
|
||||||
observacao: args.observacao,
|
observacao: args.observacao,
|
||||||
historicoAlteracoes: [{
|
historicoAlteracoes: [
|
||||||
|
{
|
||||||
data: Date.now(),
|
data: Date.now(),
|
||||||
usuarioId: usuario?._id || funcionario.gestorId!,
|
usuarioId: usuario?._id || funcionario.gestorId!,
|
||||||
acao: "Solicitação criada",
|
acao: "Solicitação criada",
|
||||||
}],
|
},
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
// Notificar gestor
|
// Notificar gestor
|
||||||
@@ -228,7 +242,9 @@ export const aprovar = mutation({
|
|||||||
if (funcionario) {
|
if (funcionario) {
|
||||||
const usuario = await ctx.db
|
const usuario = await ctx.db
|
||||||
.query("usuarios")
|
.query("usuarios")
|
||||||
.withIndex("by_funcionarioId", (q) => q.eq("funcionarioId", funcionario._id))
|
.withIndex("by_funcionarioId", (q) =>
|
||||||
|
q.eq("funcionarioId", funcionario._id)
|
||||||
|
)
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
if (usuario) {
|
if (usuario) {
|
||||||
@@ -288,7 +304,9 @@ export const reprovar = mutation({
|
|||||||
if (funcionario) {
|
if (funcionario) {
|
||||||
const usuario = await ctx.db
|
const usuario = await ctx.db
|
||||||
.query("usuarios")
|
.query("usuarios")
|
||||||
.withIndex("by_funcionarioId", (q) => q.eq("funcionarioId", funcionario._id))
|
.withIndex("by_funcionarioId", (q) =>
|
||||||
|
q.eq("funcionarioId", funcionario._id)
|
||||||
|
)
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
if (usuario) {
|
if (usuario) {
|
||||||
@@ -370,7 +388,9 @@ export const ajustarEAprovar = mutation({
|
|||||||
if (funcionario) {
|
if (funcionario) {
|
||||||
const usuario = await ctx.db
|
const usuario = await ctx.db
|
||||||
.query("usuarios")
|
.query("usuarios")
|
||||||
.withIndex("by_funcionarioId", (q) => q.eq("funcionarioId", funcionario._id))
|
.withIndex("by_funcionarioId", (q) =>
|
||||||
|
q.eq("funcionarioId", funcionario._id)
|
||||||
|
)
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
if (usuario) {
|
if (usuario) {
|
||||||
@@ -399,20 +419,23 @@ export const verificarStatusFerias = query({
|
|||||||
const solicitacoesAprovadas = await ctx.db
|
const solicitacoesAprovadas = await ctx.db
|
||||||
.query("solicitacoesFerias")
|
.query("solicitacoesFerias")
|
||||||
.withIndex("by_funcionario_and_status", (q) =>
|
.withIndex("by_funcionario_and_status", (q) =>
|
||||||
q.eq("funcionarioId", args.funcionarioId)
|
q.eq("funcionarioId", args.funcionarioId).eq("status", "aprovado")
|
||||||
.eq("status", "aprovado")
|
|
||||||
)
|
)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
const solicitacoesAjustadas = await ctx.db
|
const solicitacoesAjustadas = await ctx.db
|
||||||
.query("solicitacoesFerias")
|
.query("solicitacoesFerias")
|
||||||
.withIndex("by_funcionario_and_status", (q) =>
|
.withIndex("by_funcionario_and_status", (q) =>
|
||||||
q.eq("funcionarioId", args.funcionarioId)
|
q
|
||||||
|
.eq("funcionarioId", args.funcionarioId)
|
||||||
.eq("status", "data_ajustada_aprovada")
|
.eq("status", "data_ajustada_aprovada")
|
||||||
)
|
)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
const todasSolicitacoes = [...solicitacoesAprovadas, ...solicitacoesAjustadas];
|
const todasSolicitacoes = [
|
||||||
|
...solicitacoesAprovadas,
|
||||||
|
...solicitacoesAjustadas,
|
||||||
|
];
|
||||||
|
|
||||||
for (const solicitacao of todasSolicitacoes) {
|
for (const solicitacao of todasSolicitacoes) {
|
||||||
for (const periodo of solicitacao.periodos) {
|
for (const periodo of solicitacao.periodos) {
|
||||||
@@ -434,7 +457,6 @@ export const verificarStatusFerias = query({
|
|||||||
// Query: Obter notificações não lidas
|
// Query: Obter notificações não lidas
|
||||||
export const obterNotificacoesNaoLidas = query({
|
export const obterNotificacoesNaoLidas = query({
|
||||||
args: { usuarioId: v.id("usuarios") },
|
args: { usuarioId: v.id("usuarios") },
|
||||||
returns: v.array(v.any()),
|
|
||||||
handler: async (ctx, args) => {
|
handler: async (ctx, args) => {
|
||||||
return await ctx.db
|
return await ctx.db
|
||||||
.query("notificacoesFerias")
|
.query("notificacoesFerias")
|
||||||
@@ -469,20 +491,21 @@ export const atualizarStatusTodosFuncionarios = internalMutation({
|
|||||||
const solicitacoesAprovadas = await ctx.db
|
const solicitacoesAprovadas = await ctx.db
|
||||||
.query("solicitacoesFerias")
|
.query("solicitacoesFerias")
|
||||||
.withIndex("by_funcionario_and_status", (q) =>
|
.withIndex("by_funcionario_and_status", (q) =>
|
||||||
q.eq("funcionarioId", func._id)
|
q.eq("funcionarioId", func._id).eq("status", "aprovado")
|
||||||
.eq("status", "aprovado")
|
|
||||||
)
|
)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
const solicitacoesAjustadas = await ctx.db
|
const solicitacoesAjustadas = await ctx.db
|
||||||
.query("solicitacoesFerias")
|
.query("solicitacoesFerias")
|
||||||
.withIndex("by_funcionario_and_status", (q) =>
|
.withIndex("by_funcionario_and_status", (q) =>
|
||||||
q.eq("funcionarioId", func._id)
|
q.eq("funcionarioId", func._id).eq("status", "data_ajustada_aprovada")
|
||||||
.eq("status", "data_ajustada_aprovada")
|
|
||||||
)
|
)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
const todasSolicitacoes = [...solicitacoesAprovadas, ...solicitacoesAjustadas];
|
const todasSolicitacoes = [
|
||||||
|
...solicitacoesAprovadas,
|
||||||
|
...solicitacoesAjustadas,
|
||||||
|
];
|
||||||
|
|
||||||
let emFerias = false;
|
let emFerias = false;
|
||||||
for (const solicitacao of todasSolicitacoes) {
|
for (const solicitacao of todasSolicitacoes) {
|
||||||
@@ -510,4 +533,3 @@ export const atualizarStatusTodosFuncionarios = internalMutation({
|
|||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ const aposentadoValidator = v.optional(
|
|||||||
|
|
||||||
export const getAll = query({
|
export const getAll = query({
|
||||||
args: {},
|
args: {},
|
||||||
returns: v.array(v.any()),
|
|
||||||
handler: async (ctx) => {
|
handler: async (ctx) => {
|
||||||
// Autorização: listar funcionários
|
// Autorização: listar funcionários
|
||||||
await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, {
|
await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, {
|
||||||
@@ -47,7 +46,7 @@ export const getAll = query({
|
|||||||
});
|
});
|
||||||
const funcionarios = await ctx.db.query("funcionarios").collect();
|
const funcionarios = await ctx.db.query("funcionarios").collect();
|
||||||
// Retornar apenas os campos necessários para listagem
|
// Retornar apenas os campos necessários para listagem
|
||||||
return funcionarios.map((f: any) => ({
|
return funcionarios.map((f) => ({
|
||||||
_id: f._id,
|
_id: f._id,
|
||||||
_creationTime: f._creationTime,
|
_creationTime: f._creationTime,
|
||||||
nome: f.nome,
|
nome: f.nome,
|
||||||
@@ -65,6 +64,7 @@ export const getAll = query({
|
|||||||
simboloTipo: f.simboloTipo,
|
simboloTipo: f.simboloTipo,
|
||||||
admissaoData: f.admissaoData,
|
admissaoData: f.admissaoData,
|
||||||
desligamentoData: f.desligamentoData,
|
desligamentoData: f.desligamentoData,
|
||||||
|
descricaoCargo: f.descricaoCargo,
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -199,7 +199,9 @@ export const create = mutation({
|
|||||||
.withIndex("by_matricula", (q) => q.eq("matricula", args.matricula))
|
.withIndex("by_matricula", (q) => q.eq("matricula", args.matricula))
|
||||||
.unique();
|
.unique();
|
||||||
if (matriculaExists) {
|
if (matriculaExists) {
|
||||||
throw new Error("Já existe um funcionário com esta matrícula. Por favor, use outra ou deixe em branco.");
|
throw new Error(
|
||||||
|
"Já existe um funcionário com esta matrícula. Por favor, use outra ou deixe em branco."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -326,7 +328,9 @@ export const update = mutation({
|
|||||||
.withIndex("by_matricula", (q) => q.eq("matricula", args.matricula))
|
.withIndex("by_matricula", (q) => q.eq("matricula", args.matricula))
|
||||||
.unique();
|
.unique();
|
||||||
if (matriculaExists && matriculaExists._id !== args.id) {
|
if (matriculaExists && matriculaExists._id !== args.id) {
|
||||||
throw new Error("Já existe um funcionário com esta matrícula. Por favor, use outra ou deixe em branco.");
|
throw new Error(
|
||||||
|
"Já existe um funcionário com esta matrícula. Por favor, use outra ou deixe em branco."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user