diff --git a/apps/web/src/app.html b/apps/web/src/app.html
index 77a5ff5..bd3affa 100644
--- a/apps/web/src/app.html
+++ b/apps/web/src/app.html
@@ -1,5 +1,5 @@
-
+
diff --git a/apps/web/src/routes/(dashboard)/+layout.svelte b/apps/web/src/routes/(dashboard)/+layout.svelte
index 62b507c..1218fb8 100644
--- a/apps/web/src/routes/(dashboard)/+layout.svelte
+++ b/apps/web/src/routes/(dashboard)/+layout.svelte
@@ -5,7 +5,7 @@
{@render children()}
diff --git a/apps/web/src/routes/(dashboard)/recursos-humanos/funcionarios/+page.svelte b/apps/web/src/routes/(dashboard)/recursos-humanos/funcionarios/+page.svelte
index e69de29..b6b02d6 100644
--- a/apps/web/src/routes/(dashboard)/recursos-humanos/funcionarios/+page.svelte
+++ b/apps/web/src/routes/(dashboard)/recursos-humanos/funcionarios/+page.svelte
@@ -0,0 +1,172 @@
+
+
+
+
+
Funcionários
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | Nome |
+ CPF |
+ Matrícula |
+ Tipo |
+ Cidade |
+ UF |
+ Ações |
+
+
+
+ {#each filtered as f}
+
+ | {f.nome} |
+ {f.cpf} |
+ {f.matricula} |
+ {f.simboloTipo} |
+ {f.cidade} |
+ {f.uf} |
+
+
+
+
+ - Editar
+
+
+
+ |
+
+ {/each}
+
+
+
+
+
+
+
diff --git a/apps/web/src/routes/(dashboard)/recursos-humanos/funcionarios/[funcionarioId]/editar/+page.svelte b/apps/web/src/routes/(dashboard)/recursos-humanos/funcionarios/[funcionarioId]/editar/+page.svelte
index 1fc15be..8209cf0 100644
--- a/apps/web/src/routes/(dashboard)/recursos-humanos/funcionarios/[funcionarioId]/editar/+page.svelte
+++ b/apps/web/src/routes/(dashboard)/recursos-humanos/funcionarios/[funcionarioId]/editar/+page.svelte
@@ -15,7 +15,39 @@
const onlyDigits = (s: string) => (s || "").replace(/\D/g, "");
const maskCPF = (v: string) => onlyDigits(v).slice(0, 11).replace(/(\d{3})(\d)/, "$1.$2").replace(/(\d{3})(\d)/, "$1.$2").replace(/(\d{3})(\d{1,2})$/, "$1-$2");
- const maskRG = (v: string) => onlyDigits(v).slice(0, 9).replace(/(\d{2})(\d)/, "$1.$2").replace(/(\d{3})(\d)/, "$1.$2").replace(/(\d{3})(\d{1})$/, "$1-$2");
+ const rgFormatByUF: Record
= {
+ RJ: [2, 3, 2, 1], SP: [2, 3, 3, 1], MG: [2, 3, 3, 1], ES: [2, 3, 3, 1],
+ PR: [2, 3, 3, 1], SC: [2, 3, 3, 1], RS: [2, 3, 3, 1], BA: [2, 3, 3, 1],
+ PE: [2, 3, 3, 1], CE: [2, 3, 3, 1], PA: [2, 3, 3, 1], AM: [2, 3, 3, 1],
+ AC: [2, 3, 3, 1], AP: [2, 3, 3, 1], AL: [2, 3, 3, 1], RN: [2, 3, 3, 1],
+ PB: [2, 3, 3, 1], MA: [2, 3, 3, 1], PI: [2, 3, 3, 1], DF: [2, 3, 3, 1],
+ GO: [2, 3, 3, 1], MT: [2, 3, 3, 1], MS: [2, 3, 3, 1], RO: [2, 3, 3, 1],
+ RR: [2, 3, 3, 1], TO: [2, 3, 3, 1],
+ };
+ function maskRGByUF(uf: string, v: string) {
+ const raw = (v || "").toUpperCase().replace(/[^0-9X]/g, "");
+ const [a, b, c, dv] = rgFormatByUF[uf?.toUpperCase()] ?? [2, 3, 3, 1];
+ const baseMax = a + b + c;
+ const baseDigits = raw.replace(/X/g, "").slice(0, baseMax);
+ const verifier = raw.slice(baseDigits.length, baseDigits.length + dv).slice(0, 1);
+ const g1 = baseDigits.slice(0, a);
+ const g2 = baseDigits.slice(a, a + b);
+ const g3 = baseDigits.slice(a + b, a + b + c);
+ let out = g1;
+ if (g2) out += `.${g2}`;
+ if (g3) out += `.${g3}`;
+ if (verifier) out += `-${verifier}`;
+ return out;
+ }
+ function padRGLeftByUF(uf: string, v: string) {
+ const raw = (v || "").toUpperCase().replace(/[^0-9X]/g, "");
+ const [a, b, c, dv] = rgFormatByUF[uf?.toUpperCase()] ?? [2, 3, 3, 1];
+ const baseMax = a + b + c;
+ let base = raw.replace(/X/g, "");
+ const verifier = raw.slice(base.length, base.length + dv).slice(0, 1);
+ if (base.length < baseMax) base = base.padStart(baseMax, "0");
+ return maskRGByUF(uf, base + (verifier || ""));
+ }
const maskCEP = (v: string) => onlyDigits(v).slice(0, 8).replace(/(\d{5})(\d{1,3})$/, "$1-$2");
const maskPhone = (v: string) => {
const d = onlyDigits(v).slice(0, 11);
@@ -45,7 +77,7 @@
nome: z.string().min(3),
matricula: z.string().min(1),
cpf: z.string().min(1).refine(isValidCPF, "CPF inválido"),
- rg: z.string().min(1).refine((v) => /^\d{2}\.\d{3}\.\d{3}-\d$/.test(maskRG(v)), "RG inválido"),
+ rg: z.string().min(1).refine((v) => /^\d+$/.test(v), "RG inválido"),
nascimento: z.string().refine(isValidDateBR, "Data inválida"),
email: z.string().email(),
telefone: z.string().min(1).refine((v) => /\(\d{2}\) \d{4,5}-\d{4}/.test(maskPhone(v)), "Telefone inválido"),
@@ -128,7 +160,7 @@
form.setFieldValue("nome", doc.nome as any);
form.setFieldValue("matricula", doc.matricula as any);
form.setFieldValue("cpf", maskCPF(doc.cpf) as any);
- form.setFieldValue("rg", maskRG(doc.rg) as any);
+ form.setFieldValue("rg", (doc.rg || "") as any);
form.setFieldValue("nascimento", doc.nascimento as any);
form.setFieldValue("email", doc.email as any);
form.setFieldValue("telefone", maskPhone(doc.telefone) as any);
@@ -141,6 +173,20 @@
form.setFieldValue("admissaoData", (doc.admissaoData ?? "") as any);
}
+ async function fillFromCEP(cepMasked: string) {
+ const cep = onlyDigits(cepMasked);
+ if (cep.length !== 8) return;
+ try {
+ const res = await fetch(`https://viacep.com.br/ws/${cep}/json/`);
+ const data = await res.json();
+ if (!data || data.erro) return;
+ const enderecoFull = [data.logradouro, data.bairro].filter(Boolean).join(", ");
+ form.setFieldValue("endereco", enderecoFull as any);
+ form.setFieldValue("cidade", (data.localidade || "") as any);
+ form.setFieldValue("uf", (data.uf || "") as any);
+ } catch {}
+ }
+
load();
@@ -177,6 +223,42 @@
+
+
+
+ {#snippet children({ name, state, handleChange })}
+
+
+ handleChange((e.target as HTMLInputElement).value)} required />
+
+ {/snippet}
+
+
-
-
-
- {#snippet children({ name, state, handleChange })}
-
-
- handleChange((e.target as HTMLInputElement).value)} required />
-
- {/snippet}
-
diff --git a/apps/web/src/routes/(dashboard)/recursos-humanos/funcionarios/cadastro/+page.svelte b/apps/web/src/routes/(dashboard)/recursos-humanos/funcionarios/cadastro/+page.svelte
index 7b6545e..31b2073 100644
--- a/apps/web/src/routes/(dashboard)/recursos-humanos/funcionarios/cadastro/+page.svelte
+++ b/apps/web/src/routes/(dashboard)/recursos-humanos/funcionarios/cadastro/+page.svelte
@@ -41,12 +41,58 @@
return d[9] === String(d1) && d[10] === String(d2);
}
- function maskRG(v: string) {
- const d = onlyDigits(v).slice(0, 9);
- return d
- .replace(/(\d{2})(\d)/, "$1.$2")
- .replace(/(\d{3})(\d)/, "$1.$2")
- .replace(/(\d{3})(\d{1})$/, "$1-$2");
+ const rgFormatByUF: Record = {
+ RJ: [2, 3, 2, 1],
+ SP: [2, 3, 3, 1],
+ MG: [2, 3, 3, 1],
+ ES: [2, 3, 3, 1],
+ PR: [2, 3, 3, 1],
+ SC: [2, 3, 3, 1],
+ RS: [2, 3, 3, 1],
+ BA: [2, 3, 3, 1],
+ PE: [2, 3, 3, 1],
+ CE: [2, 3, 3, 1],
+ PA: [2, 3, 3, 1],
+ AM: [2, 3, 3, 1],
+ AC: [2, 3, 3, 1],
+ AP: [2, 3, 3, 1],
+ AL: [2, 3, 3, 1],
+ RN: [2, 3, 3, 1],
+ PB: [2, 3, 3, 1],
+ MA: [2, 3, 3, 1],
+ PI: [2, 3, 3, 1],
+ DF: [2, 3, 3, 1],
+ GO: [2, 3, 3, 1],
+ MT: [2, 3, 3, 1],
+ MS: [2, 3, 3, 1],
+ RO: [2, 3, 3, 1],
+ RR: [2, 3, 3, 1],
+ TO: [2, 3, 3, 1],
+ };
+ function maskRGByUF(uf: string, v: string) {
+ const raw = (v || "").toUpperCase().replace(/[^0-9X]/g, "");
+ const [a, b, c, dv] = rgFormatByUF[uf?.toUpperCase()] ?? [2, 3, 3, 1];
+ const baseMax = a + b + c;
+ const baseDigits = raw.replace(/X/g, "").slice(0, baseMax);
+ const verifier = raw.slice(baseDigits.length, baseDigits.length + dv).slice(0, 1);
+ const g1 = baseDigits.slice(0, a);
+ const g2 = baseDigits.slice(a, a + b);
+ const g3 = baseDigits.slice(a + b, a + b + c);
+ let out = g1;
+ if (g2) out += `.${g2}`;
+ if (g3) out += `.${g3}`;
+ if (verifier) out += `-${verifier}`;
+ return out;
+ }
+
+ function padRGLeftByUF(uf: string, v: string) {
+ const raw = (v || "").toUpperCase().replace(/[^0-9X]/g, "");
+ const [a, b, c, dv] = rgFormatByUF[uf?.toUpperCase()] ?? [2, 3, 3, 1];
+ const baseMax = a + b + c;
+ let base = raw.replace(/X/g, "");
+ const verifier = raw.slice(base.length, base.length + dv).slice(0, 1);
+ if (base.length < baseMax) base = base.padStart(baseMax, "0");
+ return maskRGByUF(uf, base + (verifier || ""));
}
function maskCEP(v: string) {
@@ -88,7 +134,7 @@
rg: z
.string()
.min(1, "Obrigatório")
- .refine((v) => /^\d{2}\.\d{3}\.\d{3}-\d$/.test(maskRG(v)), "RG inválido"),
+ .refine((v) => /^\d+$/.test(v), "RG inválido"),
nascimento: z.string().refine(isValidDateBR, "Data inválida (dd/mm/aaaa)"),
email: z.string().email("E-mail inválido"),
telefone: z
@@ -161,9 +207,9 @@
setTimeout(() => goto("/recursos-humanos/funcionarios"), 600);
} catch (e: any) {
const msg = e?.message || String(e);
- if (/CPF j[aá] cadastrado/i.test(msg)) formApi.setFieldError("cpf", "CPF já cadastrado");
- if (/Matr[ií]cula j[aá] cadastrada/i.test(msg)) formApi.setFieldError("matricula", "Matrícula já cadastrada");
- notice = { kind: "error", text: "Erro ao cadastrar funcionário." };
+ if (/CPF j[aá] cadastrado/i.test(msg)) notice = { kind: "error", text: "CPF já cadastrado." };
+ else if (/Matr[ií]cula j[aá] cadastrada/i.test(msg)) notice = { kind: "error", text: "Matrícula já cadastrada." };
+ else notice = { kind: "error", text: "Erro ao cadastrar funcionário." };
}
}
}));
@@ -171,6 +217,20 @@
let notice: { kind: "success" | "error"; text: string } | null = null;
loadSimbolos();
+
+ async function fillFromCEP(cepMasked: string) {
+ const cep = onlyDigits(cepMasked);
+ if (cep.length !== 8) return;
+ try {
+ const res = await fetch(`https://viacep.com.br/ws/${cep}/json/`);
+ const data = await res.json();
+ if (!data || data.erro) return;
+ const enderecoFull = [data.logradouro, data.bairro].filter(Boolean).join(", ");
+ form.setFieldValue("endereco", enderecoFull as any);
+ form.setFieldValue("cidade", (data.localidade || "") as any);
+ form.setFieldValue("uf", (data.uf || "") as any);
+ } catch {}
+ }
+
+
+
+ {#snippet children({ name, state, handleChange })}
+
+
+ handleChange((e.target as HTMLInputElement).value)} required />
+
+ {/snippet}
+
+
-
-
-
- {#snippet children({ name, state, handleChange })}
-
-
- handleChange((e.target as HTMLInputElement).value)} required />
-
- {/snippet}
-
{#snippet children({ name, state, handleChange })}