# Businesses

Like many other entities `BusinessEntity` connected to **teamId**, so only members of certain team will access to it. Also it connected `@ManyToMany` to `LeadEntity` so leads can be connected to many busineeses. Each business can have **contactPerson** field, which is `@OnetoOne` relation with `TeamMembershipEntity`. Relation isn't with `UserEntity` because each user can have many memberships which will be connected to different businesses.

`BusinessEntity` support:

* Soft delete
* Multi delete, restore, archive
* Global search
* CRUD
* Photo CRUD
* Audit logs
* Comments

{% hint style="success" %}
Business logs will record if a lead was assigned or unassigned to them.
{% endhint %}

### Comments

On each business any member have ability to leave comments, delete and edit **own** comments. Each edit, delete action is monitored by audit logs, also each edited comment will have preposition "edited".

On frontend you can find logs and comments in `BusinessPreview`.  All comments, logs sorted by `createdAt` field **descend** always.  Both logs and comment are implemented with `InfiniteScroll` library.&#x20;

Here you can see example of usage `InfiniteScroll` component:

<pre class="language-javascript"><code class="lang-javascript"><strong>                &#x3C;div
</strong>                    id="scrollableDiv"
                    style={{
                        height: 408,
                        overflow: 'auto',
                    }}
                >
                    &#x3C;InfiniteScroll
                        ref={infiniteScrollRef}
                        dataLength={businessLogs.length}
                        next={fetchLogs}
                        hasMore={hasMore}
                        height={408}
                        loader={
                            &#x3C;Skeleton
                                avatar
                                paragraph={{
                                    rows: 1,
                                }}
                                active
                            />
                        }
                        onScroll={handleScroll}
                        style={{ scrollbarWidth: 'thin', scrollbarColor: '#888 #f1f1f1' }}
                    >
                        &#x3C;LogsList logs={businessLogs} entityName={'businesses'} loading={loading} />
                    &#x3C;/InfiniteScroll>
                &#x3C;/div>
</code></pre>

Props explanation:

* ref - reference made with useRef() hook to have ability to scroll from code (to top for example)
* dataLength - count of records used by lib to calculate optimal scroll height
* next - function which is used to fetch next record when we scrolled to bottom enough
* hasMore - bollean value which tells library if it is necessary to call next() function more
* height - pixel height of scrollable container
* loader - jsx which is shown at the bottom when we fetching records
* handleScroll - fucntion which is called every time we are scrolling
* style - css styling of container

For correct data fetching, we need to know how many records to skip, which can be determined by the number of records already on the page. Additionally, we need to keep the `hasMore` state updated. If the server doesn't send any records or sends fewer than requested, we can set `hasMore` to `false`. To maintain the correct order of records, we spread the previous logs and append the newly received records.

Code example:

```javascript
const response = await getBusinessLogs(id, businessLogs.length, RECORD_LIMIT_PER_REQUEST);

            if (response.logs.length === 0 || response.logs.length < RECORD_LIMIT_PER_REQUEST) {
                setHasMore(false);
            }

            setBusinessLogs(prevLogs => [...prevLogs, ...response.logs]);
```

### Getting logs and comments together in right order

To achieve the correct order by `createdAt`, we should fetch twice the requested number of records from both entities. This handles cases where, for example, we request 20 records (10 logs, 10 comments) but one entity has all 20 records created earlier than the other. We then combine the records into one array and sort them by `createdAt`. Afterward, we count the number of each record type to provide the correct count to the frontend. Finally, we slice the array to 20 records and return it.

Code example:

```typescript
const logs = await this.auditLoggerService.findEntityLogs(
            AuditLogEntityTypes.Businesses,
            id,
            logsSkip,
            logsTake * 2
        );

const comments = await this.getBusinessComments(id, teamId, commentsSkip, commentsTake * 2);

const sortedLogsAndComments = [...logs.logs, ...comments.comments];

sortedLogsAndComments.sort((a, b) => dayjs(b.createdAt).valueOf() - dayjs(a.createdAt).valueOf());

let commentsCount = 0;
let logsCount = 0;

for (const item of sortedLogsAndComments.slice(0, 20)) {
    if ('action' in item) {
        logsCount++;
    } else {
        commentsCount++;
    }
}

return { logsAndComments: sortedLogsAndComments.slice(0, 20), logsCount, commentsCount };
```

{% hint style="success" %}
Lead page has same logic with logs and comments
{% endhint %}


---

# 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/businesses.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.
