How to Build an Invoicing App with Next.js, Strapi, and jspdf

This page summarizes the projects mentioned and recommended in the original post on dev.to

InfluxDB - Power Real-Time Data Analytics at Scale
Get real-time insights from all types of time series data with InfluxDB. Ingest, query, and analyze billions of data points in real-time with unbounded cardinality.
www.influxdata.com
featured
SaaSHub - Software Alternatives and Reviews
SaaSHub helps you find the best software and product alternatives
www.saashub.com
featured
  • axios

    Promise based HTTP client for the browser and node.js

    Axios: Axios is a JavaScript library used to send asynchronous HTTP requests to REST endpoints. It's commonly used to perform CRUD operations. Install Axios using this command:

  • InfluxDB

    Power Real-Time Data Analytics at Scale. Get real-time insights from all types of time series data with InfluxDB. Ingest, query, and analyze billions of data points in real-time with unbounded cardinality.

    InfluxDB logo
  • Invoicing-App

    Invoicing application built using Next.js, Strapi, and jsPDF

    "mb-4 flex flex-row justify-between"> "flex flex-col w-[45%]"> "block text-gray-700 text-sm font-bold mb-2" htmlFor="shippingAddress" > Shipping Address "shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="shippingAddress" name="shippingAddress" placeholder="Office address of recipient" onChange={handleInputChange} value={formFields.shippingAddress} required />

    "flex flex-col w-[45%]"> "invoiceNote" className="block text-gray-700 text-sm font-bold mb-2 w-full" > Invoice Note <span class="dl">"</span><span class="nx">shadow</span> <span class="nx">appearance</span><span class="o">-</span><span class="nx">none</span> <span class="nx">border</span> <span class="nx">rounded</span> <span class="nx">w</span><span class="o">-</span><span class="nx">full</span> <span class="nx">py</span><span class="o">-</span><span class="mi">2</span> <span class="nx">px</span><span class="o">-</span><span class="mi">3</span> <span class="nx">text</span><span class="o">-</span><span class="nx">gray</span><span class="o">-</span><span class="mi">700</span> <span class="nx">mb</span><span class="o">-</span><span class="mi">3</span> <span class="nx">leading</span><span class="o">-</span><span class="nx">tight</span> <span class="na">focus</span><span class="p">:</span><span class="nx">outline</span><span class="o">-</span><span class="nx">none</span> <span class="na">focus</span><span class="p">:</span><span class="nx">shadow</span><span class="o">-</span><span class="nx">outline</span><span class="dl">"</span><span class="s2"> id=</span><span class="dl">"</span><span class="nx">invoiceNote</span><span class="dl">"</span><span class="s2"> name=</span><span class="dl">"</span><span class="nx">invoiceNote</span><span class="dl">"</span><span class="s2"> placeholder=</span><span class="dl">"</span><span class="nx">Account</span> <span class="nx">details</span><span class="dl">"</span><span class="s2"> onChange={handleInputChange} value={formFields.invoiceNote} required /> </div> </div> <div className=</span><span class="dl">"</span><span class="nx">flex</span> <span class="nx">justify</span><span class="o">-</span><span class="nx">center</span> <span class="nx">items</span><span class="o">-</span><span class="nx">center</span><span class="dl">"</span><span class="s2">> <label htmlFor=</span><span class="dl">"</span><span class="nx">description</span><span class="dl">"</span><span class="s2"> className=</span><span class="dl">"</span><span class="nx">block</span> <span class="nx">text</span><span class="o">-</span><span class="nx">gray</span><span class="o">-</span><span class="mi">700</span> <span class="nx">text</span><span class="o">-</span><span class="nx">sm</span> <span class="nx">font</span><span class="o">-</span><span class="nx">bold</span> <span class="nx">mb</span><span class="o">-</span><span class="mi">2</span> <span class="nx">w</span><span class="o">-</span><span class="nx">full</span> <span class="nx">mr</span><span class="o">-</span><span class="mi">5</span><span class="dl">"</span><span class="s2"> > Invoice Item <input className=</span><span class="dl">"</span><span class="nx">shadow</span> <span class="nx">appearance</span><span class="o">-</span><span class="nx">none</span> <span class="nx">border</span> <span class="nx">rounded</span> <span class="nx">w</span><span class="o">-</span><span class="nx">full</span> <span class="nx">py</span><span class="o">-</span><span class="mi">2</span> <span class="nx">px</span><span class="o">-</span><span class="mi">3</span> <span class="nx">text</span><span class="o">-</span><span class="nx">gray</span><span class="o">-</span><span class="mi">700</span> <span class="nx">mb</span><span class="o">-</span><span class="mi">3</span> <span class="nx">leading</span><span class="o">-</span><span class="nx">tight</span> <span class="na">focus</span><span class="p">:</span><span class="nx">outline</span><span class="o">-</span><span class="nx">none</span> <span class="na">focus</span><span class="p">:</span><span class="nx">shadow</span><span class="o">-</span><span class="nx">outline</span><span class="dl">"</span><span class="s2"> id=</span><span class="dl">"</span><span class="nx">description</span><span class="dl">"</span><span class="s2"> name=</span><span class="dl">"</span><span class="nx">description</span><span class="dl">"</span><span class="s2"> type=</span><span class="dl">"</span><span class="nx">text</span><span class="dl">"</span><span class="s2"> placeholder=</span><span class="dl">"</span><span class="nx">Reason</span> <span class="k">for</span> <span class="nx">invoice</span><span class="dl">"</span><span class="s2"> onChange={handleInputChange} value={formFields.description} required /> </label> <label htmlFor=</span><span class="dl">"</span><span class="nx">qty</span><span class="dl">"</span><span class="s2"> className=</span><span class="dl">"</span><span class="nx">block</span> <span class="nx">text</span><span class="o">-</span><span class="nx">gray</span><span class="o">-</span><span class="mi">700</span> <span class="nx">text</span><span class="o">-</span><span class="nx">sm</span> <span class="nx">font</span><span class="o">-</span><span class="nx">bold</span> <span class="nx">mb</span><span class="o">-</span><span class="mi">2</span> <span class="nx">w</span><span class="o">-</span><span class="nx">full</span> <span class="nx">mr</span><span class="o">-</span><span class="mi">5</span><span class="dl">"</span><span class="s2"> > Quantity <input className=</span><span class="dl">"</span><span class="nx">shadow</span> <span class="nx">appearance</span><span class="o">-</span><span class="nx">none</span> <span class="nx">border</span> <span class="nx">rounded</span> <span class="nx">w</span><span class="o">-</span><span class="nx">full</span> <span class="nx">py</span><span class="o">-</span><span class="mi">2</span> <span class="nx">px</span><span class="o">-</span><span class="mi">3</span> <span class="nx">text</span><span class="o">-</span><span class="nx">gray</span><span class="o">-</span><span class="mi">700</span> <span class="nx">mb</span><span class="o">-</span><span class="mi">3</span> <span class="nx">leading</span><span class="o">-</span><span class="nx">tight</span> <span class="na">focus</span><span class="p">:</span><span class="nx">outline</span><span class="o">-</span><span class="nx">none</span> <span class="na">focus</span><span class="p">:</span><span class="nx">shadow</span><span class="o">-</span><span class="nx">outline</span><span class="dl">"</span><span class="s2"> id=</span><span class="dl">"</span><span class="nx">qty</span><span class="dl">"</span><span class="s2"> name=</span><span class="dl">"</span><span class="nx">qty</span><span class="dl">"</span><span class="s2"> type=</span><span class="dl">"</span><span class="nx">number</span><span class="dl">"</span><span class="s2"> onChange={handleInputChange} value={formFields.qty} required /> </label> <label htmlFor=</span><span class="dl">"</span><span class="nx">rate</span><span class="dl">"</span><span class="s2"> className=</span><span class="dl">"</span><span class="nx">block</span> <span class="nx">text</span><span class="o">-</span><span class="nx">gray</span><span class="o">-</span><span class="mi">700</span> <span class="nx">text</span><span class="o">-</span><span class="nx">sm</span> <span class="nx">font</span><span class="o">-</span><span class="nx">bold</span> <span class="nx">mb</span><span class="o">-</span><span class="mi">2</span> <span class="nx">w</span><span class="o">-</span><span class="nx">full</span> <span class="nx">mr</span><span class="o">-</span><span class="mi">5</span><span class="dl">"</span><span class="s2"> > Rate <input className=</span><span class="dl">"</span><span class="nx">shadow</span> <span class="nx">appearance</span><span class="o">-</span><span class="nx">none</span> <span class="nx">border</span> <span class="nx">rounded</span> <span class="nx">w</span><span class="o">-</span><span class="nx">full</span> <span class="nx">py</span><span class="o">-</span><span class="mi">2</span> <span class="nx">px</span><span class="o">-</span><span class="mi">3</span> <span class="nx">text</span><span class="o">-</span><span class="nx">gray</span><span class="o">-</span><span class="mi">700</span> <span class="nx">mb</span><span class="o">-</span><span class="mi">3</span> <span class="nx">leading</span><span class="o">-</span><span class="nx">tight</span> <span class="na">focus</span><span class="p">:</span><span class="nx">outline</span><span class="o">-</span><span class="nx">none</span> <span class="na">focus</span><span class="p">:</span><span class="nx">shadow</span><span class="o">-</span><span class="nx">outline</span><span class="dl">"</span><span class="s2"> id=</span><span class="dl">"</span><span class="nx">rate</span><span class="dl">"</span><span class="s2"> name=</span><span class="dl">"</span><span class="nx">rate</span><span class="dl">"</span><span class="s2"> type=</span><span class="dl">"</span><span class="nx">number</span><span class="dl">"</span><span class="s2"> onChange={handleInputChange} value={formFields.rate} required /> </label> <div className=</span><span class="dl">"</span><span class="nx">block</span> <span class="nx">text</span><span class="o">-</span><span class="nx">gray</span><span class="o">-</span><span class="mi">700</span> <span class="nx">text</span><span class="o">-</span><span class="nx">sm</span> <span class="nx">font</span><span class="o">-</span><span class="nx">bold</span> <span class="nx">mb</span><span class="o">-</span><span class="mi">2</span> <span class="nx">w</span><span class="o">-</span><span class="nx">full</span> <span class="nx">mr</span><span class="o">-</span><span class="mi">5</span><span class="dl">"</span><span class="s2">> <label>Total</label> <div className=</span><span class="dl">"</span><span class="nx">shadow</span> <span class="nx">appearance</span><span class="o">-</span><span class="nx">none</span> <span class="nx">border</span> <span class="nx">rounded</span> <span class="nx">w</span><span class="o">-</span><span class="nx">full</span> <span class="nx">py</span><span class="o">-</span><span class="mi">2</span> <span class="nx">px</span><span class="o">-</span><span class="mi">3</span> <span class="nx">text</span><span class="o">-</span><span class="nx">gray</span><span class="o">-</span><span class="mi">700</span> <span class="nx">mb</span><span class="o">-</span><span class="mi">3</span> <span class="nx">leading</span><span class="o">-</span><span class="nx">tight</span><span class="dl">"</span><span class="s2">> {formFields.total} </div> </div> </div> <hr className=</span><span class="dl">"</span><span class="nx">mt</span><span class="o">-</span><span class="mi">5</span> <span class="nx">border</span><span class="o">-</span><span class="mi">1</span><span class="dl">"</span><span class="s2"> /> <div className=</span><span class="dl">"</span><span class="nx">mt</span><span class="o">-</span><span class="mi">4</span> <span class="nx">flex</span> <span class="nx">justify</span><span class="o">-</span><span class="nx">center</span><span class="dl">"</span><span class="s2">> <button type=</span><span class="dl">"</span><span class="nx">button</span><span class="dl">"</span><span class="s2"> className=</span><span class="dl">"</span><span class="nx">py</span><span class="o">-</span><span class="mi">2</span> <span class="nx">px</span><span class="o">-</span><span class="mi">4</span> <span class="nx">border</span> <span class="nx">border</span><span class="o">-</span><span class="nx">transparent</span> <span class="nx">shadow</span><span class="o">-</span><span class="nx">sm</span> <span class="nx">text</span><span class="o">-</span><span class="nx">sm</span> <span class="nx">font</span><span class="o">-</span><span class="nx">medium</span> <span class="nx">rounded</span><span class="o">-</span><span class="nx">md</span> <span class="nx">text</span><span class="o">-</span><span class="nx">white</span> <span class="nx">bg</span><span class="o">-</span><span class="nx">green</span><span class="o">-</span><span class="mi">600</span> <span class="na">hover</span><span class="p">:</span><span class="nx">bg</span><span class="o">-</span><span class="nx">green</span><span class="o">-</span><span class="mi">700</span> <span class="na">focus</span><span class="p">:</span><span class="nx">outline</span><span class="o">-</span><span class="nx">none</span> <span class="na">focus</span><span class="p">:</span><span class="nx">ring</span><span class="o">-</span><span class="mi">2</span> <span class="na">focus</span><span class="p">:</span><span class="nx">ring</span><span class="o">-</span><span class="nx">offset</span><span class="o">-</span><span class="mi">2</span> <span class="na">focus</span><span class="p">:</span><span class="nx">ring</span><span class="o">-</span><span class="nx">green</span><span class="o">-</span><span class="mi">500</span><span class="dl">"</span><span class="s2"> onClick={handleSendInvoice} > {selectedInvoice ? </span><span class="dl">"</span><span class="nx">Update</span> <span class="nx">Invoice</span><span class="dl">"</span><span class="s2"> : </span><span class="dl">"</span><span class="nx">Send</span> <span class="nx">Invoice</span><span class="dl">"</span><span class="s2">} </button> </div> </form> </section> </main> </> ); }; export default InvoiceForm; </span></code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <h4> <a name="code-explanation" href="#code-explanation"> </a> Code explanation </h4> <ul> <li><p>After importing the necessary libraries, we defined the <code>InvoiceFormProps</code> interface by passing in three props. The first one <code>onClose</code> is the function to close the form. The second prop <code>setInvoices</code> is a function to update the invoices state in the parent component. The third one <code>selectedInvoice</code> is the invoice being edited if any has been selected.</p></li> <li><p>We then defined <code>Invoice</code> interfaces to type-check the props and state used in the component.</p></li> <li><p>The <code>initialState</code> object defines the initial state for the form fields. The <code>reducer</code> function updates the state based on the field and value provided to handle form field updates. The next line uses <code>useReducer</code> hook to manage form fields state, initializing with <code>initialState</code>. </p></li> <li><p>The first <code>useEffect</code> function is for pre-filling the form when user wants to edit the invoice. It runs whenever the <code>selectedInvoice</code> changes. If there is a <code>selectedInvoice</code> (indicating the user is editing an existing invoice), it populates the form fields with the invoice data. If there is no selected invoice, it resets the form fields to their initial values. <br> The <code>[selectedInvoice]</code> dependency array ensures this effect runs only when <code>selectedInvoice</code> changes.</p></li> <li><p>The second <code>useEffect</code> function is for calculating the total amount of the invoice, so that the total value of an invoice will be the quantity of the item multiplied by the rate being charged. This effect calculates the <code>total</code> whenever the <code>qty</code> or <code>rate</code> changes. <br> It extracts <code>qty</code> and <code>rate</code> from <code>formFields</code>, calculates the <code>total</code> by multiplying them, and then dispatches an action to update the <code>total</code> field in the state with the calculated value.</p></li> <li><p>The <code>handleInputChange</code> function handles changes to the form input fields and updates the corresponding state fields. <br> It destructures <code>name</code> and <code>value</code> from the event target and then dispatches an action to update the state field corresponding to <code>name</code> with the new <code>value</code>.</p></li> <li><p>The <code>handleSendInvoice</code> funtion handles the logic for sending an invoice to the Strapi backend. It sends a POST request to create a new invoice or a PUT request to update an existing one. It first extracts the necessary invoice details from <code>formFields</code> and then checks if <code>selectedInvoice</code> exists.</p></li> <li><p>If it exists, it means the user is updating an existing invoice. So it sends a PUT request to update the existing invoice on the server. It also updates the local state with the modified invoice data. </p></li> <li><p>If it does not exist, it means the user is creating a new invoice. So it sends a <code>POST</code> request to create a new invoice on the server. It then adds the newly created invoice to the local state.</p></li> <li><p>The <code>onClose</code> function is called to close the form whenever a user submits the form and the error handling to catch any errors during the request and log them to the console.</p></li> <li><p>The JSX for the invoice form is rendered. The form has a header that dynamically displays "Edit Invoice" or "Create Invoice" based on whether <code>selectedInvoice</code> is active.<br> The form is displayed with fields for the sender's name, email, recipient's email, dates, shipping address, invoice note, item description, quantity, rate, and total.<br> A button is provided to send the invoice, which triggers the <code>handleSendInvoice</code> function.</p></li> </ul> <h4> <a name="display-invoices" href="#display-invoices"> </a> Display Invoices </h4> <p>This component is responsible for displaying the invoice and its contents. When users retrieve invoices from Strapi, they will be displayed here, along with buttons for generating, updating, deleting, and downloading them. The form for creating an invoice will also be displayed on this page.</p> <p>In your <code>Invoices.tsx</code> component, paste these lines of code:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight javascript"><code><span class="k">import</span> <span class="nx">React</span><span class="p">,</span> <span class="p">{</span> <span class="nx">useEffect</span><span class="p">,</span> <span class="nx">useState</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">react</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="nx">axios</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">axios</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="nx">InvoiceForm</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./InvoiceForm</span><span class="dl">"</span><span class="p">;</span> <span class="kr">interface</span> <span class="nx">Invoice</span> <span class="p">{</span> <span class="p">[</span><span class="nx">x</span><span class="p">:</span> <span class="nx">string</span><span class="p">]:</span> <span class="nx">any</span><span class="p">;</span> <span class="nl">id</span><span class="p">:</span> <span class="nx">number</span><span class="p">;</span> <span class="nl">name</span><span class="p">:</span> <span class="nx">string</span><span class="p">;</span> <span class="nl">senderEmail</span><span class="p">:</span> <span class="nx">string</span><span class="p">;</span> <span class="nl">recipientEmail</span><span class="p">:</span> <span class="nx">string</span><span class="p">;</span> <span class="nl">date</span><span class="p">:</span> <span class="nx">string</span><span class="p">;</span> <span class="nl">dueDate</span><span class="p">:</span> <span class="nx">string</span><span class="p">;</span> <span class="nl">shippingAddress</span><span class="p">:</span> <span class="nx">string</span><span class="p">;</span> <span class="nl">invoiceNote</span><span class="p">:</span> <span class="nx">string</span><span class="p">;</span> <span class="nl">description</span><span class="p">:</span> <span class="nx">string</span><span class="p">;</span> <span class="nl">qty</span><span class="p">:</span> <span class="nx">number</span><span class="p">;</span> <span class="nl">rate</span><span class="p">:</span> <span class="nx">number</span><span class="p">;</span> <span class="nl">total</span><span class="p">:</span> <span class="nx">number</span><span class="p">;</span> <span class="p">}</span> <span class="kd">const</span> <span class="nx">Invoices</span><span class="p">:</span> <span class="nx">React</span><span class="p">.</span><span class="nx">FC</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{</span> <span class="kd">const</span> <span class="p">[</span><span class="nx">invoices</span><span class="p">,</span> <span class="nx">setInvoices</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="o"><</span><span class="nx">Invoice</span><span class="p">[]</span><span class="o">></span><span class="p">([]);</span> <span class="kd">const</span> <span class="p">[</span><span class="nx">isInvoiceFormOpen</span><span class="p">,</span> <span class="nx">setIsInvoiceFormOpen</span><span class="p">]</span> <span class="o">=</span> <span class="nf">useState</span><span class="p">(</span><span class="kc">false</span><span class="p">);</span> <span class="kd">const</span> <span class="p">[</span><span class="nx">selectedInvoice</span><span class="p">,</span> <span class="nx">setSelectedInvoice</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="o"><</span><span class="nx">Invoice</span> <span class="o">|</span> <span class="kc">null</span><span class="o">></span><span class="p">(</span><span class="kc">null</span><span class="p">);</span> <span class="nf">useEffect</span><span class="p">(()</span> <span class="o">=></span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">fetchInvoices</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{</span> <span class="nf">fetch</span><span class="p">(</span><span class="dl">"</span><span class="s2">http://localhost:1337/api/invoices?populate=invoice</span><span class="dl">"</span><span class="p">)</span> <span class="p">.</span><span class="nf">then</span><span class="p">((</span><span class="nx">res</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span> <span class="k">if </span><span class="p">(</span><span class="o">!</span><span class="nx">res</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="p">{</span> <span class="k">throw</span> <span class="k">new</span> <span class="nc">Error</span><span class="p">(</span><span class="dl">"</span><span class="s2">Network response was not ok</span><span class="dl">"</span><span class="p">);</span> <span class="p">}</span> <span class="k">return</span> <span class="nx">res</span><span class="p">.</span><span class="nf">json</span><span class="p">();</span> <span class="p">})</span> <span class="p">.</span><span class="nf">then</span><span class="p">((</span><span class="nx">data</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">Fetched invoices:</span><span class="dl">"</span><span class="p">,</span> <span class="nx">data</span><span class="p">);</span> <span class="k">if </span><span class="p">(</span><span class="nb">Array</span><span class="p">.</span><span class="nf">isArray</span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">data</span><span class="p">))</span> <span class="p">{</span> <span class="nf">setInvoices</span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">data</span><span class="p">);</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nf">error</span><span class="p">(</span><span class="dl">"</span><span class="s2">Fetched data is not an array</span><span class="dl">"</span><span class="p">);</span> <span class="p">}</span> <span class="p">})</span> <span class="p">.</span><span class="k">catch</span><span class="p">((</span><span class="nx">error</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nf">error</span><span class="p">(</span><span class="dl">"</span><span class="s2">Error fetching invoices:</span><span class="dl">"</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span> <span class="p">});</span> <span class="p">};</span> <span class="nf">fetchInvoices</span><span class="p">();</span> <span class="p">},</span> <span class="p">[]);</span> <span class="kd">const</span> <span class="nx">handleOpenInvoiceForm</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{</span> <span class="nf">setSelectedInvoice</span><span class="p">(</span><span class="kc">null</span><span class="p">);</span> <span class="nf">setIsInvoiceFormOpen</span><span class="p">(</span><span class="kc">true</span><span class="p">);</span> <span class="p">};</span> <span class="kd">const</span> <span class="nx">handleCloseInvoiceForm</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{</span> <span class="nf">setSelectedInvoice</span><span class="p">(</span><span class="kc">null</span><span class="p">);</span> <span class="nf">setIsInvoiceFormOpen</span><span class="p">(</span><span class="kc">false</span><span class="p">);</span> <span class="p">};</span> <span class="kd">const</span> <span class="nx">handleEditInvoice</span> <span class="o">=</span> <span class="p">(</span><span class="na">invoice</span><span class="p">:</span> <span class="nx">Invoice</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">Invoice being edited:</span><span class="dl">"</span><span class="p">,</span> <span class="nx">invoice</span><span class="p">);</span> <span class="nf">setSelectedInvoice</span><span class="p">(</span><span class="nx">invoice</span><span class="p">);</span> <span class="nf">setIsInvoiceFormOpen</span><span class="p">(</span><span class="kc">true</span><span class="p">);</span> <span class="p">};</span> <span class="kd">const</span> <span class="nx">handleDeleteInvoice</span> <span class="o">=</span> <span class="k">async </span><span class="p">(</span><span class="na">id</span><span class="p">:</span> <span class="nx">number</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span> <span class="k">try</span> <span class="p">{</span> <span class="nf">alert</span><span class="p">(</span><span class="dl">"</span><span class="s2">Are you sure you want to delete this invoice?</span><span class="dl">"</span><span class="p">);</span> <span class="k">await</span> <span class="nx">axios</span><span class="p">.</span><span class="k">delete</span><span class="p">(</span><span class="s2">`http://localhost:1337/api/invoices/</span><span class="p">${</span><span class="nx">id</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span> <span class="nf">setInvoices</span><span class="p">(</span><span class="nx">invoices</span><span class="p">.</span><span class="nf">filter</span><span class="p">((</span><span class="nx">invoice</span><span class="p">)</span> <span class="o">=></span> <span class="nx">invoice</span><span class="p">.</span><span class="nx">id</span> <span class="o">!==</span> <span class="nx">id</span><span class="p">));</span> <span class="p">}</span> <span class="k">catch </span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nf">error</span><span class="p">(</span><span class="nx">error</span><span class="p">);</span> <span class="p">}</span> <span class="p">};</span> <span class="k">return </span><span class="p">(</span> <span class="o"><</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="dl">"</span><span class="s2">flex flex-col items-center justify-center</span><span class="dl">"</span><span class="o">></span> <span class="o"><</span><span class="nx">section</span> <span class="nx">className</span><span class="o">=</span><span class="dl">"</span><span class="s2">w-[65%] flex flex-row justify-between py-4</span><span class="dl">"</span><span class="o">></span> <span class="o"><</span><span class="nx">h2</span> <span class="nx">className</span><span class="o">=</span><span class="dl">"</span><span class="s2">text-3xl text-gray-700 font-medium</span><span class="dl">"</span><span class="o">></span><span class="nx">INVOICE</span><span class="o"><</span><span class="sr">/h2</span><span class="err">> </span> <span class="o"><</span><span class="nx">button</span> <span class="nx">onClick</span><span class="o">=</span><span class="p">{</span><span class="nx">handleOpenInvoiceForm</span><span class="p">}</span> <span class="nx">className</span><span class="o">=</span><span class="dl">"</span><span class="s2">bg-green-500 p-2 w-30 text-white rounded-lg</span><span class="dl">"</span> <span class="o">></span> <span class="nx">Create</span> <span class="nx">invoice</span> <span class="o"><</span><span class="sr">/button</span><span class="err">> </span> <span class="o"><</span><span class="sr">/section</span><span class="err">> </span> <span class="p">{</span><span class="nx">isInvoiceFormOpen</span> <span class="o">&&</span> <span class="p">(</span> <span class="o"><</span><span class="nx">InvoiceForm</span> <span class="nx">onClose</span><span class="o">=</span><span class="p">{</span><span class="nx">handleCloseInvoiceForm</span><span class="p">}</span> <span class="nx">setInvoices</span><span class="o">=</span><span class="p">{</span><span class="nx">setInvoices</span><span class="p">}</span> <span class="nx">selectedInvoice</span><span class="o">=</span><span class="p">{</span><span class="nx">selectedInvoice</span><span class="p">}</span> <span class="sr">/</span><span class="err">> </span> <span class="p">)}</span> <span class="p">{</span><span class="nx">invoices</span><span class="p">.</span><span class="nx">length</span> <span class="o">===</span> <span class="mi">0</span> <span class="p">?</span> <span class="p">(</span> <span class="o"><</span><span class="nx">p</span><span class="o">></span><span class="nx">No</span> <span class="nx">invoice</span> <span class="nx">yet</span><span class="p">.</span><span class="o"><</span><span class="sr">/p</span><span class="err">> </span> <span class="p">)</span> <span class="p">:</span> <span class="p">(</span> <span class="o"><</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="dl">"</span><span class="s2">w-[70%]</span><span class="dl">"</span><span class="o">></span> <span class="o"><</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="dl">"</span><span class="s2">px-5 py-5 mx-auto</span><span class="dl">"</span><span class="o">></span> <span class="p">{</span><span class="nx">invoices</span><span class="p">.</span><span class="nf">map</span><span class="p">((</span><span class="nx">invoice</span><span class="p">)</span> <span class="o">=></span> <span class="p">(</span> <span class="o"><></span> <span class="o"><</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="dl">"</span><span class="s2">flex flex-wrap border-t-2 border-b-2 border-gray-200 border-opacity-60</span><span class="dl">"</span> <span class="nx">key</span><span class="o">=</span><span class="p">{</span><span class="nx">invoice</span><span class="p">.</span><span class="nx">id</span><span class="p">}</span> <span class="o">></span> <span class="o"><</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="dl">"</span><span class="s2">lg:w-1/3 md:w-full px-8 py-6 border-opacity-60</span><span class="dl">"</span><span class="o">></span> <span class="o"><</span><span class="nx">div</span><span class="o">></span> <span class="o"><</span><span class="nx">h2</span> <span class="nx">className</span><span class="o">=</span><span class="dl">"</span><span class="s2">text-base text-gray-900 font-medium mb-1</span><span class="dl">"</span><span class="o">></span> <span class="na">Issued</span><span class="p">:</span> <span class="o"><</span><span class="sr">/h2</span><span class="err">> </span> <span class="o"><</span><span class="nx">p</span> <span class="nx">className</span><span class="o">=</span><span class="dl">"</span><span class="s2">leading-relaxed text-sm mb-4</span><span class="dl">"</span><span class="o">></span> <span class="p">{</span><span class="nx">invoice</span><span class="p">.</span><span class="nx">attributes</span><span class="p">.</span><span class="nx">date</span><span class="p">}</span> <span class="o"><</span><span class="sr">/p</span><span class="err">> </span> <span class="o"><</span><span class="sr">/div</span><span class="err">> </span> <span class="o"><</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="dl">"</span><span class="s2">mt-12</span><span class="dl">"</span><span class="o">></span> <span class="o"><</span><span class="nx">h2</span> <span class="nx">className</span><span class="o">=</span><span class="dl">"</span><span class="s2">text-base text-gray-900 font-medium</span><span class="dl">"</span><span class="o">></span> <span class="na">Due</span><span class="p">:</span> <span class="o"><</span><span class="sr">/h2</span><span class="err">> </span> <span class="o"><</span><span class="nx">p</span> <span class="nx">className</span><span class="o">=</span><span class="dl">"</span><span class="s2">leading-relaxed text-sm mb-4</span><span class="dl">"</span><span class="o">></span> <span class="p">{</span><span class="nx">invoice</span><span class="p">.</span><span class="nx">attributes</span><span class="p">.</span><span class="nx">dueDate</span><span class="p">}</span> <span class="o"><</span><span class="sr">/p</span><span class="err">> </span> <span class="o"><</span><span class="sr">/div</span><span class="err">> </span> <span class="o"><</span><span class="sr">/div</span><span class="err">> </span> <span class="o"><</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="dl">"</span><span class="s2">lg:w-1/3 md:w-full px-8 py-6 border-l-2 border-gray-200 border-opacity-60</span><span class="dl">"</span><span class="o">></span> <span class="o"><</span><span class="nx">h2</span> <span class="nx">className</span><span class="o">=</span><span class="dl">"</span><span class="s2">text-base text-gray-900 font-medium mb-2</span><span class="dl">"</span><span class="o">></span> <span class="nx">Billed</span> <span class="na">To</span><span class="p">:</span> <span class="o"><</span><span class="sr">/h2</span><span class="err">> </span> <span class="o"><</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="dl">""</span><span class="o">></span> <span class="o"><</span><span class="nx">h2</span> <span class="nx">className</span><span class="o">=</span><span class="dl">"</span><span class="s2"> text-gray-900 text-sm mb-1 font-medium</span><span class="dl">"</span><span class="o">></span> <span class="nx">Recipient</span><span class="dl">'</span><span class="s1">s Email </h2> <p className="leading-relaxed text-sm mb-5"> {invoice.attributes.recipientEmail} </p> </div> <div> <h2 className=" text-gray-900 text-sm mb-1 font-medium"> Shipping Address </h2> <p className="leading-relaxed text-sm mb-4"> {invoice.attributes.shippingAddress} </p> </div> </div> <div className="lg:w-1/3 md:w-full px-8 py-6 border-l-2 border-gray-200 border-opacity-60"> <h2 className="text-base text-gray-900 font-medium mb-2"> From: </h2> <div className=""> <h2 className=" text-gray-900 text-sm mb-1 font-medium"> Sender</span><span class="dl">'</span><span class="nx">s</span> <span class="nx">Name</span> <span class="o"><</span><span class="sr">/h2</span><span class="err">> </span> <span class="o"><</span><span class="nx">p</span> <span class="nx">className</span><span class="o">=</span><span class="dl">"</span><span class="s2">leading-relaxed text-sm mb-5</span><span class="dl">"</span><span class="o">></span> <span class="p">{</span><span class="nx">invoice</span><span class="p">.</span><span class="nx">attributes</span><span class="p">.</span><span class="nx">name</span><span class="p">}</span> <span class="o"><</span><span class="sr">/p</span><span class="err">> </span> <span class="o"><</span><span class="sr">/div</span><span class="err">> </span> <span class="o"><</span><span class="nx">div</span><span class="o">></span> <span class="o"><</span><span class="nx">h2</span> <span class="nx">className</span><span class="o">=</span><span class="dl">"</span><span class="s2"> text-gray-900 text-sm mb-1 font-medium</span><span class="dl">"</span><span class="o">></span> <span class="nx">Sender</span><span class="dl">'</span><span class="s1">s Email </h2> <p className="leading-relaxed text-sm mb-4"> {invoice.attributes.senderEmail} </p> </div> </div> </div> <div className="w-full px-5 py-12 mx-auto"> <div className="flex flex-row justify-between border-b-2 border-gray-300"> <div> <h2 className="text-lg font-medium text-gray-700 mb-2"> Invoice Item </h2> </div> <div className="flex flex-row mb-2"> <p className="ml-2 text-lg font-medium text-gray-800"> Qty </p> <p className="ml-[6rem] text-lg font-medium text-gray-800"> Rate </p> <p className="ml-[6rem] text-lg font-medium text-gray-800"> Total </p> </div> </div> <div className="flex flex-row justify-between mt-4"> <div> <h2 className="text-base text-gray-700 mb-4"> {invoice.attributes.description} </h2> </div> <div className="flex flex-row mb-4"> <p className="ml-2 text-base text-gray-800"> {invoice.attributes.qty} </p> <p className="ml-[6rem] text-base text-gray-800"> ${invoice.attributes.rate} </p> <p className="ml-[6rem] text-base text-gray-800"> ${invoice.attributes.total} </p> </div> </div> <div className="grid justify-end pt-[2.5rem]"> <div className="flex flex-row justify-between"> <div> <h2 className="text-lg font-medium text-gray-700 mb-4"> Tax (0%) </h2> </div> <div className="flex flex-row"> <p className="ml-[10rem] text-base text-gray-800"> 0.00 </p> </div> </div> <div className="flex flex-row justify-between border-y-2 border-green-400"> <div className="pt-4"> <h2 className="text-lg font-medium text-gray-700 mb-4"> Amount due: </h2> </div> <div className="flex flex-row pt-4"> <p className="ml-[10rem] text-lg font-medium text-gray-800"> ${invoice.attributes.total}.00 </p> </div> </div> </div> </div> <div className="flex flex-row justify-between w-full mt-1"> <div> <button className="bg-blue-500 px-2 py-2 rounded text-white hover:bg-blue-600"> Download invoice </button> <button className="bg-green-500 px-2 py-2 rounded text-white hover:bg-green-600 ml-4" onClick={() => handleEditInvoice(invoice)} > Edit invoice </button> </div> <div className="flex justify-end bg-red-400 px-2 py-2 rounded text-white hover:bg-red-500"> <button onClick={() => handleDeleteInvoice(invoice.id)}> Delete invoice </button> </div> </div> </> ))} </div> </div> )} </div> ); }; export default Invoices; </span></code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <h4> <a name="code-explanation" href="#code-explanation"> </a> Code explanation </h4> <ul> <li><p>First, we imported the <code>InvoiceForm</code> component which will be used here, along with the libraries installed.</p></li> <li><p>Since we're working with TypeScript, we set TypeScript interface to define the structure of the invoice object.</p></li> <li><p>We then set three states. The first state <code>const [invoices, setInvoices] = useState<Invoice[]>([]);</code> is an array to store fetched invoices. The second state <code>const [isInvoiceFormOpen, setIsInvoiceFormOpen] = useState(false);</code> will manage the visibility of the <code>InvoiceForm</code>. The third state <code>const [selectedInvoice, setSelectedInvoice] = useState<Invoice | null>(null);</code> will store the invoice currently being edited.</p></li> <li><p>The <code>useEffect</code> hook is used to fetch invoices from the Strapi backend when the component mounts using the fetch API. The fetched data is stored in the invoices state.</p></li> <li><p>If the response is not OK, an error is thrown. If it's ok, the response is parsed as JSON. We also set a condition to check if the fetched invoices is an array so we'd be able to map through it to display the invoices. If is an array, it sets this data in the <code>invoices</code> state. If not, it logs an error.</p></li> <li><p>The <code>handleCloseInvoiceForm</code> and <code>handleCloseInvoiceForm</code> functions handle the opening and closing of the form modal.</p></li> <li><p>We defined the <code>handleEditInvoice</code> function that opens the invoice form pre-populated with the selected invoice's details for editing. It sets the <code>selectedInvoice</code> to the invoice to be edited and opens the invoice form by setting <code>isInvoiceFormOpen</code> to true.</p></li> <li><p>Next is the <code>handleDeleteInvoice</code> function that deletes an invoice by selecting its <code>id</code> and sending a <code>DELETE</code> request to the API. This removes or filters out the deleted invoice from the invoices state, and logs any error that will occur during the request.</p></li> <li><p>The component renders a list of invoices by mapping through the <code>invoices</code> array and rendering each invoice with a unique key. Each invoice displays details and buttons for editing and deleting.<br> If the <code>isInvoiceFormOpen</code> state is true, the <code>InvoiceForm</code> component is rendered for creating or editing invoices.</p></li> <li><p>This JSX will also conditionally render a message if there are no invoices to be displayed, otherwise renders the list of invoices. This is added so that the page doesn't look blank when there are no invoices to be displayed.</p></li> </ul> <h3> <a name="adding-the-jspdf-download-functionality" href="#adding-the-jspdf-download-functionality"> </a> Adding the jsPDF Download Functionality </h3> <p>To enable users download any created invoice in PDF format, let's utilize the jsPDF library. We'll also customize the PDF format a bit.</p> <p>In your <code>Invoices.tsx</code> component:</p> <h4> <a name="step-1-import-jspdf-and-autotable-plugin" href="#step-1-import-jspdf-and-autotable-plugin"> </a> Step 1: Import jsPDF and autotable Plugin </h4> <p>Import the jsPDF library and the autotable plugin in the component:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight javascript"><code><span class="k">import</span> <span class="nx">jsPDF</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">jspdf</span><span class="dl">'</span><span class="p">;</span> <span class="k">import</span> <span class="dl">'</span><span class="s1">jspdf-autotable</span><span class="dl">'</span><span class="p">;</span> </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <h4> <a name="step-2-allow-generating-tables-in-pdfs" href="#step-2-allow-generating-tables-in-pdfs"> </a> Step 2: Allow Generating Tables in PDFs </h4> <p>The custom class <code>PDFWithAutoTable</code> is included to extend jsPDF to include the <code>autoTable</code> method for generating tables in PDFs.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight javascript"><code><span class="kd">class</span> <span class="nc">PDFWithAutoTable</span> <span class="kd">extends</span> <span class="nc">jsPDF</span> <span class="p">{</span> <span class="nf">autoTable</span><span class="p">(</span><span class="nx">options</span><span class="p">:</span> <span class="nx">any</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// @ts-ignore</span> <span class="k">super</span><span class="p">.</span><span class="nf">autoTable</span><span class="p">(</span><span class="nx">options</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <h4> <a name="step-3-handle-invoice-download" href="#step-3-handle-invoice-download"> </a> Step 3: Handle Invoice Download </h4> <p>The last step is to create a function to handle invoice download and add this function to the "Download invoice" button in the JSX. <br> The <code>handleDownloadPDF</code> function initializes a new <code>PDFWithAutoTable</code> document, sets the font size and style. It sets the invoice data to be included in the table '<code>const tableData[{ }]</code>'. It then uses <code>autoTable</code> property from the jspdf-autotable library to add the data to the PDF in a table format. </p> <p>The last line saves the generated PDF with a filename that includes the invoice ID for easy identification.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight javascript"><code><span class="kd">const</span> <span class="nx">handleDownloadPDF</span> <span class="o">=</span> <span class="p">(</span><span class="nx">invoice</span><span class="p">:</span> <span class="nx">Invoice</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">doc</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">PDFWithAutoTable</span><span class="p">();</span> <span class="c1">// Set the font size and style</span> <span class="nx">doc</span><span class="p">.</span><span class="nf">setFontSize</span><span class="p">(</span><span class="mi">12</span><span class="p">);</span> <span class="nx">doc</span><span class="p">.</span><span class="nf">setFont</span><span class="p">(</span><span class="dl">"</span><span class="s2">helvetica</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">normal</span><span class="dl">"</span><span class="p">);</span> <span class="c1">// Tabular format of the invoice with corresponding information</span> <span class="kd">const</span> <span class="nx">tableData</span> <span class="o">=</span> <span class="p">[</span> <span class="p">[</span><span class="dl">"</span><span class="s2">Invoice id</span><span class="dl">"</span><span class="p">,</span> <span class="s2">`</span><span class="p">${</span><span class="nx">invoice</span><span class="p">.</span><span class="nx">id</span><span class="p">}</span><span class="s2">`</span><span class="p">],</span> <span class="p">[</span><span class="dl">"</span><span class="s2">Sender's name</span><span class="dl">"</span><span class="p">,</span> <span class="s2">`</span><span class="p">${</span><span class="nx">invoice</span><span class="p">.</span><span class="nx">attributes</span><span class="p">.</span><span class="nx">name</span><span class="p">}</span><span class="s2">`</span><span class="p">],</span> <span class="p">[</span><span class="dl">"</span><span class="s2">Sender's email</span><span class="dl">"</span><span class="p">,</span> <span class="s2">`</span><span class="p">${</span><span class="nx">invoice</span><span class="p">.</span><span class="nx">attributes</span><span class="p">.</span><span class="nx">senderEmail</span><span class="p">}</span><span class="s2">`</span><span class="p">],</span> <span class="p">[</span><span class="dl">"</span><span class="s2">Recipient's email</span><span class="dl">"</span><span class="p">,</span> <span class="s2">`</span><span class="p">${</span><span class="nx">invoice</span><span class="p">.</span><span class="nx">attributes</span><span class="p">.</span><span class="nx">recipientEmail</span><span class="p">}</span><span class="s2">`</span><span class="p">],</span> <span class="p">[</span><span class="dl">"</span><span class="s2">Invoice date</span><span class="dl">"</span><span class="p">,</span> <span class="s2">`</span><span class="p">${</span><span class="nx">invoice</span><span class="p">.</span><span class="nx">attributes</span><span class="p">.</span><span class="nx">date</span><span class="p">}</span><span class="s2">`</span><span class="p">],</span> <span class="p">[</span><span class="dl">"</span><span class="s2">Due date</span><span class="dl">"</span><span class="p">,</span> <span class="s2">`</span><span class="p">${</span><span class="nx">invoice</span><span class="p">.</span><span class="nx">attributes</span><span class="p">.</span><span class="nx">dueDate</span><span class="p">}</span><span class="s2">`</span><span class="p">],</span> <span class="p">[</span><span class="dl">"</span><span class="s2">Shipping address</span><span class="dl">"</span><span class="p">,</span> <span class="s2">`</span><span class="p">${</span><span class="nx">invoice</span><span class="p">.</span><span class="nx">attributes</span><span class="p">.</span><span class="nx">shippingAddress</span><span class="p">}</span><span class="s2">`</span><span class="p">],</span> <span class="p">[</span><span class="dl">"</span><span class="s2">Invoice note</span><span class="dl">"</span><span class="p">,</span> <span class="s2">`</span><span class="p">${</span><span class="nx">invoice</span><span class="p">.</span><span class="nx">attributes</span><span class="p">.</span><span class="nx">invoiceNote</span><span class="p">}</span><span class="s2">`</span><span class="p">],</span> <span class="p">[</span><span class="dl">"</span><span class="s2">Invoice description</span><span class="dl">"</span><span class="p">,</span> <span class="s2">`</span><span class="p">${</span><span class="nx">invoice</span><span class="p">.</span><span class="nx">attributes</span><span class="p">.</span><span class="nx">description</span><span class="p">}</span><span class="s2">`</span><span class="p">],</span> <span class="p">[</span><span class="dl">"</span><span class="s2">Item quantity</span><span class="dl">"</span><span class="p">,</span> <span class="s2">`(</span><span class="p">${</span><span class="nx">invoice</span><span class="p">.</span><span class="nx">attributes</span><span class="p">.</span><span class="nx">qty</span><span class="p">}</span><span class="s2">)`</span><span class="p">],</span> <span class="p">[</span><span class="dl">"</span><span class="s2">Rate</span><span class="dl">"</span><span class="p">,</span> <span class="s2">`</span><span class="p">${</span><span class="nx">invoice</span><span class="p">.</span><span class="nx">attributes</span><span class="p">.</span><span class="nx">rate</span><span class="p">}</span><span class="s2">`</span><span class="p">],</span> <span class="p">[</span><span class="dl">"</span><span class="s2">Total</span><span class="dl">"</span><span class="p">,</span> <span class="s2">`</span><span class="p">${</span><span class="nx">invoice</span><span class="p">.</span><span class="nx">attributes</span><span class="p">.</span><span class="nx">total</span><span class="p">}</span><span class="s2">`</span><span class="p">],</span> <span class="p">];</span> <span class="c1">// Customizing the table</span> <span class="nx">doc</span><span class="p">.</span><span class="nf">autoTable</span><span class="p">({</span> <span class="na">startY</span><span class="p">:</span> <span class="mi">40</span><span class="p">,</span> <span class="na">head</span><span class="p">:</span> <span class="p">[[</span><span class="dl">"</span><span class="s2">Item</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">Details</span><span class="dl">"</span><span class="p">]],</span> <span class="na">body</span><span class="p">:</span> <span class="nx">tableData</span><span class="p">,</span> <span class="na">headStyles</span><span class="p">:</span> <span class="p">{</span> <span class="na">fontSize</span><span class="p">:</span> <span class="mi">18</span><span class="p">,</span> <span class="na">fontStyle</span><span class="p">:</span> <span class="dl">"</span><span class="s2">bold</span><span class="dl">"</span> <span class="p">},</span> <span class="na">styles</span><span class="p">:</span> <span class="p">{</span> <span class="na">fontSize</span><span class="p">:</span> <span class="mi">15</span><span class="p">,</span> <span class="na">fontStyle</span><span class="p">:</span> <span class="dl">"</span><span class="s2">semibold</span><span class="dl">"</span> <span class="p">},</span> <span class="p">});</span> <span class="c1">// To save the PDF with a specific filename. In this case, with the invoice id</span> <span class="nx">doc</span><span class="p">.</span><span class="nf">save</span><span class="p">(</span><span class="s2">`Invoice_</span><span class="p">${</span><span class="nx">invoice</span><span class="p">.</span><span class="nx">id</span><span class="p">}</span><span class="s2">.pdf`</span><span class="p">);</span> <span class="p">};</span> </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>You're free to customize the PDF any way you want to. Here's a list of <a href="https://artskydj.github.io/jsPDF/docs/index.html">jsPDF classes</a>.</p> <p>Add an <code>onClick</code> event to the download button and you're set.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight html"><code><span class="nt"><button</span> <span class="na">onClick=</span><span class="s">{()</span> <span class="err">=</span><span class="nt">></span> handleDownloadPDF(invoice)}> Download invoice <span class="nt"></button></span> </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>That's it! We've been able to build a functional invoicing app using Strapi as the backend to store the invoice data.</p> <h2> <a name="demo-time" href="#demo-time"> </a> Demo Time! </h2> <ul> <li><p>Create invoice demo.<br> <a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7gx5mpvh89frroc7tubl.gif" class="article-body-image-wrapper"><img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7gx5mpvh89frroc7tubl.gif" alt="Image description" loading="lazy" width="600" height="323" data-animated="true"></a></p></li> <li><p>Edit invoice demo.<br> <a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm72j6mmajqq5kbm2zdud.gif" class="article-body-image-wrapper"><img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm72j6mmajqq5kbm2zdud.gif" alt="Image description" loading="lazy" width="600" height="323" data-animated="true"></a></p></li> <li><p>Delete invoice demo.<br> <a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbgrv0l97uskfh5zle39n.gif" class="article-body-image-wrapper"><img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbgrv0l97uskfh5zle39n.gif" alt="Image description" loading="lazy" width="600" height="323" data-animated="true"></a></p></li> </ul> <p>If you followed the steps in this tutorial, you should have a functional invoicing app where users can create, edit, and delete invoices on the frontend. You'll also be able to manage the data on the Strapi backend and also download the invoice in a PDF format.</p> <h2> <a name="conclusion" href="#conclusion"> </a> Conclusion </h2> <p>In this tutorial, we explored the steps involved in creating an invoicing app using technologies like Next.js for the frontend development, Strapi for the backend content management, and jsPDF for PDF generation. </p> <p>We also learnt how to set up the development environment, creating the data collection in Strapi, how to connect the Strapi backend to the frontend, how to implement CRUD operations in Strapi, and how to integrate PDF generation functionality.</p> <p>Using an invoicing app offers ready-made templates that allow quick generation of invoices and helps one keep track of outstanding invoices and due dates.</p> <p>For reference, here's the <a href="https://github.com/OmaJuliet/Invoicing-App">GitHub repository</a> where you can view the complete code for this project.</p> <h2> <a name="additionalrelated-resources" href="#additionalrelated-resources"> </a> Additional/Related Resources </h2> <ul> <li> <a href="https://github.com/OmaJuliet/Invoicing-App">Github Repo</a> for this project.</li> <li> <a href="https://strapi.io/blog/how-to-build-an-invoice-generator-app-with-next-js-strapi-and-tailwind-css">How to Build an Invoice Generator App with Next.js, Strapi & Tailwind CSS</a>.</li> <li> <a href="https://strapi.io/blog/how-to-build-a-react-pdf-invoice-generator-app-with-refine-and-strapi">How to Build a React PDF Invoice Generator App with refine and Strapi</a>. </li> </ul>

NOTE: The number of mentions on this list indicates mentions on common posts plus user suggested alternatives. Hence, a higher number means a more popular project.

Suggest a related project

Related posts

  • Simple SVG Animations

    1 project | dev.to | 12 Jul 2024
  • Scanning Documents to Web Pages with JavaScript and Dynamic Web TWAIN RESTful API

    2 projects | dev.to | 17 Jun 2024
  • How to Read a JSON File in JavaScript

    1 project | dev.to | 11 Jun 2024
  • Axios Adds Fetch Adapter

    1 project | news.ycombinator.com | 8 May 2024
  • React Magic: How to Create a Custom Hook for Seamless Data Retrieval.

    1 project | dev.to | 30 Apr 2024

Did you konow that JavaScript is
the 3rd most popular programming language
based on number of metions?