Fix usuarios page #6
@@ -99,6 +99,18 @@
|
|||||||
let mostrarFormularioCurso = $state(false);
|
let mostrarFormularioCurso = $state(false);
|
||||||
let cursoAtual = $state({ descricao: "", data: "", arquivo: null as File | null });
|
let cursoAtual = $state({ descricao: "", data: "", arquivo: null as File | null });
|
||||||
|
|
||||||
|
// Dependentes
|
||||||
|
let dependentes = $state<Array<{ id: string; parentesco: string; nome: string; cpf: string; nascimento: string; documentoId?: string }>>([]);
|
||||||
|
let mostrarFormularioDependente = $state(false);
|
||||||
|
let dependenteAtual = $state<{ parentesco: string; nome: string; cpf: string; nascimento: string; arquivo: File | null; documentoId?: string }>({
|
||||||
|
parentesco: "",
|
||||||
|
nome: "",
|
||||||
|
cpf: "",
|
||||||
|
nascimento: "",
|
||||||
|
arquivo: null,
|
||||||
|
documentoId: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
function adicionarCurso() {
|
function adicionarCurso() {
|
||||||
if (!cursoAtual.descricao.trim() || !cursoAtual.data.trim()) {
|
if (!cursoAtual.descricao.trim() || !cursoAtual.data.trim()) {
|
||||||
alert("Preencha a descrição e a data do curso");
|
alert("Preencha a descrição e a data do curso");
|
||||||
@@ -129,6 +141,49 @@
|
|||||||
return result.storageId;
|
return result.storageId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function uploadDocumentoDependente(file: File): Promise<string> {
|
||||||
|
const uploadUrl = await client.mutation(api.documentos.generateUploadUrl, {});
|
||||||
|
const response = await fetch(uploadUrl, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": file.type },
|
||||||
|
body: file,
|
||||||
|
});
|
||||||
|
const result = await response.json();
|
||||||
|
return result.storageId as string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function adicionarDependente() {
|
||||||
|
if (dependentes.length >= 10) {
|
||||||
|
alert("Limite de 10 dependentes atingido");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!dependenteAtual.parentesco || !dependenteAtual.nome.trim() || !dependenteAtual.cpf.trim() || !dependenteAtual.nascimento.trim()) {
|
||||||
|
alert("Preencha Parentesco, Nome, CPF e Data de Nascimento do dependente");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!validateCPF(dependenteAtual.cpf)) {
|
||||||
|
alert("CPF do dependente inválido");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!validateDate(dependenteAtual.nascimento)) {
|
||||||
|
alert("Data de nascimento do dependente inválida");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dependentes.push({
|
||||||
|
id: crypto.randomUUID(),
|
||||||
|
parentesco: dependenteAtual.parentesco,
|
||||||
|
nome: dependenteAtual.nome.trim(),
|
||||||
|
cpf: onlyDigits(dependenteAtual.cpf),
|
||||||
|
nascimento: dependenteAtual.nascimento,
|
||||||
|
documentoId: dependenteAtual.documentoId,
|
||||||
|
});
|
||||||
|
dependenteAtual = { parentesco: "", nome: "", cpf: "", nascimento: "", arquivo: null, documentoId: undefined };
|
||||||
|
}
|
||||||
|
|
||||||
|
function removerDependente(id: string) {
|
||||||
|
dependentes = dependentes.filter((d) => d.id !== id);
|
||||||
|
}
|
||||||
|
|
||||||
async function loadSimbolos() {
|
async function loadSimbolos() {
|
||||||
const list = await client.query(api.simbolos.getAll, {} as any);
|
const list = await client.query(api.simbolos.getAll, {} as any);
|
||||||
simbolos = list.map((s: any) => ({
|
simbolos = list.map((s: any) => ({
|
||||||
@@ -267,6 +322,16 @@
|
|||||||
...Object.fromEntries(
|
...Object.fromEntries(
|
||||||
Object.entries(documentosStorage).map(([key, value]) => [key, value as any])
|
Object.entries(documentosStorage).map(([key, value]) => [key, value as any])
|
||||||
),
|
),
|
||||||
|
// Dependentes (opcional)
|
||||||
|
dependentes: dependentes.length
|
||||||
|
? dependentes.map((d) => ({
|
||||||
|
parentesco: d.parentesco,
|
||||||
|
nome: d.nome,
|
||||||
|
cpf: d.cpf,
|
||||||
|
nascimento: d.nascimento,
|
||||||
|
documentoId: d.documentoId as any,
|
||||||
|
}))
|
||||||
|
: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
const novoFuncionarioId = await client.mutation(api.funcionarios.create, payload as any);
|
const novoFuncionarioId = await client.mutation(api.funcionarios.create, payload as any);
|
||||||
@@ -358,7 +423,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<!-- Formulário -->
|
<!-- Formulário -->
|
||||||
<form class="space-y-6" onsubmit={(e) => { e.preventDefault(); handleSubmit(); }}>
|
<form class="space-y-6" onsubmit={(e) => { e.preventDefault(); }}>
|
||||||
|
|
||||||
<!-- Card 1: Informações Pessoais -->
|
<!-- Card 1: Informações Pessoais -->
|
||||||
<div class="card bg-base-100 shadow-xl">
|
<div class="card bg-base-100 shadow-xl">
|
||||||
@@ -1060,6 +1125,96 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Card 4.5: Dependentes -->
|
||||||
|
<div class="card bg-base-100 shadow-xl">
|
||||||
|
<div class="card-body space-y-4">
|
||||||
|
<h2 class="card-title text-xl border-b pb-3">
|
||||||
|
<svg 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="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2a3 3 0 00-5.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2a3 3 0 015.356-1.857M15 7a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||||
|
</svg>
|
||||||
|
Dependentes
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
{#if dependentes.length > 0}
|
||||||
|
<div class="space-y-2">
|
||||||
|
<h3 class="font-semibold text-sm">Dependentes adicionados ({dependentes.length}/10)</h3>
|
||||||
|
{#each dependentes as dep}
|
||||||
|
<div class="flex items-center gap-3 p-3 bg-base-200 rounded-lg">
|
||||||
|
<div class="flex-1 text-sm">
|
||||||
|
<p class="font-semibold">{dep.nome} — {dep.parentesco}</p>
|
||||||
|
<p class="text-xs text-base-content/70">CPF: {dep.cpf} • Nasc.: {dep.nascimento}</p>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-sm btn-error btn-square" aria-label="Remover dependente" onclick={() => removerDependente(dep.id)}>
|
||||||
|
<svg 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="M6 18L18 6M6 6l12 12" /></svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if dependentes.length < 10}
|
||||||
|
<div class="collapse collapse-arrow border border-base-300 bg-base-200">
|
||||||
|
<input type="checkbox" bind:checked={mostrarFormularioDependente} />
|
||||||
|
<div class="collapse-title font-medium">Adicionar Dependente</div>
|
||||||
|
<div class="collapse-content">
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 pt-2">
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label" for="dep-parentesco">
|
||||||
|
<span class="label-text font-medium">Grau de Parentesco</span>
|
||||||
|
</label>
|
||||||
|
<select id="dep-parentesco" class="select select-bordered w-full" bind:value={dependenteAtual.parentesco}>
|
||||||
|
<option value="">Selecione...</option>
|
||||||
|
<option value="filho">Filho</option>
|
||||||
|
<option value="filha">Filha</option>
|
||||||
|
<option value="conjuge">Cônjuge</option>
|
||||||
|
<option value="outro">Outros</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-control lg:col-span-2">
|
||||||
|
<label class="label" for="dep-nome">
|
||||||
|
<span class="label-text font-medium">Nome do Dependente</span>
|
||||||
|
</label>
|
||||||
|
<input id="dep-nome" type="text" class="input input-bordered w-full" bind:value={dependenteAtual.nome} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label" for="dep-cpf">
|
||||||
|
<span class="label-text font-medium">CPF do Dependente</span>
|
||||||
|
</label>
|
||||||
|
<input id="dep-cpf" type="text" inputmode="numeric" class="input input-bordered w-full" value={dependenteAtual.cpf}
|
||||||
|
oninput={(e) => { const t = e.target as HTMLInputElement; dependenteAtual.cpf = maskCPF(t.value); t.value = dependenteAtual.cpf; }} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label" for="dep-nascimento">
|
||||||
|
<span class="label-text font-medium">Data de Nascimento</span>
|
||||||
|
</label>
|
||||||
|
<input id="dep-nascimento" type="text" inputmode="numeric" placeholder="dd/mm/aaaa" class="input input-bordered w-full" value={dependenteAtual.nascimento}
|
||||||
|
oninput={(e) => { const t = e.target as HTMLInputElement; dependenteAtual.nascimento = maskDate(t.value); t.value = dependenteAtual.nascimento; }} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-control lg:col-span-3">
|
||||||
|
<label class="label" for="dep-doc">
|
||||||
|
<span class="label-text font-medium">Certidão de Nascimento/Casamento ou Outros (opcional)</span>
|
||||||
|
</label>
|
||||||
|
<input id="dep-doc" type="file" class="file-input file-input-bordered w-full" accept=".pdf,.jpg,.jpeg,.png"
|
||||||
|
onchange={async (e) => { const file = e.currentTarget.files?.[0]; dependenteAtual.arquivo = file || null; if (file) { try { dependenteAtual.documentoId = await uploadDocumentoDependente(file); } catch { alert("Falha no upload do documento do dependente"); } } }} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="lg:col-span-3">
|
||||||
|
<button type="button" class="btn btn-primary btn-sm gap-2" onclick={adicionarDependente}>
|
||||||
|
<svg 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 4v16m8-8H4" /></svg>
|
||||||
|
Adicionar Dependente
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Card 5: Cargo e Vínculo -->
|
<!-- Card 5: Cargo e Vínculo -->
|
||||||
<div class="card bg-base-100 shadow-xl">
|
<div class="card bg-base-100 shadow-xl">
|
||||||
<div class="card-body space-y-4">
|
<div class="card-body space-y-4">
|
||||||
@@ -1345,9 +1500,10 @@
|
|||||||
Cancelar
|
Cancelar
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="button"
|
||||||
class="btn btn-primary btn-lg"
|
class="btn btn-primary btn-lg"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
|
onclick={() => (document.getElementById('confirmar_cadastro_func') as HTMLDialogElement)?.showModal()}
|
||||||
>
|
>
|
||||||
{#if loading}
|
{#if loading}
|
||||||
<span class="loading loading-spinner"></span>
|
<span class="loading loading-spinner"></span>
|
||||||
@@ -1362,5 +1518,23 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Modal de Confirmação Legal -->
|
||||||
|
<dialog id="confirmar_cadastro_func" class="modal">
|
||||||
|
<div class="modal-box max-w-3xl">
|
||||||
|
<h3 class="font-bold text-lg mb-3">Declaração de Veracidade</h3>
|
||||||
|
<div class="prose text-sm max-w-none">
|
||||||
|
<p>
|
||||||
|
Declaro, sob as penas da lei (art. 299 do Código Penal Brasileiro, que trata do crime de falsidade ideológica, e demais cominações legais aplicáveis), a veracidade e autenticidade de todas as informações e documentos por mim prestados/enviados neste ato. Tenho ciência de que a falsidade das informações implicará nas penalidades cabíveis, podendo resultar na nulidade do ato ou no dever de ressarcir eventuais valores recebidos indevidamente.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-action">
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<button type="button" class="btn" onclick={() => (document.getElementById('confirmar_cadastro_func') as HTMLDialogElement)?.close()}>Cancelar</button>
|
||||||
|
<button type="button" class="btn btn-primary" onclick={() => { (document.getElementById('confirmar_cadastro_func') as HTMLDialogElement)?.close(); handleSubmit(); }}>Ciente</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
</form>
|
</form>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -174,6 +174,23 @@ export const create = mutation({
|
|||||||
declaracaoIdoneidade: v.optional(v.id("_storage")),
|
declaracaoIdoneidade: v.optional(v.id("_storage")),
|
||||||
termoNepotismo: v.optional(v.id("_storage")),
|
termoNepotismo: v.optional(v.id("_storage")),
|
||||||
termoOpcaoRemuneracao: v.optional(v.id("_storage")),
|
termoOpcaoRemuneracao: v.optional(v.id("_storage")),
|
||||||
|
// Dependentes (opcional)
|
||||||
|
dependentes: v.optional(
|
||||||
|
v.array(
|
||||||
|
v.object({
|
||||||
|
parentesco: v.union(
|
||||||
|
v.literal("filho"),
|
||||||
|
v.literal("filha"),
|
||||||
|
v.literal("conjuge"),
|
||||||
|
v.literal("outro")
|
||||||
|
),
|
||||||
|
nome: v.string(),
|
||||||
|
cpf: v.string(),
|
||||||
|
nascimento: v.string(),
|
||||||
|
documentoId: v.optional(v.id("_storage")),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
),
|
||||||
},
|
},
|
||||||
returns: v.id("funcionarios"),
|
returns: v.id("funcionarios"),
|
||||||
handler: async (ctx, args) => {
|
handler: async (ctx, args) => {
|
||||||
@@ -303,6 +320,23 @@ export const update = mutation({
|
|||||||
declaracaoIdoneidade: v.optional(v.id("_storage")),
|
declaracaoIdoneidade: v.optional(v.id("_storage")),
|
||||||
termoNepotismo: v.optional(v.id("_storage")),
|
termoNepotismo: v.optional(v.id("_storage")),
|
||||||
termoOpcaoRemuneracao: v.optional(v.id("_storage")),
|
termoOpcaoRemuneracao: v.optional(v.id("_storage")),
|
||||||
|
// Dependentes (opcional)
|
||||||
|
dependentes: v.optional(
|
||||||
|
v.array(
|
||||||
|
v.object({
|
||||||
|
parentesco: v.union(
|
||||||
|
v.literal("filho"),
|
||||||
|
v.literal("filha"),
|
||||||
|
v.literal("conjuge"),
|
||||||
|
v.literal("outro")
|
||||||
|
),
|
||||||
|
nome: v.string(),
|
||||||
|
cpf: v.string(),
|
||||||
|
nascimento: v.string(),
|
||||||
|
documentoId: v.optional(v.id("_storage")),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
),
|
||||||
},
|
},
|
||||||
returns: v.null(),
|
returns: v.null(),
|
||||||
handler: async (ctx, args) => {
|
handler: async (ctx, args) => {
|
||||||
|
|||||||
@@ -134,6 +134,24 @@ export default defineSchema({
|
|||||||
comprovanteResidencia: v.optional(v.id("_storage")),
|
comprovanteResidencia: v.optional(v.id("_storage")),
|
||||||
comprovanteContaBradesco: v.optional(v.id("_storage")),
|
comprovanteContaBradesco: v.optional(v.id("_storage")),
|
||||||
|
|
||||||
|
// Dependentes do funcionário (uploads opcionais)
|
||||||
|
dependentes: v.optional(
|
||||||
|
v.array(
|
||||||
|
v.object({
|
||||||
|
parentesco: v.union(
|
||||||
|
v.literal("filho"),
|
||||||
|
v.literal("filha"),
|
||||||
|
v.literal("conjuge"),
|
||||||
|
v.literal("outro")
|
||||||
|
),
|
||||||
|
nome: v.string(),
|
||||||
|
cpf: v.string(),
|
||||||
|
nascimento: v.string(),
|
||||||
|
documentoId: v.optional(v.id("_storage")),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
),
|
||||||
|
|
||||||
// Declarações (Storage IDs)
|
// Declarações (Storage IDs)
|
||||||
declaracaoAcumulacaoCargo: v.optional(v.id("_storage")),
|
declaracaoAcumulacaoCargo: v.optional(v.id("_storage")),
|
||||||
declaracaoDependentesIR: v.optional(v.id("_storage")),
|
declaracaoDependentesIR: v.optional(v.id("_storage")),
|
||||||
|
|||||||
Reference in New Issue
Block a user