diff --git a/frontend/index.html b/frontend/index.html index df462a7..e5f8138 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -10,11 +10,15 @@ +
+ + + \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index e45db3c..ed6694d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -25,7 +25,9 @@ }, "dependencies": { "@solidjs/router": "^0.8.3", + "@types/file-saver": "^2.0.5", "@types/qrcode": "^1.5.1", + "file-saver": "^2.0.5", "qrcode": "^1.5.3", "solid-js": "^1.7.6" } diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index de1669f..930467d 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -8,9 +8,15 @@ dependencies: '@solidjs/router': specifier: ^0.8.3 version: 0.8.3(solid-js@1.7.6) + '@types/file-saver': + specifier: ^2.0.5 + version: 2.0.5 '@types/qrcode': specifier: ^1.5.1 version: 1.5.1 + file-saver: + specifier: ^2.0.5 + version: 2.0.5 qrcode: specifier: ^1.5.3 version: 1.5.3 @@ -935,6 +941,10 @@ packages: '@babel/types': 7.22.5 dev: true + /@types/file-saver@2.0.5: + resolution: {integrity: sha512-zv9kNf3keYegP5oThGLaPk8E081DFDuwfqjtiTzm6PoxChdJ1raSuADf2YGCVIyrSynLrgc8JWv296s7Q7pQSQ==} + dev: false + /@types/json-schema@7.0.12: resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==} dev: true @@ -1768,6 +1778,10 @@ packages: flat-cache: 3.0.4 dev: true + /file-saver@2.0.5: + resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==} + dev: false + /fill-range@7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} diff --git a/frontend/src/certGenerator/certs/4X4.ts b/frontend/src/certGenerator/certs/4X4.ts new file mode 100644 index 0000000..fca5c81 --- /dev/null +++ b/frontend/src/certGenerator/certs/4X4.ts @@ -0,0 +1,286 @@ +/* +import { + Document, Packer, Paragraph, PageOrientation, + FrameAnchorType, + TextRun, + AlignmentType, + BorderStyle, +} from "docx"; +*/ +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, +}); + +// MANEJO EN 4x4 ROAD DANGER +const tCourse = createSimpleText({ + xPosition: 1.8, + yPosition: 8.4, + width: 20.92, + height: 1.5, + text: "MANEJO EN 4x4 ROAD DANGER", + size: 44, + bold: true, +}); + +// (caminos peligrosos) +const tCourseDesc = createSimpleText({ + xPosition: 9.7, + yPosition: 9.6, + width: 5, + height: 0.5, + text: "(CAMINOS PELIGROSOS)", + size: 20, + bold: true, +}); + +// En temas de... +const tTopics = createSimpleText({ + xPosition: 2.3, + yPosition: 10.4, + width: 20.92, + height: 1.5, + text: "En temas de: Terreno Firme y Complicado, Reacciones ante Eventos, Manejo Adecuado de Unidad, Uso Correcto de 4x4, Control Adecuado en Trocha y Pendientes, Uso de Velocidad Adecuada, equivalente a 12 horas lectivas.", + size: 22, + font: "Times New Roman", + alignment: AlignmentType.LEFT, +}); + +// Se expide certificado... +const tFinishLabel = createSimpleText({ + xPosition: 5.95, + yPosition: 11.65, + 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, +}); + + +// 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 _4X4Cert(props: CertData): Promise { + 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.5), + 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, + }); + + // 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, + tTopics, + tFinishLabel, + certificateDate, + photoSection, + tCertCode, + tCourseDesc, + new Paragraph({ + children: [ + imgFondoDoc, + imgQR, + imgCIP, + imgCEE, + imgMTC, + ], + }), + ], + }, + ], + }); + + // Return document as a buffer + return doc; +} diff --git a/frontend/src/certGenerator/certs/CertData.ts b/frontend/src/certGenerator/certs/CertData.ts new file mode 100644 index 0000000..05a223b --- /dev/null +++ b/frontend/src/certGenerator/certs/CertData.ts @@ -0,0 +1,42 @@ + +export type CertData = { + matpel: T, + /** + * Full name in format: "FIRST_NAMES LAST_NAME_1 LAST_NAME_2" + */ + personFullName: string, + /** + * DNI as a string. + * + * E.g.: "04123523" + */ + personDni: string, + /** + * Cert code as a string. Must have length = 4. + * + * E.g.: "0322" + */ + certCode: string, + /** + * Year of emission as a string. + * + * E.g.: "2023" + */ + certYear: string, + /** + * Month of emission. + * + * E.g.: "05" + */ + certMonth: string, + /** + * Day of emission. + * + * E.g.: "23" + */ + certDay: string, + /** + * Id of the certificate + */ + certIId: number, +} diff --git a/frontend/src/certGenerator/certs/IPERC.ts b/frontend/src/certGenerator/certs/IPERC.ts new file mode 100644 index 0000000..4a01528 --- /dev/null +++ b/frontend/src/certGenerator/certs/IPERC.ts @@ -0,0 +1,263 @@ +import { cmText, createSimpleText, createSimpleTextP, getImage, getQR } from "./utils"; +import { CertData } from "./CertData"; + +const { + Document, Paragraph, PageOrientation, + FrameAnchorType, + TextRun, + AlignmentType, + BorderStyle, +} = window.docx; + + + +const imgFondoDoc = getImage({ + name: "fondo_certificado_manejo_defensivo.png", + height: 21.23, + width: 29.8, + horizontalOffset: 0, + verticalOffset: -0.05, + behindDocument: true, +}); + +const imgCIP = getImage({ + name: "colegio_ingenieros_logo.png", + height: 2.15, + width: 2.15, + horizontalOffset: 26.52, + verticalOffset: 15.24, +}); + + +const imgCEE = getImage({ + name: "cee_dark_logo.png", + height: 1.6, + width: 2.25, + horizontalOffset: 0.34, + verticalOffset: 17.99, +}); + + +// CERTIFICADO +const tCertificate = createSimpleTextP({ + xPosition: 0.17, + yPosition: 5.9, + width: 11.05, + height: 1.72, + text: "CERTIFICADO", + size: 72, + font: "Times New Roman", + bold: true, +}); + +// Otorgado a +const tExpediteText = createSimpleText({ + xPosition: -1.08, + yPosition: 5.4, + width: 3, + height: 0.7, + text: "Otorgado a:", + size: 22, +}); + +// IPERC +const tCourse = createSimpleText({ + xPosition: -0.44, + yPosition: 8, + width: 20.92, + height: 1.5, + text: "IDENTIFICACIÓN DE PELIGROS, EVALUACIÓN DE RIESGOS Y CONTROLES (IPERC)", + size: 44, + bold: true, +}); + +// En temas de... +const tTopics = createSimpleTextP({ + xPosition: 2, + yPosition: 12.7, + width: 20.92, + height: 1.5, + text: "En temas de: Interpretación de Riesgos, Métodos de Identificación de Riesgos, Evaluación de Riesgos, Análisis de Riesgos, Medidas de Control y Tipos de IPERC; equivalente a 12 horas lectivas.", + size: 22, + font: "Times New Roman", + alignment: AlignmentType.CENTER, +}); + +// Se expide certificado... +const tFinishLabel = createSimpleTextP({ + xPosition: 1.9, + yPosition: 13.8, + width: 20.92, + 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, +}); + +// Recuadro de foto +const photoSection = new Paragraph({ + frame: { + position: { + x: cmText(23.35), + y: cmText(8.3), + }, + width: cmText(2.81), + 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 ipercCert(props: CertData): Promise { + const imgQR = await getQR({ + iid: props.certIId, + dni: props.personDni, + height: 2.45, + width: 2.45, + horizontalOffset: 26.4, + verticalOffset: 17.65, + }); + + // FERNANDO ARAOZ + const tName = createSimpleText({ + xPosition: -2.5, + yPosition: 6, + 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(-0.4), + y: cmText(7.3), + }, + 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, + }); + + // Fecha de Emision: ... + const certificateDate = new Paragraph({ + frame: { + position: { + x: cmText(16), + y: cmText(17.5), + }, + width: cmText(7.5), + height: cmText(0.9), + anchor: { + horizontal: FrameAnchorType.MARGIN, + vertical: FrameAnchorType.MARGIN, + }, + }, + children: [ + new TextRun({ + text: `Fecha de Emisión:\t${props.certDay} / ${props.certMonth} / ${props.certYear}`, + font: "Arial", + size: 24, + bold: true, + color: "FFFFFF", + }), + ], + alignment: AlignmentType.RIGHT, + }); + + // N° XXXX-20XX-EEG + const tCertCode = createSimpleText({ + xPosition: 23, + yPosition: 12, + 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, + tTopics, + tFinishLabel, + certificateDate, + photoSection, + tCertCode, + // tMTCLabel, + new Paragraph({ + children: [ + imgFondoDoc, + imgQR, + imgCIP, + imgCEE, + // imgMTC, + ], + }), + ], + }, + ], + }); + + // Return document as a buffer + return doc; +} + diff --git a/frontend/src/certGenerator/certs/MANEJO_DEFENSIVO.ts b/frontend/src/certGenerator/certs/MANEJO_DEFENSIVO.ts new file mode 100644 index 0000000..7f9fb9d --- /dev/null +++ b/frontend/src/certGenerator/certs/MANEJO_DEFENSIVO.ts @@ -0,0 +1,280 @@ +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_manejo_defensivo.png", + height: 21.23, + width: 29.8, + horizontalOffset: 0, + verticalOffset: -0.05, + behindDocument: true, +}); + +const imgCIP = getImage({ + name: "colegio_ingenieros_logo.png", + height: 2.15, + width: 2.15, + horizontalOffset: 26.52, + verticalOffset: 15.24, +}); + +const imgMTC = getImage({ + name: "mtc_2_logo.png", + height: 0.94, + width: 5.14, + horizontalOffset: 3.11, + verticalOffset: 19.21, +}); + +const imgCEE = getImage({ + name: "cee_dark_logo.png", + height: 1.6, + width: 2.25, + horizontalOffset: 0.34, + verticalOffset: 17.99, +}); + + +const tCertificate = createSimpleText({ + xPosition: 2.42, + yPosition: 4.2, + width: 11.05, + height: 1.72, + text: "CERTIFICADO", + size: 72, + font: "Times New Roman", + bold: true, +}); + +// Otorgado a +const tExpediteText = createSimpleText({ + xPosition: -1.08, + yPosition: 5.4, + width: 3, + height: 0.7, + text: "Otorgado a:", + size: 22, +}); + +// MANEJO DEFENSIVO +const tCourse = createSimpleText({ + xPosition: -0.44, + yPosition: 8, + width: 20.92, + height: 1.5, + text: "MANEJO DEFENSIVO", + size: 44, + bold: true, +}); + +// En temas de... +const tTopics = createSimpleText({ + xPosition: -0.44, + yPosition: 9.4, + width: 20.92, + height: 1.5, + text: "En temas de: Reacción a Eventos, Técnicas Preventivas, Puntos Ciegos, Primeros Auxilios Básico, Fatiga y Somnolencia, Normas Vigentes, Acción y Reacción Adecuada, Conducción en Condiciones Adversas, equivalente a 12 horas lectivas.", + size: 22, + font: "Times New Roman", + alignment: AlignmentType.CENTER, +}); + +// Se expide certificado... +const tFinishLabel = createSimpleText({ + xPosition: -0.44, + yPosition: 10.5, + width: 20.92, + 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, +}); + +// MTC: R.D.N° ... +const tMTCLabel = createSimpleText({ + xPosition: 0.6, + yPosition: 18, + width: 6, + height: 0.75, + text: "R.D.N° 092-2021-MTC/17.03", + size: 24, + font: "Calibri", + alignment: AlignmentType.LEFT, + color: "FFFFFF", + bold: true, +}); + + +// Recuadro de foto +const photoSection = new Paragraph({ + frame: { + position: { + x: cmText(23.35), + y: cmText(8.3), + }, + width: cmText(2.81), + 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 manejoDefensivoCert(props: CertData): Promise { + const imgQR = await getQR({ + iid: props.certIId, + dni: props.personDni, + height: 2.45, + width: 2.45, + horizontalOffset: 26.4, + verticalOffset: 17.65, + }); + + // FERNANDO ARAOZ + const tName = createSimpleText({ + xPosition: -2.5, + yPosition: 6, + 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(-0.4), + y: cmText(7.3), + }, + 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, + }); + + // Fecha de Emision: ... + const certificateDate = new Paragraph({ + frame: { + position: { + x: cmText(16), + y: cmText(16), + }, + width: cmText(7.5), + height: cmText(0.9), + anchor: { + horizontal: FrameAnchorType.MARGIN, + vertical: FrameAnchorType.MARGIN, + }, + }, + children: [ + new TextRun({ + text: `Fecha de Emisión:\t\t${props.certDay} / ${props.certMonth} / ${props.certYear}`, + font: "Times New Roman", + size: 20, + }), + ], + alignment: AlignmentType.LEFT, + }); + + // N° XXXX-20XX-EEG + const tCertCode = createSimpleText({ + xPosition: 23, + yPosition: 12, + 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, + tTopics, + tFinishLabel, + certificateDate, + photoSection, + tCertCode, + tMTCLabel, + new Paragraph({ + children: [ + imgFondoDoc, + imgQR, + imgCIP, + imgCEE, + imgMTC, + ], + }), + ], + }, + ], + }); + + // Return document as a buffer + return doc; +} diff --git a/frontend/src/certGenerator/certs/MATPEL.ts b/frontend/src/certGenerator/certs/MATPEL.ts new file mode 100644 index 0000000..7a5cbdf --- /dev/null +++ b/frontend/src/certGenerator/certs/MATPEL.ts @@ -0,0 +1,371 @@ +import { Matpel, cmText, getImage, getMatpelHours, getMatpelLabel, getQR } from "./utils"; +import { CertData } from "./CertData"; + +/* +import { + Document, Packer, Paragraph, PageOrientation, + FrameAnchorType, + TextRun, + AlignmentType, +} from "docx"; +// */ + +//* +const { + Document, Paragraph, PageOrientation, + FrameAnchorType, + TextRun, + AlignmentType, +} = window.docx; +// */ + +const imgFondoDoc = getImage({ + name: "fondo_certificado.png", + height: 20.97, + width: 29.8, + horizontalOffset: 0, + verticalOffset: 0, + behindDocument: true, +}); + +const imgMatpel = getImage({ + name: "matpel-logo.png", + height: 2.81, + width: 2.81, + horizontalOffset: 0.7, + verticalOffset: 0.7, +}); + +const imgCIP = getImage({ + name: "colegio_ingenieros_logo.png", + height: 2.15, + width: 2.15, + horizontalOffset: 0.91, + verticalOffset: 3.64, +}); + +const imgCEE = getImage({ + name: "cee_logo.png", + height: 2.22, + width: 3.11, + horizontalOffset: 0.29, + verticalOffset: 18, +}); + +const imgMTC = getImage({ + name: "mtc_logo.png", + height: 1.3, + width: 4.08, + horizontalOffset: 5.13, + verticalOffset: 19.33, +}); + +const imgEEG = getImage({ + name: "eeg_f_logo.png", + height: 4.39, + width: 6.4, + horizontalOffset: 23.35, + verticalOffset: 0, +}); + +const imgOSHA = getImage({ + name: "osha_logo.png", + height: 1.55, + width: 4.46, + horizontalOffset: 24.34, + verticalOffset: 4.95, +}); + +const imgAguila = getImage({ + name: "aguila_logo.png", + height: 2.62, + width: 2.68, + horizontalOffset: 25.45, + verticalOffset: 6.57, +}); + +const imgMichigan = getImage({ + name: "michigan_logo.png", + height: 2.47, + width: 2.6, + horizontalOffset: 25.59, + verticalOffset: 9.54, +}); + +const imgCEEM = getImage({ + name: "ceem_logo.jpg", + height: 1, + width: 3.99, + horizontalOffset: 24.97, + verticalOffset: 12.5, +}); + +const imgEATE = getImage({ + name: "eate_logo.jpg", + height: 2.21, + width: 3.28, + horizontalOffset: 25.54, + verticalOffset: 14.09, +}); + + + + +/** + * Generates a certificate in format DOCX and in-place. + * + * @param props Data to use in the certificate + * @returns A buffer of the DOCX document + */ +export async function matpelCert(props: CertData): Promise { + const certificateName = new Paragraph({ + frame: { + position: { + x: cmText(3.13), + y: cmText(1.9), + }, + width: cmText(14.28), + height: cmText(3.19), + anchor: { + horizontal: FrameAnchorType.MARGIN, + vertical: FrameAnchorType.MARGIN, + }, + }, + children: [ + new TextRun({ + text: `CAPACITACIÓN EN MANEJO; MANIPULACIÓN Y TRANSPORTE DE MATERIALES Y RESIDUOS PELIGROSOS - ${getMatpelLabel(props.matpel)}`, + bold: true, + font: "Arial", + size: 32, + }), + ], + alignment: AlignmentType.CENTER, + }); + + const certificatePersonName = new Paragraph({ + frame: { + position: { + x: cmText(0), + y: cmText(6.5), + }, + width: cmText(23), + height: cmText(1.5), + anchor: { + horizontal: FrameAnchorType.MARGIN, + vertical: FrameAnchorType.MARGIN, + }, + }, + children: [ + new TextRun({ + text: props.personFullName.toUpperCase(), + bold: true, + font: "Arial", + allCaps: true, + size: 48, + underline: {}, + }), + ], + alignment: AlignmentType.CENTER, + }); + + // "Se expide el presente certificado a:" + const certificateExpedite = new Paragraph({ + frame: { + position: { + x: cmText(2.85), + y: cmText(5.15), + }, + width: cmText(14.28), + height: cmText(1), + anchor: { + horizontal: FrameAnchorType.MARGIN, + vertical: FrameAnchorType.MARGIN, + }, + }, + children: [ + new TextRun({ + text: "Se expide el presente certificado a:", + font: "Arial", + size: 22, + }), + ], + alignment: AlignmentType.CENTER, + }); + + // N° XXXX-202X-EEG + const certificateCode = new Paragraph({ + frame: { + position: { + x: cmText(17.1), + y: cmText(5.35), + }, + width: cmText(3.68), + height: cmText(0.7), + anchor: { + horizontal: FrameAnchorType.MARGIN, + vertical: FrameAnchorType.MARGIN, + }, + }, + children: [ + new TextRun({ + text: `N° ${props.certCode}-${props.certYear}-EEG`, + font: "Arial", + size: 20, + }), + ], + alignment: AlignmentType.CENTER, + }); + + // Identificado con dni ... + const certificateBody = new Paragraph({ + frame: { + position: { + x: cmText(1.51), + y: cmText(8.35), + }, + width: cmText(20.08), + height: cmText(3.7), + anchor: { + horizontal: FrameAnchorType.MARGIN, + vertical: FrameAnchorType.MARGIN, + }, + }, + children: [ + new TextRun({ + text: "Identificado con DNI N° ", + font: "Arial", + size: 24, + }), + new TextRun({ + text: props.personDni, + font: "Arial", + size: 24, + bold: true, + }), + new TextRun({ + text: ", al haber aprobado el curso de capacitación sobre ", + font: "Arial", + size: 24, + }), + new TextRun({ + text: `MANEJO DE MATERIALES Y RESIDUOS PELIGROSOS - ${getMatpelLabel(props.matpel)}`, + font: "Arial", + size: 24, + bold: true, + }), + new TextRun({ + text: `, según lo estipulado en la Ley N°28256 (ley que regula el Transporte de Materiales y Residuos Peligrosos) y Decreto Supremo N° 021-2008-MTC (Reglamento Nacional de Transporte Terrestre de Materiales y Residuos Peligrosos) con una duración de ${getMatpelHours(props.matpel)} horas lectivas.`, + font: "Arial", + size: 24, + }), + ], + alignment: AlignmentType.JUSTIFIED, + }); + + // Se expide certificado... + const certificateFinishLabel = new Paragraph({ + frame: { + position: { + x: cmText(1.52), + y: cmText(11.25), + }, + width: cmText(20.1), + height: cmText(0.8), + anchor: { + horizontal: FrameAnchorType.MARGIN, + vertical: FrameAnchorType.MARGIN, + }, + }, + children: [ + new TextRun({ + text: "Se expide certificado para los fines que se estime conveniente.", + font: "Arial", + size: 24, + }), + ], + alignment: AlignmentType.CENTER, + }); + + // Fecha de Emision: ... + const certificateDate = new Paragraph({ + frame: { + position: { + x: cmText(16), + y: cmText(16.5), + }, + width: cmText(9), + height: cmText(1.4), + anchor: { + horizontal: FrameAnchorType.MARGIN, + vertical: FrameAnchorType.MARGIN, + }, + }, + children: [ + new TextRun({ + text: `Fecha de Emisión:\t\t${props.certDay} / ${props.certMonth} / ${props.certYear}`, + font: "Arial", + size: 20, + break: 1, + }), + new TextRun({ + text: `Fecha de Expiración:\t\t${props.certDay} / ${props.certMonth} / ${parseInt(props.certYear, 10) + 1}`, + font: "Arial", + size: 20, + break: 1, + }), + ], + alignment: AlignmentType.LEFT, + }); + + const imgQR = await getQR({ + iid: props.certIId, + dni: props.personDni, + height: 2.47, + width: 2.47, + horizontalOffset: 26.3, + verticalOffset: 16.48, + }); + + const doc = new Document({ + sections: [ + { + properties: { + page: { + size: { + orientation: PageOrientation.LANDSCAPE, + }, + }, + }, + children: [ + certificateName, + certificatePersonName, + certificateExpedite, + certificateCode, + certificateBody, + certificateFinishLabel, + certificateDate, + new Paragraph({ + children: [ + imgFondoDoc, + imgMatpel, + imgCIP, + imgCEE, + imgMTC, + imgEEG, + imgOSHA, + imgAguila, + imgMichigan, + imgCEEM, + imgEATE, + imgQR, + ], + }), + ], + }, + ], + }); + + // Return document + return doc; +} + diff --git a/frontend/src/certGenerator/certs/MECANICA_BASICA.ts b/frontend/src/certGenerator/certs/MECANICA_BASICA.ts new file mode 100644 index 0000000..6cbe0a7 --- /dev/null +++ b/frontend/src/certGenerator/certs/MECANICA_BASICA.ts @@ -0,0 +1,270 @@ +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_mecanica_basica.jpg", + height: 21.23, + width: 29.8, + horizontalOffset: 0, + verticalOffset: 0, + behindDocument: true, +}); + +const imgCIP = getImage({ + name: "colegio_ingenieros_logo.png", + height: 2.15, + width: 2.15, + horizontalOffset: 0.22, + verticalOffset: 16.45, +}); + +const imgMichigan = getImage({ + name: "michigan_logo.png", + height: 1.96, + width: 2.22, + horizontalOffset: 0.31, + verticalOffset: 18.86, +}); + +const tCertificate = createSimpleText({ + xPosition: 7.75, + yPosition: 1.64, + width: 9, + height: 1.56, + text: "CERTIFICADO", + size: 60, + bold: true, +}); + +// Se expide el presente certificado a: +const tExpediteText = createSimpleText({ + xPosition: 0.76, + yPosition: 3.45, + width: 7.88, + height: 1, + text: "Se expide el presente certificado a:", + size: 22, +}); + +// MECANICA BASICA +const tCourse = createSimpleText({ + xPosition: 1.7, + yPosition: 7, + width: 20.08, + height: 1.5, + text: "MECÁNICA BÁSICA", + size: 72, + bold: true, +}); + +// En los temas de... +const tTopics = createSimpleText({ + xPosition: 1.7, + yPosition: 8.9, + width: 19.31, + height: 1.5, + text: "En los temas de: Lineamientos del Ministerio de Transportes y Comunicaciones (MTC), Operatividad efectiva del equipo. Procedimiento de operación correcta. Equipamiento y mantenimiento preventivo, Medidas de Control y Tipos de IPERC, Cuidados de la maquina durante la operación, Uso de pedales y controles, Funcionamiento del motor;", + size: 22, + alignment: AlignmentType.JUSTIFIED, +}); + +// Con una duracion de... +const tDuration = createSimpleText({ + xPosition: 1.7, + yPosition: 11, + width: 19.31, + height: 1, + text: "Con una duración de 12 horas lectivas.", + size: 22, + alignment: AlignmentType.JUSTIFIED, +}); + +// Se expide certificado... +const tFinishLabel = createSimpleText({ + xPosition: 1.52, + yPosition: 11.65, + width: 20.1, + height: 0.8, + text: "Se expide el presente certificado para los fines que se estime conveniente", + size: 22, + alignment: AlignmentType.CENTER, + italics: true, +}); + +// Recuadro de foto +const photoSection = new Paragraph({ + frame: { + position: { + x: cmText(-1.5), + y: cmText(-1.9), + }, + width: cmText(2.81), + 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 mecanicaBasicaCert(props: CertData): Promise { + const imgQR = await getQR({ + iid: props.certIId, + dni: props.personDni, + height: 2.2, + width: 2.2, + horizontalOffset: 24.63, + verticalOffset: 17.01, + }); + + // N° XXXX-20XX-EEG + const tCertCode = createSimpleText({ + xPosition: -1.86, + yPosition: 1.8, + width: 3.67, + height: 0.8, + text: `N° ${props.certCode}-${props.certYear}-EEG`, + size: 20, + alignment: AlignmentType.CENTER, + }); + + // FERNANDO ARAOZ + const tName = createSimpleText({ + xPosition: 0.38, + yPosition: 4.35, + width: 24.08, + height: 1.51, + text: props.personFullName, + size: 52, + bold: true, + underline: {}, + }); + + // Identificado con DNI... + const tContentPart1 = new Paragraph({ + frame: { + position: { + x: cmText(1.7), + y: cmText(6.16), + }, + width: cmText(20.08), + height: cmText(1), + anchor: { + horizontal: FrameAnchorType.MARGIN, + vertical: FrameAnchorType.MARGIN, + }, + }, + children: [ + new TextRun({ + text: "Identificado con DNI N° ", + font: "Arial", + size: 22, + }), + new TextRun({ + text: props.personDni, + font: "Arial", + size: 22, + bold: true, + }), + new TextRun({ + text: ", al haber aprobado el curso de capacitación en:", + font: "Arial", + size: 22, + }), + ], + alignment: AlignmentType.LEFT, + }); + + // Fecha de Emision: ... + const certificateDate = new Paragraph({ + frame: { + position: { + x: cmText(17.25), + y: cmText(17), + }, + width: cmText(7.5), + height: cmText(0.9), + anchor: { + horizontal: FrameAnchorType.MARGIN, + vertical: FrameAnchorType.MARGIN, + }, + }, + children: [ + new TextRun({ + text: `Fecha de Emisión:\t\t${props.certDay} / ${props.certMonth} / ${props.certYear}`, + font: "Arial", + size: 18, + }), + new TextRun({ + text: `Fecha de Expiración:\t\t${props.certDay} / ${props.certMonth} / ${parseInt(props.certYear, 10) + 1}`, + font: "Arial", + size: 18, + break: 1, + }), + ], + alignment: AlignmentType.LEFT, + }); + + + const doc = new Document({ + sections: [ + { + properties: { + page: { + size: { + orientation: PageOrientation.LANDSCAPE, + }, + }, + }, + children: [ + tCertCode, + photoSection, + tCertificate, + tExpediteText, + tName, + tContentPart1, + tCourse, + tTopics, + tFinishLabel, + tDuration, + certificateDate, + new Paragraph({ + children: [ + imgFondoDoc, + imgCIP, + imgMichigan, + imgQR, + ], + }), + ], + }, + ], + }); + + // Return document as a buffer + return doc; +} diff --git a/frontend/src/certGenerator/certs/README.md b/frontend/src/certGenerator/certs/README.md new file mode 100644 index 0000000..82cd5ec --- /dev/null +++ b/frontend/src/certGenerator/certs/README.md @@ -0,0 +1,5 @@ +# Certs + +This folder contains files that generate certificates in DOCX format. + + diff --git a/frontend/src/certGenerator/certs/SEG_OP_MAQ_PES.ts b/frontend/src/certGenerator/certs/SEG_OP_MAQ_PES.ts new file mode 100644 index 0000000..95281b9 --- /dev/null +++ b/frontend/src/certGenerator/certs/SEG_OP_MAQ_PES.ts @@ -0,0 +1,254 @@ +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_seg_op_maq_pes.png", + height: 21.02, + width: 29.63, + horizontalOffset: 0.03, + verticalOffset: 0.04, + behindDocument: true, +}); + +const imgCIP = getImage({ + name: "colegio_ingenieros_logo.png", + height: 2.02, + width: 2.02, + horizontalOffset: 25.53, + verticalOffset: 14.17, +}); + +const imgMundomaq = getImage({ + name: "mundo_maq.png", + height: 4.82, + width: 6.73, + horizontalOffset: 20.87, + verticalOffset: 0.9, +}); + +const imgHazescorp = getImage({ + name: "logo_hazescorp.png", + height: 2.95, + width: 3.33, + horizontalOffset: 0.4, + verticalOffset: 17.73, +}); + +const imgEEG = getImage({ + name: "eeg_logo.png", + height: 2.38, + width: 4.94, + horizontalOffset: 1.66, + verticalOffset: 1.6, +}); + + + +// SEGURIDAD EN LA OPERACIÓN DE MAQUINARIA PESADA +const tCourse = createSimpleText({ + xPosition: 5.5, + yPosition: 6.65, + width: 12.83, + height: 1.5, + text: "SEGURIDAD EN LA OPERACIÓN DE MAQUINARIA PESADA", + size: 44, + bold: true, +}); + +// En temas de... +const tTopics = createSimpleText({ + xPosition: 1.65, + yPosition: 8.9, + width: 20.42, + height: 1.5, + text: "En los temas de: Seguridad en maquinaria pesada, IPERC, Prevención de riesgos con Maquinas, Medidas de prevención con máquinas en movimientos de tierra, Seguridad en la operación, Procedimiento de mantenimiento, Correcto llenado de IPERC, Correcto llenado de Check List, Mantenimiento Preventivo, predictivo, correctivo.", + size: 22, + font: "Arial", + alignment: AlignmentType.JUSTIFIED, +}); + +// Con una duracion... +const tHours = createSimpleText({ + xPosition: 1.65, + yPosition: 10.7, + width: 13.15, + height: 0.5, + text: "Con una duración de 36 horas lectivas", + size: 22, + font: "Arial", + alignment: AlignmentType.LEFT, +}); + +// Se expide certificado... +const tFinishLabel = createSimpleText({ + xPosition: 5.3, + yPosition: 11.5, + width: 13.15, + height: 0.5, + text: "Se expide el presente certificado para los fines que se estime conveniente", + size: 22, + font: "Arial", + italics: true, + alignment: AlignmentType.CENTER, +}); + + +// Recuadro de foto +const photoSection = new Paragraph({ + frame: { + position: { + x: cmText(22.62), + y: cmText(7), + }, + height: cmText(3.57), + width: cmText(2.81), + 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 segOpMaqPesCert(props: CertData): Promise { + const imgQR = await getQR({ + iid: props.certIId, + dni: props.personDni, + height: 2.01, + width: 2.01, + horizontalOffset: 25.53, + verticalOffset: 16.43, + }); + + // FERNANDO ARAOZ + const tName = createSimpleText({ + xPosition: 0, + yPosition: 4.15, + width: 23.56, + height: 1.5, + text: props.personFullName, + size: 52, + bold: true, + underline: {}, + }); + + // Identificado con DNI... + const tContentPart1 = new Paragraph({ + frame: { + position: { + x: cmText(1.4), + y: cmText(5.9), + }, + width: cmText(20.92), + height: cmText(0.6), + anchor: { + horizontal: FrameAnchorType.MARGIN, + vertical: FrameAnchorType.MARGIN, + }, + }, + children: [ + new TextRun({ + text: "Identificado con DNI N° ", + font: "Arial", + size: 22, + }), + new TextRun({ + text: props.personDni, + font: "Arial", + size: 24, + bold: true, + }), + new TextRun({ + text: ", al haber aprobado el curso de capacitación sobre:", + font: "Arial", + size: 22, + }), + ], + alignment: AlignmentType.CENTER, + }); + + // Fecha de Emision: ... + const certificateDate = createSimpleText({ + xPosition: 19.25, + yPosition: 16.2, + width: 5.87, + height: 0.5, + 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: 22.15, + yPosition: 10.85, + 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: [ + tName, + tContentPart1, + tCourse, + tTopics, + tHours, + tFinishLabel, + certificateDate, + photoSection, + tCertCode, + new Paragraph({ + children: [ + imgFondoDoc, + imgQR, + imgCIP, + imgMundomaq, + imgHazescorp, + imgEEG, + ], + }), + ], + }, + ], + }); + + // Return document as a buffer + return doc; +} diff --git a/frontend/src/certGenerator/certs/SUPERVISOR_ESCOLTA.ts b/frontend/src/certGenerator/certs/SUPERVISOR_ESCOLTA.ts new file mode 100644 index 0000000..7ffa980 --- /dev/null +++ b/frontend/src/certGenerator/certs/SUPERVISOR_ESCOLTA.ts @@ -0,0 +1,331 @@ +import { cmText, createSimpleTextP, getImage, getQR } from "./utils"; +import { CertData } from "./CertData"; + +const { + Document, Paragraph, PageOrientation, + FrameAnchorType, + TextRun, + AlignmentType, + BorderStyle, +} = window.docx; + +const imgFondoDoc = getImage({ + name: "fondo_super_escolta.png", + height: 20.99, + width: 29.69, + horizontalOffset: 0, + verticalOffset: 0, + behindDocument: true, +}); + +const imgEEG = getImage({ + name: "eeg_f_logo.png", + height: 4.39, + width: 6.4, + horizontalOffset: 1.19, + verticalOffset: 0.94, +}); + +const imgEATE = getImage({ + name: "eate_logo.jpg", + height: 2.28, + width: 3.39, + horizontalOffset: 0.94, + verticalOffset: 15.67, +}); + +const imgMTC = getImage({ + name: "mtc_logo.png", + height: 1.6, + width: 5, + horizontalOffset: 0.9, + verticalOffset: 18.3, +}); + +const imgOSHA = getImage({ + name: "osha_logo.png", + height: 1.99, + width: 5.72, + horizontalOffset: 22.58, + verticalOffset: 1.29, +}); + +const imgMatpelTransparent = getImage({ + name: "logo_matpel_transparente.png", + height: 18.63, + width: 18.63, + horizontalOffset: 5.38, + verticalOffset: 1.38, +}); + + + +const tCertificate = createSimpleTextP({ + xPosition: 9.18, + yPosition: 2.91, + width: 11.3, + height: 1.46, + text: "CERTIFICADO", + size: 72, + font: "Times New Roman", + bold: true, +}); + +// Se expide el presente +const tLabel3 = createSimpleTextP({ + xPosition: 11.08, + yPosition: 5.43, + width: 7.74, + height: 0.5, + text: "Se expide el presente a:", + size: 22, + font: "Arial", +}); + +// SUPERVISOR ESCOLTA MATPEL +const tCourse = createSimpleTextP({ + xPosition: 7.28, + yPosition: 8.75, + width: 15.42, + height: 1.5, + text: "SUPERVISOR ESCOLTA MATPEL", + size: 52, + bold: true, +}); + +// Por haber aprobado la dormacion... +const tLabel2 = createSimpleTextP({ + xPosition: 10.93, + yPosition: 7.95, + width: 7.74, + height: 0.6, + text: "Por haber aprobado la formación en el curso", + size: 22, + font: "Arial", +}); + +// Temas tratados... +const tTopics = createSimpleTextP({ + xPosition: 5.04, + yPosition: 10.3, + width: 19.34, + height: 1.5, + text: "Temas tratados: Inspección de Unidades y Llenado de Herramientas de Gestión Check List, IPERC, Inspección básica de Kit de Emergencias, Parada de Controles e Inspección, Delimitación y Evaluación del Evento, Sistemas de Comando de Incidentes, Practicas Reales en caso de Derrames de MATPEL con Trajes, Evacuación de Pacientes Afectados con Camioneta, Primeros Auxilios básico, Con una duración de 36 horas lectivas.", + size: 22, + font: "Arial", + alignment: AlignmentType.JUSTIFIED, +}); + +// Respaldado por: +const tHours = createSimpleTextP({ + xPosition: 1.15, + yPosition: 15, + width: 3.07, + height: 0.5, + text: "Respaldado por:", + size: 22, + font: "Arial", + italics: true, + alignment: AlignmentType.LEFT, +}); + +// Chile +const tChile = createSimpleTextP({ + xPosition: 3.07, + yPosition: 17.83, + width: 1.43, + height: 0.5, + text: "CHILE", + size: 22, + font: "Times New Roman", + bold: true, + alignment: AlignmentType.LEFT, +}); + +// Se expide certificado... +const tFinishLabel = createSimpleTextP({ + xPosition: 8.22, + yPosition: 12.68, + width: 13.15, + height: 0.5, + text: "Se expide el presente certificado para los fines que se estime conveniente", + size: 22, + font: "Arial", + italics: true, + alignment: AlignmentType.CENTER, +}); + + +// Recuadro de foto +const photoSection = new Paragraph({ + frame: { + position: { + x: cmText(25), + y: cmText(5.2), + }, + height: cmText(3.57), + width: cmText(2.81), + anchor: { + horizontal: FrameAnchorType.PAGE, + vertical: FrameAnchorType.PAGE, + }, + }, + children: [], + border: { + top: { + style: BorderStyle.DASHED, + size: 2, + }, + bottom: { + style: BorderStyle.DASHED, + size: 2, + }, + left: { + style: BorderStyle.DASHED, + size: 2, + }, + right: { + style: BorderStyle.DASHED, + size: 2, + }, + }, + alignment: AlignmentType.LEFT, +}); + + +export async function supervisorEscolta(props: CertData): Promise { + const imgQR = await getQR({ + iid: props.certIId, + dni: props.personDni, + height: 2.5, + width: 2.5, + horizontalOffset: 25.85, + verticalOffset: 17.49, + }); + + // FERNANDO ARAOZ + const tName = createSimpleTextP({ + xPosition: 3.78, + yPosition: 6.02, + width: 22.07, + height: 1.5, + text: props.personFullName, + size: 52, + bold: true, + underline: {}, + }); + + // Identificado con DNI... + const tContentPart1 = new Paragraph({ + frame: { + position: { + x: cmText(11.84), + y: cmText(7.35), + }, + width: cmText(6.02), + height: cmText(0.6), + anchor: { + horizontal: FrameAnchorType.PAGE, + vertical: FrameAnchorType.PAGE, + }, + }, + children: [ + new TextRun({ + text: "Identificado con DNI N° ", + font: "Arial", + size: 20, + }), + new TextRun({ + text: props.personDni, + font: "Arial", + size: 20, + bold: true, + }), + ], + alignment: AlignmentType.CENTER, + }); + + // Fecha de Emision: ... + const certificateDate = new Paragraph({ + frame: { + position: { + x: cmText(19.62), + y: cmText(19.2), + }, + width: cmText(5.87), + height: cmText(0.75), + anchor: { + horizontal: FrameAnchorType.PAGE, + vertical: FrameAnchorType.PAGE, + }, + }, + children: [ + new TextRun({ + text: `Fecha de Emisión:\t${props.certDay} / ${props.certMonth} / ${props.certYear}`, + font: "Arial", + size: 18, + }), + new TextRun({ + text: `Fecha de Expiración:\t${props.certDay} / ${props.certMonth} / ${parseInt(props.certYear, 10) + 1}`, + font: "Arial", + size: 18, + break: 1, + }), + ], + alignment: AlignmentType.LEFT, + }); + + // N° XXXX-20XX-EEG + const tCertCode = createSimpleTextP({ + xPosition: 24.59, + yPosition: 9, + width: 3.67, + height: 0.5, + 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, + tLabel3, + tName, + tLabel2, + tContentPart1, + tCourse, + tTopics, + tHours, + tFinishLabel, + certificateDate, + photoSection, + tCertCode, + tChile, + new Paragraph({ + children: [ + imgFondoDoc, + imgQR, + imgEATE, + imgMTC, + imgOSHA, + imgEEG, + imgMatpelTransparent, + ], + }), + ], + }, + ], + }); + + // Return document as a buffer + return doc; +} diff --git a/frontend/src/certGenerator/certs/utils.ts b/frontend/src/certGenerator/certs/utils.ts new file mode 100644 index 0000000..875ccbd --- /dev/null +++ b/frontend/src/certGenerator/certs/utils.ts @@ -0,0 +1,234 @@ +import * as QR from "qrcode"; + +const { AlignmentType, FrameAnchorType, HorizontalPositionRelativeFrom, + ImageRun, Paragraph, TextRun, + VerticalPositionRelativeFrom, convertMillimetersToTwip} = window.docx; + +// To define centimeters in a image's size +export function cm(centimeters: number) { + return convertMillimetersToTwip((100 * centimeters) / 150); +} + +// To define centimeters in a floating image's position +export function cmToEmu(cm: number) { + return Math.round(cm * 360000); +} + +// To define centimeters in a text +export function cmText(cm: number) { + return Math.round(cm * 567); +} + +type ImgConfig = { + name: string, + height: number, + width: number, + horizontalOffset: number, + verticalOffset: number, + behindDocument?: boolean, +} + +// Fetches images from the server +async function getImageData(imageName:string): Promise { + const response = await fetch(`assets/img/${imageName}`); + const blob = await response.blob(); + + return blob; +} + +export function getImage(data: ImgConfig): typeof ImageRun { + return new ImageRun({ + // Magic path, based on dist folder + data: getImageData(data.name), + transformation: { + height: cm(data.height), + width: cm(data.width), + }, + floating: { + zIndex: 0, + horizontalPosition: { + relative: HorizontalPositionRelativeFrom.LEFT_MARGIN, + offset: cmToEmu(data.horizontalOffset), + }, + verticalPosition: { + relative: VerticalPositionRelativeFrom.TOP_MARGIN, + offset: cmToEmu(data.verticalOffset), + }, + behindDocument: data.behindDocument ?? false, + }, + }); +} + +export async function getQR(data : { + /** ID of the entity in the DB */ + iid: number, + dni: string, + height: number, + width: number, + horizontalOffset: number, + verticalOffset: number, + behindDocument?: boolean, +}): Promise { + // Old URL: https://www.eegsac.com/alumnoscertificados.php?DNI=${dni()} + // New URL: https://eegsac.com/certificado/${dni()} + const qr = await QR.toDataURL(`https://eegsac.com/certificado/${data.dni}?iid=${data.iid}`, {margin: 1}); + + return new ImageRun({ + data: qr, + transformation: { + height: cm(data.height), + width: cm(data.width), + }, + floating: { + zIndex: 0, + horizontalPosition: { + relative: HorizontalPositionRelativeFrom.LEFT_MARGIN, + offset: cmToEmu(data.horizontalOffset), + }, + verticalPosition: { + relative: VerticalPositionRelativeFrom.TOP_MARGIN, + offset: cmToEmu(data.verticalOffset), + }, + behindDocument: data.behindDocument ?? false, + }, + }); +} + +export function createSimpleText(data: { + xPosition: number, + yPosition: number, + width: number, + height: number, + text: string, + font?: string, + bold?: boolean, + size: number, + underline?: Record, + alignment?: typeof AlignmentType, + italics?: boolean, + color?: string, +}): typeof Paragraph { + + return new Paragraph({ + frame: { + position: { + x: cmText(data.xPosition), + y: cmText(data.yPosition), + }, + width: cmText(data.width), + height: cmText(data.height), + anchor: { + horizontal: FrameAnchorType.MARGIN, + vertical: FrameAnchorType.MARGIN, + }, + }, + children: [ + new TextRun({ + text: data.text, + bold: data.bold, + font: data.font ?? "Arial", + size: data.size, + underline: data.underline, + italics: data.italics, + color: data.color, + }), + ], + alignment: data.alignment ?? AlignmentType.CENTER, + }); +} + +export function createSimpleTextP(data: { + xPosition: number, + yPosition: number, + width: number, + height: number, + text: string, + font?: string, + bold?: boolean, + size: number, + underline?: Record, + alignment?: typeof AlignmentType, + italics?: boolean, + color?: string, +}): typeof Paragraph { + + return new Paragraph({ + frame: { + position: { + x: cmText(data.xPosition), + y: cmText(data.yPosition), + }, + width: cmText(data.width), + height: cmText(data.height), + anchor: { + horizontal: FrameAnchorType.PAGE, + vertical: FrameAnchorType.PAGE, + }, + }, + children: [ + new TextRun({ + text: data.text, + bold: data.bold, + font: data.font ?? "Arial", + size: data.size, + underline: data.underline, + italics: data.italics, + color: data.color, + }), + ], + alignment: data.alignment ?? AlignmentType.CENTER, + }); +} + + +export enum Matpel { + _1, + _2, + _3, +} + +const matpel1Label = "MATPEL I - Advertencia"; +const matpel2Label = "MATPEL II - Operaciones"; +const matpel3Label = "MATPEL III - Técnico"; + +export function getMatpelLabel(matpel: Matpel) { + switch (matpel) { + case Matpel._1: { + return matpel1Label; + } + case Matpel._2: { + return matpel2Label; + } + case Matpel._3: { + return matpel3Label; + } + } +} + +export function getMatpelHours(matpel: Matpel): number { + switch (matpel) { + case Matpel._1: { + return 12; + } + case Matpel._2: { + return 12; + } + case Matpel._3: { + return 40; + } + } +} + +export function getMatpelFileName(matpel: Matpel) { + switch (matpel) { + case Matpel._1: { + return "MATPEL 1"; + } + case Matpel._2: { + return "MATPEL 2"; + } + case Matpel._3: { + return "MATPEL 3"; + } + } +} diff --git a/frontend/src/certGenerator/index.ts b/frontend/src/certGenerator/index.ts new file mode 100644 index 0000000..38c961e --- /dev/null +++ b/frontend/src/certGenerator/index.ts @@ -0,0 +1,70 @@ +import { saveAs } from "file-saver"; +import { _4X4Cert } from "./certs/4X4"; +import { CertData } from "./certs/CertData"; +import { matpelCert } from "./certs/MATPEL"; +import { Matpel } from "./certs/utils"; + +import { manejoDefensivoCert } from "./certs/MANEJO_DEFENSIVO"; +import { mecanicaBasicaCert } from "./certs/MECANICA_BASICA"; +import { segOpMaqPesCert } from "./certs/SEG_OP_MAQ_PES"; +import { supervisorEscolta } from "./certs/SUPERVISOR_ESCOLTA"; +import { ipercCert } from "./certs/IPERC"; + +declare global { + interface Window { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + docx: any, + } +} + +async function download(document: Document, filename: string = "filename.docx") { + const blob = await window.docx.Packer.toBlob(document); + saveAs(blob, filename); +} + +const certGeneratorGenerator = (generatorFn: (options: CertData) => Promise) => (options: CertData, filename: string) => { + generatorFn(options) + .then((doc) => { + download(doc, filename); + }); +}; + +export const certGenerator = Object.freeze({ + matpel1: (options: CertData, filename: string) => { + const options_f: CertData = { + ...options, + matpel: Matpel._1, + }; + matpelCert(options_f) + .then((doc) => { + download(doc, filename); + }); + }, + matpel2: (options: CertData, filename: string) => { + const options_f: CertData = { + ...options, + matpel: Matpel._2, + }; + matpelCert(options_f) + .then((doc) => { + download(doc, filename); + }); + }, + matpel3: (options: CertData, filename: string) => { + const options_f: CertData = { + ...options, + matpel: Matpel._3, + }; + matpelCert(options_f) + .then((doc) => { + download(doc, filename); + }); + }, + manejoDefensivo: certGeneratorGenerator(manejoDefensivoCert), + mecanicaBasica: certGeneratorGenerator(mecanicaBasicaCert), + "4x4": certGeneratorGenerator(_4X4Cert), + segOpMaqPes: certGeneratorGenerator(segOpMaqPesCert), + supervisorEscolta: certGeneratorGenerator(supervisorEscolta), + iperc: certGeneratorGenerator(ipercCert), +}); + diff --git a/frontend/src/certs/Registers/index.tsx b/frontend/src/certs/Registers/index.tsx index 3154d1b..3f46250 100644 --- a/frontend/src/certs/Registers/index.tsx +++ b/frontend/src/certs/Registers/index.tsx @@ -2,6 +2,7 @@ import { For, createEffect, createSignal } from "solid-js"; import { DownloadIcon } from "../../icons/DownloadIcon"; import { Person } from "../../types/Person"; import { Register } from "../../types/Register"; +import { courseMap } from "../../utils/allCourses"; export function Registers(props: {person: Person | null}) { const [registers, setRegisters] = createSignal>([]); @@ -38,12 +39,16 @@ export function Registers(props: {person: Person | null}) { } function RegisterEl(props: {register: Register}) { - const displayDate = () => { const [, year, month, day] = /(\d{4})-(\d{2})-(\d{2})/.exec(props.register.register_display_date) ?? []; return `${day}/${month}/${year}`; }; + const courseName = () => { + const course = courseMap()[props.register.register_course_id]; + return course?.course_name ?? "Curso no encontrado"; + }; + return (

- course_id: {props.register.register_course_id} + {courseName()}

{displayDate()} diff --git a/frontend/src/utils/allCourses.ts b/frontend/src/utils/allCourses.ts index 6d4ab41..f88b4bf 100644 --- a/frontend/src/utils/allCourses.ts +++ b/frontend/src/utils/allCourses.ts @@ -1,12 +1,24 @@ import { createSignal } from "solid-js"; import { Course } from "../types/Course"; +type CourseMap = {[k: number]: Course}; + export const [allCourses, setAllCourses] = createSignal>([]); +export const [courseMap, setCourseMap] = createSignal({}); (() => { // Get all courses from the API fetch(`${import.meta.env.VITE_BACKEND_URL}/api/course`) .then((res) => res.json()) - .then((data) => setAllCourses(data)); + .then((data: Array) => { + setAllCourses(data); + + const map: CourseMap = {}; + for (const course of data) { + map[course.course_id] = course; + } + console.log("course map", map); + setCourseMap(map); + }); })();