Building an Customizable Invoice Generator App with Refine, Strapi & Ant Design | Part II
Tags: javascript, react, invoice, webdev
Published: 2022-03-03 08:19:35

---
canonical_url: https://refine.dev/blog/refine-invoice-generator/
---

![Overview](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/447teokzxyhtqxllkw9f.gif)
 
Looking for an invoice generator that is easy to use and lets you customize your invoices? With **refine** simple and intuitive interface, you can create your own invoices in few hours. Plus, we offer a wide range of templates and customization options so you can get the exact look you want. Learn more about our invoice generator here!

## Introduction

We are almost ready to launch our **refine** Invoice Generator. In this Part II article, we'll customize it just a little more and then take an in-depth look at what you can do with the finished **refine** product!

In this part, we will create a missions part for the services your company provides. Then we will create our invoice page using these missions and the clients and contacts we created in Part I. In addition, you will not only be able to create your invoices, but you will also be able to view and download these invoices as PDF.

**This article is written as a continuation of our [Develop your Own Customizable Invoice Generator with Refine](https://refine.dev/blog/refine-react-admin-invoice-genarator/) article. If you have not read Part I, we recommend that you read Part I before reading this article.**

Let's see together how easily and in a short time we can develop our project with its refine features.

## Create New Strapi Collections

In our Part I article, we created our company, contact and client collections. In this section, let's create the `Missions` and `Invoice` [Strapi](https://strapi.io/) collections for the missions and invoices pages.


`Mission Collection:`

-   Mission(Mission Title): Text
-   Mission_description: Text
-   Day: Number
-   Daily_rate: Number


![Strapi Mission](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7ytblnk287j83f28tljy.png)


`Invoice Collection:`

-   Name: Text
-   Date: Date
-   Company : Relation with Company
-   Discount : Number
-   Tax: Number
-   Custom_id: Text
-   Contact: Relation with Contact
-   Missions: Relation with Mission


![Strapi Invoice Collection](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jbskkurqlq54y0587rkf.png)
 
 We created our missions and invoice collections fields. Our goal here is to define the products or services you offer specifically to your company and to create invoices based on them. By determining how many working days a product or service will last and its price on a daily basis, the total will be automatically reflected on your invoice. Now let's create our **refine** Missions page using this collection. And let's understand better by creating an example missions with **refine**.


## Refine Missions Page

Let's use the refine-antd package's [useTable](https://refine.dev/docs/ui-frameworks/antd/hooks/table/useTable/) hook to create our page, and let's define the fields in our Table Component.

`src/pages/MissionList.tsx:`

```tsx 
import {
    List,
    Table,
    useTable,
    TagField,
    useModalForm,
    EditButton,
} from "@pankod/refine-antd";

import { IMission } from "interfaces";

export const MissionList: React.FC = () => {
    const { tableProps } = useTable<IMission>();

    return (
        <List>
            <Table {...tableProps}>
                <Table.Column dataIndex="id" title="ID" />
                <Table.Column dataIndex="mission" title="Mission" />
                <Table.Column
                    dataIndex="mission_description"
                    title="Mission Description"
                />
                <Table.Column dataIndex="day" title="Day(s)" />
                <Table.Column
                    dataIndex="daily_rate"
                    title="Daily Rate"
                    render={(value) => <TagField value={value} color="red" />}
                />
                <Table.Column<IMission>
                    title="Total"
                    render={(_, record) => {
                        return (
                            <TagField
                                value={`${record.daily_rate * record.day} $`}
                                color="green"
                            />
                        );
                    }}
                />
                <Table.Column<IMission>
                    title="Actions"
                    dataIndex="actions"
                    key="actions"
                    render={(_value, record) => (
                        <EditButton
                            hideText
                            size="small"
                            recordItemId={record.id}
                            onClick={() => editShow(record.id)}
                        />
                    )}
                />
            </Table>
        </List>
    );
};
```

We defined the fields we created on the strapi side with the **refine** Table and created our table. Let's take a look at how our table looks like.


![Refine Missions Page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zld9nwzl7cw9mwgp6zbg.png)

As you can see, we were able to create and display our table very simply thanks to the **refine**. Let's learn how to create a Mission from our refine interface now.


### Refine Missions Create Page

Let's create a modal component for our `Mission Create` page. Let's connect our fields using Modal and Form from the `refine-antd` package.

`src/components/mission/CreateMission.tsx:`

```tsx
import {
    Modal,
    Form,
    Input,
    ModalProps,
    FormProps,
    InputNumber,
} from "@pankod/refine-antd";

type CreateMissionProps = {
    modalProps: ModalProps;
    formProps: FormProps;
};

export const CreateMission: React.FC<CreateMissionProps> = ({
    modalProps,
    formProps,
}) => {
    return (
        <Modal {...modalProps} title="Create Contact">
            <Form {...formProps} layout="vertical">
                <Form.Item
                    label="Title"
                    name="mission"
                    rules={[
                        {
                            required: true,
                        },
                    ]}
                >
                    <Input />
                </Form.Item>
                <Form.Item label="Description" name="mission_description">
                    <Input />
                </Form.Item>
                <Form.Item label="Day(s)" name="day">
                    <InputNumber defaultValue={1} />
                </Form.Item>
                <Form.Item label="Daily Rate" name="daily_rate">
                    <InputNumber defaultValue={1} />
                </Form.Item>
            </Form>
        </Modal>
    );
};
```

Let's define the `CreateMission` component we created above in our `MissionList` and fill its props with **refine** [**useModalForm**](https://refine.dev/docs/ui-frameworks/antd/hooks/form/useModalForm/).

`src/pages/MissionList.tsx:`

```tsx
import {
    List,
    Table,
    useTable,
    TagField,
    useModalForm,
} from "@pankod/refine-antd";

import { IMission } from "interfaces";
import { CreateMission, EditMission } from "components/mission";

export const MissionList: React.FC = () => {
    const { tableProps } = useTable<IMission>();

    const { formProps, modalProps, show } = useModalForm({
        resource: "missions",
        action: "create",
    });

    return (
        <>
            <List
                createButtonProps={{
                    onClick: () => {
                        show();
                    },
                }}
            >
                <Table {...tableProps}>
                    <Table.Column dataIndex="id" title="ID" />
                    <Table.Column dataIndex="mission" title="Mission" />
                    <Table.Column
                        dataIndex="mission_description"
                        title="Mission Description"
                    />
                    <Table.Column dataIndex="day" title="Day(s)" />
                    <Table.Column
                        dataIndex="daily_rate"
                        title="Daily Rate"
                        render={(value) => (
                            <TagField value={value} color="red" />
                        )}
                    />
                    <Table.Column<IMission>
                        title="Total"
                        render={(_, record) => {
                            return (
                                <TagField
                                    value={`${
                                        record.daily_rate * record.day
                                    } $`}
                                    color="green"
                                />
                            );
                        }}
                    />
                </Table>
            </List>
            <CreateMission modalProps={modalProps} formProps={formProps} />
        </>
    );
};
```

![Refine Mission Create Page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hu3gs4pahpfcucprimdm.gif)

Missions Page is now ready, you can create and manage your business's products or services here with **refine**.

Our next step is to create invoices according to these tasks and clients. Let's create, organize and display invoices with **refine**.

## Refine Invoices Page

Let's put the `Invoice Collections` fields that we created with Strapi into our Table by fetch the refine-antd useTable. Our Invoice collection has a relation with the client, company and missions collections.

Thanks to the [refine-strapi-v4 dataProvider](https://refine.dev/docs/guides-and-concepts/data-provider/strapi-v4/#relations-population), we can use the data of collections that are related to each other.

In order to use the fields of the collections that are related to each other, we must populate the collections in `metaData`.

Populate the contacts, companies and missions related with our Invoice collection in metaData.

`src/pages/InvoiceList.tsx:`

```tsx
import {
    List,
    Table,
    useTable,
    DateField,
    TagField,
    EmailField,
    Space,
    DeleteButton,
    EditButton,
    Icons,
    Button,
    Modal,
} from "@pankod/refine-antd";

import { IInvoice } from "interfaces";
import { PdfLayout } from "components/pdf";

const { FilePdfOutlined } = Icons;

export const InvoiceList: React.FC = () => {
    const { tableProps } = useTable<IInvoice>({
        metaData: {
            populate: {
                contact: { populate: ["client"] },
                company: { populate: ["logo"] },
                missions: "*",
            },
        },
    });

    return (
        <>
            <List>
                <Table {...tableProps}>
                    <Table.Column dataIndex="id" title="ID" />
                    <Table.Column<IInvoice>
                        dataIndex="name"
                        title="Invoice Name"
                        render={(_, record) => {
                            return `Invoice_#${record.id}${record.name}`;
                        }}
                    />
                    <Table.Column<IInvoice>
                        dataIndex="date"
                        title="Invoice Date"
                        render={(value) => (
                            <DateField format="LL" value={value} />
                        )}
                    />
                    <Table.Column
                        dataIndex={["company", "name"]}
                        title="Your Company"
                    />
                    <Table.Column
                        dataIndex={"missions"}
                        title="Missions"
                        render={(value) => {
                            return value.map((item: any) => {
                                return (
                                    <TagField
                                        color="blue"
                                        value={item.mission}
                                    />
                                );
                            });
                        }}
                    />
                    <Table.Column
                        dataIndex="discount"
                        title="Discount(%)"
                        render={(value) => (
                            <TagField color="blue" value={value} />
                        )}
                    />
                    <Table.Column
                        dataIndex="tax"
                        title="Tax(%)"
                        render={(value) => (
                            <TagField color="cyan" value={value} />
                        )}
                    />
                    <Table.Column
                        dataIndex="custom_id"
                        title="Custom Invoice ID"
                    />

                    <Table.Column
                        dataIndex={["contact", "email"]}
                        title="Contact"
                        render={(value) => <EmailField value={value} />}
                    />
                    <Table.Column<IInvoice>
                        title="Actions"
                        dataIndex="actions"
                        render={(_, record) => {
                            return (
                                <Space>
                                    <EditButton
                                        hideText
                                        size="small"
                                        recordItemId={record.id}
                                    />
                                    <DeleteButton
                                        hideText
                                        size="small"
                                        recordItemId={record.id}
                                    />
                                </Space>
                            );
                        }}
                    />
                </Table>
            </List>
        </>
    );
};
```

![Refine Invoice List](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/53urs4k7kwheru8o5jrz.png)

As you can see, we were able to list invoices with **refine**. Using the Invoice collection and the fields associated with it, we can create a fully featured Invoice.

Our invoice contains all the information. With `Refine Invoice Generator` you can define the company issuing the invoice, discount percentage, tax percentage, customId and similar information in a single invoice.

Let's understand better by creating an invoice example from our **refine** UI.

### Refine Create Invoice Page

Here, we first fetch the company, contacts and missions using the **refine**'s [useSelect](https://refine.dev/docs/ui-frameworks/antd/hooks/field/useSelect/) hook, and by giving it to the Select component, we create selectable components to detail our invoice.

Then, we fill our refine [Create](https://refine.dev/docs/ui-frameworks/antd/components/basic-views/create/) and Form components with the fields of the collection in the strap to perform a creation process as we did in our previous examples.


`src/pages/invoice/CreateInvoice:`

```tsx 
import { IResourceComponentsProps } from "@pankod/refine-core";

import {
    Create,
    Form,
    Input,
    Select,
    useForm,
    useSelect,
    DatePicker,
} from "@pankod/refine-antd";

import { ICompany, IContact, IMission, IInvoice } from "interfaces";

export const CreateInvoice: React.FC<IResourceComponentsProps> = () => {
    const { formProps, saveButtonProps } = useForm<IInvoice>();

    const { selectProps: companySelectProps } = useSelect<ICompany>({
        resource: "companies",
        optionLabel: "name",
    });

    const { selectProps: contactSelectProps } = useSelect<IContact>({
        resource: "contacts",
        optionLabel: "first_name",
    });

    const { selectProps: missionSelectProps } = useSelect<IMission>({
        resource: "missions",
        optionLabel: "mission",
    });

    return (
        <Create saveButtonProps={saveButtonProps}>
            <Form {...formProps} layout="vertical">
                <Form.Item label="Invoice Name" name="name">
                    <Input />
                </Form.Item>
                <Form.Item
                    label="Select Your Company"
                    name="company"
                    rules={[
                        {
                            required: true,
                        },
                    ]}
                >
                    <Select {...companySelectProps} />
                </Form.Item>

                <Form.Item
                    label="Mission"
                    name="missions"
                    rules={[
                        {
                            required: true,
                        },
                    ]}
                >
                    <Select {...missionSelectProps} mode="multiple" />
                </Form.Item>
                <Form.Item label="Discount(%)" name="discount">
                    <Input />
                </Form.Item>
                <Form.Item label="Tax(%)" name="tax">
                    <Input />
                </Form.Item>
                <Form.Item label="Custom ID" name="custom_id">
                    <Input />
                </Form.Item>
                <Form.Item
                    label="Contact"
                    name="contact"
                    rules={[
                        {
                            required: true,
                        },
                    ]}
                >
                    <Select {...contactSelectProps} />
                </Form.Item>
                <Form.Item label="Invoice Date" name="date">
                    <DatePicker style={{ width: "50%" }} />
                </Form.Item>
            </Form>
        </Create>
    );
};
```

![Refine Invoice Create](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1uufvuil14ljs4i2v55m.gif)
 
Our invoice generator is almost ready! As you can see, we can now create a fully featured invoice with **refine** and display it in our table. As the last step, let's view and download the invoices we created as PDF.

## View and Download Invoice as PDF

In this example, we will use the [KendoReact PDF](https://www.telerik.com/kendo-react-ui/components/pdfprocessing/) package to view as PDF. Let's start our process by installing our package.

Let's start our process by installing our package.

### Installation

```bash
npm i @progress/kendo-react-pdf
```

### Usage

To begin, let's create a pdf layout and add props to receive the data in our Invoice List.

`src/components/pdf/PdfLayout.tsx:`

```tsx 
import { useRef } from "react";

import "./pdf.css";

import { PDFExport } from "@progress/kendo-react-pdf";
import { IInvoice } from "interfaces";

type PdfProps = {
    record: IInvoice | undefined;
};

export const PdfLayout: React.FC<PdfProps> = ({ record }) => {
    return <></>;
};
```

Let's create a button to display our PDF arrangement in the Invoice List, as well as a modal component for this field to appear.

`src/pages/invoice/InvoiceList.tsx:`

```tsx 

import { useState } from "react";
import { useModal } from "@pankod/refine-core";
import {
    List,
    Table,
    useTable,
    DateField,
    TagField,
    EmailField,
    Space,
    DeleteButton,
    EditButton,
    Icons,
    Button,
    Modal,
} from "@pankod/refine-antd";

import { IInvoice } from "interfaces";
import { PdfLayout } from "components/pdf";

const { FilePdfOutlined } = Icons;

export const InvoiceList: React.FC = () => {
    const [record, setRecord] = useState<IInvoice>();

    const { tableProps } = useTable<IInvoice>({
        metaData: {
            populate: {
                contact: { populate: ["client"] },
                company: { populate: ["logo"] },
                missions: "*",
            },
        },
    });

    const { show, visible, close } = useModal();

    return (
        <>
            <List>
                <Table {...tableProps}>
                    ...
                    <Table.Column<IInvoice>
                        title="Actions"
                        dataIndex="actions"
                        render={(_, record) => {
                            return (
                                <Space>
                                    <EditButton
                                        hideText
                                        size="small"
                                        recordItemId={record.id}
                                    />
                                    <DeleteButton
                                        hideText
                                        size="small"
                                        recordItemId={record.id}
                                    />
                                    <Button
                                        size="small"
                                        icon={<FilePdfOutlined />}
                                        onClick={() => {
                                            setRecord(record);
                                            show();
                                        }}
                                    />
                                </Space>
                            );
                        }}
                    />
                </Table>
            </List>
            <Modal visible={visible} onCancel={close} width={700} footer={null}>
                <PdfLayout record={record} />
            </Modal>
        </>
    );
};
```

We`ve created a button in our Invoice List and when this button is pressed, we show the Modal component that contains the PdfLayout we created. Finally, as you can see, we passed the record data in the Invoice List as PdfLayout props.

Now that we have the data of the Invoices we can edit the PdfLayout.

[Check out PdfLayout design and code](https://github.com/pankod/refine/tree/master/examples/blog/invoiceGenerator/src/components/pdf/pdfLayout.tsx)

![Refine Invoice PDF](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/87lypf3domajaconct5q.gif)
 
## Conclusion

In this post, we've created a fully customizable and completely functional Invoice Generator app. If you want to construct an application similar to this, you may add any feature with **refine** and personalize your invoice generator to your liking. We developed in very short amount of time, thanks to **refine**'s features and the possibilities it provides for customization.

You can develop any web application or admin panel you want in a very short time with **refine**.

With **refine**'s headless and SSR-Next.js features, it is possible and very easy to develop both **B2B** and **B2C** applications using a single framework.

### Live CodeSandbox Example

PDF download may not work in codeSandbox mode. With [**this**](https://n59710.csb.app/invoices) link, you can open the example in the browser and try it.

-   `Username`: demo
-   `Password`: demodemo

[CodeSandbox Link](https://codesandbox.io/s/refine-invoice-generator-n59710)

[Source Code](https://github.com/pankod/refine/tree/master/examples/blog/invoiceGenerator)

[For more information about refine ->](https://refine.dev/docs/getting-started/overview/)