Lead count over period widget

ui/src/components/adminDashboard/widgets/LeadCountOverPeriodWidget

The LeadCountOverPeriodWidget displays the number of leads added for each status and the percentage difference between the current period's total and that of the previous equivalent period (e.g., if October is selected, the past period will be September). The component is built using the Ant Design Carousel and LeadAmountDifferenceWidget components.

For correct display of widgets (4 per carousel page) we should chunk our array of data by smaller one's with size of 4. Then we well map chunked arrays and map each array sepaarately to generate widgets. Code example:

<Carousel infinite={false} draggable className={classes.carousel}>
                {chunkedLeadAmount.map((chunk, index) => (
                    <div key={index} className={classes.chunk}>
                        {chunk.map((item, idx) => (
                            <LeadAmountDiffenceWidget
                                key={idx}
                                title={item.name}
                                icon={leadAmountWidgetIcons[item.name]}
                                iconBgColor={item.backgroundColor}
                                sparkLineColor={item.color}
                                leadAmount={item.current}
                                percentageDifference={item.percentageChange}
                            />
                        ))}
                    </div>
                ))}
            </Carousel>

In LeadAmountDiffenceWidget we will display passed information and add a SparkLine with correct color and with direction based on percentageDifference. Inside LeadAmountDiffenceWidget we create a variable with value of "Ascend" | "Descend" | "none" called type:

    const type =
        percentageDifference > 0 ? sortTypes.ascent : percentageDifference < 0 ? sortTypes.descent : sortTypes.none;

Which we pass to SparkLine component

<SparkLine type={type} color={sparkLineColor} />

Inside the component it will be 3 different svg lines which will returned depends on type. Also each line have linear gradient under it. To achieve correct gradient display we should create an unique gradient id in the following way:

const gradientId = `gradient_${type}_${color.replace('#', '')}`;

You may use also UUID or something else for that purpose.

Then use this gradientId in your svg tag:

            <svg
                style={svgStyle}
                width="134"
                height="64"
                viewBox="0 0 67 32"
                fill="none"
                xmlns="http://www.w3.org/2000/svg"
            >
                <path
                    d="M15.173 20.5956L1 31H66V1L50.8327 9.85036C45.8547 12.7551 40.1946 14.2857 34.4311 14.2857C27.5038 14.2857 20.7572 16.4962 15.173 20.5956Z"
                    fill={`url(#${gradientId})`}
                    fillOpacity="0.3"
                />
                <path
                    d="M1 31L15.173 20.5956C20.7572 16.4962 27.5038 14.2857 34.4311 14.2857V14.2857C40.1946 14.2857 45.8547 12.7551 50.8327 9.85036L66 1"
                    stroke={color}
                    strokeLinecap="round"
                />
                <defs>
                    <linearGradient id={gradientId} x1="33.5" y1="1" x2="33.5" y2="31" gradientUnits="userSpaceOnUse">
                        <stop stopColor={color} />
                        <stop offset="1" stopColor={color} stopOpacity="0" />
                    </linearGradient>
                </defs>
            </svg>

As you can see, we need to pass it to the fill attribute of the first path tag with a URL to ensure the correct color. To apply the gradient under the line correctly, use linearGradient with a unique gradientId. If gradientId is not unique, all lines will appear black and lack gradient.

Backend

On backend we have countLeadsOverPeriod function which return us an array of object for each lead status with following fields:

  • name - the name of lead status

  • current - total amount of leads with current status

  • percentageChange - rounded difference between current and past period in percentage

  • color - status primary color

  • backgroundColor - status secondary color

To create that array, we first need to retrieve all statuses for the user's teamId. Then, we create a query for each status using a for loop, which will return the count of leads that were created within the required period and currently have the specified status. Query:

const currentQuery = this.leadsRepository
                .createQueryBuilder('lead')
                .leftJoin('lead.business', 'business')
                .leftJoin('lead.status', 'status')
                .where('lead.statusId = :statusId', { statusId })
                .andWhere('lead.teamId = :teamId', { teamId })
                .andWhere('lead.createdAt BETWEEN :startOfDay AND :endOfDay', { startOfDay, endOfDay })
                .andWhere('business.id IN (:...businessesIds)', { businessesIds });

To get the lead count for the previous time period (to calculate the percentage difference), we will run almost the same query but with a different startOfDay and endOfDay. After that, we calculate the percentage difference and push an object with the required values into the resulting array. Here is the full code of the complete cycle:

for (const status of statuses) {
            const statusId = status.id;

            const currentQuery = this.leadsRepository
                .createQueryBuilder('lead')
                .leftJoin('lead.business', 'business')
                .leftJoin('lead.status', 'status')
                .where('lead.statusId = :statusId', { statusId })
                .andWhere('lead.teamId = :teamId', { teamId })
                .andWhere('lead.createdAt BETWEEN :startOfDay AND :endOfDay', { startOfDay, endOfDay })
                .andWhere('business.id IN (:...businessesIds)', { businessesIds });

            const currentCount = await currentQuery.getCount();

            const previousQuery = this.leadsRepository
                .createQueryBuilder('lead')
                .leftJoin('lead.business', 'business')
                .leftJoin('lead.status', 'status')
                .where('lead.statusId = :statusId', { statusId })
                .andWhere('lead.teamId = :teamId', { teamId })
                .andWhere('lead.createdAt BETWEEN :previousStart AND :previousEnd', { previousStart, previousEnd })
                .andWhere('business.id IN (:...businessesIds)', { businessesIds });

            const previousCount = await previousQuery.getCount();

            let percentageChange;

            if (previousCount !== 0) {
                percentageChange = ((currentCount - previousCount) / previousCount) * 100;
            } else if (currentCount !== 0) {
                percentageChange = 100 * currentCount;
            } else {
                percentageChange = 0;
            }

            result.push({
                name: status.name,
                current: currentCount,
                percentageChange: Math.round(percentageChange),
                color: status.primaryColor,
                backgroundColor: status.secondaryColor,
            });
        }

Last updated