Skip to content
Open
Changes from all 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
47 changes: 24 additions & 23 deletions src/pages/clients/Clients.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ import { Plus } from 'lucide-react'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faCircle } from '@fortawesome/free-solid-svg-icons'

import { ClientSearchV2Api, type PageClientSearchData } from '@/fineract-api'
import { ClientApi } from '@/fineract-api'
import { getConfiguration } from '@/lib/fineract-openapi'
import { useTranslation } from 'react-i18next'

const clientSearchApi = new ClientSearchV2Api(getConfiguration())
const clientApi = new ClientApi(getConfiguration())

/** Row shape returned by the search endpoint at runtime */
interface ClientRow {
Expand Down Expand Up @@ -69,15 +69,21 @@ const Clients = () => {

;(async () => {
try {
const res = await clientSearchApi.searchByText({
request: { text: searchTerm || undefined },
page: Math.max(0, page - 1),
size: itemsPerPage,
})

const data: PageClientSearchData = res.data || {}
const content = (data.content ?? []) as unknown as ClientRow[]
const totalElements: number = data.totalElements ?? content.length
const res = await clientApi.retrieveAll21(
undefined,
undefined,
searchTerm || undefined,
undefined,
undefined,
includePending ? undefined : 'active',
undefined,
Math.max(0, page - 1) * itemsPerPage,
itemsPerPage
)
const data = res.data as any
const content = (data.pageItems ?? []) as unknown as ClientRow[]
const totalElements: number =
data.totalFilteredRecords ?? content.length
Comment on lines +83 to +86

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm the generated return type of retrieveAll21 exposes pageItems / totalFilteredRecords
fd -t f -e ts -e d.ts . -x rg -nP -l 'retrieveAll21' {} \; | head -20
rg -nP -C5 'retrieveAll21\s*\(' --iglob '*.d.ts' --iglob '*api*.ts' | head -80
rg -nP -C3 'totalFilteredRecords|pageItems' --iglob '*client*' | head -80

Repository: openMF/mifos-x-web-app-react

Length of output: 4376


🏁 Script executed:

# Find the retrieveAll21 function definition and its return type
fd -type f -name "*.ts" -o -name "*.d.ts" | xargs rg -l "retrieveAll21" | head -10

Repository: openMF/mifos-x-web-app-react

Length of output: 431


🏁 Script executed:

# Look for the Fineract API client definition
fd -type f \( -name "*api*.ts" -o -name "*client*api*" \) | head -20

Repository: openMF/mifos-x-web-app-react

Length of output: 243


🏁 Script executed:

# Check the referenced example files
cat -n src/pages/accounting/create-journal-entry/SearchJournalEntry.tsx | sed -n '92,99p'

Repository: openMF/mifos-x-web-app-react

Length of output: 505


🏁 Script executed:

# Check Navigation.tsx example
cat -n src/pages/navigation/Navigation.tsx | sed -n '71,77p'

Repository: openMF/mifos-x-web-app-react

Length of output: 446


🏁 Script executed:

# Find where the API clients are defined
fd -name "*.ts" -o -name "*.d.ts" | xargs rg -l "retrieveAll21" -B 5 -A 5

Repository: openMF/mifos-x-web-app-react

Length of output: 491


🏁 Script executed:

# Search for ClientRow type definition
rg "type ClientRow|interface ClientRow" -A 5

Repository: openMF/mifos-x-web-app-react

Length of output: 380


🏁 Script executed:

# Look for API client file
fd -name "*api*.ts" -path "*/api/*" | head -20

Repository: openMF/mifos-x-web-app-react

Length of output: 303


🏁 Script executed:

# Find Fineract API or generated types
fd -type d -name "api" -o -name "types" -o -name "generated"

Repository: openMF/mifos-x-web-app-react

Length of output: 243


🏁 Script executed:

# Find where retrieveAll21 is defined/imported
rg "retrieveAll21" -B 3 -A 3 src/pages/clients/Clients.tsx

Repository: openMF/mifos-x-web-app-react

Length of output: 233


🏁 Script executed:

# Look for API client imports in Clients.tsx
head -50 src/pages/clients/Clients.tsx

Repository: openMF/mifos-x-web-app-react

Length of output: 1488


🏁 Script executed:

# Find API client files
find . -path ./node_modules -prune -o -name "*client*" -type f | grep -i api | head -20

Repository: openMF/mifos-x-web-app-react

Length of output: 54


🏁 Script executed:

# Find fineract-api module
find . -path ./node_modules -prune -o -type d -name "fineract-api" -print

Repository: openMF/mifos-x-web-app-react

Length of output: 54


🏁 Script executed:

# Search for ClientApi class definition
rg "class ClientApi|export.*ClientApi" -A 10 | head -50

Repository: openMF/mifos-x-web-app-react

Length of output: 54


🏁 Script executed:

# Look in src directory structure
ls -la src/

Repository: openMF/mifos-x-web-app-react

Length of output: 993


🏁 Script executed:

# Check tsconfig for path aliases
cat tsconfig.json | grep -A 10 "paths"

Repository: openMF/mifos-x-web-app-react

Length of output: 124


🏁 Script executed:

# Search for fineract-api in vite config
cat vite.config.ts 2>/dev/null || cat vite.config.js 2>/dev/null

Repository: openMF/mifos-x-web-app-react

Length of output: 1197


🏁 Script executed:

# Look for any typescript configuration
find . -maxdepth 2 -name "tsconfig.json" -o -name "vite.config.*" -o -name "vitest.config.*" | xargs cat 2>/dev/null | head -100

Repository: openMF/mifos-x-web-app-react

Length of output: 1496


🏁 Script executed:

# Search for ClientApi definition directly
rg "class ClientApi" -A 20

Repository: openMF/mifos-x-web-app-react

Length of output: 54


🏁 Script executed:

# Look for fineract-api in src
ls -la src/fineract-api/ 2>/dev/null | head -30

Repository: openMF/mifos-x-web-app-react

Length of output: 54


🏁 Script executed:

# Search for retrieveAll21 method signature/type
rg "retrieveAll21" -A 5 src/ 2>/dev/null | head -100

Repository: openMF/mifos-x-web-app-react

Length of output: 1624


🏁 Script executed:

# Look for package.json to understand dependencies
cat package.json | head -50

Repository: openMF/mifos-x-web-app-react

Length of output: 2032


🏁 Script executed:

# Find fineract.yaml
find . -name "fineract.yaml" -o -name "fineract.yml"

Repository: openMF/mifos-x-web-app-react

Length of output: 88


🏁 Script executed:

# Look for any OpenAPI spec files
find . -maxdepth 2 -name "*.yaml" -o -name "*.yml" | grep -v node_modules

Repository: openMF/mifos-x-web-app-react

Length of output: 143


🏁 Script executed:

# Check if src/fineract-api is in gitignore
cat .gitignore | grep -i fineract

Repository: openMF/mifos-x-web-app-react

Length of output: 90


🏁 Script executed:

# Search for retrieveAll21 in fineract.yaml
rg "retrieveAll21" fineract.yaml -A 30 | head -100

Repository: openMF/mifos-x-web-app-react

Length of output: 54


🏁 Script executed:

# Look for the endpoint path and response schema
rg "retrieveAll21|pageItems" fineract.yaml -B 5 -A 15 | head -150

Repository: openMF/mifos-x-web-app-react

Length of output: 4015


🏁 Script executed:

# Find GetClientsPageItemsResponse schema definition
rg "GetClientsPageItemsResponse:" fineract.yaml -A 30 | head -80

Repository: openMF/mifos-x-web-app-react

Length of output: 830


🏁 Script executed:

# Also check GetClientsPageItems to understand the difference
rg "GetClientsPageItems:" fineract.yaml -A 30 | head -80

Repository: openMF/mifos-x-web-app-react

Length of output: 837


🏁 Script executed:

# Look at the full context around line 83-86 to see how the data is being transformed
cat -n src/pages/clients/Clients.tsx | sed -n '75,95p'

Repository: openMF/mifos-x-web-app-react

Length of output: 868


Remove as any and use the generated Fineract API response type.

ESLint flags @typescript-eslint/no-explicit-any here. The retrieveAll21 method returns a typed GetClientsResponse (generated from the OpenAPI spec) with pageItems and totalFilteredRecords properties, so the as any cast is unnecessary. Use the generated response type directly instead of a custom inline type.

♻️ Suggested refactor
-        const data = res.data as any
-        const content = (data.pageItems ?? []) as unknown as ClientRow[]
-        const totalElements: number =
-          data.totalFilteredRecords ?? content.length
+        const { pageItems = [], totalFilteredRecords = 0 } = res.data as GetClientsResponse
+        const content: ClientRow[] = pageItems as ClientRow[]
+        const totalElements: number = totalFilteredRecords
📝 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 data = res.data as any
const content = (data.pageItems ?? []) as unknown as ClientRow[]
const totalElements: number =
data.totalFilteredRecords ?? content.length
const { pageItems = [], totalFilteredRecords = 0 } = res.data as GetClientsResponse
const content: ClientRow[] = pageItems as ClientRow[]
const totalElements: number = totalFilteredRecords
🧰 Tools
🪛 ESLint

[error] 83-83: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/clients/Clients.tsx` around lines 83 - 86, Replace the explicit any
cast by using the generated OpenAPI response type: treat res.data as
GetClientsResponse (the type returned by retrieveAll21) and derive content and
totalElements from that typed object; update the declarations using
GetClientsResponse for data, use (data.pageItems ?? []) mapped/cast to
ClientRow[] for content, and derive totalElements from data.totalFilteredRecords
?? content.length so ESLint no longer flags `@typescript-eslint/no-explicit-any`
and the code uses the correct generated types (referencing retrieveAll21,
GetClientsResponse, data, content, ClientRow, and totalElements).


if (!cancelled) {
setRows(content)
Expand All @@ -95,15 +101,7 @@ const Clients = () => {
return () => {
cancelled = true
}
}, [searchTerm, page, itemsPerPage])

// toggle pending/active filter
const filtered = rows.filter((c: ClientRow) => {
const statusId = c?.status?.id ?? 0
return includePending
? statusId === 300 || statusId === 100
: statusId === 300
})
}, [searchTerm, page, itemsPerPage, includePending])

const totalPages = Math.max(1, Math.ceil(total / itemsPerPage))

Expand Down Expand Up @@ -182,7 +180,10 @@ const Clients = () => {
<Checkbox
id="pending-clients"
checked={includePending}
onCheckedChange={v => setIncludePending(!!v)}
onCheckedChange={v => {
setIncludePending(!!v)
setPage(1)
}}
/>
<label htmlFor="pending-clients" className="text-base dark:text-white">
{t('pending.showPendingClients')}
Expand All @@ -194,7 +195,7 @@ const Clients = () => {
<Table>
<TableCaption className="text-sm text-gray-500 dark:text-gray-400 pt-6 pb-2">
{tc('pagination.showing', {
current: filtered.length,
current: rows.length,
total,
page,
pages: totalPages,
Expand All @@ -218,7 +219,7 @@ const Clients = () => {
</TableHeader>

<TableBody>
{filtered.map((c: ClientRow) => (
{rows.map((c: ClientRow) => (
<TableRow
key={c.id}
onClick={() => c.id && navigate(`/clients/${c.id}/general`)}
Expand Down