[Customer Portal][Web] Add Time Card State Filter & Infinite Pagination - #287
Conversation
Represent TimeCard.state as an object ({id, label} | null) instead of a plain string. Update usages to read state.label (with a "--" fallback) and adapt getTimeCardStateColorPath to accept the state object and normalize state.label for color resolution. This ensures consistency with other typed fields and prevents runtime errors when rendering or computing chip colors.
Update tests to match the new TimeCard state shape: replace string states with objects ({ id, label }) in ProjectTimeTracking and TimeTrackingCard tests, and use null for missing state in the fallback case. This keeps test fixtures aligned with the component's expected prop structure.
Introduce TIME_CARD_STATE and TimeCardState type in projectDetailsConstants.ts to represent time card lifecycle values (Pending, Submitted, Approved, Rejected, Processed, Recalled). Update getTimeCardStateColorPath in projectDetails.ts to import and use the new constant, switch on explicit state values with a defensive null check, and map each state to the appropriate color token. Also add a console.error in formatProjectDateTime to log formatting failures for easier debugging.
Support filtering time cards by state across API, model, UI, and tests. Introduces an optional `state` field on TimeCardSearchRequest and UseSearchProjectTimeCards (propagated into the request filters), adds a state Select control to TimeCardsDateFilter with props `state`, `onStateChange`, and `timeCardStates`, and wires state handling in ProjectTimeTracking (including fetching `timeCardStates` via useGetProjectFilters). Tests updated/mocked to cover the new filter behavior.
Rework the TimeCardsDateFilter UI to separate Date Range and State filters, improve layout/responsiveness, and tidy up labels and icons. Removed shownCount, totalCount and isLoading props from the component and updated ProjectTimeTracking to stop passing those props. Updated unit tests to remove assertions for counts/loading and instead assert the state dropdown and labels are rendered. Files changed: ProjectTimeTracking.tsx, TimeCardsDateFilter.tsx, and its test.
Replace useQuery with useInfiniteQuery in the project time cards hook to support paginated/infinite fetching. The hook now accepts states[] instead of a single state, uses pageParam as the offset with a default limit of 10, and includes initialPageParam/getNextPageParam logic to compute the next offset. The TimeCardSearchRequest model was updated to use filters.states: string[]. The ProjectTimeTracking component was updated to pass states as an array, auto-fetch remaining pages (fetchNextPage) in a useEffect, and flatten pages into a single timeCards array; totalRecords is taken from the first page. Query keys and debug logging were updated to include the offset.
Rework the TimeCardsDateFilter component to simplify and improve the date/state filter UI. Changes include: - Remove unused Filter icon import and usages; keep Calendar icon only. - Align container items to center and unify spacing. - Consolidate and restructure date inputs: add a "Filter by Date Range:" label, add explicit "From:" and "To:" labels, and replace the previous duplicated markup with two TextField date inputs using InputAdornment(Calendar). - Add ids and htmlFor attributes for better accessibility and apply nowrap styling to labels. - Move and inline the state filter label with the Select control for a more compact layout. These updates improve readability, reduce duplicated markup, and enhance accessibility and visual consistency.
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (5)
📝 WalkthroughWalkthroughReplaces single-page time card query with an infinite query using pageParam-based pagination and state filters; changes TimeCard.state from a string to an object ({id, label} | null); adds a state dropdown filter and auto-fetch logic in the time-tracking UI; updates models, utils, and tests accordingly. Changes
Sequence DiagramsequenceDiagram
participant User
participant Component as ProjectTimeTracking<br/>Component
participant FilterAPI as useGetProjectFilters
participant SearchAPI as useSearchProjectTimeCards
participant Backend as Backend API
User->>Component: Mount / interact
Component->>FilterAPI: request timeCardStates
FilterAPI->>Backend: GET /filters
Backend-->>FilterAPI: { timeCardStates: [...] }
FilterAPI-->>Component: timeCardStates
Component->>SearchAPI: fetch initial page (pageParam=0, states?)
SearchAPI->>Backend: POST /search (pagination: {limit:10, offset:0}, filters.states?)
Backend-->>SearchAPI: { timeCards: [...], totalRecords: N }
SearchAPI-->>Component: pages[0]
Component->>Component: flatten pages & render
Component->>SearchAPI: (on hasNextPage) fetchNextPage (pageParam=offsetNext)
SearchAPI->>Backend: POST /search (offset: offsetNext, states?)
Backend-->>SearchAPI: next page
SearchAPI-->>Component: pages[1]...
Estimated Code Review Effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly Related PRs
Suggested Reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/customer-portal/webapp/src/components/project-details/time-tracking/__tests__/ProjectTimeTracking.test.tsx (1)
119-158:⚠️ Potential issue | 🟠 MajorFix mock shape for infinite-query contract—wrap timeCards in
pagesarray.The test mocks
useSearchProjectTimeCardswith a flatdataobject{ timeCards: [...] }, but the component readsdata?.pages.flatMap((page) => page.timeCards)at line 99 anddata?.pages?.[0]?.totalRecordsat line 103. This mismatch breaks the test: assertions for "Log 1", "Log 2", and time card count will fail becausedata.pagesis undefined.Update the mock to match the InfiniteData shape:
Mock structure fix
const mockData = { - timeCards: [ + pages: [ + { + timeCards: [ { id: "1", case: { label: "Log 1", number: "CS001", id: "c1" }, totalTime: 60, state: { id: "approved", label: "Approved" }, hasBillable: false, approvedBy: null, project: { id: "p1", label: "Project 1" }, createdOn: "2025-12-10", }, { id: "2", case: { label: "Log 2", number: "CS002", id: "c2" }, totalTime: 30, state: { id: "submitted", label: "Submitted" }, hasBillable: true, approvedBy: null, project: { id: "p1", label: "Project 1" }, createdOn: "2025-12-11", }, - ], - totalRecords: 2, - offset: 0, - limit: 10, + ], + totalRecords: 2, + offset: 0, + limit: 10, + }, + ], + pageParams: [0], }; vi.mocked(useSearchProjectTimeCards).mockReturnValue({ data: mockData, isLoading: false, isError: false, + hasNextPage: false, + fetchNextPage: vi.fn(), } as any);Also apply the same fix to the mocks at lines 192 and 208.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/customer-portal/webapp/src/components/project-details/time-tracking/__tests__/ProjectTimeTracking.test.tsx` around lines 119 - 158, The test's mock for useSearchProjectTimeCards uses a flat data object but the component expects React Query InfiniteData shape (data.pages array); update the mocked return value for useSearchProjectTimeCards so data is { pages: [{ timeCards: [...], totalRecords: 2, offset: 0, limit: 10 }] } instead of { timeCards: [...] }, and make the same change for the other mocks mentioned (the ones around lines 192 and 208) so assertions that rely on data.pages and pages[0].totalRecords succeed; keep useGetTimeCardsStats mock as-is if it remains unrelated.
🧹 Nitpick comments (6)
apps/customer-portal/webapp/src/components/project-details/time-tracking/__tests__/TimeTrackingCard.test.tsx (1)
51-63: Assert state fallback explicitly in the null-state scenario.On Line 54 you set
state: null, but the test only verifiesapprovedBy. Add an assertion that the state label fallback is rendered to lock this behavior in.💡 Minimal test hardening
render(<TimeTrackingCard card={incompleteCard} />); expect(screen.getByText(/Approved by: --/)).toBeInTheDocument(); + expect(screen.queryByText("Approved")).not.toBeInTheDocument();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/customer-portal/webapp/src/components/project-details/time-tracking/__tests__/TimeTrackingCard.test.tsx` around lines 51 - 63, The test sets state: null on the TimeCard (incompleteCard) but only asserts the approvedBy fallback; add an assertion to verify the state label fallback is rendered by TimeTrackingCard (e.g., assert that the "State: --" text is present). Locate the test using TimeTrackingCard, TimeCard and mockCard and append an expectation like expect(screen.getByText(/State:\s*--/)).toBeInTheDocument() to lock the null-state behavior.apps/customer-portal/webapp/src/components/project-details/time-tracking/__tests__/TimeCardsDateFilter.test.tsx (1)
49-63: Test intent and assertion don’t fully match.The test says it validates dropdown options, but it only checks the section label. Please assert that provided state options are actually rendered/selectable.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/customer-portal/webapp/src/components/project-details/time-tracking/__tests__/TimeCardsDateFilter.test.tsx` around lines 49 - 63, The test currently titled to check dropdown options for TimeCardsDateFilter only asserts the label; update the test to assert that the provided timeCardStates (mockTimeCardStates) are rendered and selectable: locate the state select (e.g., getByRole('combobox') or getByLabelText("Filter by State:")), assert each mockTimeCardStates item appears as an option (getByText or within getAllByRole('option')), and simulate selection using userEvent.selectOptions or fireEvent.change to ensure onStateChange behavior can be triggered; keep the render call for TimeCardsDateFilter and reuse the existing props (startDate, endDate, onStartDateChange, onEndDateChange, state, onStateChange, timeCardStates) while adding these option-rendering and selection assertions.apps/customer-portal/webapp/src/components/project-details/time-tracking/TimeCardsDateFilter.tsx (1)
44-45: JSDoc is stale after prop changes.Line 44 still says “handlers, and counts”, but count-related props were removed. Please align the doc comment with the current API.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/customer-portal/webapp/src/components/project-details/time-tracking/TimeCardsDateFilter.tsx` around lines 44 - 45, The JSDoc for TimeCardsDateFilter is stale: update the comment for TimeCardsDateFilterProps/TimeCardsDateFilter to reflect current props (remove references to "counts" and "count-related props") and accurately list the remaining props (date values and handlers) and return type; ensure the `@param` description matches the prop shape used in the component signature (TimeCardsDateFilterProps) so the doc no longer references removed count props.apps/customer-portal/webapp/src/utils/projectDetails.ts (2)
40-59: Prefer stable state identifiers over labels in color mapping.At Line 44, switching on
state.labelties color logic to display text. If labels change (copy update/localization), chip colors can silently regress. Prefer usingstate.idfor the switch and keeplabelfor UI only.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/customer-portal/webapp/src/utils/projectDetails.ts` around lines 40 - 59, The color mapping currently switches on state.label which couples colors to display text; change the guard and switch to use state.id instead: replace the initial null/empty check (currently if (!state?.label)) with a check for id (if (!state?.id) return "text.secondary") and switch on state.id in the switch statement, keeping the same case branches using the TIME_CARD_STATE constants (e.g., TIME_CARD_STATE.APPROVED, SUBMITTED, REJECTED, RECALLED, PENDING, PROCESSED) and leave state.label solely for UI display.
173-173: Avoid directconsole.errorin shared formatting utility.Line 173 can spam console noise from bad upstream payloads. Consider routing through the project logger (or logging once at call-site boundaries) instead of raw console calls in this low-level helper.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/customer-portal/webapp/src/utils/projectDetails.ts` at line 173, Replace the raw console.error call in projectDetails.ts with the project logging infrastructure: remove console.error and instead call the shared logger (e.g., projectLogger.error or logger.error) from the same module or import it, passing a descriptive message and the error/context (e.g., input value) so the low-level helper (function formatDateString / formatDate in projectDetails.ts) logs via the project logger rather than console; alternatively if no logger is available, remove the log and surface the error (throw or return null) so callers can log once at their boundary.apps/customer-portal/webapp/src/api/useSearchProjectTimeCards.ts (1)
63-89: Add abort signal support to prevent stale requests.The
queryFnat lines 63-89 should destructure thesignalparameter from the context and pass it tofetchFn. Without this, cancelled queries (e.g., from rapid filter/date changes) may continue executing and waste network resources. The pattern is already implemented inuseGetCaseAttachments.tsand thefetchFnsupports it viaRequestInit.Example from useGetCaseAttachments.ts
queryFn: async ({ pageParam, signal }): Promise<CaseAttachmentsResponse> => { // ... const response = await fetchFn(requestUrl, { method: "GET", signal });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/customer-portal/webapp/src/api/useSearchProjectTimeCards.ts` around lines 63 - 89, Destructure the abort Signal from the query context in the queryFn of useSearchProjectTimeCards (i.e., change the parameter to include signal) and pass that signal into the fetchFn call via the RequestInit options object; update the fetchFn invocation inside queryFn (where TimeCardSearchRequest is constructed and requestUrl is used) to include signal so cancelled queries are aborted and no stale network requests continue.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@apps/customer-portal/webapp/src/components/project-details/time-tracking/ProjectTimeTracking.tsx`:
- Around line 91-95: The current useEffect in ProjectTimeTracking (the effect
watching data, hasNextPage, fetchNextPage) auto-fetches pages unboundedly which
can create request bursts; modify this effect to guard against aggressive
pagination by limiting consecutive auto-fetches (e.g., track a local counter or
timestamp inside the effect to fetch at most N additional pages per render or
per user action), or debounce/throttle calls to fetchNextPage so only a small
batch (configurable, e.g., 1–3 pages) is fetched automatically, and keep
hasNextPage and fetchNextPage checks; implement the guard within the same
component (ProjectTimeTracking) using a ref/state like autoFetchCountRef or
lastAutoFetchRef to enforce the limit and reset it on filter changes.
In
`@apps/customer-portal/webapp/src/components/project-details/time-tracking/TimeCardsDateFilter.tsx`:
- Around line 136-156: The "Filter by State" label (Typography) is not
programmatically associated with the Select, harming screen reader
accessibility; fix by adding an accessible label id and wiring it to the Select:
give the label element (replace or augment Typography) an id like
"state-filter-label" (or create an InputLabel with that id) and set Select's
labelId="state-filter-label" and id="state-filter-select" (or add
aria-labelledby="state-filter-label") so the Select (component Select) is
announced correctly; update usages around TimeCardsDateFilter where Typography,
FormControl and Select are declared (e.g., the Typography/InputLabel and Select
components).
---
Outside diff comments:
In
`@apps/customer-portal/webapp/src/components/project-details/time-tracking/__tests__/ProjectTimeTracking.test.tsx`:
- Around line 119-158: The test's mock for useSearchProjectTimeCards uses a flat
data object but the component expects React Query InfiniteData shape (data.pages
array); update the mocked return value for useSearchProjectTimeCards so data is
{ pages: [{ timeCards: [...], totalRecords: 2, offset: 0, limit: 10 }] } instead
of { timeCards: [...] }, and make the same change for the other mocks mentioned
(the ones around lines 192 and 208) so assertions that rely on data.pages and
pages[0].totalRecords succeed; keep useGetTimeCardsStats mock as-is if it
remains unrelated.
---
Nitpick comments:
In `@apps/customer-portal/webapp/src/api/useSearchProjectTimeCards.ts`:
- Around line 63-89: Destructure the abort Signal from the query context in the
queryFn of useSearchProjectTimeCards (i.e., change the parameter to include
signal) and pass that signal into the fetchFn call via the RequestInit options
object; update the fetchFn invocation inside queryFn (where
TimeCardSearchRequest is constructed and requestUrl is used) to include signal
so cancelled queries are aborted and no stale network requests continue.
In
`@apps/customer-portal/webapp/src/components/project-details/time-tracking/__tests__/TimeCardsDateFilter.test.tsx`:
- Around line 49-63: The test currently titled to check dropdown options for
TimeCardsDateFilter only asserts the label; update the test to assert that the
provided timeCardStates (mockTimeCardStates) are rendered and selectable: locate
the state select (e.g., getByRole('combobox') or getByLabelText("Filter by
State:")), assert each mockTimeCardStates item appears as an option (getByText
or within getAllByRole('option')), and simulate selection using
userEvent.selectOptions or fireEvent.change to ensure onStateChange behavior can
be triggered; keep the render call for TimeCardsDateFilter and reuse the
existing props (startDate, endDate, onStartDateChange, onEndDateChange, state,
onStateChange, timeCardStates) while adding these option-rendering and selection
assertions.
In
`@apps/customer-portal/webapp/src/components/project-details/time-tracking/__tests__/TimeTrackingCard.test.tsx`:
- Around line 51-63: The test sets state: null on the TimeCard (incompleteCard)
but only asserts the approvedBy fallback; add an assertion to verify the state
label fallback is rendered by TimeTrackingCard (e.g., assert that the "State:
--" text is present). Locate the test using TimeTrackingCard, TimeCard and
mockCard and append an expectation like
expect(screen.getByText(/State:\s*--/)).toBeInTheDocument() to lock the
null-state behavior.
In
`@apps/customer-portal/webapp/src/components/project-details/time-tracking/TimeCardsDateFilter.tsx`:
- Around line 44-45: The JSDoc for TimeCardsDateFilter is stale: update the
comment for TimeCardsDateFilterProps/TimeCardsDateFilter to reflect current
props (remove references to "counts" and "count-related props") and accurately
list the remaining props (date values and handlers) and return type; ensure the
`@param` description matches the prop shape used in the component signature
(TimeCardsDateFilterProps) so the doc no longer references removed count props.
In `@apps/customer-portal/webapp/src/utils/projectDetails.ts`:
- Around line 40-59: The color mapping currently switches on state.label which
couples colors to display text; change the guard and switch to use state.id
instead: replace the initial null/empty check (currently if (!state?.label))
with a check for id (if (!state?.id) return "text.secondary") and switch on
state.id in the switch statement, keeping the same case branches using the
TIME_CARD_STATE constants (e.g., TIME_CARD_STATE.APPROVED, SUBMITTED, REJECTED,
RECALLED, PENDING, PROCESSED) and leave state.label solely for UI display.
- Line 173: Replace the raw console.error call in projectDetails.ts with the
project logging infrastructure: remove console.error and instead call the shared
logger (e.g., projectLogger.error or logger.error) from the same module or
import it, passing a descriptive message and the error/context (e.g., input
value) so the low-level helper (function formatDateString / formatDate in
projectDetails.ts) logs via the project logger rather than console;
alternatively if no logger is available, remove the log and surface the error
(throw or return null) so callers can log once at their boundary.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
apps/customer-portal/webapp/src/api/useSearchProjectTimeCards.tsapps/customer-portal/webapp/src/components/project-details/time-tracking/ProjectTimeTracking.tsxapps/customer-portal/webapp/src/components/project-details/time-tracking/TimeCardsDateFilter.tsxapps/customer-portal/webapp/src/components/project-details/time-tracking/TimeTrackingCard.tsxapps/customer-portal/webapp/src/components/project-details/time-tracking/__tests__/ProjectTimeTracking.test.tsxapps/customer-portal/webapp/src/components/project-details/time-tracking/__tests__/TimeCardsDateFilter.test.tsxapps/customer-portal/webapp/src/components/project-details/time-tracking/__tests__/TimeTrackingCard.test.tsxapps/customer-portal/webapp/src/constants/projectDetailsConstants.tsapps/customer-portal/webapp/src/models/requests.tsapps/customer-portal/webapp/src/models/responses.tsapps/customer-portal/webapp/src/utils/projectDetails.ts
Add abort signal to search API requests and include it in fetch options to allow request cancellation. Limit automatic pagination fetches in ProjectTimeTracking (useRef counter, max 3) and reset the counter when filters change to avoid request bursts. Make TimeCardsDateFilter more accessible by adding InputLabel/id/labelId/aria-labelledby attributes and adjust imports; enhance tests to interact with the Select via userEvent and assert state selection. Update tests to match paginated response shape (pages/timeCards), provide hasNextPage and fetchNextPage mocks, and add an assertion for State display in TimeTrackingCard. Fix getTimeCardStateColorPath to switch on state.id (not label) and simplify the catch block in formatProjectDateTime.
Remove unused InputLabel import from TimeCardsDateFilter. Update TimeCardsDateFilter tests to use fireEvent and waitFor (remove userEvent), open the select via mouseDown, wait for options to appear, and click the option to assert selection. Fix formatProjectDateTime catch block to accept the error parameter and log a clearer message including the original date string.
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
apps/customer-portal/webapp/src/components/project-details/time-tracking/__tests__/ProjectTimeTracking.test.tsx (1)
37-39: Consider assertingTimeCardsDateFilterprop wiring instead of fully stubbing it out.The static mock hides regressions in
state/onStateChangeintegration and auto-fetch reset behavior. A lightweight prop-capturing mock would improve coverage for the new filter flow.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/customer-portal/webapp/src/components/project-details/time-tracking/__tests__/ProjectTimeTracking.test.tsx` around lines 37 - 39, Replace the fully static mock of TimeCardsDateFilter with a lightweight prop-capturing mock so tests can assert prop wiring (state and onStateChange) and confirm auto-fetch reset behavior; specifically, update the mock for TimeCardsDateFilter to render a simple element (e.g., div with data-testid="date-filter") that also stores or forwards received props (state, onStateChange) so the test can simulate calling onStateChange and assert that ProjectTimeTracking (or the tested component) responds by updating state and triggering the expected fetch/reset logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@apps/customer-portal/webapp/src/components/project-details/time-tracking/ProjectTimeTracking.tsx`:
- Around line 93-99: The auto-fetch cap in the useEffect (autoFetchCountRef +
maxAutoFetches) stops loading additional pages even when hasNextPage is true;
update the logic to provide a fallback so results aren’t truncated: either (A)
implement a controlled background fetchRemainingPages routine (called from
useEffect when cap reached and hasNextPage is true) that continues calling
fetchNextPage in a throttled/async loop until hasNextPage is false (respecting
rate limits), or (B) when autoFetchCountRef.current >= maxAutoFetches and
hasNextPage is true, set a state flag (e.g., showLoadMore) to surface a user
action that calls fetchNextPage manually to load remaining pages; modify
useEffect, autoFetchCountRef, and add the fetchRemainingPages or showLoadMore
handling so large histories fully load.
- Around line 102-104: The effect that resets autoFetchCountRef.current runs on
changes to startDate, endDate, and state but not when the selected project
changes; update the dependency array of the useEffect that sets
autoFetchCountRef.current = 0 to also include projectId so the counter is reset
whenever projectId changes (look for useEffect that references
autoFetchCountRef, startDate, endDate, and state and add projectId to its
dependencies).
In
`@apps/customer-portal/webapp/src/components/project-details/time-tracking/TimeCardsDateFilter.tsx`:
- Around line 155-158: The MenuItem values are using stateOption.label which
couples filtering to display text; change the MenuItem value to use
stateOption.id instead (in the map where timeCardStates and stateOption are
used) and update the Select binding/handler (the Select's value prop and
onChange consumer) to expect and store the canonical id rather than the label so
filtering and localization remain stable.
In `@apps/customer-portal/webapp/src/utils/projectDetails.ts`:
- Around line 44-58: getTimeCardStateColorPath is switching on state.id but test
data uses lowercase IDs so valid states fall through; normalize state.id before
the switch (e.g., call toUpperCase()/toLowerCase() on state.id or compute a
normalizedId variable) and use that normalized value in the switch so
comparisons against TIME_CARD_STATE (the enum/consts referenced in the switch)
match correctly; update the switch in getTimeCardStateColorPath (and any callers
if necessary) to use the normalizedId to ensure "approved", "Approved", or
"APPROVED" all map to the correct token like "success.main".
---
Nitpick comments:
In
`@apps/customer-portal/webapp/src/components/project-details/time-tracking/__tests__/ProjectTimeTracking.test.tsx`:
- Around line 37-39: Replace the fully static mock of TimeCardsDateFilter with a
lightweight prop-capturing mock so tests can assert prop wiring (state and
onStateChange) and confirm auto-fetch reset behavior; specifically, update the
mock for TimeCardsDateFilter to render a simple element (e.g., div with
data-testid="date-filter") that also stores or forwards received props (state,
onStateChange) so the test can simulate calling onStateChange and assert that
ProjectTimeTracking (or the tested component) responds by updating state and
triggering the expected fetch/reset logic.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
apps/customer-portal/webapp/src/api/useSearchProjectTimeCards.tsapps/customer-portal/webapp/src/components/project-details/time-tracking/ProjectTimeTracking.tsxapps/customer-portal/webapp/src/components/project-details/time-tracking/TimeCardsDateFilter.tsxapps/customer-portal/webapp/src/components/project-details/time-tracking/__tests__/ProjectTimeTracking.test.tsxapps/customer-portal/webapp/src/components/project-details/time-tracking/__tests__/TimeCardsDateFilter.test.tsxapps/customer-portal/webapp/src/components/project-details/time-tracking/__tests__/TimeTrackingCard.test.tsxapps/customer-portal/webapp/src/utils/projectDetails.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/customer-portal/webapp/src/components/project-details/time-tracking/tests/TimeCardsDateFilter.test.tsx
Introduce a paginated "Load More" UI and control auto-fetching: add showLoadMore state, cap automatic fetches with maxAutoFetches, show the Load More button when the cap is reached, reset behavior on project/filter changes, and add a handleLoadMore that resumes fetching. Fix TimeCardsDateFilter to emit stateOption.id (not label) so filters use IDs consistently. Update tests to mock the date filter with props (state, start/end dates, onStateChange) to reflect the new behavior. Normalize time card state IDs in projectDetails util (title-case) before switching to ensure comparisons match TIME_CARD_STATE constants and avoid mismatches.
Refactor pagination auto-fetch logic to prevent request bursts: keep an early return if there's no data or no next page, increment autoFetchCount and fetch up to maxAutoFetches, then stop auto-fetching. Add a dedicated effect to set showLoadMore when there are more pages and the auto-fetch limit has been reached. Remove the redundant setShowLoadMore(false) from the filter-reset effect and rely on the new effect to manage visibility. Also combine a few variable declarations for minor formatting cleanup.
76bff44
into
wso2-open-operations:customer-portal-milestone-1
Description
This pull request introduces a major enhancement to the project time tracking feature by adding support for filtering time cards by state, improving infinite pagination, and updating related data structures and tests to support these changes. The most important changes are grouped below:
Feature: Time Card State Filtering
ProjectTimeTracking.tsx,TimeCardsDateFilter.tsx,useSearchProjectTimeCards.ts,useGetProjectFilters,projectDetailsConstants.ts,models/requests.ts,models/responses.ts) [1] [2] [3] [4] [5] [6] [7] [8] [9]Enhancement: Infinite Pagination for Time Cards
useSearchProjectTimeCardshook to useuseInfiniteQueryinstead ofuseQuery, enabling seamless loading of all time card pages and automatically fetching additional pages as needed. (useSearchProjectTimeCards.ts) [1] [2] [3] [4] [5]Data Model Updates
TimeCardmodel so that itsstateproperty is now an object withidandlabel, instead of a string. This change is reflected throughout the UI and tests to support richer state information and display. (models/responses.ts,TimeTrackingCard.tsx, related tests) [1] [2] [3] [4] [5] [6]Tests and Mocks
TimeCardstate structure. Mocks for project filters and time card states were added to ensure comprehensive coverage. (ProjectTimeTracking.test.tsx,TimeCardsDateFilter.test.tsx,TimeTrackingCard.test.tsx) [1] [2] [3] [4] [5] [6] [7]Constants and Types
TIME_CARD_STATEconstant and associatedTimeCardStatetype to standardize possible time card states across the codebase. (projectDetailsConstants.ts)These changes collectively improve the user experience when viewing and filtering project time cards, provide more robust data handling, and ensure the codebase is well-tested for the new functionality.
Summary by CodeRabbit
New Features
Tests