feat: add UserAvatar component to display employee profile pictures in various HR pages, enhancing visual representation of employee data
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
import ErrorModal from '$lib/components/ErrorModal.svelte';
|
||||
import CalendarioAfastamentos from '$lib/components/CalendarioAfastamentos.svelte';
|
||||
import AreaChart from '$lib/components/ti/charts/AreaChart.svelte';
|
||||
import UserAvatar from '$lib/components/chat/UserAvatar.svelte';
|
||||
import type { Id } from '@sgse-app/backend/convex/_generated/dataModel';
|
||||
import jsPDF from 'jspdf';
|
||||
import autoTable from 'jspdf-autotable';
|
||||
@@ -1578,7 +1579,16 @@
|
||||
<tbody>
|
||||
{#each registrosFiltrados.atestados as atestado}
|
||||
<tr>
|
||||
<td>{atestado.funcionario?.nome || '-'}</td>
|
||||
<td>
|
||||
<div class="flex items-center gap-3">
|
||||
<UserAvatar
|
||||
fotoPerfilUrl={atestado.fotoPerfilUrl}
|
||||
nome={atestado.funcionario?.nome || 'Funcionário'}
|
||||
size="sm"
|
||||
/>
|
||||
<span>{atestado.funcionario?.nome || '-'}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<span
|
||||
class="badge {atestado.tipo === 'atestado_medico'
|
||||
@@ -1651,7 +1661,16 @@
|
||||
{/each}
|
||||
{#each registrosFiltrados.licencas as licenca}
|
||||
<tr>
|
||||
<td>{licenca.funcionario?.nome || '-'}</td>
|
||||
<td>
|
||||
<div class="flex items-center gap-3">
|
||||
<UserAvatar
|
||||
fotoPerfilUrl={licenca.fotoPerfilUrl}
|
||||
nome={licenca.funcionario?.nome || 'Funcionário'}
|
||||
size="sm"
|
||||
/>
|
||||
<span>{licenca.funcionario?.nome || '-'}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<span
|
||||
class="badge {licenca.tipo === 'maternidade'
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import { Clock, Plus, X, Trash2, AlertTriangle } from 'lucide-svelte';
|
||||
import type { Id } from '@sgse-app/backend/convex/_generated/dataModel';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import UserAvatar from '$lib/components/chat/UserAvatar.svelte';
|
||||
|
||||
const client = useConvexClient();
|
||||
|
||||
@@ -43,7 +44,7 @@
|
||||
|
||||
// Lista de funcionários do time
|
||||
const funcionarios = $derived.by(() => {
|
||||
const funcs: Array<{ _id: Id<'funcionarios'>; nome: string; matricula?: string }> = [];
|
||||
const funcs: Array<{ _id: Id<'funcionarios'>; nome: string; matricula?: string; fotoPerfilUrl?: string | null }> = [];
|
||||
for (const time of subordinados) {
|
||||
for (const membro of time.membros) {
|
||||
if (membro.funcionario && !funcs.find((f) => f._id === membro.funcionario._id)) {
|
||||
@@ -51,6 +52,7 @@
|
||||
_id: membro.funcionario._id,
|
||||
nome: membro.funcionario.nome,
|
||||
matricula: membro.funcionario.matricula,
|
||||
fotoPerfilUrl: membro.funcionario.fotoPerfilUrl,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -199,12 +201,19 @@
|
||||
<div class="max-h-60 overflow-y-auto border border-base-300 rounded-lg p-4 space-y-2">
|
||||
{#each funcionarios as funcionario}
|
||||
<label class="flex items-center justify-between p-3 rounded-lg hover:bg-base-200 transition-colors cursor-pointer">
|
||||
<span class="label-text font-medium">
|
||||
{funcionario.nome}
|
||||
{#if funcionario.matricula}
|
||||
<span class="text-base-content/60 ml-2">({funcionario.matricula})</span>
|
||||
{/if}
|
||||
</span>
|
||||
<div class="flex items-center gap-3">
|
||||
<UserAvatar
|
||||
fotoPerfilUrl={funcionario.fotoPerfilUrl}
|
||||
nome={funcionario.nome}
|
||||
size="sm"
|
||||
/>
|
||||
<span class="label-text font-medium">
|
||||
{funcionario.nome}
|
||||
{#if funcionario.matricula}
|
||||
<span class="text-base-content/60 ml-2">({funcionario.matricula})</span>
|
||||
{/if}
|
||||
</span>
|
||||
</div>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox checkbox-primary"
|
||||
@@ -319,13 +328,21 @@
|
||||
{#each dispensas as dispensa}
|
||||
<tr>
|
||||
<td>
|
||||
{dispensa.funcionario?.nome || '-'}
|
||||
{#if dispensa.funcionario?.matricula}
|
||||
<br />
|
||||
<span class="text-sm text-base-content/70">
|
||||
Mat: {dispensa.funcionario.matricula}
|
||||
</span>
|
||||
{/if}
|
||||
<div class="flex items-center gap-3">
|
||||
<UserAvatar
|
||||
fotoPerfilUrl={dispensa.fotoPerfilUrl}
|
||||
nome={dispensa.funcionario?.nome || 'Funcionário'}
|
||||
size="sm"
|
||||
/>
|
||||
<div>
|
||||
<div class="font-medium">{dispensa.funcionario?.nome || '-'}</div>
|
||||
{#if dispensa.funcionario?.matricula}
|
||||
<span class="text-sm text-base-content/70">
|
||||
Mat: {dispensa.funcionario.matricula}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="text-sm">
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import type { Id } from '@sgse-app/backend/convex/_generated/dataModel';
|
||||
import { formatarHoraPonto, getTipoRegistroLabel } from '$lib/utils/ponto';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import UserAvatar from '$lib/components/chat/UserAvatar.svelte';
|
||||
|
||||
const client = useConvexClient();
|
||||
|
||||
@@ -156,7 +157,7 @@
|
||||
|
||||
// Lista de funcionários do time
|
||||
const funcionarios = $derived.by(() => {
|
||||
const funcs: Array<{ _id: Id<'funcionarios'>; nome: string; matricula?: string }> = [];
|
||||
const funcs: Array<{ _id: Id<'funcionarios'>; nome: string; matricula?: string; fotoPerfilUrl?: string | null }> = [];
|
||||
for (const time of subordinados) {
|
||||
for (const membro of time.membros) {
|
||||
if (membro.funcionario && !funcs.find((f) => f._id === membro.funcionario._id)) {
|
||||
@@ -164,6 +165,7 @@
|
||||
_id: membro.funcionario._id,
|
||||
nome: membro.funcionario.nome,
|
||||
matricula: membro.funcionario.matricula,
|
||||
fotoPerfilUrl: membro.funcionario.fotoPerfilUrl,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -873,13 +875,21 @@
|
||||
</td>
|
||||
{#if !funcionarioSelecionado}
|
||||
<td>
|
||||
{homologacao.funcionario?.nome || '-'}
|
||||
{#if homologacao.funcionario?.matricula}
|
||||
<br />
|
||||
<span class="text-xs text-base-content/70">
|
||||
Mat: {homologacao.funcionario.matricula}
|
||||
</span>
|
||||
{/if}
|
||||
<div class="flex items-center gap-3">
|
||||
<UserAvatar
|
||||
fotoPerfilUrl={homologacao.fotoPerfilUrl}
|
||||
nome={homologacao.funcionario?.nome || 'Funcionário'}
|
||||
size="sm"
|
||||
/>
|
||||
<div>
|
||||
<div class="font-medium">{homologacao.funcionario?.nome || '-'}</div>
|
||||
{#if homologacao.funcionario?.matricula}
|
||||
<span class="text-xs text-base-content/70">
|
||||
Mat: {homologacao.funcionario.matricula}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
{/if}
|
||||
<td>
|
||||
@@ -980,14 +990,22 @@
|
||||
})}
|
||||
</div>
|
||||
<div>
|
||||
<span class="font-medium">Funcionário:</span>{' '}
|
||||
{homologacaoSelecionada.funcionario?.nome || '-'}
|
||||
{#if homologacaoSelecionada.funcionario?.matricula}
|
||||
<br />
|
||||
<span class="text-xs text-base-content/70">
|
||||
Mat: {homologacaoSelecionada.funcionario.matricula}
|
||||
</span>
|
||||
{/if}
|
||||
<span class="font-medium">Funcionário:</span>
|
||||
<div class="flex items-center gap-3 mt-2">
|
||||
<UserAvatar
|
||||
fotoPerfilUrl={homologacaoSelecionada.fotoPerfilUrl}
|
||||
nome={homologacaoSelecionada.funcionario?.nome || 'Funcionário'}
|
||||
size="sm"
|
||||
/>
|
||||
<div>
|
||||
<div class="font-medium">{homologacaoSelecionada.funcionario?.nome || '-'}</div>
|
||||
{#if homologacaoSelecionada.funcionario?.matricula}
|
||||
<span class="text-xs text-base-content/70">
|
||||
Mat: {homologacaoSelecionada.funcionario.matricula}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span class="font-medium">Gestor:</span>{' '}
|
||||
|
||||
@@ -45,9 +45,23 @@ export const listarTodos = query({
|
||||
try {
|
||||
const funcionario = await ctx.db.get(a.funcionarioId);
|
||||
const criadoPor = await ctx.db.get(a.criadoPor);
|
||||
|
||||
// Buscar foto do perfil do funcionário através do usuário associado
|
||||
let fotoPerfilUrl: string | null = null;
|
||||
if (funcionario) {
|
||||
const usuario = await ctx.db
|
||||
.query('usuarios')
|
||||
.withIndex('by_funcionarioId', (q) => q.eq('funcionarioId', funcionario._id))
|
||||
.first();
|
||||
if (usuario?.fotoPerfil) {
|
||||
fotoPerfilUrl = await ctx.storage.getUrl(usuario.fotoPerfil);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...a,
|
||||
funcionario,
|
||||
fotoPerfilUrl,
|
||||
criadoPorNome: criadoPor?.nome || 'Sistema',
|
||||
dias: calcularDias(a.dataInicio, a.dataFim),
|
||||
status: new Date(a.dataFim) >= new Date() ? 'ativo' : 'finalizado'
|
||||
@@ -57,6 +71,7 @@ export const listarTodos = query({
|
||||
return {
|
||||
...a,
|
||||
funcionario: null,
|
||||
fotoPerfilUrl: null,
|
||||
criadoPorNome: 'Sistema',
|
||||
dias: calcularDias(a.dataInicio, a.dataFim),
|
||||
status: new Date(a.dataFim) >= new Date() ? 'ativo' : 'finalizado'
|
||||
@@ -73,9 +88,23 @@ export const listarTodos = query({
|
||||
const licencaOriginal = l.licencaOriginalId
|
||||
? await ctx.db.get(l.licencaOriginalId)
|
||||
: null;
|
||||
|
||||
// Buscar foto do perfil do funcionário através do usuário associado
|
||||
let fotoPerfilUrl: string | null = null;
|
||||
if (funcionario) {
|
||||
const usuario = await ctx.db
|
||||
.query('usuarios')
|
||||
.withIndex('by_funcionarioId', (q) => q.eq('funcionarioId', funcionario._id))
|
||||
.first();
|
||||
if (usuario?.fotoPerfil) {
|
||||
fotoPerfilUrl = await ctx.storage.getUrl(usuario.fotoPerfil);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...l,
|
||||
funcionario,
|
||||
fotoPerfilUrl,
|
||||
criadoPorNome: criadoPor?.nome || 'Sistema',
|
||||
licencaOriginal,
|
||||
dias: calcularDias(l.dataInicio, l.dataFim),
|
||||
@@ -86,6 +115,7 @@ export const listarTodos = query({
|
||||
return {
|
||||
...l,
|
||||
funcionario: null,
|
||||
fotoPerfilUrl: null,
|
||||
criadoPorNome: 'Sistema',
|
||||
licencaOriginal: null,
|
||||
dias: calcularDias(l.dataInicio, l.dataFim),
|
||||
|
||||
@@ -985,8 +985,28 @@ export const listarRegistrosPeriodo = query({
|
||||
|
||||
console.log('[listarRegistrosPeriodo] Total de registros a retornar:', registrosFiltrados.length);
|
||||
|
||||
// Buscar fotos de perfil dos funcionários
|
||||
const funcionariosComFoto = await Promise.all(
|
||||
funcionarios.map(async (funcionario) => {
|
||||
if (!funcionario) return { funcionario: null, fotoPerfilUrl: null };
|
||||
|
||||
let fotoPerfilUrl: string | null = null;
|
||||
const usuario = await ctx.db
|
||||
.query('usuarios')
|
||||
.withIndex('by_funcionarioId', (q) => q.eq('funcionarioId', funcionario._id))
|
||||
.first();
|
||||
if (usuario?.fotoPerfil) {
|
||||
fotoPerfilUrl = await ctx.storage.getUrl(usuario.fotoPerfil);
|
||||
}
|
||||
|
||||
return { funcionario, fotoPerfilUrl };
|
||||
})
|
||||
);
|
||||
|
||||
return registrosFiltrados.map((registro) => {
|
||||
const funcionario = funcionarios.find((f) => f?._id === registro.funcionarioId);
|
||||
const funcionarioComFoto = funcionariosComFoto.find((f) => f.funcionario?._id === registro.funcionarioId);
|
||||
const funcionario = funcionarioComFoto?.funcionario;
|
||||
const fotoPerfilUrl = funcionarioComFoto?.fotoPerfilUrl || null;
|
||||
const chave = `${registro.funcionarioId}-${registro.data}`;
|
||||
const saldoMinutos = saldosPorDataFuncionario[chave] || 0;
|
||||
const horas = Math.floor(Math.abs(saldoMinutos) / 60);
|
||||
@@ -1002,6 +1022,7 @@ export const listarRegistrosPeriodo = query({
|
||||
descricaoCargo: funcionario.descricaoCargo,
|
||||
}
|
||||
: null,
|
||||
fotoPerfilUrl,
|
||||
saldoDiario: {
|
||||
saldoMinutos,
|
||||
horas,
|
||||
@@ -1618,6 +1639,18 @@ export const listarHomologacoes = query({
|
||||
const gestor = await ctx.db.get(h.gestorId);
|
||||
const registro = h.registroId ? await ctx.db.get(h.registroId) : null;
|
||||
|
||||
// Buscar foto do perfil do funcionário através do usuário associado
|
||||
let fotoPerfilUrl: string | null = null;
|
||||
if (funcionario) {
|
||||
const usuario = await ctx.db
|
||||
.query('usuarios')
|
||||
.withIndex('by_funcionarioId', (q) => q.eq('funcionarioId', funcionario._id))
|
||||
.first();
|
||||
if (usuario?.fotoPerfil) {
|
||||
fotoPerfilUrl = await ctx.storage.getUrl(usuario.fotoPerfil);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...h,
|
||||
funcionario: funcionario
|
||||
@@ -1626,6 +1659,7 @@ export const listarHomologacoes = query({
|
||||
matricula: funcionario.matricula,
|
||||
}
|
||||
: null,
|
||||
fotoPerfilUrl,
|
||||
gestor: gestor
|
||||
? {
|
||||
nome: gestor.nome,
|
||||
@@ -1862,6 +1896,18 @@ export const listarDispensas = query({
|
||||
const funcionario = await ctx.db.get(d.funcionarioId);
|
||||
const gestor = await ctx.db.get(d.gestorId);
|
||||
|
||||
// Buscar foto do perfil do funcionário através do usuário associado
|
||||
let fotoPerfilUrl: string | null = null;
|
||||
if (funcionario) {
|
||||
const usuario = await ctx.db
|
||||
.query('usuarios')
|
||||
.withIndex('by_funcionarioId', (q) => q.eq('funcionarioId', funcionario._id))
|
||||
.first();
|
||||
if (usuario?.fotoPerfil) {
|
||||
fotoPerfilUrl = await ctx.storage.getUrl(usuario.fotoPerfil);
|
||||
}
|
||||
}
|
||||
|
||||
// Verificar se expirou (se não for isento)
|
||||
let expirada = false;
|
||||
if (!d.isento) {
|
||||
@@ -1880,6 +1926,7 @@ export const listarDispensas = query({
|
||||
matricula: funcionario.matricula,
|
||||
}
|
||||
: null,
|
||||
fotoPerfilUrl,
|
||||
gestor: gestor
|
||||
? {
|
||||
nome: gestor.nome,
|
||||
|
||||
@@ -163,9 +163,22 @@ export const listarSubordinadosDoGestorAtual = query({
|
||||
for (const rel of membrosRelacoes) {
|
||||
const funcionario = await ctx.db.get(rel.funcionarioId);
|
||||
if (funcionario) {
|
||||
// Buscar foto do perfil do funcionário através do usuário associado
|
||||
let fotoPerfilUrl: string | null = null;
|
||||
const usuario = await ctx.db
|
||||
.query("usuarios")
|
||||
.withIndex("by_funcionarioId", (q) => q.eq("funcionarioId", funcionario._id))
|
||||
.first();
|
||||
if (usuario?.fotoPerfil) {
|
||||
fotoPerfilUrl = await ctx.storage.getUrl(usuario.fotoPerfil);
|
||||
}
|
||||
|
||||
membros.push({
|
||||
relacaoId: rel._id,
|
||||
funcionario,
|
||||
funcionario: {
|
||||
...funcionario,
|
||||
fotoPerfilUrl,
|
||||
},
|
||||
dataEntrada: rel.dataEntrada,
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user