Repository: bcgov/lcfs
Pull request #2186: fix: transaction id sorting
Author: login: kevin-hashimoto
Created: 2025-03-07T01:21:15Z | Merged: 2025-03-11T16:33:48Z

- fix: transaction id not sorting correctly by numerical value, closes #2069 
- refactor: bc grid viewer to new bc grid viewer 2, touches on #2152 

Linked issue #2069: LCFS - BUG - Transaction ID Column Sorting Incorrect on BCeID and IDIR Transaction Pages
**Describe the Bug:**  
The **Transaction ID** column does not sort correctly on both the **BCeID** and **IDIR** transaction pages. Instead of sorting numerically, the IDs are sorted as text, leading to incorrect sequences such as **CR1, CR100, CR11** instead of **CR1, CR11, CR100**.  

**Expected Behaviour:**  
- The **Transaction ID** column should be sorted **numerically**, ensuring IDs appear in proper sequential order.  
- Sorting should maintain grouping by prefix.

**Actual Behaviour:**  
- Sorting treats transaction IDs as **text** rather than numbers.  
- Incorrect sequences occur, such as **CR1, CR100, CR11** instead of **CR1, CR11, CR100**.  

**Implications:**  
- Users cannot efficiently locate or track transactions.  
- Sorting inconsistencies may lead to reporting errors or confusion.  
- The issue affects **both BCeID and IDIR** users, impacting multiple roles.  

**Steps To Reproduce:**  
User/Role: **BCeID or IDIR**  
1. Navigate to the **Transactions** page.  
2. Click on the **Transaction ID** column to apply sorting.  
3. Observe that IDs are not sorted numerically but as text (e.g., **CR1, CR100, CR11**).  
4. Verify that sorting fails to follow expected numeric order while maintaining grouping by prefix.  

**Notes:**  
- Update sorting logic to recognize and **sort numeric values correctly**, even when prefixed (e.g., "CR").  
- Ensure sorting behaves correctly across different transaction ID formats.  
- Test sorting behavior across both **ascending and descending orders**.  
- Validate fix for **both BCeID and IDIR** transaction pages.  


Diff:
diff --git a/frontend/src/views/Transactions/Transactions.jsx b/frontend/src/views/Transactions/Transactions.jsx
index c8d280012..ee88ee530 100644
--- a/frontend/src/views/Transactions/Transactions.jsx
+++ b/frontend/src/views/Transactions/Transactions.jsx
@@ -24,9 +24,16 @@ import { govRoles, roles } from '@/constants/roles'
 import OrganizationList from './components/OrganizationList'
 import Loading from '@/components/Loading'
 import { ConditionalLinkRenderer } from '@/utils/grid/cellRenderers.jsx'
-import { BCGridViewer } from '@/components/BCDataGrid/BCGridViewer'
+import { BCGridViewer2 } from '@/components/BCDataGrid/BCGridViewer2'
 import { useGetTransactionList } from '@/hooks/useTransactions'
 
+const initialPaginationOptions = {
+  page: 1,
+  size: 10,
+  sortOrders: defaultSortModel,
+  filters: []
+}
+
 export const Transactions = () => {
   const { t } = useTranslation(['common', 'transaction'])
   const navigate = useNavigate()
@@ -44,8 +51,21 @@ export const Transactions = () => {
   const [alertMessage, setAlertMessage] = useState('')
   const [alertSeverity, setAlertSeverity] = useState('info')
 
-  const [gridKey, setGridKey] = useState('transactions-grid')
-  const [resetGridFn, setResetGridFn] = useState(null)
+  const [paginationOptions, setPaginationOptions] = useState(
+    initialPaginationOptions
+  )
+  const [selectedOrgId, setSelectedOrgId] = useState(null)
+
+  const queryData = useGetTransactionList(
+    {
+      ...paginationOptions,
+      selectedOrgId
+    },
+    {
+      cacheTime: 0,
+      staleTime: 0
+    }
+  )
 
   const getRowId = useCallback((params) => {
     return (
@@ -55,8 +75,6 @@ export const Transactions = () => {
     )
   }, [])
 
-  const [selectedOrgId, setSelectedOrgId] = useState(null)
-
   const shouldRenderLink = (props) => {
     return (
       props.data.transactionType !== 'ComplianceReport' ||
@@ -172,15 +190,10 @@ export const Transactions = () => {
     }
   }, [location.state])
 
-  const handleSetResetGrid = useCallback((fn) => {
-    setResetGridFn(() => fn)
-  }, [])
-
-  const handleClearFilters = useCallback(() => {
-    if (resetGridFn) {
-      resetGridFn()
-    }
-  }, [resetGridFn])
+  const handleClearFilters = () => {
+    gridRef.current?.resetGrid()
+    setPaginationOptions(initialPaginationOptions)
+  }
 
   if (!currentUser) {
     return <Loading />
@@ -273,31 +286,30 @@ export const Transactions = () => {
           }}
         >
           <Role roles={govRoles}>
-              <OrganizationList
-                onOrgChange={setSelectedOrgId}
-                onlyRegistered={false}
-              />
+            <OrganizationList
+              onOrgChange={setSelectedOrgId}
+              onlyRegistered={false}
+            />
           </Role>
         </Grid>
       </Grid>
       <BCBox component="div" sx={{ height: '100%', width: '100%' }}>
-        <BCGridViewer
-          gridRef={gridRef}
-          gridKey={gridKey}
+        <BCGridViewer2
+          ref={gridRef}
+          gridKey="transactions-grid"
           columnDefs={transactionsColDefs(t)}
-          query={useGetTransactionList}
-          queryParams={{ selectedOrgId }}
-          dataKey={'transactions'}
           getRowId={getRowId}
           overlayNoRowsTemplate={t('txn:noTxnsFound')}
-          autoSizeStrategy={{
-            type: 'fitGridWidth',
-            defaultMinWidth: 50,
-            defaultMaxWidth: 600
-          }}
           defaultColDef={defaultColDef}
-          defaultSortModel={defaultSortModel}
-          onSetResetGrid={handleSetResetGrid}
+          queryData={queryData}
+          dataKey="transactions"
+          initialPaginationOptions={initialPaginationOptions}
+          onPaginationChange={(newPagination) =>
+            setPaginationOptions((prev) => ({
+              ...prev,
+              ...newPagination
+            }))
+          }
           highlightedRowId={highlightedId}
         />
       </BCBox>
diff --git a/frontend/src/views/Transactions/_schema.js b/frontend/src/views/Transactions/_schema.js
index 7b7844fc3..87a25e61f 100644
--- a/frontend/src/views/Transactions/_schema.js
+++ b/frontend/src/views/Transactions/_schema.js
@@ -31,6 +31,12 @@ export const transactionsColDefs = (t) => [
     },
     filterParams: {
       buttons: ['clear']
+    },
+    comparator: (valueA, valueB) => {
+      const numberA = parseInt(valueA.slice(1), 10)
+      const numberB = parseInt(valueB.slice(1), 10)
+
+      return numberB - numberA
     }
   },
   {


Reviews:
user: login: areyeslo
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-07T16:49:28Z
user: login: kevin-hashimoto
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-11T00:38:51Z
user: login: areyeslo
body: LGTM
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T15:44:12Z

Review comments:
diff_hunk: @@ -31,6 +31,12 @@ export const transactionsColDefs = (t) => [
     },
     filterParams: {
       buttons: ['clear']
+    },
+    comparator: (valueA, valueB) => {
+      const numberA = parseInt(valueA.slice(1), 10)
+      const numberB = parseInt(valueB.slice(1), 10)
+
+      return numberB - numberA
path: frontend/src/views/Transactions/_schema.js
user: login: areyeslo
body: is it possible to sort by the transactions at the repo level for each of the endpoints?
created_at: 2025-03-07T16:49:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -31,6 +31,12 @@ export const transactionsColDefs = (t) => [
     },
     filterParams: {
       buttons: ['clear']
+    },
+    comparator: (valueA, valueB) => {
+      const numberA = parseInt(valueA.slice(1), 10)
+      const numberB = parseInt(valueB.slice(1), 10)
+
+      return numberB - numberA
path: frontend/src/views/Transactions/_schema.js
user: login: kevin-hashimoto
body: I've implemented BCGridViewer2 so that we can have control of the sorting on the backend but even though we sort in the backend, ag grid will run a sort on the front end. so it looks like we will need this on the front end as well. 
created_at: 2025-03-11T00:38:51Z
author_association: COLLABORATOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-03-07T01:25:25Z
body: ## Frontend Test Results
  1 files  130 suites   54s ⏱️
461 tests 441 ✅ 20 💤 0 ❌
463 runs  443 ✅ 20 💤 0 ❌

Results for commit 36f5f4c3.

[test-results]:data:application/gzip;base64,H4sIABSGz2cC/03MSw7CIBSF4a00jB0AF1DcjGmhNyG2xVAYGffu7UNh+H8nOW+GYRpXdu/EpWNrCfkI4JS+pD6HuBBoRU1b3lZlxK8ea3FuI9XQM7yIJP8L9mEiqTCmFNMpqSzHKZxRP6s0lzs0j3u3hy7Oc8gUDAxqVA6k0OhBX9EZKYzkDgdp9WDNzYPlAtnnC6EP/6AIAQAA

:recycle: This comment has been updated with latest results.
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-07T01:27:28Z
body: ## Backend Test Results
600 tests   600 ✅  2m 19s ⏱️
  1 suites    0 💤
  1 files      0 ❌

Results for commit 36f5f4c3.

[test-results]:data:application/gzip;base64,H4sIAICGz2cC/12MSw7CIBQAr9KwdsFHULyMaR99yYttMXxWxrsL9UPrcmaSeTCkaYzs0olDx2Km9AOXQ5/ILxWVLaKkVKPh/EvXmAH+1Y3uRTWBPU07MYbgw8eEvLRnhf3ybdpx5c1w5e0P/DxTKsCUQY1HUFJodEqfEIwURnLAQVo9WHN2ynKB7PkCY9s5ugUBAAA=

:recycle: This comment has been updated with latest results.
author_association: NONE