Save/export PDF from Appgyver+Github - walkthrough

Hello everyone, for sometime i’ve being trying to implement the export PDF function to my app, to save a ‘Service Order’ of my business, unfortunately Appgyver don’t support third parties library yet, so i came up with a solution to work around this problem, I’ll explain my method and give the walkthrough to anyone who needs it.

In my case I need to save a PDF with some information from my backend database so, i coded a html page to retrieve the parameter from Appgyver send it to the database, retrieve the data from my backend and print a PDF with the data, after that I hosted the index.html on hostgator, but you can host it on Github using the same method in this guide:

For me all i needed was an ID parameter to send to my backend that I passed using the flow function ‘open web browser’ when i clicked a button, with the formula:

‘myurl.com.br/index.html?id=‘id_i_want_to_send’’


Using ? as a indicative of parameter to the target url, in my html code I used the command url search parameter to retrieve the ID from Appgyver then stored in a variable to use later, lke this:

let urlParamsOs = new URLSearchParams(window.location.search);
let os_id = urlParamsOs.get('id');

So if you want to send more than one parameter, all you need to do is use:
‘?parameterName=‘your_parameter’’ and ‘new URLSearchParams(window.location.search)’ for each parameter, like this:

On Appgyver:

https://username.github.io/appgyver-deploy/index.html?param1=p1?param2=p2 and so on, where:
‘param1’ is the name of your parameter;
‘p1’ is the parameter it self;

On your html code:

let urlParams1 = new URLSearchParams(window.location.search);
let parameter1 = urlParamsOs.get(‘param1’);
let urlParams2 = new URLSearchParams(window.location.search);
let parameter2 = urlParamsOs.get(‘param2’);

You can use the name you want but the parameter name in ‘urlParamsOs.get’ needs to be the exact parameter name in your url to work.

After that i just coded the html to my needs and how I wanted the PDF result to be then save it, unfortunately my code has some sensitive info I can’t share but I’ll leave here the most part of it including the jsPDF libraries i used:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>Gerar PDF</title>
</head>

<body onload="generatePDF()"> //here the pdf generates automatically when the page loads

// I used a lot of jsPDF libraries to make shure the code works
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.4.0/jspdf.umd.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf-autotable/3.5.28/jspdf.plugin.autotable.min.js"
        integrity="sha512-03CCNkeosDFN2zCCu4vLpu3pJfZcrL48F3yB8k87ejT+OVMwco7IH3FW02vtbGhdncS6gyYZ/duYaC/K62xQPQ=="
        crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.4.0/jspdf.umd.js"
        integrity="sha512-3LA5JnOY0t71uVlxhnpeBlDAeWc9cDSS83/TV9VctW3qSRPlEN7XDYsoEllTeJWUinWno2401LGunftBEhepXg=="
        crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.4.0/polyfills.umd.js"
        integrity="sha512-o4YDtieOrM60o1MlFM365jeuZPo8kUFtl46qn0oTtLVcyKs82JVaDhLhCIq28mYVvGoeenqVdBvOcA+ia5eryA=="
        crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.4.0/polyfills.umd.min.js"
        integrity="sha512-1hJbZYD+iAQ+06SZL+GF660EyonOu8lhvKVsDfbqf279ihyIgSX8SO2oFSawAjfb5enKKF41gAo0AkFn+7VtPA=="
        crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <script src="https://unpkg.com/jspdf-autotable@3.5.28/dist/jspdf.plugin.autotable.js"></script> // You need to use this library if you gonna use tables in the pdf
    <script>

        // links to my API endpoint
        let osApiEndpoint = 'link1';
        let clienteApiEndpoint = `link2`;

        // Get the OS ID from the URL
        let urlParamsOs = new URLSearchParams(window.location.search);Preformatted text
        let os_id = urlParamsOs.get('id');

        //labels of my Service order
        let labels = {
//my labels
};


        getOsData(); // command to run function

        //Function to retrieve os Data and return the json
        function getOsData() {
            return fetch(osApiEndpoint+`?os_id=`+os_id)
                .then(response => response.json())
                .then(data => {
                    console.log(data);
                    return data;
                })
                .catch(error => {
                    console.error(error);
                    return null;
                });
        };

        //Function to retrieve client data and return the json
        async function getClientData() {
            let osData = await getOsData();
            return fetch(clienteApiEndpoint+`?id=`+osData.clientes_id)
                .then(response => response.json())
                .then(data => {
                    console.log(data);
                    return data;
                })
                .catch(error => {
                    console.error(error);
                    return null;
                });
        }

        //Function to generate the PDF with determined style i needed
        async function generatePDF() {
            let clienteData = await getClientData(); //Command to wait the client data before generate the pdf, only works with 'async function'
            let osData = await getOsData(); //Command to wait the os data
            console.log(osData);
            console.log(clienteData);
            let doc = new window.jspdf.jsPDF({
                unit: `mm`,
            });

            doc.setFontSize(20);
            doc.setFont("helvetica", "bold");
            let osName = `ORDEM DE SERVIÇO Nº` + osData.id;
            doc.text(osName, 10, 20);

            doc.setFontSize(12);
            doc.setLineWidth(0.5);

            let agendamento = new Date(osData.agendamento).toLocaleDateString()
            let inicio = new Date(osData.inicio).toLocaleDateString()
            let conclusao = new Date(osData.conclusao).toLocaleDateString()
            let endereco = clienteData.endereco + clienteData.complemento + ` - ` + clienteData.cep

            let trueTags = [];

            Object.keys(osData.tag).forEach(key => {
                if (osData.tag[key] === true) {
                    trueTags.push(` ` + key);
                }
            });

            //Constants i need to set the table parameters
            const titleSize = 14;
            const valueSize = 12;
            const headerFillColor = 70;
            const headerTextColor = 255;
            const bodyFillColor = 255;
            const BodyTextColor = 0;
            const fontFamily = `helvetica`;
            const fontType = `bold`;
            const maxWidth = 210 - 14.1;
            const cellHalfWidth = ((210 / 2) - 14.1);
            const lineHeight = `linebreak`;
            const alignH = `left`;
            const alignV = `top`;
            const headerCSS = { halign: alignH, fillColor: headerFillColor, textColor: headerTextColor, fontStyle: fontType, fontSize: titleSize };
            const bodyCSS = { cellWidth: cellHalfWidth, cellPadding: { top: 5, bottom: 5, left: 2, right: 2 }, textColor: BodyTextColor, font: fontFamily, overflow: lineHeight, halign: alignH, valign: alignV, fontSize: valueSize };
            const resumoCSS = { margin: { top: 2, bottom: 2, left: 7.05, right: 7.05 }, cellPadding: { top: 5, bottom: 5, left: 2, right: 2 }, textColor: BodyTextColor, font: fontFamily, overflow: lineHeight, halign: alignH, valign: alignV, fontSize: valueSize };
            
            //Table on my PDF
            doc.autoTable({
                theme: `grid`,
                styles: headerCSS,
                body: [
                    [`Cliente`],
                ],
                startY: 42,
            });
            if ((clienteData.telefone === '' || clienteData.telefone === null) && (clienteData.contato === '' || clienteData.contato === null)) {
                var body = [[clienteData.name, endereco],
                ];
            } else {
                var body = [[clienteData.name, endereco],
                [clienteData.telefone, clienteData.contato],
                ];
            };

            doc.autoTable({
                theme: `grid`,
                bodyStyles: bodyCSS,
                body: body,
                startY: doc.lastAutoTable.finalY,
            });
            doc.autoTable({
                theme: `grid`,
                styles: headerCSS,
                body: [
                    [`Atendimento`],
                ],
                startY: doc.lastAutoTable.finalY,
            });
            doc.autoTable({
                theme: `grid`,
                bodyStyles: bodyCSS,
                body: [
                    [agendamento, osData.tipo],
                    [osData.inicio, osData.conclusao],
                ],
                startY: doc.lastAutoTable.finalY,
            });
            doc.autoTable({
                theme: `grid`,
                styles: headerCSS,
                body: [
                    [`Status técnico da OS:`],
                ],
                startY: doc.lastAutoTable.finalY,
            });

            trueTags = '';
            osData.descricao = ``;
            if ((osData.descricao === '' || osData.descricao === null) && (trueTags === '' || trueTags === null)) {
                var body2 = [[osData.situacao, osData.user]];
            } else {
                var body2 = [[osData.situacao, osData.user],
                [osData.descricao, trueTags]];
            };

            doc.autoTable({
                theme: `grid`,
                bodyStyles: bodyCSS,
                body: body2,
                startY: doc.lastAutoTable.finalY,
            });
            doc.autoTable({
                theme: `grid`,
                styles: headerCSS,
                body: [
                    [`Resumo da atividade:`],
                ],
                startY: doc.lastAutoTable.finalY,
            });
            doc.autoTable({
                theme: `grid`,
                bodyStyles: resumoCSS,
                body: [
                    [osData.resumo],
                ],
                startY: doc.lastAutoTable.finalY,
            });



// command to save the PDF with a specific name
doc.save(`ordem-de-servico-${osData.id}.pdf`);
            
            //command to close this window
            window.close();
            
        }

    </script>
</body>

</html>

The pdf result from this code:
ordem-de-servico-40 (2).pdf (7.1 KB)

Depending on the elements and the command you use you’ll may need a different library, you can check the commands here:

https://artskydj.github.io/jsPDF/docs/jsPDF.html

https://simonbengtsson.github.io/jsPDF-AutoTable/

I started coding in February this year so i used ChatGPT to help me, thats why the code may not be the cleanest or the most efficient, but I hope this guide can help someone, I’ll keep looking this post to answer any questions.

Best regards from Brazil :grinning: :grinning:

1 Like

Very impressive! Ive found myself looking for workarounds similar to this and making graphs. I have fkund there are so many companies offering free or cheap apis that can do many of the things I need. I made a video making a pdf file using a free tier in craftmypdf.com which i will share below. Keep up the tutorial making!

2 Likes

And I would go even further with Xano or Firestore cloud functions.
There is a simple “html to pdf” API, which generates pdfs from your input HTML. This is easily templated and can be called even from within AG with an HTTP request. Please ping me so that I don’t forget to write more detailed information on this. :sweat_smile:

1 Like

Xano has this pre built?

No, but since most of the documentation of these apis hava a “cUrl” example, it is not an issue to integrate any of those into Xano.

I use Xano as my backend, and i find their solution to generate PDF with their API very confusing, that why i did this script, it works but its confusing…

You need to install a PDF generator API to yours instance but it free