feat: optimize pre-enrollment dynamic banners, field uploads and phone masks
Build and Deploy (Gitea) / build-and-deploy (push) Successful in 2m8s
Details
Build and Deploy (Gitea) / build-and-deploy (push) Successful in 2m8s
Details
This commit is contained in:
parent
bd4a2ca0bb
commit
f10f213b9e
|
|
@ -15,6 +15,7 @@ interface Props {
|
||||||
const FIELD_TYPES: Record<string, string> = {
|
const FIELD_TYPES: Record<string, string> = {
|
||||||
text: 'Texto', email: 'Email', phone: 'Telefone', cpf: 'CPF',
|
text: 'Texto', email: 'Email', phone: 'Telefone', cpf: 'CPF',
|
||||||
date: 'Data', select: 'Seleção', textarea: 'Texto Longo', number: 'Número',
|
date: 'Data', select: 'Seleção', textarea: 'Texto Longo', number: 'Número',
|
||||||
|
file: 'Imagem / Documento (Upload)',
|
||||||
banner: 'Folder / Panfleto de Promoção (Banner)'
|
banner: 'Folder / Panfleto de Promoção (Banner)'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
import pg from 'pg';
|
||||||
|
|
||||||
|
const DATABASE_URL = 'postgresql://edumanager:EduManager2026!Seguro@150.230.87.131:5432/edumanager';
|
||||||
|
const pool = new pg.Pool({ connectionString: DATABASE_URL });
|
||||||
|
|
||||||
|
async function cleanup() {
|
||||||
|
try {
|
||||||
|
const { rowCount } = await pool.query(
|
||||||
|
`DELETE FROM notas_boletim
|
||||||
|
WHERE prova_id IS NOT NULL AND prova_id NOT IN (SELECT id FROM provas)`
|
||||||
|
);
|
||||||
|
console.log(`[Sucesso] Limpeza concluída: ${rowCount} registros órfãos de notas removidos!`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[Erro] Falha ao limpar banco de dados:', error);
|
||||||
|
} finally {
|
||||||
|
await pool.end();
|
||||||
|
process.exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup();
|
||||||
|
|
@ -79,7 +79,7 @@ function getPreMatriculaHTML(slug) {
|
||||||
body{font-family:'Inter',sans-serif;background:#f8fafc;min-height:100vh;display:flex;align-items:center;justify-content:center;padding:20px}
|
body{font-family:'Inter',sans-serif;background:#f8fafc;min-height:100vh;display:flex;align-items:center;justify-content:center;padding:20px}
|
||||||
.container{max-width:640px;width:100%;background:#fff;border-radius:24px;box-shadow:0 25px 50px -12px rgba(0,0,0,.1);overflow:hidden}
|
.container{max-width:640px;width:100%;background:#fff;border-radius:24px;box-shadow:0 25px 50px -12px rgba(0,0,0,.1);overflow:hidden}
|
||||||
.header{padding:40px 32px 24px;text-align:center}
|
.header{padding:40px 32px 24px;text-align:center}
|
||||||
.header img{width:96px;height:96px;border-radius:50%;margin:0 auto 24px;display:block;object-fit:cover;border:3px solid var(--primary,#4f46e5);box-shadow:0 10px 15px -3px rgba(0,0,0,.1)}
|
.header img{max-height:80px;max-width:240px;margin:0 auto 24px;display:block;object-fit:contain}
|
||||||
.header h1{font-size:28px;font-weight:900;color:#0f172a;margin-bottom:8px}
|
.header h1{font-size:28px;font-weight:900;color:#0f172a;margin-bottom:8px}
|
||||||
.header p{font-size:14px;color:#64748b;line-height:1.6}
|
.header p{font-size:14px;color:#64748b;line-height:1.6}
|
||||||
.form{padding:0 32px 32px}
|
.form{padding:0 32px 32px}
|
||||||
|
|
@ -151,12 +151,21 @@ function maskCEP(v){
|
||||||
document.addEventListener('input',function(e){
|
document.addEventListener('input',function(e){
|
||||||
if(!e.target)return;
|
if(!e.target)return;
|
||||||
const dt=e.target.getAttribute('data-type');
|
const dt=e.target.getAttribute('data-type');
|
||||||
if(dt==='phone'){
|
if(dt==='phone' || dt==='cpf' || dt==='cep'){
|
||||||
e.target.value=maskPhone(e.target.value);
|
const originalVal = e.target.value;
|
||||||
}else if(dt==='cpf'){
|
let newVal = originalVal;
|
||||||
e.target.value=maskCPF(e.target.value);
|
if(dt==='phone') newVal = maskPhone(originalVal);
|
||||||
}else if(dt==='cep'){
|
else if(dt==='cpf') newVal = maskCPF(originalVal);
|
||||||
e.target.value=maskCEP(e.target.value);
|
else if(dt==='cep') newVal = maskCEP(originalVal);
|
||||||
|
|
||||||
|
if (newVal !== originalVal) {
|
||||||
|
const start = e.target.selectionStart;
|
||||||
|
e.target.value = newVal;
|
||||||
|
try {
|
||||||
|
const diff = newVal.length - originalVal.length;
|
||||||
|
e.target.setSelectionRange(start + diff, start + diff);
|
||||||
|
} catch(err) {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -238,7 +247,7 @@ async function init(){
|
||||||
}else if(f.id==='_email'){
|
}else if(f.id==='_email'){
|
||||||
html+='<input name="_email" type="email" placeholder="'+(f.placeholder||'seu@email.com')+'">';
|
html+='<input name="_email" type="email" placeholder="'+(f.placeholder||'seu@email.com')+'">';
|
||||||
}else if(f.id==='_telefone'){
|
}else if(f.id==='_telefone'){
|
||||||
html+='<input name="_telefone" data-type="phone" placeholder="'+(f.placeholder||'(00) 00000-0000')+'">';
|
html+='<input name="_telefone" data-type="phone" placeholder="'+(f.placeholder||'(00) 00000-0000')+'" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false">';
|
||||||
}else if(f.id==='_turma'){
|
}else if(f.id==='_turma'){
|
||||||
if(data.turmas.length>0){
|
if(data.turmas.length>0){
|
||||||
html+='<select name="_turma"'+(f.obrigatorio?' required':'')+'><option value="">Selecione a turma...</option>';
|
html+='<select name="_turma"'+(f.obrigatorio?' required':'')+'><option value="">Selecione a turma...</option>';
|
||||||
|
|
@ -278,7 +287,7 @@ async function init(){
|
||||||
}else{
|
}else{
|
||||||
let t=tipoDetectado;
|
let t=tipoDetectado;
|
||||||
if(t==='phone'||t==='cpf'||t==='cep')t='text';
|
if(t==='phone'||t==='cpf'||t==='cep')t='text';
|
||||||
let dt=(tipoDetectado==='phone'||tipoDetectado==='cpf'||tipoDetectado==='cep')?(' data-type="'+tipoDetectado+'"'):'';
|
let dt=(tipoDetectado==='phone'||tipoDetectado==='cpf'||tipoDetectado==='cep')?(' data-type="'+tipoDetectado+'" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"'):'';
|
||||||
html+='<input type="'+t+'" name="'+f.id+'"'+(f.obrigatorio?' required':'')+dt+' placeholder="'+(f.placeholder||'')+'">';
|
html+='<input type="'+t+'" name="'+f.id+'"'+(f.obrigatorio?' required':'')+dt+' placeholder="'+(f.placeholder||'')+'">';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1050,15 +1050,40 @@ app.get('/manifest.json', async (req, res) => {
|
||||||
const name = schoolData.profile?.name || 'EduManager';
|
const name = schoolData.profile?.name || 'EduManager';
|
||||||
const logo = normalizeStorageUrl(schoolData.logo) || '/vite.svg';
|
const logo = normalizeStorageUrl(schoolData.logo) || '/vite.svg';
|
||||||
|
|
||||||
|
// Detectar dinamicamente o tipo MIME com base na extensão do arquivo de logo (ex: webp, png, svg)
|
||||||
|
const ext = logo.split('.').pop().split('?')[0].toLowerCase();
|
||||||
|
let mimeType = 'image/png';
|
||||||
|
if (ext === 'jpg' || ext === 'jpeg') mimeType = 'image/jpeg';
|
||||||
|
else if (ext === 'svg') mimeType = 'image/svg+xml';
|
||||||
|
else if (ext === 'webp') mimeType = 'image/webp';
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
"short_name": name,
|
"short_name": name,
|
||||||
"name": `${name} - Portal do Aluno`,
|
"name": `${name} - Portal do Aluno`,
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": logo,
|
"src": logo,
|
||||||
"sizes": "192x192 512x512",
|
"sizes": "192x192",
|
||||||
"type": "image/png",
|
"type": mimeType,
|
||||||
"purpose": "any maskable"
|
"purpose": "any"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": logo,
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": mimeType,
|
||||||
|
"purpose": "any"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": logo,
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": mimeType,
|
||||||
|
"purpose": "maskable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": logo,
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": mimeType,
|
||||||
|
"purpose": "maskable"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"start_url": "/",
|
"start_url": "/",
|
||||||
|
|
@ -1074,7 +1099,13 @@ app.get('/manifest.json', async (req, res) => {
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "/vite.svg",
|
"src": "/vite.svg",
|
||||||
"sizes": "192x192 512x512",
|
"sizes": "192x192",
|
||||||
|
"type": "image/svg+xml",
|
||||||
|
"purpose": "any"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/vite.svg",
|
||||||
|
"sizes": "512x512",
|
||||||
"type": "image/svg+xml",
|
"type": "image/svg+xml",
|
||||||
"purpose": "any"
|
"purpose": "any"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue