React
How to getting started with Joyfill in a React application
Overview
We currently support any React and JS. However, Joyfill Embedded or Joyfill Native can be interwoven to make up various combinations of solutions to fit your product's needs. We support really any JS setup whether you are using Angular, NextJS, ASP.Net for web, or even mobile JS hybrids like React-Native and Ionic.
Joyfill Platform solutions can all be used with just about any workflow you need.
Joyfill Platform Solutions
In this guide we will be using the following Joyfill Platform Solutions
Guide
userAccessTokens & identifiers will need to be stored on your end (usually on a user and set of existing form field-based data) in order to interact with our API and UI Components effectively
Project Requirements
- React and React DOM v18+
Install Dependency
npm install @joyfill/components
yarn add @joyfill/components
Implement your code
Below is a usable example of our react functionality.
Make sure to replace the userAccessToken
and documentId
. Note that documentID
is just for this example, you can call our List all documents endpoint and grab an ID from there.
import React, { useState, useEffect } from 'react';
import './App.css';
import Loading from "./Loading";
// 1. import the JoyDoc component
import { JoyDoc } from '@joyfill/components';
import {
joyfillSave,
joyfillGenerate,
joyfillRetrieve,
} from './api.js';
// 2, replace here with steps from our Getting Started -> Setup guide
const userAccessToken = '<replace_me>';
const documentId = '<replace_me>';
function App() {
const [ doc, setDoc ] = useState(null);
const [ mode, setMode ] = useState('edit');
const [pdfLink, setPdfLink] = useState(null);
const [loading, setLoading] = useState(null);
// retrieve the document from our api (you can also pass an initial documentId into JoyDoc)
useEffect(() => {
retrieveJofillDocument();
}, []);
const retrieveJofillDocument = async () => {
setLoading('retrieving document...');
const response = await joyfillRetrieve(documentId, userAccessToken);
setDoc(response);
setLoading(null);
}
// save the form and generate a pdf as an example
const saveForm = async (doc) => {
setLoading('saving document & generating pdf...');
await joyfillSave(doc, userAccessToken);
const downloadableLink = await joyfillGenerate(doc.identifier, userAccessToken);
setPdfLink(downloadableLink);
setLoading(null);
}
return (
<div className="App">
<div className="row">
<column>
<a
className="App-link"
href="https://docs.joyfill.io/"
target="_blank"
>
Checkout Joyfill Docs for More
</a>
</column>
<column>
<img src={'https://joyfill.io/wp-content/uploads/2022/03/Joyfill-logo-website-mobile.svg'} className="App-logo" alt="logo" />
</column>
<column>
<button onClick={() => setMode(mode === 'edit' ? 'fill' : 'edit')}>
Toggle Form Mode
</button>
<button onClick={() => saveForm(doc)}>
Save Changes
</button>
</column>
</div>
<Loading text={loading} />
{pdfLink && <a href={pdfLink} download>Download PDF</a>}
<div className='form'>
{/* 3. implement the JoyDoc in your react project */}
<JoyDoc
mode={mode}
doc={doc}
onChange={(params, changes, doc) => {
console.log('onChange doc: ', doc);
setDoc(doc);
}}
onUploadAsync={async ({ documentId }, fileUploads) => {
// to see a full utilization of upload see api.js -> examples
console.log('onUploadAsync: ', fileUploads);
}}
/>
</div>
</div>
);
}
export default App;
// 2. setup helpers
export const joyfillGenerate = async (identifier, userAccessToken) => {
const response = await fetch("https://api-joy.joyfill.io/v1/documents/exports/pdf", {
method: 'POST',
mode:'cors',
headers: {
Authorization: `Bearer ${userAccessToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ document: identifier })
});
const data = await response.json();
return data.download_url;
}
export const joyfillRetrieve = async (documentIdentifier, userAccessToken) => {
const response = await fetch(`https://api-joy.joyfill.io/v1/documents/${documentIdentifier}`, {
method: 'GET',
mode: 'cors',
headers: {
Authorization: `Bearer ${userAccessToken}`,
'Content-Type': 'application/json'
},
});
const data = await response.json();
return data;
}
export const joyfillSave = async (doc, userAccessToken) => {
const response = await fetch(`https://api-joy.joyfill.io/v1/documents/${doc.identifier}`, {
method: 'POST',
mode:'cors',
headers: {
Authorization: `Bearer ${userAccessToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
type: "document",
stage: "published",
name: "ACME_WorkOrder",
files: [doc.files[0]]
})
});
const data = await response.json();
return data;
};
export const getDataUriForFileUpload = async (fileUpload) => {
return new Promise((ful, rej) => {
const reader = new FileReader();
reader.readAsDataURL(fileUpload);
reader.onloadend = async () => {
ful(reader.result);
};
});
};
export const uploadFileAsync = async (documentId, dataUri, userAccessToken) => {
const response = await fetch(`https://api-joy.joyfill.io/v1/documents/${documentId}/files/datauri`, {
method: 'POST',
mode: 'no-cors',
headers: {
Authorization: `Bearer ${userAccessToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ file: dataUri })
});
const data = await response.json();
return data;
};
export const convertPDFPagesToBase64Async = async (dataUri, userAccessToken) => {
try {
const response = await fetch(`https://api-joy.joyfill.io/v1/utilities/pdf/to/png/datauri`, {
method: 'POST',
mode: 'no-cors',
headers: {
Authorization: `Bearer ${userAccessToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ file: dataUri })
});
return await response.json();
} catch (error) {
throw error;
}
};
import React from "react";
export default function LoadingSpinner({ text }) {
return text ? (
<>
<div className="spinner-container">
<div className="loading-spinner"></div>
</div>
<p>{text}...</p>
</>
) : null;
}
.App {
text-align: center;
}
.App-logo {
height: 7vmin;
pointer-events: none;
margin-top: 5px;
margin-left: -65px;
}
.App-body {
background-color: #282c34;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #0870f0;
}
.form {
border: 2px solid rgb(235, 235, 235);
margin: 15px;
padding: 15px;
border-radius: 10px;
}
.form a {
padding: 10px;
}
.row {
display: flex;
flex-direction: row;
flex: 1;
justify-content: space-between;
align-items: center;
background-color: rgb(235, 235, 235);
padding: 5px 0;
margin: 10px;
border-radius: 10px;
}
.row a {
padding: 0 30px;
}
button {
padding: 10px;
border-radius: 8px;
background-color: #0897f0;
color: white;
border: 0;
margin-right: 25px;
cursor: pointer;
}
.column {
flex-direction: column;
}
@keyframes spinner {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.loading-spinner {
width: 10px;
height: 10px;
border: 4px solid #f3f3f3;
border-top: 4px solid #383636;
border-radius: 70%;
animation: spinner 1.5s linear infinite;
}
.spinner-container {
display: grid;
justify-content: center;
align-items: center;
margin-top: 15px;
}
Updated about 2 months ago