Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.sample
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
REACT_APP_API_URL=http://localhost:4000
REACT_APP_API_URL=http://localhost:4000/
Copy link

@coderabbitai coderabbitai bot Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Verify trailing slash handling in API URL usage.

The trailing slash added to REACT_APP_API_URL could cause double-slash issues (e.g., http://localhost:4000//api/endpoint) if the code concatenates paths without accounting for it. Ensure all API calls handle this correctly.

Run the following script to check how the API URL is used throughout the codebase:


🏁 Script executed:

#!/bin/bash
# Description: Find usages of REACT_APP_API_URL to verify trailing slash handling

# Search for direct usage of REACT_APP_API_URL
rg -n "REACT_APP_API_URL" --type ts --type tsx --type js --type jsx -C3

# Search for common API call patterns that might concatenate URLs
rg -n "process\.env\.REACT_APP_API_URL\s*\+\s*['\"]/" --type ts --type tsx --type js --type jsx -C3

Length of output: 55


🏁 Script executed:

#!/bin/bash
# Broad search for REACT_APP_API_URL usages
rg -n "REACT_APP_API_URL" -C3

# Search for concatenation patterns
rg -n "process\.env\.REACT_APP_API_URL\s*\+" -C3

# Search for template literal usage
rg -n "\`.*process\.env\.REACT_APP_API_URL.*\`" -C3

Length of output: 16087


Normalize or remove trailing slash in REACT_APP_API_URL
.env.sample currently defines REACT_APP_API_URL=http://localhost:4000/, which leads to URLs like http://localhost:4000//upload-file when concatenated. Either drop the trailing slash in .env.sample or strip it at runtime (e.g. envApiUrl.replace(/\/$/, '')) before appending paths.

🤖 Prompt for AI Agents
In .env.sample around line 1, REACT_APP_API_URL includes a trailing slash which
can produce double-slash URLs when concatenating paths; remove the trailing
slash from the sample value (change to http://localhost:4000) or, alternatively,
ensure the app normalizes the env value at startup by stripping any trailing
slash (e.g., call a single-strip operation like replace(/\/$/, '') on the loaded
REACT_APP_API_URL) before using it to build endpoint paths so concatenation
never produces duplicate slashes.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ene odoo yajiinaa mi hu

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ene odoo yajiinaa mi hu

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@KhBaterdene Thank you for confirming! If you've verified that the trailing slash works correctly with your API endpoint handling, that's great. The change looks good to proceed.

Just for future reference, if you notice any double-slash issues in API URLs (like http://localhost:4000//api/...), you'll know the trailing slash in the .env file is the place to adjust. ✨

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@KhBaterdene Glad to hear it's working well now! 🎉


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

NODE_ENV=development
MONGO_URL=mongodb://localhost:27017/erxes
NODE_OPTIONS=--max-old-space-size=1536
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const POS_CURSOR_SESSION_KEY = 'pos-cursor';
export const LEADS_CURSOR_SESSION_KEY = 'leads-cursor';
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { useLocation } from 'react-router';
import { PosPath } from '~/modules/types/path/PosPath';
import { LEADS_CURSOR_SESSION_KEY, POS_CURSOR_SESSION_KEY } from '~/modules/constants/PosCursorSessionKey';


export const useIsPosLeadSessionKey = () => {
const { pathname } = useLocation();
const isLead = new RegExp(`(^|/)${PosPath.Leads}(/|$)`).test(pathname);
return {
isLead,
sessionKey: isLead
? LEADS_CURSOR_SESSION_KEY
: POS_CURSOR_SESSION_KEY,
};
};
168 changes: 168 additions & 0 deletions frontend/plugins/pos_ui/src/modules/pos/PosFilter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import {
IconCalendar,
IconCalendarPlus,
IconCalendarTime,
IconCalendarUp,
IconLabel,
IconSearch,
} from '@tabler/icons-react';
import {
Combobox,
Command,
Filter,
useFilterQueryState,
useMultiQueryState,
} from 'erxes-ui';
import { SelectMember, TagsFilter, SelectBrand } from 'ui-modules';
import { PosTotalCount } from './PosTotalCount';
import { PosHotKeyScope } from '../types/posHotkeyScope';
import { useIsPosLeadSessionKey } from '../pos-detail/hooks/UsePosLeadSessionKey';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Use absolute import paths instead of relative paths per coding standards

Context Used: Rule from dashboard - Use absolute import paths instead of relative paths when importing modules in TypeScript/JavaScript ... (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: frontend/plugins/pos_ui/src/modules/pos/PosFilter.tsx
Line: 16:19

Comment:
**style:** Use absolute import paths instead of relative paths per coding standards

**Context Used:** Rule from `dashboard` - Use absolute import paths instead of relative paths when importing modules in TypeScript/JavaScript ... ([source](https://app.greptile.com/review/custom-context?memory=c22f4dca-4fa7-48c2-911a-3f50a8f6d5a8))

How can I resolve this? If you propose a fix, please make it concise.

const PosFilterPopover = () => {
const [queries] = useMultiQueryState<{
tags: string[];
searchValue: string;
created: string;
updated: string;
lastSeen: string;
brand: string;
}>(['tags', 'searchValue', 'created', 'updated', 'lastSeen', 'brand']);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 'birthday' filter UI is defined later but not included in the useMultiQueryState keys. Verify if this omission is intentional.


const hasFilters = Object.values(queries || {}).some(
(value) => value !== null,
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Add 'birthday' to the query keys array.

The birthday filter is defined in the UI (lines 70-73, 89-91, 107-109) but is missing from the useMultiQueryState query keys array. This causes hasFilters to not detect when the birthday filter is active, resulting in the Filter.Trigger not showing as filtered despite the filter being applied.

Apply this diff to fix the issue:

   const [queries] = useMultiQueryState<{
     tags: string[];
     searchValue: string;
     created: string;
     updated: string;
     lastSeen: string;
     brand: string;
-  }>(['tags', 'searchValue', 'created', 'updated', 'lastSeen', 'brand']);
+    birthday: string;
+  }>(['tags', 'searchValue', 'created', 'updated', 'lastSeen', 'brand', 'birthday']);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const PosFilterPopover = () => {
const [queries] = useMultiQueryState<{
tags: string[];
searchValue: string;
created: string;
updated: string;
lastSeen: string;
brand: string;
}>(['tags', 'searchValue', 'created', 'updated', 'lastSeen', 'brand']);
const hasFilters = Object.values(queries || {}).some(
(value) => value !== null,
);
const PosFilterPopover = () => {
const [queries] = useMultiQueryState<{
tags: string[];
searchValue: string;
created: string;
updated: string;
lastSeen: string;
brand: string;
birthday: string;
}>(['tags', 'searchValue', 'created', 'updated', 'lastSeen', 'brand', 'birthday']);
const hasFilters = Object.values(queries || {}).some(
(value) => value !== null,
);
🤖 Prompt for AI Agents
In frontend/plugins/pos_ui/src/modules/pos/PosFilter.tsx around lines 20 to 32,
the 'birthday' query key is missing from the useMultiQueryState keys array so
hasFilters doesn't detect the birthday filter; add 'birthday' to the array of
keys passed to useMultiQueryState (i.e., include 'birthday' alongside 'tags',
'searchValue', 'created', 'updated', 'lastSeen', 'brand') so the birthday filter
state is tracked and hasFilters reflects it.


return (
<>
<Filter.Popover scope={PosHotKeyScope.PosPage}>
<Filter.Trigger isFiltered={hasFilters} />
<Combobox.Content>
<Filter.View>
<Command>
<Filter.CommandInput
placeholder="Filter"
variant="secondary"
className="bg-background"
/>
<Command.List className="p-1">
<Filter.Item value="searchValue" inDialog>
<IconSearch />
Search
</Filter.Item>
<TagsFilter />
<Filter.Item value="brand">
<IconLabel />
Brand
</Filter.Item>
<SelectMember.FilterItem />
<Command.Separator className="my-1" />
<Filter.Item value="created">
<IconCalendarPlus />
Created At
</Filter.Item>
<Filter.Item value="updated">
<IconCalendarUp />
Updated At
</Filter.Item>
<Filter.Item value="lastSeen">
<IconCalendarTime />
Last Seen At
</Filter.Item>
<Filter.Item value="birthday">
<IconCalendar />
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

happy birthday

Birthday
</Filter.Item>
</Command.List>
</Command>
</Filter.View>
<SelectMember.FilterView />
<SelectBrand.FilterView />
<TagsFilter.View tagType="core:pos" />
<Filter.View filterKey="created">
<Filter.DateView filterKey="created" />
</Filter.View>
<Filter.View filterKey="updated">
<Filter.DateView filterKey="updated" />
</Filter.View>
<Filter.View filterKey="lastSeen">
<Filter.DateView filterKey="lastSeen" />
</Filter.View>
<Filter.View filterKey="birthday">
<Filter.DateView filterKey="birthday" />
</Filter.View>
</Combobox.Content>
</Filter.Popover>
<Filter.Dialog>
<Filter.View filterKey="searchValue" inDialog>
<Filter.DialogStringView filterKey="searchValue" />
</Filter.View>
<Filter.View filterKey="created" inDialog>
<Filter.DialogDateView filterKey="created" />
</Filter.View>
<Filter.View filterKey="updated" inDialog>
<Filter.DialogDateView filterKey="updated" />
</Filter.View>
<Filter.View filterKey="lastSeen" inDialog>
<Filter.DialogDateView filterKey="lastSeen" />
</Filter.View>
<Filter.View filterKey="birthday" inDialog>
<Filter.DialogDateView filterKey="birthday" />
</Filter.View>
</Filter.Dialog>
</>
);
};

export const PosFilter = () => {
const [searchValue] = useFilterQueryState<string>('searchValue');
const { sessionKey } = useIsPosLeadSessionKey();

return (
<Filter id="pos-filter" sessionKey={sessionKey}>
<Filter.Bar>
<Filter.BarItem queryKey="searchValue">
<Filter.BarName>
<IconSearch />
Search
</Filter.BarName>
<Filter.BarButton filterKey="searchValue" inDialog>
{searchValue}
</Filter.BarButton>
</Filter.BarItem>
<TagsFilter.Bar tagType="core:pos" />
<Filter.BarItem queryKey="created">
<Filter.BarName>
<IconCalendarPlus />
Created At
</Filter.BarName>
<Filter.Date filterKey="created" />
</Filter.BarItem>
<Filter.BarItem queryKey="updated">
<Filter.BarName>
<IconCalendarUp />
Updated At
</Filter.BarName>
<Filter.Date filterKey="updated" />
</Filter.BarItem>
<Filter.BarItem queryKey="lastSeen">
<Filter.BarName>
<IconCalendarTime />
Last Seen At
</Filter.BarName>
<Filter.Date filterKey="lastSeen" />
</Filter.BarItem>
<Filter.BarItem queryKey="birthday">
<Filter.BarName>
<IconCalendar />
Birthday
</Filter.BarName>
<Filter.Date filterKey="birthday" />
</Filter.BarItem>
<SelectMember.FilterBar />
<SelectBrand.FilterBar />
<PosFilterPopover />
<PosTotalCount />
</Filter.Bar>
</Filter>
);
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

LGTM! Consider verifying SelectMember filter detection.

The component structure is well-organized and follows coding guidelines correctly. The use of sessionKey from useIsPosLeadSessionKey ensures proper filter state management between POS and Leads views.

One additional consideration: SelectMember.FilterBar (line 160) may have its own query key (e.g., assignedUserId) that might also be missing from PosFilterPopover's hasFilters check. If SelectMember's filter state doesn't trigger the filtered indicator on the Filter.Trigger, you'll need to add its query key to the array in PosFilterPopover as well.

Run the following script to check what query key SelectMember uses:


🏁 Script executed:

#!/bin/bash
# Description: Find the query key used by SelectMember.FilterBar

# Search for the query key definition in SelectMember components
ast-grep --pattern 'useFilterQueryState$$$' frontend/libs/ui-modules/src/modules/team-members/components/SelectMember.tsx

# Also search for any hardcoded query key strings
rg -n "queryKey.*=" frontend/libs/ui-modules/src/modules/team-members/components/SelectMember.tsx

Length of output: 60


Add 'assignedTo' to PosFilterPopover.hasFilters
PosFilterPopover currently doesn’t include the SelectMember default queryKey ('assignedTo') in its hasFilters array, so member selections won’t trigger the active-filter indicator. Update hasFilters in PosFilterPopover to include 'assignedTo'.

🤖 Prompt for AI Agents
In frontend/plugins/pos_ui/src/modules/pos/PosFilter.tsx around lines 115 to
167, PosFilterPopover's hasFilters array is missing the SelectMember default
queryKey 'assignedTo', so selecting a member doesn't mark the filter as active;
update the PosFilterPopover component to include 'assignedTo' in its hasFilters
array (add 'assignedTo' alongside the existing keys) so member selections
trigger the active-filter indicator.


17 changes: 17 additions & 0 deletions frontend/plugins/pos_ui/src/modules/pos/PosTotalCount.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { isUndefinedOrNull, Skeleton } from 'erxes-ui';
import { useAtomValue } from 'jotai';
import { posTotalCountAtom } from '~/modules/states/PosCount';


export const PosTotalCount = () => {
const totalCount = useAtomValue(posTotalCountAtom);
return (
<div className="text-muted-foreground font-medium text-sm whitespace-nowrap h-7 leading-7">
{isUndefinedOrNull(totalCount) ? (
<Skeleton className="w-20 h-4 inline-block mt-1.5" />
) : (
`${totalCount} records found`
)}
</div>
);
};
3 changes: 3 additions & 0 deletions frontend/plugins/pos_ui/src/modules/states/PosCount.tsx
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ene ter chigeere ustaagui yu

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { atom } from 'jotai';

export const posTotalCountAtom = atom<number | null>(null);
9 changes: 9 additions & 0 deletions frontend/plugins/pos_ui/src/modules/types/path/PosPath.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const PosPath = {
Index: '/contacts',
Customers: '/customers',
Leads: '/leads',
Companies: '/companies',
Vendors: '/vendors',
Clients: '/clients',
} as const;
export type PosPath = typeof PosPath[keyof typeof PosPath];
9 changes: 7 additions & 2 deletions frontend/plugins/pos_ui/src/pages/PosIndexPage.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { IconCashRegister, IconPlus, IconSettings } from '@tabler/icons-react';
import { Breadcrumb, Button, Separator } from 'erxes-ui';
import { Breadcrumb, Button, PageSubHeader, Separator } from 'erxes-ui';
import { PageHeader } from 'ui-modules';
import { Link, useSearchParams } from 'react-router-dom';
import { PosRecordTable } from '@/components/PosRecordTable';
import { useAtom } from 'jotai';
import { PosCreate } from '@/create-pos/components/index/pos-create';
import { PosEdit } from '@/pos-detail/components/posDetail';
import { renderingPosCreateAtom } from '@/create-pos/states/renderingPosCreateAtom';
import { PosFilter } from '@/pos/PosFilter';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Extra trailing spaces after the import statement

Suggested change
import { PosFilter } from '@/pos/PosFilter';
import { PosFilter } from '@/pos/PosFilter';
Prompt To Fix With AI
This is a comment left during a code review.
Path: frontend/plugins/pos_ui/src/pages/PosIndexPage.tsx
Line: 10:10

Comment:
**style:** Extra trailing spaces after the import statement

```suggestion
import { PosFilter } from '@/pos/PosFilter';
```

How can I resolve this? If you propose a fix, please make it concise.



export const PosIndexPage = () => {
const [, setSearchParams] = useSearchParams();
Expand All @@ -17,7 +19,7 @@ export const PosIndexPage = () => {
setSearchParams({ create: 'true' });
};
return (
<div className="flex flex-col h-full">
<div className="flex flex-col h-full">
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Line contains excessive trailing whitespace that should be removed for cleaner code

Suggested change
<div className="flex flex-col h-full">
<div className="flex flex-col h-full">
Prompt To Fix With AI
This is a comment left during a code review.
Path: frontend/plugins/pos_ui/src/pages/PosIndexPage.tsx
Line: 22:22

Comment:
**style:** Line contains excessive trailing whitespace that should be removed for cleaner code

```suggestion
    <div className="flex flex-col h-full">
```

How can I resolve this? If you propose a fix, please make it concise.

<PageHeader>
<PageHeader.Start>
<Breadcrumb>
Expand Down Expand Up @@ -48,6 +50,9 @@ export const PosIndexPage = () => {
</Button>
</PageHeader.End>
</PageHeader>
<PageSubHeader>
<PosFilter />
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Missing proper indentation - should align with parent component structure

Prompt To Fix With AI
This is a comment left during a code review.
Path: frontend/plugins/pos_ui/src/pages/PosIndexPage.tsx
Line: 54:54

Comment:
**style:** Missing proper indentation - should align with parent component structure

How can I resolve this? If you propose a fix, please make it concise.

</PageSubHeader>
<PosCreate />
<PosRecordTable />
<PosEdit />
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"dev:apis": "dotenv -e .env -- node scripts/start-api-dev.js",
"dev:uis": "dotenv -e .env -- node scripts/start-ui-dev.js",
"create-plugin": "node scripts/create-plugin.js"

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yaahgeed bainaa zaluuu

},
"engines": {
"pnpm": ">=8",
Expand Down
Loading