Skip to content

[CSM Portal][FE] feat(csm-timecards): core utility functions and tests (2/6) - #994

Merged
rksk merged 1 commit into
wso2-open-operations:v2from
Hesara2003:feature/timecards-p2-api
Jul 2, 2026
Merged

rksk merged 1 commit into
wso2-open-operations:v2from
Hesara2003:feature/timecards-p2-api

Conversation

@Hesara2003

@Hesara2003 Hesara2003 commented Jul 1, 2026 •

Copy link
Copy Markdown
Contributor

Purpose

Adds the three pure utility modules the API store depends on (timeCardTotals, timeSheetWeek, timeSheetState) together with their unit tests. Requires PR 1/6 (#992) to be merged first.

Goals

  • timeCardTotals.ts — compute total hours and per-activity summaries
  • timeSheetWeek.ts — ISO week boundary helpers
  • timeSheetState.ts — cardActions / sheetActions RBAC logic, sheetStatus rollup, TimecardAction / SheetAction types
  • Unit tests for totals and week helpers

Approach

Pure functions with no side-effects placed before the API layer so timeCardStore.ts (PR 3/6) can import them without forward-reference errors.

User stories

ISSU-009 — Time Management (Epic: Issue Management, Priority: Must have)

As a CS Engineer, I want to log time spent on an issue so that effort is tracked for operational reporting and billing purposes.

Release note

Internal utility functions for time-cards. No user-visible change in this PR.

Documentation

N/A

Training

N/A

Certification

N/A

Marketing

N/A

Automation tests

  • Unit tests: timeCardTotals.test.ts, timeSheetWeek.test.ts — totals calculation and week-boundary edge cases

Security checks

Samples

N/A

Related PRs

6-PR series for ISSU-009 — merge into v2 in order:

  1. [CSM Portal][FE] feat(csm-timecards): foundation types, constants and config keys (1/6) #992 — Foundation ← merge first
  2. This PR — Core utility functions and tests
  3. PR 3/6 — API data layer
  4. PR 4/6 — Notification util, role hooks and remaining tests
  5. PR 5/6 — UI components
  6. PR 6/6 — Page and case-detail integration

Migrations

N/A

Test environment

macOS 14, Node 20, Chrome 126

Learning

N/A

Summary by CodeRabbit

  • New Features

    • Added improved timecard and timesheet handling, including clearer week ranges, stable week calculations, and more accurate hour totals.
    • Added state-aware actions for timecards and timesheets so available options better match the current status and permissions.
  • Bug Fixes

    • Improved date handling to reduce timezone-related issues.
    • Tightened draft validation to flag missing required fields and ensure time entries include logged hours.

@coderabbitai

coderabbitai Bot commented Jul 1, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds three new utility modules for the CSM timecards feature: timeCardTotals.ts (breakdown totals and draft validation), timeSheetWeek.ts (ISO week boundary calculations), and timeSheetState.ts (card/sheet state-to-action derivation), each accompanied by Vitest test suites.

Changes

Timecard utility modules

Layer / File(s) Summary
Totals and draft validation
apps/csm-portal/webapp/src/features/csm-timecards/utils/timeCardTotals.ts, .../__tests__/timeCardTotals.test.ts
Adds emptyBreakdown, roundHours, totalHours, hasLoggedHours, TimeCardDraft/TimeCardDraftErrors types, and timeCardDraftErrors validator; tests cover zero-breakdown totals, fractional-hour accuracy, logged-hours detection, and validation error cases.
Week boundary calculations
apps/csm-portal/webapp/src/features/csm-timecards/utils/timeSheetWeek.ts, .../__tests__/timeSheetWeek.test.ts
Adds weekStartOf, weekEndOf, weekKey, localTodayIso, and weekLabel UTC-based ISO date helpers; tests verify Monday/Sunday alignment, idempotence, and label formatting.
Card/sheet state-to-action logic
apps/csm-portal/webapp/src/features/csm-timecards/utils/timeSheetState.ts
Adds TimecardRoleCtx, TimecardAction, EDITABLE_STATES, cardActions, sheetStatus, SheetAction, and sheetActions for permission-based action derivation on cards and sheets.

Estimated code review effort: 2 (Simple) | ~15 minutes

Possibly related PRs

Suggested reviewers: rksk, cloby99

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: core timecard utility functions and tests.
Description check ✅ Passed The description follows the template well and covers purpose, goals, approach, tests, security, and related PR context.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

cloby99
cloby99 previously approved these changes Jul 2, 2026
@Hesara2003
Hesara2003 dismissed cloby99’s stale review July 2, 2026 17:07

The merge-base changed after approval.

cloby99
cloby99 previously approved these changes Jul 2, 2026
@Hesara2003
Hesara2003 dismissed cloby99’s stale review July 2, 2026 17:16

The merge-base changed after approval.

rksk
rksk previously approved these changes Jul 2, 2026

@rksk rksk left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approving to land the time-cards stack on v2. Post-merge hardening tracked separately (search truncation, self-approval, hour rounding, identity gate).

@Hesara2003
Hesara2003 dismissed rksk’s stale review July 2, 2026 17:38

The merge-base changed after approval.

rksk
rksk previously approved these changes Jul 2, 2026
@Hesara2003
Hesara2003 dismissed rksk’s stale review July 2, 2026 17:44

The merge-base changed after approval.

rksk
rksk previously approved these changes Jul 2, 2026
@Hesara2003
Hesara2003 dismissed rksk’s stale review July 2, 2026 17:49

The merge-base changed after approval.

Adds the three utilities the API store depends on (timeCardTotals,
timeSheetWeek, timeSheetState) together with their unit tests so
downstream branches can compile cleanly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@rksk
rksk force-pushed the feature/timecards-p2-api branch from d06df55 to 47c6177 Compare July 2, 2026 17:55

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (5)
apps/csm-portal/webapp/src/features/csm-timecards/utils/timeCardTotals.ts (1)

22-31: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Derive emptyBreakdown from ACTIVITY_KEYS to avoid duplication.

The keys are hardcoded here in addition to ACTIVITY_KEYS export const ACTIVITY_KEYS = [ "analysisDebugging", "reproduce", "settingUp", "providingSolution", "answering", ] as const;. TypeScript catches drift via the Record type, but keeping a single source of truth is cleaner.

♻️ Proposed refactor
 export function emptyBreakdown(): ActivityBreakdown {
-  return {
-    analysisDebugging: 0,
-    reproduce: 0,
-    settingUp: 0,
-    providingSolution: 0,
-    answering: 0,
-  };
+  return ACTIVITY_KEYS.reduce(
+    (acc, key) => ({ ...acc, [key]: 0 }),
+    {} as ActivityBreakdown,
+  );
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/csm-portal/webapp/src/features/csm-timecards/utils/timeCardTotals.ts`
around lines 22 - 31, `emptyBreakdown` is duplicating the activity names that
already live in `ACTIVITY_KEYS`. Refactor `emptyBreakdown` in
`timeCardTotals.ts` to derive the returned `ActivityBreakdown` from
`ACTIVITY_KEYS` so there is a single source of truth, while still preserving the
same zeroed values for each key. Keep the change localized around
`ACTIVITY_KEYS`, `ActivityBreakdown`, and `emptyBreakdown` so future activity
additions only need one update.
apps/csm-portal/webapp/src/features/csm-timecards/utils/timeSheetWeek.ts (2)

45-58: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider adding test coverage for weekKey and localTodayIso.

These are exported and used elsewhere in the stack (per PR objectives), but the test file only covers weekStartOf, weekEndOf, and weekLabel.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/csm-portal/webapp/src/features/csm-timecards/utils/timeSheetWeek.ts`
around lines 45 - 58, Add test coverage for the exported helpers weekKey and
localTodayIso in the existing timeSheetWeek test suite. Verify that weekKey
delegates to weekStartOf and returns the expected week-start ISO string, and add
a deterministic test for localTodayIso that asserts it produces the local
calendar date (not UTC) by controlling the system date/time or timezone. Use the
function names weekKey, localTodayIso, and weekStartOf to locate the relevant
assertions.

60-72: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

weekLabel year is taken only from the week end, which can mislabel year-spanning weeks.

For a week like Dec 29, 2026 – Jan 4, 2027, the label would render as "Dec 29 – Jan 4, 2027", implying the start date is also in 2027. Consider showing both years when they differ.

♻️ Suggested fix
   const year = end.getUTCFullYear();
+  const startYear = start.getUTCFullYear();
   if (mon(start) === mon(end)) {
     return `${mon(start)} ${day(start)} – ${day(end)}, ${year}`;
   }
-  return `${mon(start)} ${day(start)} – ${mon(end)} ${day(end)}, ${year}`;
+  return startYear === year
+    ? `${mon(start)} ${day(start)} – ${mon(end)} ${day(end)}, ${year}`
+    : `${mon(start)} ${day(start)}, ${startYear} – ${mon(end)} ${day(end)}, ${year}`;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/csm-portal/webapp/src/features/csm-timecards/utils/timeSheetWeek.ts`
around lines 60 - 72, The weekLabel helper currently always uses the end date’s
year, which mislabels weeks that span two years. Update weekLabel in
timeSheetWeek.ts so it compares the start and end years (using toUtcDate,
weekEndOf, and the existing mon/day helpers) and includes both years in the
returned label when they differ, while keeping the current format for same-year
weeks.
apps/csm-portal/webapp/src/features/csm-timecards/utils/timeSheetState.ts (2)

54-115: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

No test coverage for this layer's RBAC logic.

Unlike the other two utility modules in this PR (timeCardTotals, timeSheetWeek), this file has no accompanying __tests__ suite despite encoding non-trivial, security-relevant branching (cardActions, sheetStatus, sheetActions). Given this drives which actions render/are permitted per role and state, unit tests covering each state × role combination would materially reduce regression risk.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/csm-portal/webapp/src/features/csm-timecards/utils/timeSheetState.ts`
around lines 54 - 115, The RBAC branching in cardActions, sheetStatus, and
sheetActions is not covered by tests, so add a new __tests__ suite for
timeSheetState that exercises the state × role combinations for each helper.
Verify the returned actions for cardActions across
owner/admin/approver/non-privileged roles, confirm sheetStatus precedence for
rejected/recalled/submitted/approved/open, and cover sheetActions for editable,
submitted, and approved card mixes so regressions in permission logic are
caught.

46-46: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

EDITABLE_STATES is exported as a mutable array.

It's exported and consumed by RBAC logic in sheetActions (Line 104). Since it's a mutable TimeCardState[], any accidental external mutation (push/splice) would silently change permission checks everywhere it's used.

♻️ Proposed fix
-export const EDITABLE_STATES: TimeCardState[] = ["pending", "rejected", "recalled"];
+export const EDITABLE_STATES: readonly TimeCardState[] = ["pending", "rejected", "recalled"] as const;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/csm-portal/webapp/src/features/csm-timecards/utils/timeSheetState.ts` at
line 46, `EDITABLE_STATES` is exported as a mutable array, so external callers
like the RBAC checks in `sheetActions` can accidentally mutate shared permission
state. Make `EDITABLE_STATES` immutable in `timeSheetState` by switching it to a
readonly/frozen constant, and ensure any consumers only read from it rather than
modifying it. Keep the symbol name `EDITABLE_STATES` so existing imports
continue to work, but prevent `push`/`splice`-style changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@apps/csm-portal/webapp/src/features/csm-timecards/utils/timeCardTotals.ts`:
- Around line 22-31: `emptyBreakdown` is duplicating the activity names that
already live in `ACTIVITY_KEYS`. Refactor `emptyBreakdown` in
`timeCardTotals.ts` to derive the returned `ActivityBreakdown` from
`ACTIVITY_KEYS` so there is a single source of truth, while still preserving the
same zeroed values for each key. Keep the change localized around
`ACTIVITY_KEYS`, `ActivityBreakdown`, and `emptyBreakdown` so future activity
additions only need one update.

In `@apps/csm-portal/webapp/src/features/csm-timecards/utils/timeSheetState.ts`:
- Around line 54-115: The RBAC branching in cardActions, sheetStatus, and
sheetActions is not covered by tests, so add a new __tests__ suite for
timeSheetState that exercises the state × role combinations for each helper.
Verify the returned actions for cardActions across
owner/admin/approver/non-privileged roles, confirm sheetStatus precedence for
rejected/recalled/submitted/approved/open, and cover sheetActions for editable,
submitted, and approved card mixes so regressions in permission logic are
caught.
- Line 46: `EDITABLE_STATES` is exported as a mutable array, so external callers
like the RBAC checks in `sheetActions` can accidentally mutate shared permission
state. Make `EDITABLE_STATES` immutable in `timeSheetState` by switching it to a
readonly/frozen constant, and ensure any consumers only read from it rather than
modifying it. Keep the symbol name `EDITABLE_STATES` so existing imports
continue to work, but prevent `push`/`splice`-style changes.

In `@apps/csm-portal/webapp/src/features/csm-timecards/utils/timeSheetWeek.ts`:
- Around line 45-58: Add test coverage for the exported helpers weekKey and
localTodayIso in the existing timeSheetWeek test suite. Verify that weekKey
delegates to weekStartOf and returns the expected week-start ISO string, and add
a deterministic test for localTodayIso that asserts it produces the local
calendar date (not UTC) by controlling the system date/time or timezone. Use the
function names weekKey, localTodayIso, and weekStartOf to locate the relevant
assertions.
- Around line 60-72: The weekLabel helper currently always uses the end date’s
year, which mislabels weeks that span two years. Update weekLabel in
timeSheetWeek.ts so it compares the start and end years (using toUtcDate,
weekEndOf, and the existing mon/day helpers) and includes both years in the
returned label when they differ, while keeping the current format for same-year
weeks.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 42147022-2727-43f2-800e-c0dc59023fd2

📥 Commits

Reviewing files that changed from the base of the PR and between 6bedb06 and 47c6177.

📒 Files selected for processing (5)
  • apps/csm-portal/webapp/src/features/csm-timecards/utils/__tests__/timeCardTotals.test.ts
  • apps/csm-portal/webapp/src/features/csm-timecards/utils/__tests__/timeSheetWeek.test.ts
  • apps/csm-portal/webapp/src/features/csm-timecards/utils/timeCardTotals.ts
  • apps/csm-portal/webapp/src/features/csm-timecards/utils/timeSheetState.ts
  • apps/csm-portal/webapp/src/features/csm-timecards/utils/timeSheetWeek.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

App/CSM Portal Area/Frontend Platform/Web Type/New Feature Represents a request or task for a new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants