# Leads

## Backend

On the backend for leads responsible leads module.&#x20;

***your\_project/api/src/leads***

There you can find dto\`s, entities, controller and service. \
\
There are three entities responsible for functionality of the leads page: `Leads entity` (the main one),  `Lead Status entity`, `Lead Comments entity`, which is separate entity for the comment on the leads.&#x20;

{% hint style="success" %}
N.B. How they work read the docs in Businesses part of modules documentation.&#x20;
{% endhint %}

Leads are in `@ManyToOne`relations with `Teams`**,** `Lead Statuses`**,** `@ManyToMany` **with** `Businesses`**.** Leads have connections with `Audit Logger Service` and it's entity from `audit-logger module`. Also it have connection with `File services` from `files module`.&#x20;

{% hint style="success" %}
N.B. About them and how they work you can read the information in the relevant documentation modules.
{% endhint %}

If talking about lead statuses - then each team will have at minimum 4 default statuses for their leads. Maximum 12. It means that after the team was created - it will receive this 4 statuses. How to change and them you can read in **App Settings** module.

In Services you will find methods for:

* Soft delete
* Multi delete, restore, archive
* Global search
* Leads CRUD
* Lead Status CRUD
* Photo CRUD
* Audit logs
* Comments
* Statistic (Dashboard page)

{% hint style="success" %}
N.B. There is a separate module about **Global Search.**
{% endhint %}

You can easily navigate in this service just looking at "regions". Each region have their specific methods. For example:&#x20;

```typescript
// #region Statuses
```

This will means that next portion of code are responsive for lead statuses. Or

```typescript
// #region CRUD and search
```

This for leads CRUD and search. And so on and so forth.\
\
Code example of method from statistic block:

```typescript
async getLeadsAmountPerPeriod(startDate: string, endDate: string, businessesIds: number[], teamId: string) {
        // if (businessesIds.length > 5) {
        //     throw new BadRequestException('Maximal businesses count is 5');
        // }

        const start = dayjs(startDate).startOf('day');
        const end = dayjs(endDate).endOf('day');

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

        const startOfDay = start.startOf('day').toDate();
        const endOfDay = end.endOf('day').toDate();

        const query = this.leadsRepository.createQueryBuilder('lead').leftJoinAndSelect('lead.business', 'business');

        query.andWhere('lead.teamId = :teamId', { teamId });

        query.andWhere('lead.createdAt BETWEEN :startOfDay AND :endOfDay', { startOfDay, endOfDay });

        query.andWhere('business.id IN (:...businessesIds)', { businessesIds });

        const leadsAmount = await query.getCount();

        return leadsAmount;
    }
```

Code example of lead status method:

```typescript
    async applyDefaultLeadStatuses(teamId: string) {
        return Promise.all(
            defaultStatuses.map(async status => {
                return await this.leadStatusRepository.save({
                    name: status.name,
                    primaryColor: status.primaryColor,
                    secondaryColor: LeadSecondaryColorMap[status.primaryColor],
                    team: { id: teamId },
                });
            })
        );
    }
```

Example above is responsible for applying 4 default lead statuses for the new team.

Go there and find all what you reading about!

## Frontend

To get there use side menu\
\
![](/files/hNibCDfw0FoLvlP036eN)\
\
or type next address:\
\
<http://your-website/leads>

#### Architecture:

Page file:&#x20;

***your\_project/ui/pages/leads/***

Components related only to leads page:

***your\_project/ui/components/leads/***

Other components:

***your\_project/ui/common/components/***

### Page

On the page you will find:

* Table with all the information about leads, selection, additional info row, sorting and filtering
* Selector of active and archive leads
* Leads search
* Add lead button
* Filters and other CRUD buttons which renders in certain conditions.
* Pagination on the bottom of the page

<figure><img src="/files/DqRFLHkeG7x5AwkeyM1n" alt=""><figcaption></figcaption></figure>

Search, sorting and filtering results will appear in table. Pagination affects it also.

{% hint style="success" %}
N.B. How this logic works, you can find a "findAll" and "search" method in backend part. There you can find queryBuilder and how it forms in different conditions.
{% endhint %}

{% hint style="danger" %}
N.B. Please notice that we use lodash library in frontend and throttling guards in backend. This will prevent too many requests to the server and keep it in safe from sudden fall off.
{% endhint %}

Take into account the fact we use Ant Design. How work certain things, please read their [documents](https://ant.design/components/overview) about their components.\
\
All works great on mobile devices too. In the main file you can find constants that control render in different breakpoint.\
\
**i18n** responsible for translation. How it work - see relevant documentation in module block.

### Filters

Filters are the hardest part of the page. Especially in the frontend part. All because how AntD components renders and how they works with each other.

Lets start from the states:

```javascript
const [filteredInfo, setFilteredInfo] = useState({});
```

This is the main state. It is used to form the query to the DB and the result of it we'll see later in the table. \
Next states is used to control the components work.

```javascript
const [selectedFilterValue, setSelectedFilterValue] = useState({});
const [sortedInfo, setSortedInfo] = useState({});

const [selectedFilters, setSelectedFilters] = useState({
        states: [],
        statuses: [],
        businesses: [],
    });

const filterOptions = {
        state: leadState.map(state => ({
            text: state.label,
            value: state.value,
        })),
        status: statuses.map(status => ({
            text: status.name,
            value: status.id,
        })),
        business: [
            {
                text: 'No business',
                value: 'No business',
            },
            ...businesses.map(business => ({
                text: business.name,
                value: business.name,
            })),
        ],
    };
```

Last state controls table filters. \
\
filterOptions - controls the filters in separate filter components

```javascript
const [searchParams, setSearchParams] = useState({
        pageSize: 10,
        currentPage: 1,
        isDeleted: false,
    });
const [searchText, setSearchText] = useState('');
```

This is search and pagination.\
\
In "fetchData" you will find how params forms the request to the server.

On the page you may notice that we have filters in different places, but it renders in certain conditions. Components are different from each other and we need to control them by those states, that's why we need them and they are so many.\
\
Some of them use AntD's [Popover ](https://ant.design/components/popover)component, some [Dropdown](https://ant.design/components/dropdown) or [Select ](https://ant.design/components/select)(or our Custom Select).\
\
Why table filters and other desktop filter are different? It's because of how AntD components work today and this may be changed in the future.\
\
Desktop filters and mobile filters are different too. Mobile filters have drawer which pops up from the bottom up.

## Permissions

Each action in this page will check users permission. About them and how they work, please read related block of module.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://intercode.gitbook.io/intercode-saas-kit/pages/leads.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
