Skip to content

chore: promote staging to main#328

Merged
parth0025 merged 22 commits into
mainfrom
staging
Jul 3, 2026
Merged

chore: promote staging to main#328
parth0025 merged 22 commits into
mainfrom
staging

Conversation

@parth0025

@parth0025 parth0025 commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

Promotion PR: merges staging into main for release. Merge with a merge commit β€” do NOT squash.

What's included

Feature work and fixes accumulated on staging since the last promotion:

Type of change

  • πŸ”§ chore β€” promotion / release

Breaking changes

  • ❌ No breaking changes
  • ⚠️ Yes β€” mail now sends via SMTP (NODEMAILER_*) instead of AWS SES. Ensure prod SMTP credentials are set before/at deploy.

Notes for reviewers

Standard staging β†’ main promotion. After merge, back-merge main β†’ staging and cut the release tag.

Summary by CodeRabbit

  • New Features
    • Added Resource Utilization dashboard cards (project utilization/progress, live work, free resources, worked tasks, team effort breakdown, logged vs ETA, On Leave) with drill-down project modals.
    • Added per-card time-period selectors and a dashboard-wide date-range picker with Auto mode.
    • Added β€œWork by Category” task-type mapping UI.
  • Bug Fixes
    • Improved role-scoped dashboard metrics and β€œcurrently tracking” memo selection for live work.
  • Chores / Tests
    • Updated development hooks (lint-staged, JSON validation, commit message linting, branch naming checks) and added dashboard test-case specs.
    • Enforced LF line endings for Husky scripts.

parth0025 and others added 14 commits July 1, 2026 10:26
chore: back-merge main into staging (v14.7.0)
Add five read-only, company-scoped dashboard cards under a new "Project Progress" category, served by one additive endpoint (POST /api/v1/dashboard/project-metrics):

- Active Projects β€” company-wide active count
- Projects by Type β€” active projects grouped by ProjectType
- Running Projects β€” active projects with logged time in the period
- Active Time Trackers β€” who is tracking now + task/project + tracker memo
- Work by Category β€” task count per user, bucketed into a user-defined category -> task-type template

Work by Category reuses the standard edit-card settings modal for its filters (Projects, Teams/Users, Include-subtasks) plus an embedded category-template editor (CategoryTaskTypeMapper); period + refresh live in the card chrome. All queries are read-only and role-scoped (roleType 1/2 see all, others see only themselves).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…dashboard-cards-v14.8.0

feat(dashboard): add Project Progress cards (AHE-3789)
Replace the shared shield/gear icon on SSO, SCIM, Audit Log, Two-Factor Authentication and Integrations with distinct, purpose-built icons (key, people, document, padlock, puzzle) in the same inactive-gray (#535358) / active-blue (#2F3990) format. Additive: 10 new SVGs + icon wiring in the settings nav; no behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-v14.8.0

feat(settings): distinct nav icons for security and integration menus
# Conflicts:
#	Modules/UserDashboard/controller.js
#	Modules/UserDashboard/routes.js
#	frontend/src/plugins/dashboard/views/HomePage.vue
#	utils/cardComponent.json
…lision

Both branches introduced a dashboard card keyed 'LiveWorkTableCard'
(ours mapping to LiveWorkTableCard.vue, staging's AHE-3789 card mapping
to LiveWorkCard.vue), producing duplicate switch cases after the merge.
Our card is renamed everywhere (component, switch cases, cardComponent
key, dashboardTemplate componentId); staging's key is unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Mirrors the abudhabi-web setup (husky + lint-staged pre-commit),
adapted to this repo: frontend files get eslint --fix via the config
in frontend/package.json; backend .js files get a node --check syntax
gate and *.json files a JSON.parse validation (lint-staged.config.js).
The commit-msg hook wires the existing commitlint.config.js locally,
matching the commitlint CI workflow.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
core.autocrlf=true rewrote the hook scripts with CRLF on checkout,
which makes sh fail with 'husky - command not found' on Windows.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e-cards

feat(dashboard): resource-utilization cards + merge of AHE-3789 project-progress cards
Replace the AWS SES implementation in Modules/service.js with the
nodemailer SMTP implementation from servicewithoutAWS.js. All three
mail functions (SendEmail, SendNotificationEmail, sendAttachMail) now
use the SMTP transport configured via NODEMAILER_* env vars, and the
AWS SES imports/dependencies are removed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ailer-smtp

refactor(service): use nodemailer SMTP transport in service.js
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Review Change Stack

πŸ“ Walkthrough

Walkthrough

This PR adds resource dashboard analytics and cards, wires dashboard period/global-range controls, migrates email delivery to Nodemailer, and adds Husky/lint-staged tooling plus dashboard test-case coverage.

Changes

Backend resource-utilization analytics

Layer / File(s) Summary
Resource helper utilities
Modules/UserDashboard/helpers/resourceHelpers.js
Adds date-range bounds, team mapping, status bucketing, logged/task joining, user-name mapping, and sprint-type mapping helpers.
Employee workload and analytics handlers
Modules/UserDashboard/controller.js
Adds viewer scoping, advanced task filtering, memo tracking, PTO overlap handling, per-user day totals, and the new project and team analytics endpoints.
Routes and dashboard data
Modules/UserDashboard/routes.js, utils/cardComponent.json, utils/dashboardTemplate.json
Registers new routes and adds card/template JSON definitions for the new dashboard cards.
Test-case documentation
.claude/test-cases/AHE-3789-project-progress-cards.md, .claude/test-cases/DashboardEnhancements-v2.md
Adds dashboard card specs covering prerequisites, behaviors, persistence, and visibility rules.

Email delivery migration

Layer / File(s) Summary
Nodemailer transport migration
Modules/service.js
Rewrites SendEmail, SendNotificationEmail, and sendAttachMail to use Nodemailer transporters and SMTP-style configuration.

Frontend dashboard cards and wiring

Layer / File(s) Summary
Period selector and dashboard wiring
frontend/src/components/molecules/DashBoardCard/DashBoardCard.vue, frontend/src/plugins/dashboard/views/HomePage.vue, frontend/src/composable/useResourceWorkload.js, frontend/src/composable/commonFunction.js, frontend/src/config/env.js, frontend/src/locales/en.js
Adds card period selection, global dashboard range handling, refreshability logic, shared range/status helpers, card sizing, route constants, and locale strings.
Category mapping and UsersByCategoryCard
frontend/src/components/molecules/CardFieldComponent/CardFieldComponent.vue, frontend/src/components/molecules/CategoryTaskTypeMapper/*, frontend/src/components/organisms/UsersByCategoryCard/UsersByCategoryCard.vue
Adds task-type-to-category mapping UI and the category-based report card.
Resource and progress dashboard cards
frontend/src/components/atom/CardSkeleton/CardSkeleton.vue, frontend/src/components/molecules/ProjectListModal/ProjectListModal.vue, frontend/src/components/organisms/{ActiveWorkTableCard,FreeResourcesCard,LiveWorkCard,MetricSummaryCard,OnLeaveCard,ProjectMetricsCard,ProjectPulseCard,ProjectResourceCard,TeamCategoryBreakdownCard,TeamLoggedVsEtaCard,WorkedTasksTableCard}/*
Adds the new card, skeleton, and modal components used by the dashboard and drill-down flows.

Developer tooling

Layer / File(s) Summary
Git hooks and line endings
.claude/settings.local.json, .gitattributes, .husky/*
Adds commit-msg/pre-commit/pre-push hooks and LF enforcement for hook scripts.
Lint-staged and JSON validation
lint-staged.config.js, scripts/validate-json.js, package.json, frontend/package.json
Adds lint-staged configs, JSON validation, and husky/lint-staged package wiring.

Settings tab icons

Layer / File(s) Summary
Icon swaps
frontend/src/components/templates/Settings/Settings.vue
Replaces generic icons with dedicated icons for SSO, SCIM, Audit Log, Two-Factor, and Integrations tabs.

Estimated code review effort: 4 (Complex) | ~75 minutes

Possibly related PRs

Suggested reviewers: joshishiv4, harmit001

πŸš₯ Pre-merge checks | βœ… 5
βœ… Passed checks (5 passed)
Check name Status Explanation
Title check βœ… Passed The title accurately reflects this PR as a staging-to-main promotion and is concise and conventional.
Description check βœ… Passed The description covers the main template sections and clearly states the promotion, included changes, breaking change, and reviewer notes.
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 docstrings
  • Create stacked PR
  • Commit on current branch
πŸ§ͺ Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch staging

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.

@parth0025 parth0025 requested a review from joshishiv4 July 3, 2026 09:45

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 12

🧹 Nitpick comments (10)
Modules/service.js (1)

17-28: πŸ“ Maintainability & Code Quality | πŸ”΅ Trivial | ⚑ Quick win

Extract the duplicated transporter setup into a shared helper.

The identical nodemailer.createTransport({ host, port, secure, auth, tls }) block is copied across SendEmail, SendNotificationEmail, and sendAttachMail. Consolidating into a single factory removes the three-way drift risk and makes fixes (e.g., the TLS change above) apply in one place.

♻️ Example helper
const createTransporter = () => nodemailer.createTransport({
    host: config.NODEMAILER_HOST,
    port: config.NODEMAILER_PORT,
    secure: config.NODEMAILER_PORT == 465,
    auth: {
        user: config.NODEMAILER_EMAIL,
        pass: config.NODEMAILER_EMAIL_PASSWORD,
    },
});
πŸ€– 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 `@Modules/service.js` around lines 17 - 28, The transporter setup is duplicated
in SendEmail, SendNotificationEmail, and sendAttachMail, so extract the repeated
nodemailer.createTransport configuration into a shared helper such as
createTransporter. Update each mail-sending function to call that helper instead
of inlining host/port/secure/auth/tls, and keep the transport configuration in
one place so future changes like TLS behavior only need to be made once.
frontend/src/components/organisms/LiveWorkCard/LiveWorkCard.vue (1)

116-117: πŸ“ Maintainability & Code Quality | πŸ”΅ Trivial | ⚑ Quick win

Swallowed fetch error β€” no logging.

Unlike sibling cards' catch blocks (e.g., ActiveWorkTableCard.vue Line 115), errors here are dropped silently with no console.error, making failures harder to diagnose.

πŸ€– 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 `@frontend/src/components/organisms/LiveWorkCard/LiveWorkCard.vue` around lines
116 - 117, The LiveWorkCard fetch error is being swallowed in the catch block
with no diagnostics. Update the catch in LiveWorkCard.vue to log the caught
exception with console.error before resetting data.value, following the pattern
used by sibling components such as ActiveWorkTableCard so failures can be
diagnosed.
frontend/src/components/organisms/ActiveWorkTableCard/ActiveWorkTableCard.vue (2)

89-92: πŸ“ Maintainability & Code Quality | πŸ”΅ Trivial | ⚑ Quick win

Duplicated taskMatch-building IIFE.

This exact IIFE (flatten filterData then buildFilterQuery) is repeated verbatim in FreeResourcesCard, TeamLoggedVsEtaCard, and WorkedTasksTableCard. Consider extracting a buildTaskMatch(filterData, userId) helper into useResourceWorkload.js to avoid drift between copies.

πŸ€– 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
`@frontend/src/components/organisms/ActiveWorkTableCard/ActiveWorkTableCard.vue`
around lines 89 - 92, The taskMatch IIFE in ActiveWorkTableCard duplicates the
same flatten-and-build logic used in FreeResourcesCard, TeamLoggedVsEtaCard, and
WorkedTasksTableCard. Extract that repeated logic into a shared
buildTaskMatch(filterData, userId) helper in useResourceWorkload.js, then
replace the inline IIFE in this component with a call to the helper so all cards
stay consistent.

74-120: 🩺 Stability & Availability | πŸ”΅ Trivial | ⚑ Quick win

No request sequencing guard against concurrent load() calls.

refreshTrigger, cardData (deep), and filterData (deep) each independently trigger load(). If two calls overlap, responses can resolve out of order and the older response can overwrite rows after the newer one, leaving stale data displayed until the next refresh.

♻️ Suggested fix β€” sequence token
+let requestSeq = 0;
 const load = async () => {
+    const seq = ++requestSeq;
     loading.value = true;
     try {
         ...
-        rows.value = flat;
+        if (seq === requestSeq) rows.value = flat;
     } catch (e) {
         console.error('ActiveWorkTableCard fetch error:', e);
-        rows.value = [];
+        if (seq === requestSeq) rows.value = [];
     } finally {
-        loading.value = false;
+        if (seq === requestSeq) loading.value = false;
     }
 };
πŸ€– 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
`@frontend/src/components/organisms/ActiveWorkTableCard/ActiveWorkTableCard.vue`
around lines 74 - 120, The load() flow in ActiveWorkTableCard.vue has no
sequencing protection, so overlapping refreshTrigger/cardData/filterData-driven
requests can resolve out of order and overwrite newer rows with stale data. Add
a request sequence/token guard inside load() so each invocation captures its own
id and only the latest successful response is allowed to assign rows.value,
while older in-flight responses are ignored even in the finally loading reset
path.
frontend/src/components/organisms/WorkedTasksTableCard/WorkedTasksTableCard.vue (1)

73-76: πŸ“ Maintainability & Code Quality | πŸ”΅ Trivial | ⚑ Quick win

PERIOD_LABELS duplicated with TeamLoggedVsEtaCard.vue.

Identical object literal (Lines 73-76 here vs. TeamLoggedVsEtaCard.vue Lines 101) β€” extract to a shared constant (e.g. in commonFunction.js or useResourceWorkload.js) to keep the two in sync.

πŸ€– 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
`@frontend/src/components/organisms/WorkedTasksTableCard/WorkedTasksTableCard.vue`
around lines 73 - 76, The PERIOD_LABELS object is duplicated in
WorkedTasksTableCard and TeamLoggedVsEtaCard, so move this shared mapping to a
common location such as commonFunction.js or useResourceWorkload.js and update
both components to import and use the same constant. Keep the existing
PERIOD_LABELS symbol name or a similarly clear shared export so both views stay
in sync from one source of truth.
frontend/src/components/organisms/ProjectMetricsCard/ProjectMetricsCard.vue (1)

66-69: πŸ“ Maintainability & Code Quality | πŸ”΅ Trivial | πŸ’€ Low value

Hardcoded English labels bypass i18n.

TYPE_LABELS values ("Fixed", "Hourly", "In House", "Retainer", "Unspecified") are hardcoded strings rendered directly, unlike other labels in this file that use $t(...). These won't be translated for non-English locales.

πŸ€– 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 `@frontend/src/components/organisms/ProjectMetricsCard/ProjectMetricsCard.vue`
around lines 66 - 69, The TYPE_LABELS mapping in ProjectMetricsCard is using
hardcoded English display values, which bypasses the existing i18n flow. Update
the label resolution used by ProjectMetricsCard so the displayed type names come
from $t(...) or equivalent translation keys instead of literal strings, and make
sure every entry referenced by the component (such as Fix, Hourly, InHouse,
Retainer, and Unspecified) is localized consistently with the other labels in
this file.
frontend/src/components/organisms/TeamCategoryBreakdownCard/TeamCategoryBreakdownCard.vue (1)

122-125: 🩺 Stability & Availability | πŸ”΅ Trivial | ⚑ Quick win

Three independent watchers can race on load().

refreshTrigger, deep cardData, and deep filterData watches each call load() without cancelling in-flight requests. Rapid successive changes (e.g., toggling the dimension select then changing a filter) can let a stale response overwrite fresher data.

πŸ€– 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
`@frontend/src/components/organisms/TeamCategoryBreakdownCard/TeamCategoryBreakdownCard.vue`
around lines 122 - 125, Three separate watchers in TeamCategoryBreakdownCard are
triggering load() concurrently and can let stale responses win. Update the load
flow in TeamCategoryBreakdownCard.vue so refreshTrigger, cardData, and
filterData changes do not start overlapping requests without protection; for
example, add request cancellation or a sequence guard inside load() and keep the
existing watch/onMounted hooks calling that guarded loader. Make sure the latest
invocation of load() is the only one allowed to update the component state.
frontend/src/components/organisms/ProjectPulseCard/ProjectPulseCard.vue (3)

116-138: 🩺 Stability & Availability | πŸ”΅ Trivial | ⚑ Quick win

No guard against out-of-order responses across concurrent watchers.

refreshTrigger and a deep watch on cardData both invoke load() independently with no request sequencing/abort. Rapid consecutive triggers (e.g., quick period changes) can let an older response overwrite a newer one.

πŸ€– 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 `@frontend/src/components/organisms/ProjectPulseCard/ProjectPulseCard.vue`
around lines 116 - 138, The load flow in ProjectPulseCard.vue can apply stale
data because refreshTrigger and the deep watch on props.cardData both call
load() without any sequencing or cancellation. Update the load function and its
callers so only the latest request can commit state, using a request
token/version or AbortController-style guard around apiRequest and the
data.value assignment. Keep the check tied to the existing load, watch, and
onMounted hooks so older responses are ignored if a newer load starts first.

62-66: πŸ“ Maintainability & Code Quality | πŸ”΅ Trivial | πŸ’€ Low value

Duplicate hardcoded PERIOD_LABELS object, not localized.

The same PERIOD_LABELS map is duplicated verbatim in TeamCategoryBreakdownCard.vue, and its values are plain English strings not routed through $t(...) like other labels in this file.

πŸ€– 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 `@frontend/src/components/organisms/ProjectPulseCard/ProjectPulseCard.vue`
around lines 62 - 66, The PERIOD_LABELS map in ProjectPulseCard is duplicated
elsewhere and uses hardcoded English strings instead of localization. Replace
the local hardcoded PERIOD_LABELS/periodLabel lookup with the shared source used
by TeamCategoryBreakdownCard, and route each label through $t(...) so the
timerange labels are translated consistently with the rest of the component.

69-114: 🎯 Functional Correctness | πŸ”΅ Trivial | ⚑ Quick win

Reuse the shared date-range helper here. ProjectPulseCard.vue duplicates the timerange→ISO mapping that useResourceWorkload.resolveIsoRange already centralizes. Pulling from the shared composable keeps the range IDs in one place and avoids drift if the semantics change later.

πŸ€– 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 `@frontend/src/components/organisms/ProjectPulseCard/ProjectPulseCard.vue`
around lines 69 - 114, The date range conversion in resolveDateRange is
duplicating logic that already exists in useResourceWorkload.resolveIsoRange.
Replace the local switch-based mapping in ProjectPulseCard.vue with the shared
helper so the range IDs and ISO date semantics stay centralized and consistent.
Keep the component using resolveDateRange only as a thin adapter, or remove it
entirely if the composable can be called directly from the existing code path.
πŸ€– 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.

Inline comments:
In @.claude/test-cases/AHE-3789-project-progress-cards.md:
- Line 52: The boot-integrity test case row is stale because it still expects 24
cards instead of the current 30 from utils/cardComponent.json. Update the PPC-11
entry in AHE-3789-project-progress-cards.md so the expected card count matches
30, keeping the rest of the boot integrity description unchanged.

In `@frontend/src/components/organisms/FreeResourcesCard/FreeResourcesCard.vue`:
- Line 71: The threshold handling in FreeResourcesCard.vue currently uses
Number(props.cardData?.freeThresholdHours) || 3, which overrides an explicit 0
value with the default. Update the thresholdHours computed logic so it only
falls back to 3 when the prop is missing or not a valid number, while preserving
0 as a valid configured value. Locate the fix in the FreeResourcesCard component
where thresholdHours is defined.
- Around line 64-65: The FreeResourcesCard setup is reading settings/teams once
into teamsArr, which can go stale if the store updates after mount. Update the
FreeResourcesCard component to derive teamsArr from a computed based on
getters['settings/teams'] so teamIdToUserId() always sees the latest teams, and
keep the existing symbol names useStore, getters, teamsArr, and teamIdToUserId
in place while refactoring.

In `@frontend/src/components/organisms/LiveWorkCard/LiveWorkCard.vue`:
- Around line 82-91: The LiveWorkCard load flow is still using a global payload
and ignores the new card-specific inputs. Update the LiveWorkCard.vue logic
around load() to include cardData and filterData in the request payload, and add
the same deep watch behavior used by the sibling card components so changes to
assignee/project and advanced filters retrigger loading; if this card is meant
to stay global, explicitly document that in the component instead.

In `@frontend/src/components/organisms/ProjectMetricsCard/ProjectMetricsCard.vue`:
- Around line 88-92: The project-metrics request is sending callerUserId and
callerRoleType from the client, which can be forged to bypass caller-scoped
restrictions. Update the backend handling for the project-metrics endpoint to
derive the authenticated user and role from the server session/context instead
of trusting request body fields, and use that same server-side source wherever
the logic in ProjectMetricsCard, ProjectResourceCard, and
TeamCategoryBreakdownCard depends on caller scoping.

In
`@frontend/src/components/organisms/ProjectResourceCard/ProjectResourceCard.vue`:
- Around line 45-62: The week-range logic in resolveRange is inconsistent with
the other dashboard cards, causing different results for the same timerange ids.
Update the shared resolveIsoRange helper to use the Monday-based week boundaries
used by ProjectPulseCard, then switch ProjectResourceCard to call that shared
helper instead of keeping its own Sunday-based calculations. Make sure the
timerange 3 and 4 cases now resolve to the same week definition everywhere.

In
`@frontend/src/components/organisms/TeamCategoryBreakdownCard/TeamCategoryBreakdownCard.vue`:
- Around line 73-74: The settings/teams Vuex getter is being read once during
setup in TeamCategoryBreakdownCard.vue, so teamsArr can become stale after the
store updates. Make teamsArr reactive by wrapping the getter access in a
computed/ref-style wrapper and update load() to read teamsArr.value when
building teamIdToUserId, so later store changes are reflected.

In `@Modules/service.js`:
- Around line 37-40: The callback error path in service.js is returning the raw
Error object, which causes downstream consumers like
Modules/EmailNotification/routes.js to lose the message when serializing
result.error. Update the error branches in the relevant functions, including
SendNotificationEmail, to pass err.message instead of err while keeping the
existing status:false shape so the callback and catch behavior are consistent.
- Line 100: The error logging in the send-verify-email catch block is using a
misspelled property on the Error object, so the message is lost. Update the
logger.error call in the catch path for the send verification email flow to use
the correct error message property on the error variable, keeping the existing
log context intact.
- Around line 25-28: The Nodemailer transporter configuration currently disables
TLS certificate validation via tls.rejectUnauthorized in the mail setup, which
should be removed from all transporters in service.js. Update the transporter
configuration so certificate verification remains enabled by default, and if
self-signed certificates must be supported, guard that behavior behind an
explicit environment-driven opt-in in the transport creation logic.

In `@package.json`:
- Around line 104-114: The prepare hook in package.json should be made safe for
production installs because npm ci --omit=dev still runs it and the backend-deps
image stage has no .git and no husky binary. Update the prepare script so it
no-ops when husky is unavailable, or otherwise guard it behind a check that only
runs in a git/dev environment. Keep the fix localized to the prepare lifecycle
entry and ensure it does not break the Dockerfile backend-deps build path.

In `@utils/cardComponent.json`:
- Around line 2104-2141: The TeamCategoryBreakdownCard config is missing an
editable dimension selector, so it stays on the backend default instead of
letting the user choose. Update the card definition in cardComponent.json for
TeamCategoryBreakdownCard by adding a new field named dimension alongside the
existing data fields, and make its options match the backend-supported values
(type, effort_nature, work_category, billable). Ensure the default aligns with
the current template behavior while allowing the settings UI to override it.

---

Nitpick comments:
In
`@frontend/src/components/organisms/ActiveWorkTableCard/ActiveWorkTableCard.vue`:
- Around line 89-92: The taskMatch IIFE in ActiveWorkTableCard duplicates the
same flatten-and-build logic used in FreeResourcesCard, TeamLoggedVsEtaCard, and
WorkedTasksTableCard. Extract that repeated logic into a shared
buildTaskMatch(filterData, userId) helper in useResourceWorkload.js, then
replace the inline IIFE in this component with a call to the helper so all cards
stay consistent.
- Around line 74-120: The load() flow in ActiveWorkTableCard.vue has no
sequencing protection, so overlapping refreshTrigger/cardData/filterData-driven
requests can resolve out of order and overwrite newer rows with stale data. Add
a request sequence/token guard inside load() so each invocation captures its own
id and only the latest successful response is allowed to assign rows.value,
while older in-flight responses are ignored even in the finally loading reset
path.

In `@frontend/src/components/organisms/LiveWorkCard/LiveWorkCard.vue`:
- Around line 116-117: The LiveWorkCard fetch error is being swallowed in the
catch block with no diagnostics. Update the catch in LiveWorkCard.vue to log the
caught exception with console.error before resetting data.value, following the
pattern used by sibling components such as ActiveWorkTableCard so failures can
be diagnosed.

In `@frontend/src/components/organisms/ProjectMetricsCard/ProjectMetricsCard.vue`:
- Around line 66-69: The TYPE_LABELS mapping in ProjectMetricsCard is using
hardcoded English display values, which bypasses the existing i18n flow. Update
the label resolution used by ProjectMetricsCard so the displayed type names come
from $t(...) or equivalent translation keys instead of literal strings, and make
sure every entry referenced by the component (such as Fix, Hourly, InHouse,
Retainer, and Unspecified) is localized consistently with the other labels in
this file.

In `@frontend/src/components/organisms/ProjectPulseCard/ProjectPulseCard.vue`:
- Around line 116-138: The load flow in ProjectPulseCard.vue can apply stale
data because refreshTrigger and the deep watch on props.cardData both call
load() without any sequencing or cancellation. Update the load function and its
callers so only the latest request can commit state, using a request
token/version or AbortController-style guard around apiRequest and the
data.value assignment. Keep the check tied to the existing load, watch, and
onMounted hooks so older responses are ignored if a newer load starts first.
- Around line 62-66: The PERIOD_LABELS map in ProjectPulseCard is duplicated
elsewhere and uses hardcoded English strings instead of localization. Replace
the local hardcoded PERIOD_LABELS/periodLabel lookup with the shared source used
by TeamCategoryBreakdownCard, and route each label through $t(...) so the
timerange labels are translated consistently with the rest of the component.
- Around line 69-114: The date range conversion in resolveDateRange is
duplicating logic that already exists in useResourceWorkload.resolveIsoRange.
Replace the local switch-based mapping in ProjectPulseCard.vue with the shared
helper so the range IDs and ISO date semantics stay centralized and consistent.
Keep the component using resolveDateRange only as a thin adapter, or remove it
entirely if the composable can be called directly from the existing code path.

In
`@frontend/src/components/organisms/TeamCategoryBreakdownCard/TeamCategoryBreakdownCard.vue`:
- Around line 122-125: Three separate watchers in TeamCategoryBreakdownCard are
triggering load() concurrently and can let stale responses win. Update the load
flow in TeamCategoryBreakdownCard.vue so refreshTrigger, cardData, and
filterData changes do not start overlapping requests without protection; for
example, add request cancellation or a sequence guard inside load() and keep the
existing watch/onMounted hooks calling that guarded loader. Make sure the latest
invocation of load() is the only one allowed to update the component state.

In
`@frontend/src/components/organisms/WorkedTasksTableCard/WorkedTasksTableCard.vue`:
- Around line 73-76: The PERIOD_LABELS object is duplicated in
WorkedTasksTableCard and TeamLoggedVsEtaCard, so move this shared mapping to a
common location such as commonFunction.js or useResourceWorkload.js and update
both components to import and use the same constant. Keep the existing
PERIOD_LABELS symbol name or a similarly clear shared export so both views stay
in sync from one source of truth.

In `@Modules/service.js`:
- Around line 17-28: The transporter setup is duplicated in SendEmail,
SendNotificationEmail, and sendAttachMail, so extract the repeated
nodemailer.createTransport configuration into a shared helper such as
createTransporter. Update each mail-sending function to call that helper instead
of inlining host/port/secure/auth/tls, and keep the transport configuration in
one place so future changes like TLS behavior only need to be made once.
πŸͺ„ Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
βš™οΈ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8394ee4d-e3e1-4255-8eea-350391bc9794

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between e82ac44 and a34c55b.

β›” Files ignored due to path filters (12)
  • frontend/package-lock.json is excluded by !**/package-lock.json
  • frontend/src/assets/images/svg/auditDoc.svg is excluded by !**/*.svg
  • frontend/src/assets/images/svg/auditDocActive.svg is excluded by !**/*.svg
  • frontend/src/assets/images/svg/integrationPuzzle.svg is excluded by !**/*.svg
  • frontend/src/assets/images/svg/integrationPuzzleActive.svg is excluded by !**/*.svg
  • frontend/src/assets/images/svg/scimUsers.svg is excluded by !**/*.svg
  • frontend/src/assets/images/svg/scimUsersActive.svg is excluded by !**/*.svg
  • frontend/src/assets/images/svg/ssoKey.svg is excluded by !**/*.svg
  • frontend/src/assets/images/svg/ssoKeyActive.svg is excluded by !**/*.svg
  • frontend/src/assets/images/svg/twoFactorLock.svg is excluded by !**/*.svg
  • frontend/src/assets/images/svg/twoFactorLockActive.svg is excluded by !**/*.svg
  • package-lock.json is excluded by !**/package-lock.json
πŸ“’ Files selected for processing (35)
  • .claude/settings.local.json
  • .claude/test-cases/AHE-3789-project-progress-cards.md
  • .gitattributes
  • .husky/commit-msg
  • .husky/pre-commit
  • .husky/pre-push
  • Modules/UserDashboard/controller.js
  • Modules/UserDashboard/helpers/resourceHelpers.js
  • Modules/UserDashboard/routes.js
  • Modules/service.js
  • frontend/package.json
  • frontend/src/components/molecules/CardFieldComponent/CardFieldComponent.vue
  • frontend/src/components/molecules/CategoryTaskTypeMapper/CategoryTaskTypeMapper.vue
  • frontend/src/components/molecules/DashBoardCard/DashBoardCard.vue
  • frontend/src/components/organisms/ActiveWorkTableCard/ActiveWorkTableCard.vue
  • frontend/src/components/organisms/FreeResourcesCard/FreeResourcesCard.vue
  • frontend/src/components/organisms/LiveWorkCard/LiveWorkCard.vue
  • frontend/src/components/organisms/ProjectMetricsCard/ProjectMetricsCard.vue
  • frontend/src/components/organisms/ProjectPulseCard/ProjectPulseCard.vue
  • frontend/src/components/organisms/ProjectResourceCard/ProjectResourceCard.vue
  • frontend/src/components/organisms/TeamCategoryBreakdownCard/TeamCategoryBreakdownCard.vue
  • frontend/src/components/organisms/TeamLoggedVsEtaCard/TeamLoggedVsEtaCard.vue
  • frontend/src/components/organisms/UsersByCategoryCard/UsersByCategoryCard.vue
  • frontend/src/components/organisms/WorkedTasksTableCard/WorkedTasksTableCard.vue
  • frontend/src/components/templates/Settings/Settings.vue
  • frontend/src/composable/commonFunction.js
  • frontend/src/composable/useResourceWorkload.js
  • frontend/src/config/env.js
  • frontend/src/locales/en.js
  • frontend/src/plugins/dashboard/views/HomePage.vue
  • lint-staged.config.js
  • package.json
  • scripts/validate-json.js
  • utils/cardComponent.json
  • utils/dashboardTemplate.json

Comment thread .claude/test-cases/AHE-3789-project-progress-cards.md Outdated
Comment thread frontend/src/components/organisms/FreeResourcesCard/FreeResourcesCard.vue Outdated
Comment thread frontend/src/components/organisms/FreeResourcesCard/FreeResourcesCard.vue Outdated
Comment thread frontend/src/components/organisms/LiveWorkCard/LiveWorkCard.vue
Comment thread frontend/src/components/organisms/ProjectMetricsCard/ProjectMetricsCard.vue Outdated
Comment thread Modules/service.js
Comment thread Modules/service.js
Comment thread Modules/service.js Outdated
Comment thread package.json
Comment thread utils/cardComponent.json
joshishiv4 and others added 3 commits July 3, 2026 15:38
- OnLeaveCard: approved leave tickets from the configured HR project for
  the selected window, with AB (absent) / PR (present) headcounts; new
  read-only POST /api/v1/dashboard/on-leave endpoint
- Project-count drill-downs: clicking Active/Working counters, type-mix
  bars, Active Projects, Projects by Type, or Running Projects opens a
  modal listing the projects behind the number with colour-coded status
  (per-project projectStatusData palette); includeProjects mode on the
  existing endpoints
- Header period dropdown replaces the static range label on Project
  Pulse, Worked Tasks, Team Effort Breakdown and Team Logged vs ETA
- Dashboard-level date range picker (CalenderCompo range mode) with a
  per-card "Auto" period (timerange 0) that follows it; persisted per
  user in localStorage
- Shared CardSkeleton shimmer for all self-fetching cards (no more "...")
- Refresh icon on every card added in the recent dashboard PRs
- LiveWorkCard header shows distinct-project count alongside users
- Avatars in OnLeaveCard resolve via the UserProfile atom (Wasabi keys,
  default-avatar fallback)

All new queries are companyId-scoped and read-only. Backend restart
required (card catalog is loaded at boot). Test cases in
.claude/test-cases/DashboardEnhancements-v2.md

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Currently Tracking card: live_work metric now returns per-row logged
  minutes today on the tracked task and the user's day total; table adds
  right-aligned "This Task" / "Logged Today" columns (mirrors the Active
  Work table; a running tracker counts once stopped/synced)
- On Leave card: ticket key/name is a link opening the TaskDetail
  sidebar (same pattern as Active Time Trackers); on-leave endpoint now
  returns the ticket's sprintId for the sidebar

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-drilldowns-daterange-v14.8.0

feat(dashboard): on-leave card, project drill-downs, global date range (v14.8.0)
joshishiv4
joshishiv4 previously approved these changes Jul 3, 2026
"prepare": "husky" broke production deploys β€” the server installs
without devDependencies, so npm install died with "sh: husky: not
found" (exit 127). prepare now runs .husky/install.mjs, which exits
silently when NODE_ENV=production/CI or husky isn't installed, and
bootstraps the hooks normally on dev machines.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
Modules/UserDashboard/controller.js (1)

1083-1087: πŸ”’ Security & Privacy | 🟠 Major | ⚑ Quick win

Do not source viewer identity/role from req.body. callerRoleType can be spoofed here, and userScope() returns {} when callerUserId is missing, so non-admin requests can fall back to company-wide log queries. Pull both values from authenticated request context and treat a missing scoped user as no access.

πŸ€– 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 `@Modules/UserDashboard/controller.js` around lines 1083 - 1087, The role-based
log scoping in controller.js is trusting spoofable values from the request
payload, and missing callerUserId can incorrectly widen access. Update the
callerUserId/callerRoleType lookup in the current log-query flow to read from
authenticated request context instead of payload, and make userScope() deny
access when the scoped user is absent rather than returning an empty filter.
Keep the fix localized around the callerUserId, callerRoleType, restrictToSelf,
and userScope logic so non-admin requests cannot fall back to company-wide logs.
frontend/src/composable/useResourceWorkload.js (1)

71-79: 🎯 Functional Correctness | 🟑 Minor | ⚑ Quick win

Rounding can overflow minutes to 60.

Math.round(n % 60) is applied after splitting hours/minutes, so a fractional n near a minute boundary (e.g. 119.5) yields h=1, m=60 β†’ renders "1h 60m" instead of "2h 00m".

πŸ› Proposed fix
 export function formatMinutes(min) {
-    const n = Number(min) || 0;
+    const n = Math.round(Number(min) || 0);
     if (n <= 0) return '0h';
     const h = Math.floor(n / 60);
-    const m = Math.round(n % 60);
+    const m = n % 60;
     if (h === 0) return `${m}m`;
     return m === 0 ? `${h}h` : `${h}h ${String(m).padStart(2, '0')}m`;
 }
πŸ€– 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 `@frontend/src/composable/useResourceWorkload.js` around lines 71 - 79, The
`formatMinutes` helper in `useResourceWorkload` can produce invalid β€œ60m” output
because minutes are rounded after splitting hours and minutes. Update the
calculation to round the total minute count first, then derive hours and
remaining minutes from that rounded value so boundary values like 119.5 format
as β€œ2h 00m” instead of β€œ1h 60m”.
πŸ€– 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.

Inline comments:
In `@Modules/UserDashboard/controller.js`:
- Around line 714-720: The active-project filtering is inconsistent between the
summary endpoints, causing different totals for the same company. Update
getProjectUtilizationSummary and getProjectProgressMetric to reuse the same
shared active-project predicate instead of each building its own
MongoDbCrudOpration filter, and make sure the shared condition covers both
deletedStatusKey: 0 and the legacy/unset project rows. Use the existing
activeProjects query in controller.js as the place to centralize the filter
logic.

---

Outside diff comments:
In `@frontend/src/composable/useResourceWorkload.js`:
- Around line 71-79: The `formatMinutes` helper in `useResourceWorkload` can
produce invalid β€œ60m” output because minutes are rounded after splitting hours
and minutes. Update the calculation to round the total minute count first, then
derive hours and remaining minutes from that rounded value so boundary values
like 119.5 format as β€œ2h 00m” instead of β€œ1h 60m”.

In `@Modules/UserDashboard/controller.js`:
- Around line 1083-1087: The role-based log scoping in controller.js is trusting
spoofable values from the request payload, and missing callerUserId can
incorrectly widen access. Update the callerUserId/callerRoleType lookup in the
current log-query flow to read from authenticated request context instead of
payload, and make userScope() deny access when the scoped user is absent rather
than returning an empty filter. Keep the fix localized around the callerUserId,
callerRoleType, restrictToSelf, and userScope logic so non-admin requests cannot
fall back to company-wide logs.
πŸͺ„ Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
βš™οΈ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 79d983b1-4d62-47cd-960f-7ba87aa10f6b

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between a34c55b and 0eb8991.

πŸ“’ Files selected for processing (22)
  • .claude/test-cases/DashboardEnhancements-v2.md
  • Modules/UserDashboard/controller.js
  • Modules/UserDashboard/routes.js
  • frontend/src/components/atom/CardSkeleton/CardSkeleton.vue
  • frontend/src/components/molecules/ProjectListModal/ProjectListModal.vue
  • frontend/src/components/organisms/ActiveWorkTableCard/ActiveWorkTableCard.vue
  • frontend/src/components/organisms/FreeResourcesCard/FreeResourcesCard.vue
  • frontend/src/components/organisms/LiveWorkCard/LiveWorkCard.vue
  • frontend/src/components/organisms/MetricSummaryCard/MetricSummaryCard.vue
  • frontend/src/components/organisms/OnLeaveCard/OnLeaveCard.vue
  • frontend/src/components/organisms/ProjectMetricsCard/ProjectMetricsCard.vue
  • frontend/src/components/organisms/ProjectPulseCard/ProjectPulseCard.vue
  • frontend/src/components/organisms/ProjectResourceCard/ProjectResourceCard.vue
  • frontend/src/components/organisms/TeamCategoryBreakdownCard/TeamCategoryBreakdownCard.vue
  • frontend/src/components/organisms/TeamLoggedVsEtaCard/TeamLoggedVsEtaCard.vue
  • frontend/src/components/organisms/UsersByCategoryCard/UsersByCategoryCard.vue
  • frontend/src/components/organisms/WorkedTasksTableCard/WorkedTasksTableCard.vue
  • frontend/src/composable/commonFunction.js
  • frontend/src/composable/useResourceWorkload.js
  • frontend/src/locales/en.js
  • frontend/src/plugins/dashboard/views/HomePage.vue
  • utils/cardComponent.json
βœ… Files skipped from review due to trivial changes (2)
  • .claude/test-cases/DashboardEnhancements-v2.md
  • frontend/src/locales/en.js
🚧 Files skipped from review as they are similar to previous changes (11)
  • Modules/UserDashboard/routes.js
  • frontend/src/composable/commonFunction.js
  • frontend/src/components/organisms/LiveWorkCard/LiveWorkCard.vue
  • utils/cardComponent.json
  • frontend/src/components/organisms/ActiveWorkTableCard/ActiveWorkTableCard.vue
  • frontend/src/components/organisms/WorkedTasksTableCard/WorkedTasksTableCard.vue
  • frontend/src/components/organisms/ProjectMetricsCard/ProjectMetricsCard.vue
  • frontend/src/components/organisms/TeamLoggedVsEtaCard/TeamLoggedVsEtaCard.vue
  • frontend/src/components/organisms/FreeResourcesCard/FreeResourcesCard.vue
  • frontend/src/components/organisms/TeamCategoryBreakdownCard/TeamCategoryBreakdownCard.vue
  • frontend/src/components/organisms/UsersByCategoryCard/UsersByCategoryCard.vue

Comment thread Modules/UserDashboard/controller.js
…uction-install

fix: skip husky setup when devDependencies are omitted
joshishiv4
joshishiv4 previously approved these changes Jul 3, 2026
parth0025 and others added 2 commits July 3, 2026 18:12
- SECURITY (critical): dashboard endpoints scoped data visibility from
  client-supplied callerRoleType/callerUserId β€” a forged callerRoleType 1/2
  could read every user's workload/time data. Now derive the caller from the
  authenticated session (req.uid) and resolve roleType server-side from
  company_users; body values are ignored. Applied to employee-workload,
  team-tasktype-breakdown, team-logged-vs-eta, project-metrics, on-leave.
- FreeResourcesCard/TeamCategoryBreakdownCard: read settings/teams fresh in
  load() instead of capturing the getter once at setup (went stale on late
  store load).
- FreeResourcesCard: treat an explicit freeThresholdHours 0 as valid (was
  falsy-coerced to the default 3).
- docs: correct the boot-integrity expected card count (24 -> 31).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…328)

- Security: TLS certificate validation is now ON by default across all three
  mail paths; self-signed certs require an explicit NODEMAILER_ALLOW_SELF_SIGNED
  opt-in (was a hardcoded rejectUnauthorized:false β†’ MITM exposure).
- Return err.message instead of the raw Error object (which serialized to {})
  in the send callbacks, so the failure reason reaches the caller.
- Fix the error.messge typo that logged undefined.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…cope-v14.8.0

fix: resolve CodeRabbit PR #328 review findings

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
Modules/service.js (1)

5-8: πŸ“ Maintainability & Code Quality | πŸ”΅ Trivial | ⚑ Quick win

Centralize the SMTP transport config.

The same security-sensitive Nodemailer config is duplicated in all three helpers. Extract it once and rename the module-level flag to UPPER_SNAKE_CASE while touching this code.

As per coding guidelines, **/*.{js,ts,vue}: β€œUse UPPER_SNAKE_CASE for constant values.”

♻️ Proposed refactor
-const allowSelfSigned = String(process.env.NODEMAILER_ALLOW_SELF_SIGNED || "").toLowerCase() === "true";
+const ALLOW_SELF_SIGNED = String(process.env.NODEMAILER_ALLOW_SELF_SIGNED || "").toLowerCase() === "true";
+
+const createMailerTransport = () => nodemailer.createTransport({
+    host: config.NODEMAILER_HOST,
+    port: config.NODEMAILER_PORT,
+    secure: config.NODEMAILER_PORT == 465,
+    auth: {
+        user: config.NODEMAILER_EMAIL,
+        pass: config.NODEMAILER_EMAIL_PASSWORD,
+    },
+    tls: {
+        rejectUnauthorized: !ALLOW_SELF_SIGNED
+    }
+});

Then replace each duplicated nodemailer.createTransport({...}) block with:

-let transporter = nodemailer.createTransport({
-    ...
-});
+const transporter = createMailerTransport();

Also applies to: 22-33, 73-84, 123-134

πŸ€– 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 `@Modules/service.js` around lines 5 - 8, The SMTP transport setup is
duplicated across the three Nodemailer helpers, and the module-level opt-in flag
should follow the constant naming rule. Centralize the shared security-sensitive
transport configuration into one reusable module-level object or factory used by
the helper functions, and rename allowSelfSigned to UPPER_SNAKE_CASE while
keeping the same NODEMAILER_ALLOW_SELF_SIGNED behavior. Update the Nodemailer
helper entry points that currently build transport inline to consume the shared
config instead of repeating nodemailer.createTransport calls.

Source: Coding guidelines

πŸ€– 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 `@Modules/service.js`:
- Around line 5-8: The SMTP transport setup is duplicated across the three
Nodemailer helpers, and the module-level opt-in flag should follow the constant
naming rule. Centralize the shared security-sensitive transport configuration
into one reusable module-level object or factory used by the helper functions,
and rename allowSelfSigned to UPPER_SNAKE_CASE while keeping the same
NODEMAILER_ALLOW_SELF_SIGNED behavior. Update the Nodemailer helper entry points
that currently build transport inline to consume the shared config instead of
repeating nodemailer.createTransport calls.

ℹ️ Review info
βš™οΈ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 225ef5ea-ebdc-4758-b61d-46278f1d752d

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 52c184f and 00b5622.

πŸ“’ Files selected for processing (5)
  • .claude/test-cases/AHE-3789-project-progress-cards.md
  • Modules/UserDashboard/controller.js
  • Modules/service.js
  • frontend/src/components/organisms/FreeResourcesCard/FreeResourcesCard.vue
  • frontend/src/components/organisms/TeamCategoryBreakdownCard/TeamCategoryBreakdownCard.vue
🚧 Files skipped from review as they are similar to previous changes (4)
  • .claude/test-cases/AHE-3789-project-progress-cards.md
  • frontend/src/components/organisms/FreeResourcesCard/FreeResourcesCard.vue
  • Modules/UserDashboard/controller.js
  • frontend/src/components/organisms/TeamCategoryBreakdownCard/TeamCategoryBreakdownCard.vue

@parth0025 parth0025 merged commit f6efb9a into main Jul 3, 2026
9 checks passed
@github-actions github-actions Bot mentioned this pull request Jul 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants