[FE] Integrate cert generator

This commit is contained in:
Araozu 2023-08-29 11:14:33 -05:00
parent 58c7d069f9
commit 3c2d2c77c5
16 changed files with 2446 additions and 3 deletions

View File

@ -10,11 +10,15 @@
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inconsolata&family=Inter&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Inconsolata&family=Inter&display=swap" rel="stylesheet">
</head> </head>
<body> <body>
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div> <div id="root"></div>
<script src="/src/index.tsx" type="module"></script> <script src="/src/index.tsx" type="module"></script>
<!-- DOCX -->
<script src="https://unpkg.com/docx@8.2.2/build/index.umd.js"></script>
</body> </body>
</html> </html>

View File

@ -25,7 +25,9 @@
}, },
"dependencies": { "dependencies": {
"@solidjs/router": "^0.8.3", "@solidjs/router": "^0.8.3",
"@types/file-saver": "^2.0.5",
"@types/qrcode": "^1.5.1", "@types/qrcode": "^1.5.1",
"file-saver": "^2.0.5",
"qrcode": "^1.5.3", "qrcode": "^1.5.3",
"solid-js": "^1.7.6" "solid-js": "^1.7.6"
} }

View File

@ -8,9 +8,15 @@ dependencies:
'@solidjs/router': '@solidjs/router':
specifier: ^0.8.3 specifier: ^0.8.3
version: 0.8.3(solid-js@1.7.6) version: 0.8.3(solid-js@1.7.6)
'@types/file-saver':
specifier: ^2.0.5
version: 2.0.5
'@types/qrcode': '@types/qrcode':
specifier: ^1.5.1 specifier: ^1.5.1
version: 1.5.1 version: 1.5.1
file-saver:
specifier: ^2.0.5
version: 2.0.5
qrcode: qrcode:
specifier: ^1.5.3 specifier: ^1.5.3
version: 1.5.3 version: 1.5.3
@ -935,6 +941,10 @@ packages:
'@babel/types': 7.22.5 '@babel/types': 7.22.5
dev: true dev: true
/@types/file-saver@2.0.5:
resolution: {integrity: sha512-zv9kNf3keYegP5oThGLaPk8E081DFDuwfqjtiTzm6PoxChdJ1raSuADf2YGCVIyrSynLrgc8JWv296s7Q7pQSQ==}
dev: false
/@types/json-schema@7.0.12: /@types/json-schema@7.0.12:
resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==} resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==}
dev: true dev: true
@ -1768,6 +1778,10 @@ packages:
flat-cache: 3.0.4 flat-cache: 3.0.4
dev: true dev: true
/file-saver@2.0.5:
resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==}
dev: false
/fill-range@7.0.1: /fill-range@7.0.1:
resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
engines: {node: '>=8'} engines: {node: '>=8'}

View File

@ -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<null>): Promise<Document> {
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: `${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;
}

View File

@ -0,0 +1,42 @@
export type CertData<T> = {
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,
}

View File

@ -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<null>): Promise<Document> {
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: `${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;
}

View File

@ -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<null>): Promise<Document> {
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: `${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;
}

View File

@ -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<Matpel>): Promise<Document> {
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: `${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;
}

View File

@ -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<null>): Promise<Document> {
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: `${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;
}

View File

@ -0,0 +1,5 @@
# Certs
This folder contains files that generate certificates in DOCX format.

View File

@ -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<null>): Promise<Document> {
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: `${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;
}

View File

@ -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<null>): Promise<Document> {
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: `${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;
}

View File

@ -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<Blob> {
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<typeof ImageRun> {
// 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<string, never>,
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<string, never>,
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";
}
}
}

View File

@ -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 = <T>(generatorFn: (options: CertData<T>) => Promise<Document>) => (options: CertData<T>, filename: string) => {
generatorFn(options)
.then((doc) => {
download(doc, filename);
});
};
export const certGenerator = Object.freeze({
matpel1: (options: CertData<null>, filename: string) => {
const options_f: CertData<Matpel> = {
...options,
matpel: Matpel._1,
};
matpelCert(options_f)
.then((doc) => {
download(doc, filename);
});
},
matpel2: (options: CertData<null>, filename: string) => {
const options_f: CertData<Matpel> = {
...options,
matpel: Matpel._2,
};
matpelCert(options_f)
.then((doc) => {
download(doc, filename);
});
},
matpel3: (options: CertData<null>, filename: string) => {
const options_f: CertData<Matpel> = {
...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),
});

View File

@ -2,6 +2,7 @@ import { For, createEffect, createSignal } from "solid-js";
import { DownloadIcon } from "../../icons/DownloadIcon"; import { DownloadIcon } from "../../icons/DownloadIcon";
import { Person } from "../../types/Person"; import { Person } from "../../types/Person";
import { Register } from "../../types/Register"; import { Register } from "../../types/Register";
import { courseMap } from "../../utils/allCourses";
export function Registers(props: {person: Person | null}) { export function Registers(props: {person: Person | null}) {
const [registers, setRegisters] = createSignal<Array<Register>>([]); const [registers, setRegisters] = createSignal<Array<Register>>([]);
@ -38,12 +39,16 @@ export function Registers(props: {person: Person | null}) {
} }
function RegisterEl(props: {register: Register}) { function RegisterEl(props: {register: Register}) {
const displayDate = () => { const displayDate = () => {
const [, year, month, day] = /(\d{4})-(\d{2})-(\d{2})/.exec(props.register.register_display_date) ?? []; const [, year, month, day] = /(\d{4})-(\d{2})-(\d{2})/.exec(props.register.register_display_date) ?? [];
return `${day}/${month}/${year}`; return `${day}/${month}/${year}`;
}; };
const courseName = () => {
const course = courseMap()[props.register.register_course_id];
return course?.course_name ?? "Curso no encontrado";
};
return ( return (
<div class="inline-block w-[12rem] p-1 border border-c-outline rounded-md"> <div class="inline-block w-[12rem] p-1 border border-c-outline rounded-md">
<button class="rounded-full bg-c-primary-container hover:bg-c-primary transition-colors h-12 w-12"> <button class="rounded-full bg-c-primary-container hover:bg-c-primary transition-colors h-12 w-12">
@ -51,7 +56,7 @@ function RegisterEl(props: {register: Register}) {
</button> </button>
<div class="inline-block h-12 w-32 pl-2 align-middle"> <div class="inline-block h-12 w-32 pl-2 align-middle">
<p class="font-bold"> <p class="font-bold">
course_id: {props.register.register_course_id} {courseName()}
</p> </p>
<p class="font-mono text-sm"> <p class="font-mono text-sm">
{displayDate()} {displayDate()}

View File

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