diff --git a/frontend/src/certGenerator/certs/MINICARGADOR.ts b/frontend/src/certGenerator/certs/MINICARGADOR.ts new file mode 100644 index 0000000..74f4a15 --- /dev/null +++ b/frontend/src/certGenerator/certs/MINICARGADOR.ts @@ -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): Promise { + 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: `N° ${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; +} diff --git a/frontend/src/certGenerator/certs/TRACTOR_ORUGA.ts b/frontend/src/certGenerator/certs/TRACTOR_ORUGA.ts new file mode 100644 index 0000000..235fc07 --- /dev/null +++ b/frontend/src/certGenerator/certs/TRACTOR_ORUGA.ts @@ -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): Promise { + 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: `N° ${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; +} diff --git a/frontend/src/certGenerator/index.ts b/frontend/src/certGenerator/index.ts index 6ee6294..5085bfb 100644 --- a/frontend/src/certGenerator/index.ts +++ b/frontend/src/certGenerator/index.ts @@ -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), }); diff --git a/frontend/src/certs/Registers/RegisterElement.tsx b/frontend/src/certs/Registers/RegisterElement.tsx index 04afac6..0a1261a 100644 --- a/frontend/src/certs/Registers/RegisterElement.tsx +++ b/frontend/src/certs/Registers/RegisterElement.tsx @@ -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"); + } + }); + }); + }); +}