Skip to content

refactor: add changes to account for split 2 ticket ID 654#1132

Merged
carddev81 merged 2 commits into
CK-7vn/total-refactorfrom
carddev81/refactorcomponents
May 18, 2026
Merged

refactor: add changes to account for split 2 ticket ID 654#1132
carddev81 merged 2 commits into
CK-7vn/total-refactorfrom
carddev81/refactorcomponents

Conversation

@carddev81
Copy link
Copy Markdown
Contributor

Pre-Submission PR Checklist

  • No debug/console/fmt.Println statements
  • Unnecessary development comments removed
  • All acceptance criteria verified
  • Functions according to ticket specifications
  • Tested manually where applicable
  • Branch rebased with latest main
  • No business logic exists within the database layer

Description of the change

Made refactor changes per @CK-7vn 's split research. I also had to make sure the 'Take Attendance' page matched the figma because it did not. The following is what was accomplished in this ticket:

Split 2

# Area Outcome
1 Date/time helpers Migrated components/helperFunctions/formatting.tsx into lib/formatters.ts; deleted the directory; pruned 13 dead exports.
2 Event-attendance summary Extracted SummaryStatCard local component (4 tiles → 4 calls).
3 Tooltip pattern New shared <InfoTooltip>; replaces 3 sites on Dashboard.
4 Attendance configs New event-attendance/constants.ts with ATTENDANCE_STATUSES; replaces STATUS_BUTTONS (index.tsx) and ATTENDANCE_OPTIONS (AttendanceRow.tsx).
5 Note field New local <CollapsibleNoteField>; replaces NoteOnly + the duplicated note block inside ReasonAndNote. Remove now always clears the note text.
6 Dashboard card chrome New local <SurfaceCard> wrapping the bg-white dark:bg-[#171717] rounded-lg border… chain; replaces 6 Dashboard sites.
  • Related issues: Link to related Asana ticket that this closes: Ticket ID-654

@carddev81 carddev81 requested a review from a team as a code owner May 15, 2026 18:53
@carddev81 carddev81 requested review from corypride and removed request for a team May 15, 2026 18:53
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 15, 2026

Review Change Stack

Warning

Rate limit exceeded

@carddev81 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 33 minutes and 56 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: dc942687-26d2-4b01-9271-92f051c9853e

📥 Commits

Reviewing files that changed from the base of the PR and between f1f7a9f and 476e051.

📒 Files selected for processing (4)
  • frontend/src/components/shared/PageHeader.tsx
  • frontend/src/pages/Dashboard.tsx
  • frontend/src/pages/program-detail/constants.ts
  • frontend/src/pages/programs/ProgramOverviewFacilityAdmin.tsx
📝 Walkthrough

Walkthrough

This PR consolidates helper functions into a centralized library module, establishes a foundation of reusable shared components (InfoTooltip, enhanced PageHeader), modernizes the dashboard and event attendance pages with these components, unifies tab styling in global CSS, and updates TypeScript path aliases.

Changes

Helper function migration and library consolidation

Layer / File(s) Summary
Core formatters reorganization
frontend/src/lib/formatters.ts
Add Timezones enum and relocate date/time utilities (parseLocalDay, formatTimeHM, diffMinutes, formatPartialTime), recurrence helpers (parseRRule, fromLocalDateToNumericDateFormat), and month utilities (getPreviousMonth, getNextMonth, formatMonthYear); rework instructor reducer logic.
Remove obsolete formatters
frontend/src/lib/formatters.ts
Remove legacy formatDateTime, parseDuration, and formatDurationStr exports.
Import redirection for moved helpers
frontend/src/components/history/formatHistoryEntry.ts, frontend/src/components/helperFunctions/index.ts
Update parseRRule import to @/lib/formatters and remove re-exports from the deleted helper formatting module.

Shared component foundations

Layer / File(s) Summary
InfoTooltip component
frontend/src/components/shared/InfoTooltip.tsx, frontend/src/components/shared/index.ts
Introduce InfoTooltip with typed props and tooltip content styling; export it from the shared components barrel.
PageHeader enhancement
frontend/src/components/shared/PageHeader.tsx
Change subtitle to ReactNode, add meta?: ReactNode, and update layout to render subtitle and meta blocks conditionally.

Dashboard modernization with component patterns

Layer / File(s) Summary
Dashboard component infrastructure
frontend/src/pages/Dashboard.tsx
Add SurfaceCard wrapper and adjust imports to use InfoTooltip and updated heroicons.
Dashboard header unification
frontend/src/pages/Dashboard.tsx
Add DashboardHeader and replace inline headers in FacilityAdminView and DeptAdminView.
Dashboard card layout modernization
frontend/src/pages/Dashboard.tsx
Wrap MetricCard, TodaysSchedule, MissingAttendanceWidget, QuickActions, and FacilityHealthTable in SurfaceCard and replace prior tooltip UI with InfoTooltip.

Event attendance page redesign

Layer / File(s) Summary
Attendance status configuration
frontend/src/pages/event-attendance/constants.ts
Add AttendanceStatusConfig, ATTENDANCE_STATUSES, and TABLE_ATTENDANCE_STATUSES mapping statuses to labels, icons, and Tailwind classes.
Event attendance page setup and data flow
frontend/src/pages/event-attendance/index.tsx
Import formatters and shared components, add toAttendanceReason() helper, change RowState.reason to `AttendanceReason
Event attendance page layout and summary
frontend/src/pages/event-attendance/index.tsx
Replace inline title/cards with PageHeader and SummaryStatCard driven by SUMMARY_TONE; rebuild breadcrumbs to include formatted date.
Event attendance roster and note handling
frontend/src/pages/event-attendance/index.tsx, frontend/src/pages/event-attendance/AttendanceRow.tsx
Render roster buttons from ATTENDANCE_STATUSES (use shortLabel for mobile), implement CollapsibleNoteField for note editing and clearing, and coerce/clear reasons when selection changes.

UI styling consolidation and component adoption

Layer / File(s) Summary
Tab styling consolidation
frontend/src/styles/globals.css, frontend/src/pages/program-detail/constants.ts, frontend/src/pages/programs/ProgramClassManagement.tsx
Add global .tab-trigger styles and update components to use 'tab-trigger' rather than inline class constants.
SearchInput component adoption
frontend/src/pages/programs/AddClassEnrollments.tsx
Replace custom search Input + icon with shared SearchInput and remove unused imports.
Shared component import consolidation
frontend/src/pages/insights/OperationalInsights.tsx, frontend/src/pages/HelpCenter.tsx
Import PageHeader from shared barrel and refactor HelpCenter header to use PageHeader.

TypeScript configuration updates

Layer / File(s) Summary
TypeScript path alias alignment
frontend/tsconfig.app.json, frontend/tsconfig.node.json
Update @/* path alias mappings to ./src/* in both configs.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The PR title references 'split 2 ticket ID 654' but lacks specificity about the main refactoring changes; it's generic relative to the scope of work. Consider a more descriptive title that highlights the primary change, such as 'refactor: migrate formatting helpers and consolidate UI components' or 'refactor: restructure helper functions and unify shared components per ticket 654'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The PR description clearly explains the refactoring changes, cites the related ticket, includes a structured table of outcomes, and matches the changeset.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🤖 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 `@frontend/src/components/shared/InfoTooltip.tsx`:
- Around line 23-31: The icon-only button inside TooltipTrigger in
InfoTooltip.tsx lacks an accessible name; update the button element (the one
wrapping InformationCircleIcon) to include an appropriate accessible label
(e.g., aria-label or aria-labelledby) that describes its purpose, optionally
accepting a prop for the label or falling back to a sensible default, and ensure
the TooltipTrigger/InformationCircleIcon usage still renders the tooltip while
providing the screen-reader accessible name.

In `@frontend/src/lib/formatters.ts`:
- Around line 201-220: The three helpers getPreviousMonth, getNextMonth, and
formatMonthYear currently assume input is "YYYY-MM" and produce invalid dates on
malformed strings; add an input validation at the top of each function that
checks the incoming monthString/currentMonth against a strict /^\d{4}-\d{2}$/
pattern and verifies the parsed month is between 1 and 12, then on failure
either throw a clear Error (e.g. "Invalid month format, expected YYYY-MM") or
return a sentinel (empty string) depending on caller expectations — update
getPreviousMonth, getNextMonth, and formatMonthYear to perform this guard before
creating Date objects so callers never see "Invalid Date".
- Around line 6-13: The Timezones enum mixes standard/daylight abbreviations and
uses unnecessary quoted keys; update the Timezones enum so keys are unquoted and
use a region-based or neutral name for the Mountain entry (replace the 'MDT' key
with something like Mountain or MT mapped to 'America/Denver') while keeping
'MST' -> 'America/Phoenix' as-is; specifically modify the Timezones enum
(replace the 'MDT' key, remove quotes around all keys like
'CST','EST','AKST','PST','MST') so callers get a clear, non-misleading
Mountain-time mapping to 'America/Denver'.
- Around line 145-153: formatTimeHM may produce "24:00" at midnight in some
runtimes because hour12: false doesn't force the hour cycle; update formatTimeHM
to explicitly specify hourCycle: 'h23' in the toLocaleTimeString options so
midnight renders as "00:00" across environments (or alternatively replace the
locale formatter with manual padding using date.getHours() and date.getMinutes()
to build `${hh}:${mm}`). Ensure the change is applied inside the formatTimeHM
function.
- Around line 185-199: The parseRRule function currently calls rule.all()[0]
which can materialize infinite occurrences and return undefined; change the
implementation to declare rule as const inside the try, and use
rule.options.dtstart when startDtOnly is true or use rule.after(new Date(0),
true) to retrieve the first matching occurrence otherwise, guard against
undefined before passing to fromLocalDateToNumericDateFormat, and in the catch
block log the error instead of silently swallowing it (follow the rule.after() +
null-check pattern used in ClassHeader.tsx).

In `@frontend/src/pages/HelpCenter.tsx`:
- Around line 30-33: The if (close) branch renders the "Help Center" heading
with inline markup while the main return uses the PageHeader component; replace
the inline title markup in the if (close) branch with the PageHeader component
(using the same title "Help Center" and subtitle "Find answers to common
questions and get support.") so both branches render consistently; locate the
conditional that checks close and swap the inline JSX heading for <PageHeader
title="Help Center" subtitle="Find answers to common questions and get support."
/> ensuring any styling/props match the main return path.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: cdd4d180-519a-4029-9902-846b4fa083a8

📥 Commits

Reviewing files that changed from the base of the PR and between 96c36e4 and 5e1efb5.

📒 Files selected for processing (20)
  • frontend/src/components/helperFunctions/formatting.tsx
  • frontend/src/components/helperFunctions/index.ts
  • frontend/src/components/history/formatHistoryEntry.ts
  • frontend/src/components/shared/InfoTooltip.tsx
  • frontend/src/components/shared/PageHeader.tsx
  • frontend/src/components/shared/index.ts
  • frontend/src/lib/formatters.ts
  • frontend/src/pages/Dashboard.tsx
  • frontend/src/pages/HelpCenter.tsx
  • frontend/src/pages/event-attendance/AttendanceRow.tsx
  • frontend/src/pages/event-attendance/constants.ts
  • frontend/src/pages/event-attendance/index.tsx
  • frontend/src/pages/insights/OperationalInsights.tsx
  • frontend/src/pages/program-detail/constants.ts
  • frontend/src/pages/programs/AddClassEnrollments.tsx
  • frontend/src/pages/programs/ProgramClassManagement.tsx
  • frontend/src/routes/program-routes.tsx
  • frontend/src/styles/globals.css
  • frontend/tsconfig.app.json
  • frontend/tsconfig.node.json
💤 Files with no reviewable changes (2)
  • frontend/src/components/helperFunctions/index.ts
  • frontend/src/components/helperFunctions/formatting.tsx

Comment thread frontend/src/components/shared/InfoTooltip.tsx
Comment thread frontend/src/lib/formatters.ts
Comment thread frontend/src/lib/formatters.ts
Comment thread frontend/src/lib/formatters.ts
Comment thread frontend/src/lib/formatters.ts
Comment thread frontend/src/pages/HelpCenter.tsx
Copy link
Copy Markdown
Member

@CK-7vn CK-7vn left a comment

Choose a reason for hiding this comment

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

Just a couple comments, approving because it can go with or without the changes. Sweet job my friend.

<div className="flex items-start justify-between">
<div>
<h1 className="text-2xl font-bold text-foreground">{title}</h1>
<h1 className="text-[#203622] dark:text-white mb-2">
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We're removing the dark mode, so I've just been trying to pull these dark tags out whenever I catch them, not a biggie though, we're not surfacing a way for users to use dark mode currently, so it probably really doesn't matter. Just wanted to note.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

okay i'll leave this one for now then for when CSS is worked on, definitely noted.

Comment thread frontend/src/pages/Dashboard.tsx Outdated
{facilityName}
</p>
</div>
<DashboardHeader title="Facility Dashboard" subtitle={facilityName} />
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is there anyway we could use PageHeader here if we just add a className prop in PageHeader and remove the duplication in the bottom of this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

i will make this change.

@carddev81 carddev81 force-pushed the carddev81/refactorcomponents branch from 433f8c7 to f1f7a9f Compare May 18, 2026 21:11
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

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 (3)
frontend/src/pages/event-attendance/index.tsx (3)

119-141: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Wait for dates before freezing the initial row state.

initialized flips as soon as attendance data arrives, even if scheduledTimes is still empty. If the dates request resolves later, untouched rows keep check_in_at: '', and a resident switched to Partial no longer inherits the scheduled start time. Gate this effect on datesLoading === false before setting initialized.

Suggested fix
 useEffect(() => {
-    if (!data?.data || initialized) return;
+    if (!data?.data || initialized || datesLoading) return;
     const defaultCheckIn = scheduledTimes.check_in_at || '';
     const mapped: RowState[] = data.data.map((item) => {
         const hasExisting = !!item.attendance_status;
         return {
@@
     setRows(mapped);
     setInitialized(true);
-}, [data, initialized, scheduledTimes]);
+}, [data, initialized, scheduledTimes, datesLoading]);
🤖 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/pages/event-attendance/index.tsx` around lines 119 - 141, The
effect that builds initial rows (useEffect) sets initialized too early because
it only waits on data and initialized but not on scheduledTimes/dates; change
the guard to also require datesLoading === false (or !datesLoading) before
mapping and calling setRows/setInitialized so scheduledTimes.check_in_at is
available; update the effect dependencies to include datesLoading (and
scheduledTimes if needed) and ensure initialized is only set after rows use
scheduledTimes, referencing the useEffect, scheduledTimes, datesLoading,
setRows, setInitialized and RowState symbols.

648-675: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don’t let AttendanceReason.Other hide its required note.

When required is true, the note can still start collapsed, and Remove clears/collapses it again. That leaves Other selected with no visible input while the save payload still sends note: ''.

Suggested fix
-    if (!expanded) {
+    if (!expanded && !required) {
         return (
             <button
                 onClick={onExpand}
                 className="text-sm text-blue-600 hover:text-blue-700 hover:underline"
             >
@@
-                <button
-                    onClick={() => {
-                        onChange('');
-                        onCollapse();
-                    }}
-                    className="text-xs text-gray-500 hover:text-gray-700"
-                >
-                    Remove
-                </button>
+                {!required && (
+                    <button
+                        onClick={() => {
+                            onChange('');
+                            onCollapse();
+                        }}
+                        className="text-xs text-gray-500 hover:text-gray-700"
+                    >
+                        Remove
+                    </button>
+                )}
@@
                     onValueChange={(v) => {
                         const next = v as AttendanceReason;
                         onUpdate(row.user_id, {
                             reason: next,
+                            showNoteField:
+                                next === AttendanceReason.Other
+                                    ? true
+                                    : row.showNoteField,
                             ...(next !== AttendanceReason.Other &&
                                 row.reason === AttendanceReason.Other && {
-                                    note: ''
+                                    note: '',
+                                    showNoteField: false
                                 })
                         });
                     }}

Also applies to: 711-746

🤖 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/pages/event-attendance/index.tsx` around lines 648 - 675, The
Note field can be hidden while AttendanceReason.Other is selected and required,
so ensure the note is always visible in that case: when the reason switches to
AttendanceReason.Other (in your reason change handler) call onExpand() so the
input opens; when rendering, treat expanded as true if required && reason ===
AttendanceReason.Other so it never returns the collapsed "+ Add a note" button
for that case; and change the Remove button’s onClick to only call onCollapse()
when !(required && reason === AttendanceReason.Other) (otherwise just keep the
note cleared via onChange('') but remain expanded). Use the existing symbols:
AttendanceReason.Other, required, expanded, onExpand, onCollapse, onChange and
the Remove button handler to implement these checks.

228-260: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Clear check_out_at whenever a row becomes Present.

Both the single-row path and “Mark All Present” preserve an existing check_out_at. A Partial -> Present transition then submits a hidden clock-out time that the present UI no longer exposes.

Suggested fix
         const defaults =
             status === Attendance.Present
                 ? {
                       check_in_at:
                           rows.find((r) => r.user_id === userId)
                               ?.check_in_at ??
                           scheduledTimes.check_in_at ??
                           formatTimeHM(new Date()),
-                      check_out_at:
-                          rows.find((r) => r.user_id === userId)
-                              ?.check_out_at ?? ''
+                      check_out_at: ''
                   }
                 : {};
@@
                 status: Attendance.Present,
                 reason: '',
                 check_in_at: r.check_in_at || defaultCheckIn,
+                check_out_at: '',
                 showNoteField: false,
                 dirty: true
             }))
         );

Also applies to: 263-275

🤖 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/pages/event-attendance/index.tsx` around lines 228 - 260,
handleStatusChange currently preserves an existing check_out_at when
transitioning Partial -> Present, causing a hidden clock-out to be submitted;
update the logic in handleStatusChange (and the similar "Mark All Present" code
block referenced around the same area) so that when status ===
Attendance.Present you explicitly set check_out_at to an empty string instead of
reusing defaults.check_out_at, i.e., ensure updateRow receives check_out_at: ''
for Present transitions while still preserving check_in_at from defaults when
appropriate.
🤖 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 `@frontend/src/lib/formatters.ts`:
- Around line 150-153: parseLocalDay currently constructs a Date which silently
normalizes invalid calendar dates (e.g. "2026-02-31") — update parseLocalDay to
validate the parsed year, month and day instead of relying on Date
normalization: parse the components from isoDate, ensure month is 1–12 and day
is within the correct range for that month (accounting for leap years), then
construct the Date and verify date.getFullYear(), date.getMonth()+1 and
date.getDate() match the parsed values; if validation fails, return a clear
failure (throw an Error or return null) rather than returning a normalized Date.

---

Outside diff comments:
In `@frontend/src/pages/event-attendance/index.tsx`:
- Around line 119-141: The effect that builds initial rows (useEffect) sets
initialized too early because it only waits on data and initialized but not on
scheduledTimes/dates; change the guard to also require datesLoading === false
(or !datesLoading) before mapping and calling setRows/setInitialized so
scheduledTimes.check_in_at is available; update the effect dependencies to
include datesLoading (and scheduledTimes if needed) and ensure initialized is
only set after rows use scheduledTimes, referencing the useEffect,
scheduledTimes, datesLoading, setRows, setInitialized and RowState symbols.
- Around line 648-675: The Note field can be hidden while AttendanceReason.Other
is selected and required, so ensure the note is always visible in that case:
when the reason switches to AttendanceReason.Other (in your reason change
handler) call onExpand() so the input opens; when rendering, treat expanded as
true if required && reason === AttendanceReason.Other so it never returns the
collapsed "+ Add a note" button for that case; and change the Remove button’s
onClick to only call onCollapse() when !(required && reason ===
AttendanceReason.Other) (otherwise just keep the note cleared via onChange('')
but remain expanded). Use the existing symbols: AttendanceReason.Other,
required, expanded, onExpand, onCollapse, onChange and the Remove button handler
to implement these checks.
- Around line 228-260: handleStatusChange currently preserves an existing
check_out_at when transitioning Partial -> Present, causing a hidden clock-out
to be submitted; update the logic in handleStatusChange (and the similar "Mark
All Present" code block referenced around the same area) so that when status ===
Attendance.Present you explicitly set check_out_at to an empty string instead of
reusing defaults.check_out_at, i.e., ensure updateRow receives check_out_at: ''
for Present transitions while still preserving check_in_at from defaults when
appropriate.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 2fcba03e-1312-4aa4-ae95-645b2ead9e02

📥 Commits

Reviewing files that changed from the base of the PR and between 433f8c7 and f1f7a9f.

📒 Files selected for processing (20)
  • frontend/src/components/helperFunctions/formatting.tsx
  • frontend/src/components/helperFunctions/index.ts
  • frontend/src/components/history/formatHistoryEntry.ts
  • frontend/src/components/shared/InfoTooltip.tsx
  • frontend/src/components/shared/PageHeader.tsx
  • frontend/src/components/shared/index.ts
  • frontend/src/lib/formatters.ts
  • frontend/src/pages/Dashboard.tsx
  • frontend/src/pages/HelpCenter.tsx
  • frontend/src/pages/event-attendance/AttendanceRow.tsx
  • frontend/src/pages/event-attendance/constants.ts
  • frontend/src/pages/event-attendance/index.tsx
  • frontend/src/pages/insights/OperationalInsights.tsx
  • frontend/src/pages/program-detail/constants.ts
  • frontend/src/pages/programs/AddClassEnrollments.tsx
  • frontend/src/pages/programs/ProgramClassManagement.tsx
  • frontend/src/routes/program-routes.tsx
  • frontend/src/styles/globals.css
  • frontend/tsconfig.app.json
  • frontend/tsconfig.node.json
💤 Files with no reviewable changes (2)
  • frontend/src/components/helperFunctions/formatting.tsx
  • frontend/src/components/helperFunctions/index.ts

Comment on lines +150 to +153
export function parseLocalDay(isoDate: string): Date {
const [year, month, day] = isoDate.split('-').map(Number);
return new Date(year, month - 1, day);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Reject impossible calendar dates instead of rolling them forward.

new Date(year, month - 1, day) normalizes invalid inputs like 2026-02-31 to March 3. This helper is now used for route dates, so a malformed URL can render a different day than the one in the path.

Suggested fix
 export function parseLocalDay(isoDate: string): Date {
-    const [year, month, day] = isoDate.split('-').map(Number);
-    return new Date(year, month - 1, day);
+    const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(isoDate);
+    if (!match) throw new Error('Invalid date format');
+
+    const year = Number(match[1]);
+    const month = Number(match[2]);
+    const day = Number(match[3]);
+    const parsed = new Date(year, month - 1, day);
+
+    if (
+        parsed.getFullYear() !== year ||
+        parsed.getMonth() !== month - 1 ||
+        parsed.getDate() !== day
+    ) {
+        throw new Error('Invalid calendar date');
+    }
+
+    return parsed;
 }
🤖 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/lib/formatters.ts` around lines 150 - 153, parseLocalDay
currently constructs a Date which silently normalizes invalid calendar dates
(e.g. "2026-02-31") — update parseLocalDay to validate the parsed year, month
and day instead of relying on Date normalization: parse the components from
isoDate, ensure month is 1–12 and day is within the correct range for that month
(accounting for leap years), then construct the Date and verify
date.getFullYear(), date.getMonth()+1 and date.getDate() match the parsed
values; if validation fails, return a clear failure (throw an Error or return
null) rather than returning a normalized Date.

@carddev81 carddev81 merged commit 47241bd into CK-7vn/total-refactor May 18, 2026
9 checks passed
@carddev81 carddev81 deleted the carddev81/refactorcomponents branch May 18, 2026 21:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants