Создание настраиваемого приложения для генератора счетов-фактур с помощью Refine, Strapi и Ant Design | Часть II

Ищете генератор счетов, который прост в использовании и позволяет настраивать ваши счета? С простым и интуитивно понятным интерфейсом Refine вы сможете создавать свои собственные счета-фактуры за несколько часов. Кроме того, мы предлагаем широкий выбор шаблонов и вариантов настройки, чтобы вы могли получить именно тот вид, который вам нужен. Узнайте больше о нашем генераторе счетов-фактур здесь!

Введение

Мы почти готовы к запуску нашего усовершенствованного генератора счетов-фактур. В этой части II статьи мы еще немного его настроим, а затем подробно рассмотрим, что можно сделать с готовым продуктом!

В этой части мы создадим миссию для услуг, которые предоставляет ваша компания. Затем мы создадим страницу счета-фактуры, используя эти миссии, а также клиентов и контакты, которые мы создали в первой части. Кроме того, вы сможете не только создавать счета-фактуры, но и просматривать и загружать их в формате PDF.

Эта статья написана в продолжение нашей статьи Разработка собственного настраиваемого генератора счетов-фактур с помощью Refine. Если вы не читали часть I, мы рекомендуем вам прочитать ее перед прочтением этой статьи.

Давайте вместе посмотрим, как легко и за короткое время мы можем разработать наш проект с помощью функций Refine.

Создание новых коллекций Strapi

В первой части нашей статьи мы создали коллекции компаний, контактов и клиентов. В этом разделе мы создадим коллекции Missions и Invoice Strapi для страниц миссий и счетов.

Mission Collection:

  • Миссия(Название миссии): Текст
  • Mission_description: Текст
  • День: Число
  • Суточная_ставка: Число

Invoice Collection:

  • Имя: Текст
  • Дата: Дата
  • Компания : Отношение с компанией
  • Скидка : Номер
  • Налог: Номер
  • Custom_id: Текст
  • Контакт: Отношение с контактом
  • Миссии: Отношение с миссией

Мы создали поля «Миссии» и «Счета-фактуры». Наша цель здесь — определить продукты или услуги, которые вы предлагаете конкретно вашей компании, и создать на их основе счета-фактуры. Определив количество рабочих дней продукта или услуги и их стоимость на ежедневной основе, общая сумма будет автоматически отражена в вашем счете. Теперь давайте создадим нашу страницу «Уточнение миссий», используя эту коллекцию. И давайте лучше поймем, создав пример миссии с помощью refine.

Страница Refine Missions

Давайте воспользуемся хуком useTable пакета refine-antd для создания нашей страницы и определим поля в нашем компоненте Table.

src/pages/MissionList.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>
    );
};
Вход в полноэкранный режим Выход из полноэкранного режима

Мы определили поля, которые мы создали на стороне strapi с помощью refine Table, и создали нашу таблицу. Давайте посмотрим, как выглядит наша таблица.

Как видите, благодаря уточнению мы смогли создать и отобразить нашу таблицу очень просто. Теперь давайте узнаем, как создать миссию из нашего интерфейса refine.

Страница создания миссий Refine

Давайте создадим модальный компонент для нашей страницы Mission Create. Подключим наши поля с помощью Modal и Form из пакета refine-antd.

src/components/mission/CreateMission.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>
    );
};
Вход в полноэкранный режим Выход из полноэкранного режима

Определим компонент CreateMission, который мы создали выше, в нашем MissionList и заполним его реквизит с помощью refine useModalForm.

src/pages/MissionList.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.

Наш следующий шаг — создание счетов-фактур в соответствии с этими заданиями и клиентами. Давайте создадим, организуем и отобразим счета-фактуры с помощью Refine.

Страница счетов-фактур Refine

Давайте поместим поля Invoice Collections, которые мы создали с помощью Strapi, в нашу таблицу, используя fetch refine-antd useTable. Наша коллекция счетов-фактур имеет связь с коллекциями клиентов, компаний и миссий.

Благодаря refine-strapi-v4 dataProvider мы можем использовать данные коллекций, которые связаны друг с другом.

Для того чтобы использовать поля коллекций, связанных друг с другом, мы должны заполнить коллекции в metaData.

Заполните в metaData контакты, компании и миссии, связанные с нашей коллекцией Invoice.

src/pages/InvoiceList.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 и связанные с ней поля, мы можем создать полнофункциональный счет-фактуру.

Наш счет-фактура содержит всю необходимую информацию. С помощью Refine Invoice Generator вы можете определить компанию, выписывающую счет, процент скидки, процент налога, customId и подобную информацию в одном счете.

Давайте лучше разберемся в этом на примере создания счета-фактуры в нашем пользовательском интерфейсе Refine.

Страница Refine Create Invoice Page

Здесь мы сначала получаем данные о компании, контактах и миссиях с помощью хука useSelect в refine и, передавая их компоненту Select, создаем выбираемые компоненты для детализации нашего счета.

Затем мы заполняем наши компоненты refine Create и Form полями коллекции в ремешке, чтобы выполнить процесс создания, как мы делали в наших предыдущих примерах.

src/pages/invoice/CreateInvoice:

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 и отобразить его в нашей таблице. В качестве последнего шага давайте просмотрим и загрузим созданные нами счета-фактуры в формате PDF.

Просмотр и загрузка счета-фактуры в формате PDF

В этом примере мы будем использовать пакет KendoReact PDF для просмотра в формате PDF. Начнем наш процесс с установки нашего пакета.

Начнем наш процесс с установки нашего пакета.

Установка

npm i @progress/kendo-react-pdf
Вход в полноэкранный режим Выход из полноэкранного режима

Использование

Для начала давайте создадим макет pdf и добавим реквизиты для получения данных из списка счетов-фактур.

src/components/pdf/PdfLayout.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 <></>;
};
Вход в полноэкранный режим Выход из полноэкранного режима

Давайте создадим кнопку для отображения нашего расположения PDF в списке счетов-фактур, а также модальный компонент для появления этого поля.

src/pages/invoice/InvoiceList.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>
        </>
    );
};
Вход в полноэкранный режим Выход из полноэкранного режима

Мы создали кнопку в списке счетов-фактур, и когда эта кнопка нажата, мы показываем модальный компонент, который содержит созданный нами PdfLayout. Наконец, как вы можете видеть, мы передали данные записей в списке счетов в качестве реквизитов PdfLayout.

Теперь, когда у нас есть данные о счетах-фактурах, мы можем редактировать PdfLayout.

Посмотрите дизайн и код PdfLayout

Заключение

В этом посте мы создали полностью настраиваемое и полностью функциональное приложение Invoice Generator. Если вы хотите создать приложение, подобное этому, вы можете добавить любую функцию, усовершенствовать и персонализировать генератор счетов по своему вкусу. Мы разработали приложение за очень короткое время, благодаря функциям refine и возможностям, которые он предоставляет для настройки.

С помощью refine вы можете разработать любое веб-приложение или панель администратора за очень короткое время.

Благодаря функциям refine headless и SSR-Next.js, можно легко и просто разрабатывать как B2B, так и B2C приложения, используя один фреймворк.

Живой пример CodeSandbox

Загрузка PDF может не работать в режиме CodeSandbox. С помощью этой ссылки вы можете открыть пример в браузере и попробовать его.

  • Имя пользователя: demo
  • Пароль: demodemo

Ссылка на CodeSandbox

Исходный код

Для получения дополнительной информации о функции refine ->

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *