diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index afcc082d..0249c009 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -30,6 +30,10 @@ default: - echo "NEXT_PUBLIC_LTI_URL=$NEXT_PUBLIC_LTI_URL" - echo "NEXT_PUBLIC_SSO_LOGIN_URL=$NEXT_PUBLIC_SSO_LOGIN_URL" - echo "NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_API_BASE_URL" + - echo "NEXT_PUBLIC_APP_ENV=$NEXT_PUBLIC_APP_ENV" + - echo "NEXT_PUBLIC_HELPDESK_URL=$NEXT_PUBLIC_HELPDESK_URL" + - echo "NEXT_PUBLIC_DASHBOARD_ACCOUNTING_URL=$NEXT_PUBLIC_DASHBOARD_ACCOUNTING_URL" + - echo "NEXT_PUBLIC_S3_PUBLIC_BASE_URL=$NEXT_PUBLIC_S3_PUBLIC_BASE_URL" - echo "Building Next.js static export..." - npx next build - | @@ -41,7 +45,11 @@ default: "built_at": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")", "NEXT_PUBLIC_LTI_URL": "$NEXT_PUBLIC_LTI_URL", "NEXT_PUBLIC_SSO_LOGIN_URL": "$NEXT_PUBLIC_SSO_LOGIN_URL", - "NEXT_PUBLIC_API_BASE_URL": "$NEXT_PUBLIC_API_BASE_URL" + "NEXT_PUBLIC_API_BASE_URL": "$NEXT_PUBLIC_API_BASE_URL", + "NEXT_PUBLIC_APP_ENV": "$NEXT_PUBLIC_APP_ENV", + "NEXT_PUBLIC_HELPDESK_URL": "$NEXT_PUBLIC_HELPDESK_URL", + "NEXT_PUBLIC_DASHBOARD_ACCOUNTING_URL": "$NEXT_PUBLIC_DASHBOARD_ACCOUNTING_URL" + "NEXT_PUBLIC_S3_PUBLIC_BASE_URL": "NEXT_PUBLIC_S3_PUBLIC_BASE_URL" } EOF artifacts: @@ -142,6 +150,10 @@ build:dev: NEXT_PUBLIC_SSO_LOGIN_URL: 'https://dev-auth-erp.mbugroup.id' NEXT_PUBLIC_API_BASE_URL: 'https://dev-api-lti.mbugroup.id/api' NEXT_PUBLIC_CLIENT_ID: 'Lumbung-Telur-Indonesia' + NEXT_PUBLIC_APP_ENV: 'development' + NEXT_PUBLIC_HELPDESK_URL: 'https://dev-helpdesk.mbugroup.id/' + NEXT_PUBLIC_DASHBOARD_ACCOUNTING_URL: 'https://dev-dashboard-ho.mbugroup.id/' + NEXT_PUBLIC_S3_PUBLIC_BASE_URL: 'https://mbu-lti-storage.s3.ap-southeast-3.amazonaws.com' deploy:dev: <<: *deploy_template @@ -170,6 +182,9 @@ build:staging: NEXT_PUBLIC_SSO_LOGIN_URL: 'https://stg-auth-erp.mbugroup.id' NEXT_PUBLIC_API_BASE_URL: 'https://stg-api-lti.mbugroup.id/api' NEXT_PUBLIC_CLIENT_ID: 'Lumbung-Telur-Indonesia' + NEXT_PUBLIC_APP_ENV: 'staging' + NEXT_PUBLIC_HELPDESK_URL: 'https://stg-helpdesk.mbugroup.id/' + NEXT_PUBLIC_DASHBOARD_ACCOUNTING_URL: 'https://stg-dashboard-ho.mbugroup.id/' deploy:staging: <<: *deploy_template @@ -185,7 +200,7 @@ deploy:staging: url: https://stg-lti-erp.mbugroup.id # ========================================================== -# ====== STAGING (Branch production) ====== +# ====== (Branch production) ====== # ========================================================== build:production: <<: *build_template @@ -198,6 +213,10 @@ build:production: NEXT_PUBLIC_SSO_LOGIN_URL: 'https://auth-erp.mbugroup.id' NEXT_PUBLIC_API_BASE_URL: 'https://api-lti.mbugroup.id/api' NEXT_PUBLIC_CLIENT_ID: 'Lumbung-Telur-Indonesia' + NEXT_PUBLIC_APP_ENV: 'production' + NEXT_PUBLIC_HELPDESK_URL: 'https://helpdesk.mbugroup.id/' + NEXT_PUBLIC_DASHBOARD_ACCOUNTING_URL: 'https://dashboard-ho.mbugroup.id/' + NEXT_PUBLIC_S3_PUBLIC_BASE_URL: 'https://mbu-lti-storage.s3.ap-southeast-3.amazonaws.com/' deploy:production: <<: *deploy_template diff --git a/CLAUDE.md b/CLAUDE.md index d0a2f23c..d8f15df6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -161,6 +161,47 @@ const handleFilterLocationChange = useCallback( - `SupplierTable`, `KandangsTable`, `LocationsTable`, `CustomersTable` in `src/components/pages/master-data/` - Use same pattern for data tables in other modules (inventory, finance, purchase, etc.) +## Server-side sorting pattern + +Data tables use TanStack Table's `SortingState` wired to `useTableFilter` so that sorting triggers a server re-fetch rather than client-side reordering. + +**Four-part wiring:** + +1. **Local sort state** — `const [sorting, setSorting] = useState([]);` + +2. **`useTableFilter` config** — Add `sort_by` and `order_by` to `initial` and `paramMap`. The `paramMap` key is the internal name; the value is the query param name sent to the server (they can differ, e.g. `order_by` → `sort_order`): + + ```ts + initial: { sort_by: '', order_by: '' } + paramMap: { sort_by: 'sort_by', order_by: 'sort_order' } + ``` + +3. **`useEffect` sync** — Watches `sorting` and pushes changes into `useTableFilter`: + + ```ts + useEffect(() => { + if (sorting.length > 0) { + updateFilter('sort_by', sorting[0].id, true); + updateFilter('order_by', sorting[0].desc ? 'desc' : 'asc', true); + } else { + updateFilter('sort_by', ''); + updateFilter('order_by', ''); + } + }, [sorting]); + ``` + +4. **SWR key** — SWR uses `getTableFilterToQueryString()` as its key, so any filter change (including sort) automatically re-fetches with the new query params. TanStack Table's built-in client sorting is effectively disabled; the server does the sorting. + +**Pass `sorting`, `setSorting`, and `manualSorting` to ``:** + +```tsx +
+``` + +`manualSorting={true}` is required — without it TanStack Table still applies its own client-side sort pass on top of the server-sorted data, producing incorrect order. + +**Reference implementation:** `MarketingTable` in [src/components/pages/marketing/MarketingTable.tsx](src/components/pages/marketing/MarketingTable.tsx). + ## Server-side file export pattern All file exports (Excel, PDF, or any format) must use **server-side generation** — the server returns a binary blob and the browser triggers a download. Never generate files client-side with `xlsx`, `@react-pdf/renderer`, `jspdf`, or similar libraries. diff --git a/src/components/Pagination.tsx b/src/components/Pagination.tsx index 43b26d90..1f2ba533 100644 --- a/src/components/Pagination.tsx +++ b/src/components/Pagination.tsx @@ -226,7 +226,7 @@ const Pagination = ({ const PageInfo = () => ( - Page {currentPage} of {totalPages} + Total Item: {totalItems} | Page {currentPage} of {totalPages} ); diff --git a/src/components/pages/expense/ExpenseRequestContent.tsx b/src/components/pages/expense/ExpenseRequestContent.tsx index e92ecc82..a0438197 100644 --- a/src/components/pages/expense/ExpenseRequestContent.tsx +++ b/src/components/pages/expense/ExpenseRequestContent.tsx @@ -548,21 +548,15 @@ const ExpenseRequestContent = ({