Doughnut chart widget

ui/src/components/adminDashboard/widgets/DoughnutChartWidget

DoughnutChartWidget is a side widget which is displaying amount of leads for each choosen business.

Data set and fetch perform the same as on other widgets.

For correct display of label inside a chart we should add a custom plugin which will draw a text exactly at the DoughtnutChart center:

const doughnutLabel = {
        id: 'dounghnutLabel',
        afterDatasetsDraw(chart, args, plugins) {
            const { ctx } = chart;

            const centerX = chart.getDatasetMeta(0).data[0].x;
            const centerY = chart.getDatasetMeta(0).data[0].y;

            ctx.save();
            ctx.font = `500 ${fontSize.lg} Inter`;
            ctx.fillStyle = primitiveColors.gray900;
            ctx.textAlign = 'center';
            ctx.textBaseline = 'middle';
            ctx.fillText(title, centerX, centerY);
        },
    };

Also for correct display of labels exactly on chart we will use custom plugin ChartDataLabels from chartjs-plugin-datalabels library. Then we write plugin to chart options and add dataLabels styles:

                    plugins: {
                        tooltip: {
                            enabled: false,
                        },
                        datalabels: {
                            display: true,
                            backgroundColor: function (context) {
                                return primitiveColors.gray0;
                            },
                            backgroundShadowColor: colors.black,
                            borderColor: primitiveColors.gray100,
                            borderRadius: 8,
                            borderWidth: 1,
                            color: colors.black,
                            font: {
                                family: 'Inter',
                                weight: 500,
                                size: 16,
                            },
                            padding: {
                                top: 2,
                                bottom: 2,
                                left: 8,
                                right: 8,
                            },
                            shadowOffsetX: 0,
                            shadowOffsetY: 3,
                            shadowBlur: 5,
                            shadowColor: colors.black,
                        },

Backend

On backend we have a function getLeadsPerBusinesses that return us required dataset:

async getLeadsPerBusinesses(
        startDate: string,
        endDate: string,
        businessIds: number[],
        teamId: string
    ): Promise<{ [businessName: string]: number }> {
        const start = dayjs(startDate).startOf('day').toDate();
        const end = dayjs(endDate).endOf('day').toDate();

        if (!start || !end) {
            throw new BadRequestException('Invalid date format');
        }

        const leadsData = await this.leadsRepository
            .createQueryBuilder('lead')
            .innerJoin('lead.business', 'business')
            .select('business.name', 'businessName')
            .addSelect('COUNT(lead.id)', 'leadCount')
            .where('lead.createdAt BETWEEN :start AND :end', { start, end })
            .andWhere('lead.teamId = :teamId', { teamId })
            .andWhere('business.id IN (:...businessIds)', { businessIds })
            .groupBy('business.name')
            .getRawMany();

        const result: { [businessName: string]: number } = {};
        leadsData.forEach(row => {
            result[row.businessName] = parseInt(row.leadCount, 10);
        });

        return result;
    }

Here is used query that will count leads for each business based on their createdAt column. The result of query will be an array of following objects:

{ businessName: 'Retail', leadCount: '126' }

Because of that here used a loop which will combine data in acceptable way for DoughnutChart.

Last updated