[FE][Certs] Auto generate Minicargador & Tractor Oruga cert

master
Araozu 2023-11-28 15:15:46 -05:00
parent 37045f9c79
commit dae9b57710
4 changed files with 620 additions and 43 deletions

View File

@ -0,0 +1,285 @@
import { cmText, createSimpleText, getImage, getQR } from "./utils";
import { CertData } from "./CertData";
const {
Document, Paragraph, PageOrientation,
FrameAnchorType,
TextRun,
AlignmentType,
BorderStyle,
} = window.docx;
const imgFondoDoc = getImage({
name: "fondo_certificado_4x4.png",
height: 21.03,
width: 29.69,
horizontalOffset: 0,
verticalOffset: 0.01,
behindDocument: true,
});
const imgCIP = getImage({
name: "colegio_ingenieros_logo.png",
height: 2.15,
width: 2.15,
horizontalOffset: 26.76,
verticalOffset: 12.26,
});
const imgCEE = getImage({
name: "cee_dark_logo.png",
height: 1.5,
width: 2.1,
horizontalOffset: 26.89,
verticalOffset: 17.06,
});
const imgMTC = getImage({
name: "mtc_transparente.png",
height: 1.65,
width: 5.16,
horizontalOffset: 23.92,
verticalOffset: 18.83,
});
const tCertificate = createSimpleText({
xPosition: 7.7,
yPosition: 3.7,
width: 11.05,
height: 1.72,
text: "CERTIFICADO",
size: 72,
font: "Times New Roman",
bold: true,
});
// Otorgado a
const tExpediteText = createSimpleText({
xPosition: 4.2,
yPosition: 5.5,
width: 3,
height: 0.7,
text: "Otorgado a:",
size: 22,
});
// Recuadro de foto
const photoSection = new Paragraph({
frame: {
position: {
x: cmText(-1.68),
y: cmText(8.6),
},
width: cmText(2.7),
height: cmText(3.57),
anchor: {
horizontal: FrameAnchorType.MARGIN,
vertical: FrameAnchorType.MARGIN,
},
},
children: [],
border: {
top: {
style: BorderStyle.DASHED,
},
bottom: {
style: BorderStyle.DASHED,
},
left: {
style: BorderStyle.DASHED,
},
right: {
style: BorderStyle.DASHED,
},
},
alignment: AlignmentType.LEFT,
});
export async function minicargador(props: CertData<null>): Promise<Document> {
const hasCustomLabel = (props.certCustomLabel?.length ?? 0) > 0;
const customLabelYOffset = 0.8;
// OPERADOR PROFESIONAL DE VOLQUETE
const tCourse = createSimpleText({
xPosition: 2.2,
yPosition: 8.4,
width: 20,
height: 1.5,
text: "OPERADOR PROFESIONAL DE MINICARGADOR",
size: 40,
bold: true,
});
// This is split because if there's a custom label, the text must be split.
const tCourseCustomLabel = hasCustomLabel ? createSimpleText({
xPosition: 2.2,
yPosition: 8.4 + customLabelYOffset,
width: 20,
height: 1.5,
text: props.certCustomLabel ?? "",
size: 40,
bold: true,
}) : undefined;
const imgQR = await getQR({
iid: props.certIId,
dni: props.personDni,
height: 2.04,
width: 2.04,
horizontalOffset: 26.85,
verticalOffset: 14.71,
});
// FERNANDO ARAOZ
const tName = createSimpleText({
xPosition: 1,
yPosition: 6.2,
width: 23.13,
height: 1.5,
text: props.personFullName,
size: 52,
bold: true,
underline: {},
});
// Identificado con DNI...
const tContentPart1 = new Paragraph({
frame: {
position: {
x: cmText(2.3),
y: cmText(7.6),
},
width: cmText(20.92),
height: cmText(1),
anchor: {
horizontal: FrameAnchorType.MARGIN,
vertical: FrameAnchorType.MARGIN,
},
},
children: [
new TextRun({
text: "Identificado con DNI N° ",
font: "Times New Roman",
size: 22,
}),
new TextRun({
text: props.personDni,
font: "Times New Roman",
size: 24,
bold: true,
}),
new TextRun({
text: ", al haber aprobado el curso de capacitación sobre:",
font: "Times New Roman",
size: 22,
}),
],
alignment: AlignmentType.LEFT,
});
/*
* These fields may have an offset based on whether the certificate has a custom label or not
*/
// En temas de...
const tTopics = createSimpleText({
xPosition: 2.3,
// If this cert has a custom label, 0.9cm is added to the Y position
yPosition: 9.5 + (hasCustomLabel ? customLabelYOffset : 0),
width: 20.92,
height: 1.5,
text: "En temas de: Recomendaciones para la operación segura, conocimientos de la máquina y sus accesorios, Operación correcta de palanca (Joystick), Estimación del peso de la carga, Funciones principales, Condiciones de trabajo seguro, Sistema de control del brazo de elevación, tren de impulsión, Técnicas de operación en nivelación, Técnicas de operación en excavación, Técnicas de operación con carga, Carguío a volquetes; equivalente a 60 horas lectivas.",
size: 22,
font: "Times New Roman",
alignment: AlignmentType.LEFT,
});
// Se expide certificado...
const tFinishLabel = createSimpleText({
xPosition: 5.95,
// If this cert has a custom label, 0.9cm is added to the Y position
yPosition: 11.6 + (hasCustomLabel ? customLabelYOffset : 0),
width: 12.38,
height: 0.75,
text: "Se expide el presente certificado para los fines que se estime conveniente",
size: 22,
font: "Times New Roman",
alignment: AlignmentType.CENTER,
});
// Fecha de Emision: ...
const certificateDate = new Paragraph({
frame: {
position: {
x: cmText(13),
y: cmText(17.3),
},
width: cmText(7.5),
height: cmText(0.5),
anchor: {
horizontal: FrameAnchorType.MARGIN,
vertical: FrameAnchorType.MARGIN,
},
},
children: [
new TextRun({
text: `Fecha de Emisión:\t${props.certDay} / ${props.certMonth} / ${props.certYear}`,
font: "Times New Roman",
size: 20,
}),
],
alignment: AlignmentType.LEFT,
});
// N° XXXX-20XX-EEG
const tCertCode = createSimpleText({
xPosition: -2.15,
yPosition: 12.45,
width: 3.67,
height: 0.8,
text: `${props.certCode}-${props.certYear}-EEG`,
size: 20,
alignment: AlignmentType.CENTER,
});
const doc = new Document({
sections: [
{
properties: {
page: {
size: {
orientation: PageOrientation.LANDSCAPE,
},
},
},
children: [
tCertificate,
tExpediteText,
tName,
tContentPart1,
tCourse,
tCourseCustomLabel,
tTopics,
tFinishLabel,
certificateDate,
photoSection,
tCertCode,
new Paragraph({
children: [
imgFondoDoc,
imgQR,
imgCIP,
imgCEE,
imgMTC,
],
}),
],
},
],
});
// Return document as a buffer
return doc;
}

View File

@ -0,0 +1,285 @@
import { cmText, createSimpleText, getImage, getQR } from "./utils";
import { CertData } from "./CertData";
const {
Document, Paragraph, PageOrientation,
FrameAnchorType,
TextRun,
AlignmentType,
BorderStyle,
} = window.docx;
const imgFondoDoc = getImage({
name: "fondo_certificado_4x4.png",
height: 21.03,
width: 29.69,
horizontalOffset: 0,
verticalOffset: 0.01,
behindDocument: true,
});
const imgCIP = getImage({
name: "colegio_ingenieros_logo.png",
height: 2.15,
width: 2.15,
horizontalOffset: 26.76,
verticalOffset: 12.26,
});
const imgCEE = getImage({
name: "cee_dark_logo.png",
height: 1.5,
width: 2.1,
horizontalOffset: 26.89,
verticalOffset: 17.06,
});
const imgMTC = getImage({
name: "mtc_transparente.png",
height: 1.65,
width: 5.16,
horizontalOffset: 23.92,
verticalOffset: 18.83,
});
const tCertificate = createSimpleText({
xPosition: 7.7,
yPosition: 3.7,
width: 11.05,
height: 1.72,
text: "CERTIFICADO",
size: 72,
font: "Times New Roman",
bold: true,
});
// Otorgado a
const tExpediteText = createSimpleText({
xPosition: 4.2,
yPosition: 5.5,
width: 3,
height: 0.7,
text: "Otorgado a:",
size: 22,
});
// Recuadro de foto
const photoSection = new Paragraph({
frame: {
position: {
x: cmText(-1.68),
y: cmText(8.6),
},
width: cmText(2.7),
height: cmText(3.57),
anchor: {
horizontal: FrameAnchorType.MARGIN,
vertical: FrameAnchorType.MARGIN,
},
},
children: [],
border: {
top: {
style: BorderStyle.DASHED,
},
bottom: {
style: BorderStyle.DASHED,
},
left: {
style: BorderStyle.DASHED,
},
right: {
style: BorderStyle.DASHED,
},
},
alignment: AlignmentType.LEFT,
});
export async function tractorOruga(props: CertData<null>): Promise<Document> {
const hasCustomLabel = (props.certCustomLabel?.length ?? 0) > 0;
const customLabelYOffset = 0.8;
// OPERADOR PROFESIONAL DE VOLQUETE
const tCourse = createSimpleText({
xPosition: 2.2,
yPosition: 8.4,
width: 20,
height: 1.5,
text: "OPERADOR PROFESIONAL DE TRACTOR ORUGA",
size: 40,
bold: true,
});
// This is split because if there's a custom label, the text must be split.
const tCourseCustomLabel = hasCustomLabel ? createSimpleText({
xPosition: 2.2,
yPosition: 8.4 + customLabelYOffset,
width: 20,
height: 1.5,
text: props.certCustomLabel ?? "",
size: 40,
bold: true,
}) : undefined;
const imgQR = await getQR({
iid: props.certIId,
dni: props.personDni,
height: 2.04,
width: 2.04,
horizontalOffset: 26.85,
verticalOffset: 14.71,
});
// FERNANDO ARAOZ
const tName = createSimpleText({
xPosition: 1,
yPosition: 6.2,
width: 23.13,
height: 1.5,
text: props.personFullName,
size: 52,
bold: true,
underline: {},
});
// Identificado con DNI...
const tContentPart1 = new Paragraph({
frame: {
position: {
x: cmText(2.3),
y: cmText(7.6),
},
width: cmText(20.92),
height: cmText(1),
anchor: {
horizontal: FrameAnchorType.MARGIN,
vertical: FrameAnchorType.MARGIN,
},
},
children: [
new TextRun({
text: "Identificado con DNI N° ",
font: "Times New Roman",
size: 22,
}),
new TextRun({
text: props.personDni,
font: "Times New Roman",
size: 24,
bold: true,
}),
new TextRun({
text: ", al haber aprobado el curso de capacitación sobre:",
font: "Times New Roman",
size: 22,
}),
],
alignment: AlignmentType.LEFT,
});
/*
* These fields may have an offset based on whether the certificate has a custom label or not
*/
// En temas de...
const tTopics = createSimpleText({
xPosition: 2.3,
// If this cert has a custom label, 0.9cm is added to the Y position
yPosition: 9.5 + (hasCustomLabel ? customLabelYOffset : 0),
width: 20.92,
height: 1.5,
text: "En temas de: Identificación de Componentes, Especificaciones técnicas del motor, transmisión, Mecánica básica específica, Sistema del motor, Tren de potencia, Sistema de dirección y frenos, Operación, Nivelación de pendientes, perfilado, Raspado de vías, Integración de vías; equivalente a 60 horas lectivas.",
size: 22,
font: "Times New Roman",
alignment: AlignmentType.LEFT,
});
// Se expide certificado...
const tFinishLabel = createSimpleText({
xPosition: 5.95,
// If this cert has a custom label, 0.9cm is added to the Y position
yPosition: 11.2 + (hasCustomLabel ? customLabelYOffset : 0),
width: 12.38,
height: 0.75,
text: "Se expide el presente certificado para los fines que se estime conveniente",
size: 22,
font: "Times New Roman",
alignment: AlignmentType.CENTER,
});
// Fecha de Emision: ...
const certificateDate = new Paragraph({
frame: {
position: {
x: cmText(13),
y: cmText(17.3),
},
width: cmText(7.5),
height: cmText(0.5),
anchor: {
horizontal: FrameAnchorType.MARGIN,
vertical: FrameAnchorType.MARGIN,
},
},
children: [
new TextRun({
text: `Fecha de Emisión:\t${props.certDay} / ${props.certMonth} / ${props.certYear}`,
font: "Times New Roman",
size: 20,
}),
],
alignment: AlignmentType.LEFT,
});
// N° XXXX-20XX-EEG
const tCertCode = createSimpleText({
xPosition: -2.15,
yPosition: 12.45,
width: 3.67,
height: 0.8,
text: `${props.certCode}-${props.certYear}-EEG`,
size: 20,
alignment: AlignmentType.CENTER,
});
const doc = new Document({
sections: [
{
properties: {
page: {
size: {
orientation: PageOrientation.LANDSCAPE,
},
},
},
children: [
tCertificate,
tExpediteText,
tName,
tContentPart1,
tCourse,
tCourseCustomLabel,
tTopics,
tFinishLabel,
certificateDate,
photoSection,
tCertCode,
new Paragraph({
children: [
imgFondoDoc,
imgQR,
imgCIP,
imgCEE,
imgMTC,
],
}),
],
},
],
});
// Return document as a buffer
return doc;
}

View File

@ -15,6 +15,8 @@ import { retroexcavadora } from "./certs/RETROEXCAVADORA";
import { volquete } from "./certs/VOLQUETE";
import { primerosAuxiliosCert } from "./certs/PRIMEROS_AUXILIOS";
import { luchaContraIncendiosCert } from "./certs/LUCHA_CONTRA_INCENDIOS";
import { minicargador } from "./certs/MINICARGADOR";
import { tractorOruga } from "./certs/TRACTOR_ORUGA";
declare global {
interface Window {
@ -81,6 +83,8 @@ export const certGenerator: CertGenObj = Object.freeze({
"Cargador Frontal": certGeneratorGenerator(cargadorFrontal),
"Retroexcavadora": certGeneratorGenerator(retroexcavadora),
"Volquete": certGeneratorGenerator(volquete),
"Minicargador": certGeneratorGenerator(minicargador),
"Tractor Oruga": certGeneratorGenerator(tractorOruga),
"Primeros Auxilios": certGeneratorGenerator(primerosAuxiliosCert),
"Lucha contra Incendios": certGeneratorGenerator(luchaContraIncendiosCert),
});

View File

@ -63,55 +63,14 @@ export function RegisterElement(props: {register: Register, person: Person, onCl
});
const generateCarnet = () => {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
const p = props.person;
const r = props.register;
const [expiryYear, month] = r.register_display_date.split("-");
const expiryMonth = numberToMonth(parseInt(month, 10));
if (expiryMonth === null) {
alert(`El cert. tiene un mes invalido (${month}), no se puede generar carnet`);
return;
}
// QR code
QR.toDataURL(`https://eegsac.com/certificado/${p.person_dni}`, {margin: 1}, (err, base64) => {
if (err) {
alert("Error creando codigo QR para el carnet.");
console.error("Error creating QR code");
console.error(err);
return;
}
const fullname = `${p.person_names} ${p.person_paternal_surname} ${p.person_maternal_surname}`;
const svgHtml = genCarnet({
fullname,
dni: p.person_dni.toString(),
code: r.register_code.toString().padStart(4, "0"),
expiryMonth,
expiryYear,
qrBase64: base64,
});
const v = Canvg.fromString(ctx, svgHtml);
v.start();
v.ready().then(() => {
canvas.toBlob((blob) => {
if (blob !== null) {
saveAs(blob, `CARNET MATPEL - ${fullname}.png`);
} else {
console.log("blob is null... :c");
}
});
});
});
generateCarnetImpl(p, r);
};
const createdTodayClasses = () => {
// current dat in YYYY-MM-DD format
// current date in YYYY-MM-DD format
const today = new Date().toISOString()
.split("T")[0];
const createdToday = props.register.register_creation_date === today;
@ -226,3 +185,47 @@ export function generateCert(person: Person, register: Register) {
});
}
export function generateCarnetImpl(p: Person, r: Register) {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
const [expiryYear, month] = r.register_display_date.split("-");
const expiryMonth = numberToMonth(parseInt(month, 10));
if (expiryMonth === null) {
alert(`El cert. tiene un mes invalido (${month}), no se puede generar carnet`);
return;
}
// QR code
QR.toDataURL(`https://eegsac.com/certificado/${p.person_dni}`, {margin: 1}, (err, base64) => {
if (err) {
alert("Error creando codigo QR para el carnet.");
console.error("Error creating QR code");
console.error(err);
return;
}
const fullname = `${p.person_names} ${p.person_paternal_surname} ${p.person_maternal_surname}`;
const svgHtml = genCarnet({
fullname,
dni: p.person_dni.toString(),
code: r.register_code.toString().padStart(4, "0"),
expiryMonth,
expiryYear,
qrBase64: base64,
});
const v = Canvg.fromString(ctx, svgHtml);
v.start();
v.ready().then(() => {
canvas.toBlob((blob) => {
if (blob !== null) {
saveAs(blob, `CARNET MATPEL - ${fullname}.png`);
} else {
console.log("blob is null... :c");
}
});
});
});
}