Skip to content

Enhanced UI and fraud documentation system#5

Open
Kanishka105 wants to merge 2 commits into
Anuj-verse:mainfrom
Kanishka105:main
Open

Enhanced UI and fraud documentation system#5
Kanishka105 wants to merge 2 commits into
Anuj-verse:mainfrom
Kanishka105:main

Conversation

@Kanishka105
Copy link
Copy Markdown

@Kanishka105 Kanishka105 commented May 17, 2026

Summary by CodeRabbit

  • New Features

    • Added Profile, Documentation, and Authentication pages; header search and an auth-aware profile menu.
    • Introduced an expandable sidebar drawer and a mobile/Flutter profile popup flow.
  • Style

    • Redesigned dashboard with new themed metrics, motion-enhanced panels, and updated metric cards.
    • Updated Cases table visuals, GraphView intelligence sidebar, and restyled Reports & Export UI.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 17, 2026

📝 Walkthrough

Walkthrough

The PR adds AuthContext and wraps routes in AuthProvider, registers /profile and /docs, replaces MainLayout with a context-driven interactive shell (theme, anchor, animated sidebar, profile menu), redesigns Dashboard/GraphView/Cases/Reports/Authentication pages, and adds two Flutter apps implementing profile popup and premium dashboard flows.

Changes

Dashboard Navigation and Layout Redesign

Layer / File(s) Summary
Route registration & new pages
services/dashboard/src/App.tsx, services/dashboard/src/pages/Profile.tsx, services/dashboard/src/pages/Documentation.tsx
Adds AuthProvider wrapper, registers profile and docs routes, and introduces Profile and Documentation pages.
Auth context and provider
services/dashboard/src/context/AuthContext.tsx
Implements AuthContext, useAuth() hook, and AuthProvider with login/logout and localStorage persistence.
MainLayout, theme and anchor hooks
services/dashboard/src/layout/MainLayout.tsx
MainLayout now provides DashboardAnchorContext and ThemeContext with exported hooks, manages sidebar/profile drawer state, deferred dashboard scroll anchoring, document dark-class sync, body scroll locking, header search, and SidebarLink helper.
Dashboard page redesign
services/dashboard/src/pages/Dashboard.tsx
Integrates theme/anchor hooks, refactors stats/alerts fetches, simplifies WebSocket parsing, and replaces UI with a themed Framer Motion metrics grid and redesigned MetricCard.
GraphView radial layout & intelligence
services/dashboard/src/pages/GraphView.tsx
Adds getNodeTypeInfo, stores raw graphData, computes radial layout, rebuilds nodes/edges with risk-driven styling/animation, and surfaces memoized nodeIntelligence and networkInsights in an Advanced Intelligence Sidebar.
Cases UI updates
services/dashboard/src/pages/Cases.tsx
Adds metric tiles, richer Case ID/Risk Score/Status renderers, adjusted Actions column, and new empty-state message.
Reports UI restyle
services/dashboard/src/pages/Reports.tsx
Restyles Reports & Export UI, expands lucide icons (Terminal, Search, DownloadCloud), preserves existing handlers and disabled logic.
Authentication page (login/signup/OTP)
services/dashboard/src/pages/Authentication.tsx
Adds multi-step Authentication page supporting login→OTP, signup with compliance checkbox, OTP resend/timer, demo OTP verification, and redirect-to-dashboard for logged-in users.

Flutter Profile & Premium Dashboard Apps

Layer / File(s) Summary
FundGuardProfileApp and screens
flutter_profile_popup.dart
Adds a Flutter app with login form (phone/account validation), dashboard with profile popup menu and logout confirmation, and a profile management route.
FundGuardPremiumApp (main.dart)
main.dart
Adds a premium Flutter app with authenticated gating, validated login, dashboard with PremiumProfilePopupMenu, profile route page, and navigation wiring for login/logout.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 A rabbit hops through code at night,

weaving themes of dark and light,
Sidebars slide and routes take wing,
Profiles bloom and metrics sing—
Hooray! The dashboard springs to sight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

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.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Enhanced UI and fraud documentation system' is partially related to the changeset but not comprehensive; it mentions UI enhancements and documentation, yet omits substantial changes like authentication system implementation, profile management, and Flutter dashboard—making it partially accurate but missing major aspects.
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.

✨ 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 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: 7

Caution

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

⚠️ Outside diff range comments (1)
services/dashboard/src/pages/Dashboard.tsx (1)

54-56: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Missing error handling for WebSocket message parsing.

JSON.parse will throw if the server sends malformed JSON, which would crash the onmessage handler. Wrap in try-catch to handle gracefully.

🛡️ Proposed fix
     ws.onmessage = (event) => {
-      const data = JSON.parse(event.data) as RiskEvent;
+      let data: RiskEvent;
+      try {
+        data = JSON.parse(event.data) as RiskEvent;
+      } catch {
+        console.error("Failed to parse WebSocket message:", event.data);
+        return;
+      }
       setLatestEvent(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 `@services/dashboard/src/pages/Dashboard.tsx` around lines 54 - 56, The
ws.onmessage handler in Dashboard.tsx currently calls JSON.parse(event.data) as
RiskEvent without error handling, so wrap the parse and setLatestEvent call
inside a try-catch in the ws.onmessage callback (the handler that uses
setLatestEvent and parses into RiskEvent); in the catch, handle malformed JSON
gracefully (e.g., log the parse error and the raw event data and return/ignore)
so the onmessage handler doesn't throw and crash the component.
🧹 Nitpick comments (6)
services/dashboard/src/pages/Dashboard.tsx (2)

224-227: 💤 Low value

Remove commented-out code.

Dead code should be removed rather than left commented. It clutters the component and the functionality already exists at lines 235-238.

🤖 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 `@services/dashboard/src/pages/Dashboard.tsx` around lines 224 - 227, Remove
the commented-out Live badge block (the multi-line JSX comment containing <span
className={liveBadge}> ... </span>) from Dashboard.tsx; locate the commented
fragment referencing liveBadge and highlight and delete it so only the existing
Live badge implementation remains (the active JSX that renders the
live/highlighted indicator).

21-24: ⚡ Quick win

Unused state variables and hardcoded connection status.

State variables alerts, latestEvent, and wsConnected are tracked but never rendered. The connection status badge (lines 179-185) is hardcoded to "DISCONNECTED" while wsConnected state is available. Either remove unused state or integrate it into the UI.

♻️ Example fix for connection status
-          <div className="mt-8 flex items-center justify-between rounded-3xl border border-red-500/20 bg-red-500/5 px-4 py-3 text-sm text-red-700">
+          <div className={`mt-8 flex items-center justify-between rounded-3xl border px-4 py-3 text-sm ${
+            wsConnected 
+              ? "border-emerald-500/20 bg-emerald-500/5 text-emerald-700" 
+              : "border-red-500/20 bg-red-500/5 text-red-700"
+          }`}>
             <span className="inline-flex items-center gap-2 font-semibold">
-              <span className="h-2.5 w-2.5 rounded-full bg-red-500 animate-pulse shadow-red-500/50"></span>
-              🔴 Operational Stream:
+              <span className={`h-2.5 w-2.5 rounded-full ${wsConnected ? "bg-emerald-500" : "bg-red-500"} animate-pulse`}></span>
+              {wsConnected ? "🟢" : "🔴"} Operational Stream:
             </span>
-            <span className="font-medium">DISCONNECTED</span>
+            <span className="font-medium">{wsConnected ? "CONNECTED" : "DISCONNECTED"}</span>
           </div>

Also applies to: 179-185

🤖 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 `@services/dashboard/src/pages/Dashboard.tsx` around lines 21 - 24, The file
defines unused state variables alerts, latestEvent and wsConnected (and their
setters setAlerts, setLatestEvent, setWsConnected) while the connection badge is
hardcoded; either remove these unused states or wire them into the UI. Best fix:
replace the hardcoded "DISCONNECTED" badge with a dynamic check that reads
wsConnected and display "CONNECTED"/"DISCONNECTED" (and update badge styling
accordingly), and ensure incoming events update alerts and latestEvent via
setAlerts/setLatestEvent where the websocket message handler (the function that
updates streamStartedAt or handles messages) processes events. If you prefer
removal, delete alerts/latestEvent/wsConnected and their setters and any unused
refs (e.g., streamStartedAt) to avoid dead state.
services/dashboard/src/App.tsx (1)

21-22: 💤 Low value

Inconsistent indentation in new route definitions.

The new routes have inconsistent indentation compared to existing routes (lines 16-20 use consistent spacing). This is a minor formatting nit.

🧹 Proposed fix
-           <Route path="profile" element={<Profile />} />
-              <Route path="docs" element={<Documentation/>} />
+          <Route path="profile" element={<Profile />} />
+          <Route path="docs" element={<Documentation />} />
🤖 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 `@services/dashboard/src/App.tsx` around lines 21 - 22, The two newly added JSX
route entries (the Route elements rendering Profile and Documentation) have
inconsistent indentation; update the indentation of the <Route path="profile"
element={<Profile />} /> and <Route path="docs" element={<Documentation/>} />
lines to match the existing Route entries' spacing/style in App.tsx so the JSX
attributes and closing tags align consistently with the other <Route ... />
declarations.
services/dashboard/src/pages/Documentation.tsx (3)

148-148: Prefer stable keys over array indices.

Using array indices as React keys (lines 148, 155, 276, 320) is acceptable when arrays are static and never reordered, but using stable identifiers is more robust and prevents potential bugs if the data structure changes.

For sidebarItems and cards, consider using item.id or card.title as the key. For the pipeline steps array, consider restructuring it as an array of objects with an id field.

Also applies to: 155-155, 276-276, 320-320

🤖 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 `@services/dashboard/src/pages/Documentation.tsx` at line 148, Replace
array-index keys with stable identifiers: for the sidebar mapping use a stable
property on each item (e.g., sidebarItems.map((section) => ...) with
key={section.id} or key={section.title}), for the cards mapping use a stable
property on each card (e.g., key={card.id} or key={card.title}), and for the
pipeline steps convert the steps from plain strings to objects with an id field
(e.g., { id, label }) and use key={step.id} when rendering; update the map
callbacks that reference sidebarItems, cards, and the pipeline steps array to
use those stable ids instead of the index.

211-215: Non-functional search input.

The search input has no onChange handler or search logic. Users can type but no filtering or navigation occurs.

If search functionality is planned for a future iteration, consider adding a comment or TODO to track it. Otherwise, this is acceptable as a placeholder for the initial release.

🤖 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 `@services/dashboard/src/pages/Documentation.tsx` around lines 211 - 215, The
search <input> in Documentation.tsx is interactive but has no onChange handler
or wiring to search/filter logic; either implement search by adding local state
(e.g., searchQuery via useState) and an onChange on the input that updates
searchQuery and triggers/filter the displayed docs (or calls a provided filter
function/prop), or explicitly mark it as a placeholder by adding a TODO comment
above the <input> referencing planned handler names (e.g., handleSearchChange,
filterDocs) so reviewers know it’s intentionally unimplemented.

195-197: "Open Docs" button has no action.

The "Open Docs" button in the help box (line 195-197) renders with hover effects but doesn't navigate or perform any action when clicked.

Consider either adding an onClick handler to navigate to a relevant help resource, or clarifying that this is a placeholder for future functionality with a comment.

🤖 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 `@services/dashboard/src/pages/Documentation.tsx` around lines 195 - 197, The
"Open Docs" button in Documentation.tsx currently has no click behavior; add an
onClick handler (or replace with a Link) so the button navigates to the
documentation/help resource or opens a new tab. Locate the button element in
Documentation.tsx (the button with class "w-full rounded-xl bg-emerald-500...")
and either wire it to a navigation function (e.g., call a router navigate to
"/docs" or window.open("https://...") on click) or add a clear inline comment
that this is intentionally a placeholder for future functionality. Ensure
accessibility by keeping the element a button (not a div) if you attach onClick.
🤖 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 `@services/dashboard/src/layout/MainLayout.tsx`:
- Line 209: The SidebarLink in MainLayout currently hardcodes the account ID
("FG-98765-IN"); update the link to build the path dynamically (e.g.,
`/graph/${accountId}`) by retrieving the current account ID from the existing
auth/user context or prop (use the same hook or value you use elsewhere in
MainLayout such as currentAccount, useAccount(), or currentUser.id), and keep
setIsSidebarOpen(false) onClick; also provide a configurable fallback
(DEFAULT_ACCOUNT_ID) if no account ID is available to avoid breaking navigation.
- Around line 156-160: The profile popup in MainLayout.tsx currently renders
hardcoded test strings ("Yjsjs" and "FG-98765-IN"); replace those with real user
values from your auth/session context (e.g., use the authenticated user object
like currentUser.name and currentUser.id provided by your auth hook/context such
as useAuth or AuthContext) and fall back to a clear placeholder ("Your Name" /
"Loading..." or "ID unavailable") when the user object is missing; update the
JSX that renders the username and ID in MainLayout to read from that user object
and handle null/undefined safely.
- Line 216: In MainLayout (services/dashboard/src/layout/MainLayout.tsx) the
main element's className string contains conflicting padding-top classes "pt-4"
and "pt-8"; remove the unintended duplicate (keep "pt-8" as proposed) so the
className on the main element only includes a single padding-top utility.

In `@services/dashboard/src/pages/Dashboard.tsx`:
- Around line 97-98: Headings in Dashboard.tsx use hardcoded dark-mode colors
("text-emerald-300" on the <p> and "text-slate-100" on the <h1>) which will look
wrong in light mode; change these to responsive classes that switch by theme
(either use tailwind's dark: variants like "text-emerald-700
dark:text-emerald-300" and "text-slate-900 dark:text-slate-100" or compute
className conditionally using the component's isDarkMode flag) so the <p> and
<h1> render appropriate colors in both light and dark modes.

In `@services/dashboard/src/pages/Documentation.tsx`:
- Line 126: The component defines activeDoc and setActiveDoc but the main
content area doesn't use activeDoc, so clicking the sidebar changes state
without updating displayed docs; either remove the unused state
(activeDoc/setActiveDoc) and related onClick handlers to avoid misleading UI, or
wire up content switching by referencing activeDoc in the Documentation
component’s render (e.g., in the main content area render a conditional or
switch on activeDoc to return the corresponding section like "intro", "usage",
etc.), ensuring the sidebar onClick continues to call setActiveDoc and the
displayed content updates accordingly.
- Around line 314-336: In Documentation.tsx the map over the steps array
currently renders the same static description inside the <p> element for every
step; change the data structure to pair each step name with a specific
description (e.g., convert the steps array into an array of objects like {
title, description }) and update the map callback that renders the div (the
element with key={index} and the <h3> and <p> children) to use the corresponding
description for each step instead of the hardcoded "Real-time fraud
monitoring..." string so each of Data Ingestion, Risk Analysis, Decision Engine,
Alert Review, and Final Outcome displays a unique, specific sentence.

In `@services/dashboard/src/pages/Profile.tsx`:
- Around line 1-8: Update the Profile component to use the app's dark theme
utility classes: wrap contents in a centered container (e.g., add a max-width
and mx-auto), give the outer div dark background and rounded border (e.g.,
bg-gray-900, rounded-lg, border border-emerald-700), add padding and subtle
shadow (p-6, shadow-md), style the heading h1 with the emerald accent (e.g.,
text-emerald-400, font-semibold, text-xl) and the paragraph p with muted light
text (e.g., text-gray-300) and consistent spacing (mt-2); modify the JSX in
function Profile to replace the plain elements with these themed classNames to
match the Documentation and Dashboard styles.

---

Outside diff comments:
In `@services/dashboard/src/pages/Dashboard.tsx`:
- Around line 54-56: The ws.onmessage handler in Dashboard.tsx currently calls
JSON.parse(event.data) as RiskEvent without error handling, so wrap the parse
and setLatestEvent call inside a try-catch in the ws.onmessage callback (the
handler that uses setLatestEvent and parses into RiskEvent); in the catch,
handle malformed JSON gracefully (e.g., log the parse error and the raw event
data and return/ignore) so the onmessage handler doesn't throw and crash the
component.

---

Nitpick comments:
In `@services/dashboard/src/App.tsx`:
- Around line 21-22: The two newly added JSX route entries (the Route elements
rendering Profile and Documentation) have inconsistent indentation; update the
indentation of the <Route path="profile" element={<Profile />} /> and <Route
path="docs" element={<Documentation/>} /> lines to match the existing Route
entries' spacing/style in App.tsx so the JSX attributes and closing tags align
consistently with the other <Route ... /> declarations.

In `@services/dashboard/src/pages/Dashboard.tsx`:
- Around line 224-227: Remove the commented-out Live badge block (the multi-line
JSX comment containing <span className={liveBadge}> ... </span>) from
Dashboard.tsx; locate the commented fragment referencing liveBadge and highlight
and delete it so only the existing Live badge implementation remains (the active
JSX that renders the live/highlighted indicator).
- Around line 21-24: The file defines unused state variables alerts, latestEvent
and wsConnected (and their setters setAlerts, setLatestEvent, setWsConnected)
while the connection badge is hardcoded; either remove these unused states or
wire them into the UI. Best fix: replace the hardcoded "DISCONNECTED" badge with
a dynamic check that reads wsConnected and display "CONNECTED"/"DISCONNECTED"
(and update badge styling accordingly), and ensure incoming events update alerts
and latestEvent via setAlerts/setLatestEvent where the websocket message handler
(the function that updates streamStartedAt or handles messages) processes
events. If you prefer removal, delete alerts/latestEvent/wsConnected and their
setters and any unused refs (e.g., streamStartedAt) to avoid dead state.

In `@services/dashboard/src/pages/Documentation.tsx`:
- Line 148: Replace array-index keys with stable identifiers: for the sidebar
mapping use a stable property on each item (e.g., sidebarItems.map((section) =>
...) with key={section.id} or key={section.title}), for the cards mapping use a
stable property on each card (e.g., key={card.id} or key={card.title}), and for
the pipeline steps convert the steps from plain strings to objects with an id
field (e.g., { id, label }) and use key={step.id} when rendering; update the map
callbacks that reference sidebarItems, cards, and the pipeline steps array to
use those stable ids instead of the index.
- Around line 211-215: The search <input> in Documentation.tsx is interactive
but has no onChange handler or wiring to search/filter logic; either implement
search by adding local state (e.g., searchQuery via useState) and an onChange on
the input that updates searchQuery and triggers/filter the displayed docs (or
calls a provided filter function/prop), or explicitly mark it as a placeholder
by adding a TODO comment above the <input> referencing planned handler names
(e.g., handleSearchChange, filterDocs) so reviewers know it’s intentionally
unimplemented.
- Around line 195-197: The "Open Docs" button in Documentation.tsx currently has
no click behavior; add an onClick handler (or replace with a Link) so the button
navigates to the documentation/help resource or opens a new tab. Locate the
button element in Documentation.tsx (the button with class "w-full rounded-xl
bg-emerald-500...") and either wire it to a navigation function (e.g., call a
router navigate to "/docs" or window.open("https://...") on click) or add a
clear inline comment that this is intentionally a placeholder for future
functionality. Ensure accessibility by keeping the element a button (not a div)
if you attach onClick.
🪄 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

Run ID: 7f8a2ab7-a6b9-4096-a4bd-c5d1690b96a7

📥 Commits

Reviewing files that changed from the base of the PR and between 92813da and 4948d13.

📒 Files selected for processing (5)
  • services/dashboard/src/App.tsx
  • services/dashboard/src/layout/MainLayout.tsx
  • services/dashboard/src/pages/Dashboard.tsx
  • services/dashboard/src/pages/Documentation.tsx
  • services/dashboard/src/pages/Profile.tsx

Comment on lines +156 to +160
<div>
<p className="text-[11px] uppercase tracking-widest font-bold text-gray-400">USER ACCOUNT</p>
<p className="mt-1 text-base font-bold font-sans">Yjsjs</p>
<p className="text-sm font-mono text-gray-500">FG-98765-IN</p>
</div>
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

Hardcoded placeholder user data in profile popup.

The username "Yjsjs" and ID "FG-98765-IN" appear to be test data. This should either use actual user data from authentication context or clearly indicate placeholder status (e.g., "Your Name" or "Loading...").

🤖 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 `@services/dashboard/src/layout/MainLayout.tsx` around lines 156 - 160, The
profile popup in MainLayout.tsx currently renders hardcoded test strings
("Yjsjs" and "FG-98765-IN"); replace those with real user values from your
auth/session context (e.g., use the authenticated user object like
currentUser.name and currentUser.id provided by your auth hook/context such as
useAuth or AuthContext) and fall back to a clear placeholder ("Your Name" /
"Loading..." or "ID unavailable") when the user object is missing; update the
JSX that renders the username and ID in MainLayout to read from that user object
and handle null/undefined safely.

<nav className="mt-3 flex flex-col gap-3">
<SidebarLink icon={<Activity size={18} />} label="Dashboard" onClick={() => { requestDashboardScroll(); setIsSidebarOpen(false); }} />
<SidebarLink icon={<ShieldAlert size={18} />} label="Live Alerts" to="/alerts" onClick={() => setIsSidebarOpen(false)} />
<SidebarLink icon={<GitGraph size={18} />} label="Graph Intel" to="/graph/FG-98765-IN" onClick={() => setIsSidebarOpen(false)} />
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

Hardcoded account ID in Graph Intel navigation link.

The link uses a static account ID FG-98765-IN. This should dynamically reference the current user's account or a configurable default.

🤖 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 `@services/dashboard/src/layout/MainLayout.tsx` at line 209, The SidebarLink in
MainLayout currently hardcodes the account ID ("FG-98765-IN"); update the link
to build the path dynamically (e.g., `/graph/${accountId}`) by retrieving the
current account ID from the existing auth/user context or prop (use the same
hook or value you use elsewhere in MainLayout such as currentAccount,
useAccount(), or currentUser.id), and keep setIsSidebarOpen(false) onClick; also
provide a configurable fallback (DEFAULT_ACCOUNT_ID) if no account ID is
available to avoid breaking navigation.

</nav>
</motion.aside>

<main className="flex-1 w-full max-w-[1600px] mx-auto px-6 lg:px-8 pt-4 pb-6 pt-8">
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

Duplicate conflicting padding classes pt-4 and pt-8.

Both classes set padding-top with different values. This appears to be an editing artifact. Remove one based on intended spacing.

🐛 Proposed fix (keeping pt-8)
-          <main className="flex-1 w-full max-w-[1600px] mx-auto px-6 lg:px-8 pt-4 pb-6 pt-8">
+          <main className="flex-1 w-full max-w-[1600px] mx-auto px-6 lg:px-8 pb-6 pt-8">
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<main className="flex-1 w-full max-w-[1600px] mx-auto px-6 lg:px-8 pt-4 pb-6 pt-8">
<main className="flex-1 w-full max-w-[1600px] mx-auto px-6 lg:px-8 pb-6 pt-8">
🤖 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 `@services/dashboard/src/layout/MainLayout.tsx` at line 216, In MainLayout
(services/dashboard/src/layout/MainLayout.tsx) the main element's className
string contains conflicting padding-top classes "pt-4" and "pt-8"; remove the
unintended duplicate (keep "pt-8" as proposed) so the className on the main
element only includes a single padding-top utility.

Comment on lines +97 to +98
<p className="text-sm uppercase tracking-widest font-bold text-emerald-300">LIVE OVERVIEW</p>
<h1 className="text-5xl font-black tracking-tight leading-none text-slate-100">Operational Risk Insights</h1>
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

Hardcoded dark-mode text colors in headings.

text-emerald-300 and text-slate-100 are used regardless of isDarkMode. This will look incorrect in light mode.

🐛 Proposed fix
-        <p className="text-sm uppercase tracking-widest font-bold text-emerald-300">LIVE OVERVIEW</p>
-        <h1 className="text-5xl font-black tracking-tight leading-none text-slate-100">Operational Risk Insights</h1>
+        <p className={`text-sm uppercase tracking-widest font-bold ${isDarkMode ? "text-emerald-300" : "text-emerald-600"}`}>LIVE OVERVIEW</p>
+        <h1 className={`text-5xl font-black tracking-tight leading-none ${isDarkMode ? "text-slate-100" : "text-slate-900"}`}>Operational Risk Insights</h1>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<p className="text-sm uppercase tracking-widest font-bold text-emerald-300">LIVE OVERVIEW</p>
<h1 className="text-5xl font-black tracking-tight leading-none text-slate-100">Operational Risk Insights</h1>
<p className={`text-sm uppercase tracking-widest font-bold ${isDarkMode ? "text-emerald-300" : "text-emerald-600"}`}>LIVE OVERVIEW</p>
<h1 className={`text-5xl font-black tracking-tight leading-none ${isDarkMode ? "text-slate-100" : "text-slate-900"}`}>Operational Risk Insights</h1>
🤖 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 `@services/dashboard/src/pages/Dashboard.tsx` around lines 97 - 98, Headings in
Dashboard.tsx use hardcoded dark-mode colors ("text-emerald-300" on the <p> and
"text-slate-100" on the <h1>) which will look wrong in light mode; change these
to responsive classes that switch by theme (either use tailwind's dark: variants
like "text-emerald-700 dark:text-emerald-300" and "text-slate-900
dark:text-slate-100" or compute className conditionally using the component's
isDarkMode flag) so the <p> and <h1> render appropriate colors in both light and
dark modes.

Comment thread services/dashboard/src/pages/Documentation.tsx
Comment thread services/dashboard/src/pages/Documentation.tsx Outdated
Comment on lines +1 to +8
function Profile() {
return (
<div>
<h1>Profile Page</h1>
<p>Welcome to the profile component.</p>
</div>
);
}
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

Style the Profile page to match the application theme.

The Profile component renders unstyled HTML that doesn't match the dark-themed, polished design system used in the Documentation page and Dashboard (dark backgrounds, emerald accents, rounded borders, consistent spacing). This creates a jarring visual inconsistency for users.

🎨 Proposed fix to match the application theme
 function Profile() {
   return (
-    <div>
-      <h1>Profile Page</h1>
-      <p>Welcome to the profile component.</p>
+    <div className="min-h-screen bg-[`#020817`] text-white p-6 lg:p-10">
+      <div className="max-w-4xl">
+        <h1 className="text-4xl font-bold mb-4">Profile Page</h1>
+        <p className="text-gray-400 text-lg">Welcome to the profile component.</p>
+      </div>
     </div>
   );
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function Profile() {
return (
<div>
<h1>Profile Page</h1>
<p>Welcome to the profile component.</p>
</div>
);
}
function Profile() {
return (
<div className="min-h-screen bg-[`#020817`] text-white p-6 lg:p-10">
<div className="max-w-4xl">
<h1 className="text-4xl font-bold mb-4">Profile Page</h1>
<p className="text-gray-400 text-lg">Welcome to the profile component.</p>
</div>
</div>
);
}
🤖 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 `@services/dashboard/src/pages/Profile.tsx` around lines 1 - 8, Update the
Profile component to use the app's dark theme utility classes: wrap contents in
a centered container (e.g., add a max-width and mx-auto), give the outer div
dark background and rounded border (e.g., bg-gray-900, rounded-lg, border
border-emerald-700), add padding and subtle shadow (p-6, shadow-md), style the
heading h1 with the emerald accent (e.g., text-emerald-400, font-semibold,
text-xl) and the paragraph p with muted light text (e.g., text-gray-300) and
consistent spacing (mt-2); modify the JSX in function Profile to replace the
plain elements with these themed classNames to match the Documentation and
Dashboard styles.

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: 12

Caution

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

⚠️ Outside diff range comments (1)
services/dashboard/src/pages/GraphView.tsx (1)

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

Hardcoded localhost URL will break in non-local environments.

This fetch URL is hardcoded to localhost:8005, which will fail in Docker, staging, or production deployments. Use a relative URL or environment variable instead.

Suggested fix using relative URL
-        const response = await fetch(`http://localhost:8005/api/graph/${account_id}`);
+        const response = await fetch(`/api/graph/${account_id}`);

Or with an environment variable:

-        const response = await fetch(`http://localhost:8005/api/graph/${account_id}`);
+        const response = await fetch(`${import.meta.env.VITE_API_BASE_URL || ''}/api/graph/${account_id}`);
🤖 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 `@services/dashboard/src/pages/GraphView.tsx` at line 111, Replace the
hardcoded localhost fetch in GraphView.tsx (the line creating response with
fetch(`http://localhost:8005/api/graph/${account_id}`)) with a deploy-safe URL:
either use a relative path fetch(`/api/graph/${account_id}`) or read a base URL
from an env var (e.g. const BASE = process.env.REACT_APP_API_BASE_URL || '';
fetch(`${BASE}/api/graph/${account_id}`)) so the code works in
Docker/staging/production; update GraphView.tsx to use this pattern and ensure
the chosen env var is documented for deployments.
♻️ Duplicate comments (1)
services/dashboard/src/layout/MainLayout.tsx (1)

297-297: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Replace hardcoded Graph Intel account ID with dynamic user/account context.

/graph/FG-98765-IN is still static and can route users to the wrong account graph.

🤖 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 `@services/dashboard/src/layout/MainLayout.tsx` at line 297, The SidebarLink in
MainLayout currently hardcodes "/graph/FG-98765-IN"; change it to construct the
path from the current user/account context instead: retrieve the active account
id (e.g., from useAuth(), useAccount(), currentUser, or props used in
MainLayout) and interpolate it into the to prop (e.g., `/graph/${accountId}`)
while preserving the onClick that calls setIsSidebarOpen(false) and the
icon/label props; ensure you handle missing accountId (fallback or disable link)
so routing never points to the static FG-98765-IN value.
🧹 Nitpick comments (3)
services/dashboard/src/pages/Cases.tsx (1)

257-264: ⚡ Quick win

Simplify the empty state message for better user experience.

The current empty state uses technical jargon that may confuse users. Terms like "unmapped transaction anomalies", "batch cycle", and "synthetic Kafka streaming vectors" are internal implementation details that don't help users understand the state.

Consider a clearer message:

💬 Proposed simplified empty state
                   <tr>
                      <td colSpan={6} className="p-16 text-center">
                        <div className="flex flex-col items-center justify-center">
                          <span className="text-6xl mb-4 opacity-60">🛰️</span>
-                          <p className="font-medium text-gray-300">FundGuard Core Pipeline Operational</p>
+                          <p className="font-medium text-gray-300">No Cases Found</p>
                          <p className="text-gray-500 max-w-md text-center inline-block mt-2">
-                            No unmapped transaction anomalies identified in this batch cycle. Run standard system validation tests to inject synthetic Kafka streaming vectors.
+                            All transactions are currently within normal parameters. Cases will appear here when suspicious activity is detected.
                          </p>
                        </div>
                      </td>
                   </tr>
🤖 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 `@services/dashboard/src/pages/Cases.tsx` around lines 257 - 264, Replace the
verbose, technical empty-state copy in the Cases.tsx JSX block (the <td
colSpan={6} className="p-16 text-center"> containing the <p
className="font-medium text-gray-300"> and the <p className="text-gray-500
max-w-md text-center inline-block mt-2"> elements) with user-friendly language:
change the bold line to a short clear status like "No issues detected" or "All
clear" and the secondary line to a plain explanation such as "There are no
current transaction issues; the system is operating normally." Keep the emoji
and layout unchanged.
flutter_profile_popup.dart (1)

551-564: 💤 Low value

Derive avatar initial from user data.

The CircleAvatar displays a hardcoded 'R' (line 556) instead of deriving the initial from the authenticated user's data. While acceptable for a demo, this should dynamically compute the initial from the phone number or account number for consistency.

♻️ Suggested dynamic initial
         child: CircleAvatar(
           radius: 22,
           backgroundColor: const Color(0xFF6200EE),
           child: const Text(
-            'R',
+            widget.phoneNumber.isNotEmpty ? widget.phoneNumber[0].toUpperCase() : 'U',
             style: TextStyle(
               color: Colors.white,
               fontSize: 16,
               fontWeight: FontWeight.bold,
             ),
           ),
         ),
🤖 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 `@flutter_profile_popup.dart` around lines 551 - 564, Replace the hardcoded
Text('R') used inside the CircleAvatar with a computed initial derived from the
authenticated user (e.g., use user.accountNumber or user.phoneNumber). In the
widget's build logic (the scope that renders the CircleAvatar), compute a String
avatarInitial = first non-empty character of accountNumber (or phoneNumber if
accountNumber is empty), normalizing/triming non-digit chars if needed, and
fallback to '?' or the user's name initial if neither exists; then use
Text(avatarInitial) in place of the hardcoded 'R'. Ensure you reference the
existing CircleAvatar and Text('R') locations when making the change.
main.dart (1)

788-795: 💤 Low value

"Change Password" button has no implementation.

The button's onPressed handler is empty and provides no functionality. Consider adding a TODO comment or implementing the password change flow.

📝 Suggested improvement
                   child: ElevatedButton.icon(
-                    onPressed: () {},
+                    onPressed: () {
+                      // TODO: Implement password change flow
+                    },
                     icon: const Icon(Icons.lock_outline_rounded, size: 18),
                     label: const Text('Change 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 `@main.dart` around lines 788 - 795, The ElevatedButton.icon inside the
SizedBox currently has an empty onPressed handler; update the onPressed of this
ElevatedButton.icon to either (a) navigate to your ChangePassword screen or
route (e.g., pushNamed or Navigator.push to the ChangePassword widget) or (b)
open a password-change dialog/form and validate inputs before submitting; if you
don't implement the flow yet, add a clear TODO comment and a minimal stub that
shows a SnackBar or debug log so the button is not a no-op. Ensure you modify
the onPressed closure where ElevatedButton.icon is defined and reference the
ChangePassword widget/screen or the dialog method you create.
🤖 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 `@flutter_profile_popup.dart`:
- Around line 249-272: The _performLogin function currently uses Future.delayed
and hardcoded credential checks; replace that with an async call to your
authentication API (using credentials from _phoneController.text and
_accountController.text), parse the backend response to extract role and token,
call widget.onLoginSuccess(phone, account, role) on success, persist the token
securely (e.g., secure storage) for session management, and add proper error
handling for network failures and invalid credentials to update _isLoading and
show appropriate SnackBar messages; ensure _formKey validation remains and
_isLoading is set true before the request and false in all exit paths.
- Around line 654-664: The Change Password ElevatedButton currently has an empty
onPressed handler (the ElevatedButton with child Text 'Change Password'), which
is non-functional; either implement the password-change flow by wiring onPressed
to the appropriate handler (e.g., call your changePassword() method or navigate
to ChangePasswordScreen) and perform validation/UI work, or remove the button;
if you want a placeholder, disable the button (set onPressed null or use
Enabled=false) and add a Tooltip explaining "Coming soon" so users aren’t
misled.
- Around line 222-241: The UI contains hardcoded test credentials inside the
Card widget (the Text showing 'Phone: 5555555555\nA/C: 555555555') which must be
removed from production and the auth logic that checks those values should be
converted to use debug-only config; delete the Card or guard its rendering
behind Flutter's kDebugMode and move the credential values into a debug-only
constant or environment/config file, and update the authentication check (the
existing phone/account comparison logic) to reference those debug-only values
instead of hardcoded strings so production builds never include test
credentials.
- Around line 632-636: The profile page currently uses hardcoded values in
_buildSettingRow calls; update the ProfileRoutePage constructor (and its state)
to accept user fields (e.g., email, notificationsStatus, twoFactorStatus,
phoneNumber, accountNumber) and replace the hardcoded strings
('user@fundguard.com', 'Enabled', 'Active') with those instance properties when
building rows (look for _buildSettingRow usages). Also update the navigation
call in ProfilePopupMenu to pass the authenticated user's data into the new
ProfileRoutePage constructor so real values flow into the page.

In `@main.dart`:
- Line 407: Replace the undefined Colors.emerald usages with a valid color
constant: locate calls to _buildMetric where Colors.emerald is passed (e.g., the
call _buildMetric('Active', '24/7', Colors.emerald) and the other similar
invocations) and change the third argument to either Colors.green or a custom
const Color(0xFF...) value; ensure all occurrences in main.dart are updated
consistently so the code compiles.
- Around line 520-545: The code references a non-existent Colors.emerald causing
build errors; replace all usages of Colors.emerald (used in the small status
Container's decoration, its BoxShadow color, and the Text style 'SYSTEM SESSION
ACTIVE', plus the other three occurrences) with Colors.green or a project-level
const emeraldColor and use that constant instead so the app compiles and the
color is consistent across Container, BoxShadow, and Text styles.
- Around line 666-687: The CircleAvatar gradient usage is incorrect:
LinearGradient.createShader returns a Shader, not an ImageProvider; replace the
CircleAvatar with a Container (or wrap the CircleAvatar child) that uses a
BoxDecoration with the LinearGradient (refer to the CircleAvatar usage around
the radius 22, backgroundColor/backgroundImage/child) and keep the inner Text
'R' as its child so the gradient is the background. Also fix invalid color
constants named Colors.emerald (seen at the other usages) by replacing them with
a valid Flutter color such as Colors.teal or Colors.green or a custom
Color(0xFF...) wherever Colors.emerald is referenced.

In `@services/dashboard/src/context/AuthContext.tsx`:
- Around line 21-25: The AuthContext currently initializes isLoggedIn from
localStorage (useState initializer) which can be forged; instead treat
localStorage as a cache only and perform a server-verified session/token check
during bootstrap: update the AuthContext bootstrap flow (the useEffect or
bootstrapAuth routine that calls setIsLoggedIn) to call your backend session or
token verification endpoint (e.g., verifySession / getCurrentUser) and set
isLoggedIn based on that response, falling back to localStorage only while the
network call is pending; ensure any localStorage read in the isLoggedIn
initializer is used as a temporary optimistic value and that all protected
actions rely on the server-validated state returned to setIsLoggedIn and to
clear invalid localStorage tokens via setIsLoggedIn and localStorage.removeItem
when verification fails.

In `@services/dashboard/src/layout/MainLayout.tsx`:
- Around line 111-120: The useEffect in MainLayout currently checks isLoggedIn
but leaves the redirect commented out, allowing unauthenticated users on
protected routes; update the effect to call navigate("/auth") (preferably with
navigate("/auth", { replace: true })) when !isLoggedIn and the path is not
"/auth" or starting with "/auth". Ensure useNavigate() is imported/used in this
component and included in the effect dependencies (e.g., [isLoggedIn, navigate])
so the redirect executes reliably for the MainLayout shell routes.

In `@services/dashboard/src/pages/Authentication.tsx`:
- Around line 82-87: The client currently hardcodes OTP validation in the
setTimeout block by checking otpCode === "123456" and calling
login(loginForm.phoneNumber) then navigate("/dashboard"); remove this insecure
client-side bypass and instead call the backend verification endpoint (e.g.,
POST /verify-otp) with the phone number and otpCode, await the server response,
and only on a successful response call login(...) and navigate(...); update the
logic where otpCode is checked (including the other occurrence at lines
~233-235) to use the server response for success/failure handling and surface
any server error messages to the UI.

In `@services/dashboard/src/pages/Documentation.tsx`:
- Line 323: The sidebar aside with className "hidden lg:flex ..." is
desktop-only so mobile users can't change activeDoc; make a responsive mobile
selector (e.g., a top visible dropdown or horizontal tabs) that is shown when
the aside is hidden and wires into the same state handlers (activeDoc and its
setter, e.g., setActiveDoc) used by the desktop nav; locate the desktop aside
element and the corresponding nav block around the later occurrence (the other
nav at lines ~453-456) and implement a compact mobile UI that calls the same
click/change handlers to update activeDoc so behavior is identical across
viewports.
- Around line 403-407: The search input in Documentation.tsx currently uses only
placeholder text; add an accessible name by either adding an associated <label>
(e.g., label with htmlFor="doc-search" and give the input id="doc-search") or by
adding an aria-label attribute (e.g., aria-label="Search documentation") on the
input element so screen readers have an explicit, persistent label.

---

Outside diff comments:
In `@services/dashboard/src/pages/GraphView.tsx`:
- Line 111: Replace the hardcoded localhost fetch in GraphView.tsx (the line
creating response with fetch(`http://localhost:8005/api/graph/${account_id}`))
with a deploy-safe URL: either use a relative path
fetch(`/api/graph/${account_id}`) or read a base URL from an env var (e.g. const
BASE = process.env.REACT_APP_API_BASE_URL || '';
fetch(`${BASE}/api/graph/${account_id}`)) so the code works in
Docker/staging/production; update GraphView.tsx to use this pattern and ensure
the chosen env var is documented for deployments.

---

Duplicate comments:
In `@services/dashboard/src/layout/MainLayout.tsx`:
- Line 297: The SidebarLink in MainLayout currently hardcodes
"/graph/FG-98765-IN"; change it to construct the path from the current
user/account context instead: retrieve the active account id (e.g., from
useAuth(), useAccount(), currentUser, or props used in MainLayout) and
interpolate it into the to prop (e.g., `/graph/${accountId}`) while preserving
the onClick that calls setIsSidebarOpen(false) and the icon/label props; ensure
you handle missing accountId (fallback or disable link) so routing never points
to the static FG-98765-IN value.

---

Nitpick comments:
In `@flutter_profile_popup.dart`:
- Around line 551-564: Replace the hardcoded Text('R') used inside the
CircleAvatar with a computed initial derived from the authenticated user (e.g.,
use user.accountNumber or user.phoneNumber). In the widget's build logic (the
scope that renders the CircleAvatar), compute a String avatarInitial = first
non-empty character of accountNumber (or phoneNumber if accountNumber is empty),
normalizing/triming non-digit chars if needed, and fallback to '?' or the user's
name initial if neither exists; then use Text(avatarInitial) in place of the
hardcoded 'R'. Ensure you reference the existing CircleAvatar and Text('R')
locations when making the change.

In `@main.dart`:
- Around line 788-795: The ElevatedButton.icon inside the SizedBox currently has
an empty onPressed handler; update the onPressed of this ElevatedButton.icon to
either (a) navigate to your ChangePassword screen or route (e.g., pushNamed or
Navigator.push to the ChangePassword widget) or (b) open a password-change
dialog/form and validate inputs before submitting; if you don't implement the
flow yet, add a clear TODO comment and a minimal stub that shows a SnackBar or
debug log so the button is not a no-op. Ensure you modify the onPressed closure
where ElevatedButton.icon is defined and reference the ChangePassword
widget/screen or the dialog method you create.

In `@services/dashboard/src/pages/Cases.tsx`:
- Around line 257-264: Replace the verbose, technical empty-state copy in the
Cases.tsx JSX block (the <td colSpan={6} className="p-16 text-center">
containing the <p className="font-medium text-gray-300"> and the <p
className="text-gray-500 max-w-md text-center inline-block mt-2"> elements) with
user-friendly language: change the bold line to a short clear status like "No
issues detected" or "All clear" and the secondary line to a plain explanation
such as "There are no current transaction issues; the system is operating
normally." Keep the emoji and layout unchanged.
🪄 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

Run ID: 8e55d4e5-b362-4a05-b4c2-46388ef3b6fc

📥 Commits

Reviewing files that changed from the base of the PR and between 4948d13 and f537679.

📒 Files selected for processing (11)
  • flutter_profile_popup.dart
  • main.dart
  • services/dashboard/src/App.tsx
  • services/dashboard/src/context/AuthContext.tsx
  • services/dashboard/src/layout/MainLayout.tsx
  • services/dashboard/src/pages/Authentication.tsx
  • services/dashboard/src/pages/Cases.tsx
  • services/dashboard/src/pages/Dashboard.tsx
  • services/dashboard/src/pages/Documentation.tsx
  • services/dashboard/src/pages/GraphView.tsx
  • services/dashboard/src/pages/Reports.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • services/dashboard/src/pages/Dashboard.tsx

Comment on lines +222 to +241
Card(
color: const Color(0xFF151D2A),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text(
'Test Credentials:',
style: TextStyle(color: Color(0xFF6200EE), fontWeight: FontWeight.w600, fontSize: 12),
),
SizedBox(height: 8),
Text(
'Phone: 5555555555\nA/C: 555555555',
style: TextStyle(color: Color(0xFFB0B0B0), fontSize: 12, fontFamily: 'monospace'),
),
],
),
),
),
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 | 🟠 Major | 🏗️ Heavy lift

Remove hardcoded test credentials from production code.

Hardcoded credentials are displayed in the UI (lines 234-236) and used in authentication logic (lines 258-259). This creates a security risk if the code is deployed to production or distributed to end users.

Consider moving test credentials to:

  • Environment configuration
  • Debug-only conditional compilation
  • Separate test harness configuration file
🔒 Suggested approach

Remove the test credentials card from the UI and add debug-only configuration:

-             const SizedBox(height: 24),
-             Card(
-               color: const Color(0xFF151D2A),
-               child: Padding(
-                 padding: const EdgeInsets.all(16),
-                 child: Column(
-                   crossAxisAlignment: CrossAxisAlignment.start,
-                   children: const [
-                     Text(
-                       'Test Credentials:',
-                       style: TextStyle(color: Color(0xFF6200EE), fontWeight: FontWeight.w600, fontSize: 12),
-                     ),
-                     SizedBox(height: 8),
-                     Text(
-                       'Phone: 5555555555\nA/C: 555555555',
-                       style: TextStyle(color: Color(0xFFB0B0B0), fontSize: 12, fontFamily: 'monospace'),
-                     ),
-                   ],
-                 ),
-               ),
-             ),

For the authentication logic, use environment variables or configuration:

// In debug mode or test configuration only
const bool kDebugMode = true; // Use Flutter's kDebugMode constant
const String debugPhone = '5555555555';
const String debugAccount = '555555555';

if (kDebugMode && phone == debugPhone && account == debugAccount) {
  // ... test login
}
🤖 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 `@flutter_profile_popup.dart` around lines 222 - 241, The UI contains hardcoded
test credentials inside the Card widget (the Text showing 'Phone:
5555555555\nA/C: 555555555') which must be removed from production and the auth
logic that checks those values should be converted to use debug-only config;
delete the Card or guard its rendering behind Flutter's kDebugMode and move the
credential values into a debug-only constant or environment/config file, and
update the authentication check (the existing phone/account comparison logic) to
reference those debug-only values instead of hardcoded strings so production
builds never include test credentials.

Comment on lines +249 to +272
void _performLogin() async {
if (!_formKey.currentState!.validate()) return;

setState(() => _isLoading = true);
await Future.delayed(const Duration(milliseconds: 800));

final phone = _phoneController.text.trim();
final account = _accountController.text.trim();

if (phone == '5555555555' && account == '555555555') {
widget.onLoginSuccess(phone, account, 'Admin');
setState(() => _isLoading = false);
return;
}

setState(() => _isLoading = false);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Invalid Credentials for testing'),
backgroundColor: Color(0xFFCF6679),
duration: Duration(seconds: 2),
),
);
}
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 | 🟠 Major | 🏗️ Heavy lift

Replace simulated authentication with real backend integration.

The authentication flow uses Future.delayed (line 253) and hardcoded credential checks (lines 258-259) instead of calling a real authentication service. The user role is hardcoded as 'Admin' (line 259) with no role derivation logic.

Before production deployment, implement:

  • Backend API call for credential verification
  • Proper error handling for network failures
  • Role-based authentication response from the backend
  • Secure token storage for session management
🤖 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 `@flutter_profile_popup.dart` around lines 249 - 272, The _performLogin
function currently uses Future.delayed and hardcoded credential checks; replace
that with an async call to your authentication API (using credentials from
_phoneController.text and _accountController.text), parse the backend response
to extract role and token, call widget.onLoginSuccess(phone, account, role) on
success, persist the token securely (e.g., secure storage) for session
management, and add proper error handling for network failures and invalid
credentials to update _isLoading and show appropriate SnackBar messages; ensure
_formKey validation remains and _isLoading is set true before the request and
false in all exit paths.

Comment on lines +632 to +636
_buildSettingRow('Email', 'user@fundguard.com'),
const Divider(height: 24, color: Color(0xFF333333)),
_buildSettingRow('Notifications', 'Enabled'),
const Divider(height: 24, color: Color(0xFF333333)),
_buildSettingRow('Two-Factor Auth', 'Active'),
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 | 🟠 Major | ⚡ Quick win

Display actual user data instead of hardcoded placeholders.

The profile page shows hardcoded values ('user@fundguard.com', 'Enabled', 'Active') instead of displaying the authenticated user's actual data. The ProfileRoutePage constructor doesn't receive phoneNumber or accountNumber parameters, preventing it from showing real user information.

🔧 Proposed fix

Update ProfileRoutePage to accept and display user data:

 class ProfileRoutePage extends StatelessWidget {
-  const ProfileRoutePage({Key? key}) : super(key: key);
+  final String phoneNumber;
+  final String accountNumber;
+
+  const ProfileRoutePage({
+    Key? key,
+    required this.phoneNumber,
+    required this.accountNumber,
+  }) : super(key: key);

Then update the displayed data:

-                   _buildSettingRow('Email', 'user@fundguard.com'),
+                   _buildSettingRow('Phone', phoneNumber),
                    const Divider(height: 24, color: Color(0xFF333333)),
-                   _buildSettingRow('Notifications', 'Enabled'),
+                   _buildSettingRow('Account', accountNumber),

Finally, update the navigation call in ProfilePopupMenu (line 506):

               Navigator.of(context).push(
-                MaterialPageRoute(builder: (context) => const ProfileRoutePage()),
+                MaterialPageRoute(
+                  builder: (context) => ProfileRoutePage(
+                    phoneNumber: widget.phoneNumber,
+                    accountNumber: widget.accountNumber,
+                  ),
+                ),
               );
🤖 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 `@flutter_profile_popup.dart` around lines 632 - 636, The profile page
currently uses hardcoded values in _buildSettingRow calls; update the
ProfileRoutePage constructor (and its state) to accept user fields (e.g., email,
notificationsStatus, twoFactorStatus, phoneNumber, accountNumber) and replace
the hardcoded strings ('user@fundguard.com', 'Enabled', 'Active') with those
instance properties when building rows (look for _buildSettingRow usages). Also
update the navigation call in ProfilePopupMenu to pass the authenticated user's
data into the new ProfileRoutePage constructor so real values flow into the
page.

Comment on lines +654 to +664
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF6200EE),
padding: const EdgeInsets.symmetric(vertical: 14),
),
child: const Text('Change Password'),
),
),
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

Implement or remove the non-functional Change Password button.

The "Change Password" button has an empty handler (line 657) and performs no action when clicked. This creates a misleading user experience.

Either implement password change functionality or remove the button until the feature is ready. If keeping it as a placeholder, consider disabling it and adding a tooltip explaining that the feature is coming soon.

💡 Placeholder approach
                     SizedBox(
                       width: double.infinity,
                       child: ElevatedButton(
-                        onPressed: () {},
+                        onPressed: null, // Disabled
                         style: ElevatedButton.styleFrom(
-                          backgroundColor: const Color(0xFF6200EE),
+                          backgroundColor: const Color(0xFF6200EE),
+                          disabledBackgroundColor: const Color(0xFF333333),
                           padding: const EdgeInsets.symmetric(vertical: 14),
                         ),
-                        child: const Text('Change Password'),
+                        child: const Text('Change Password (Coming Soon)'),
                       ),
                     ),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF6200EE),
padding: const EdgeInsets.symmetric(vertical: 14),
),
child: const Text('Change Password'),
),
),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: null, // Disabled
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF6200EE),
disabledBackgroundColor: const Color(0xFF333333),
padding: const EdgeInsets.symmetric(vertical: 14),
),
child: const Text('Change Password (Coming Soon)'),
),
),
🤖 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 `@flutter_profile_popup.dart` around lines 654 - 664, The Change Password
ElevatedButton currently has an empty onPressed handler (the ElevatedButton with
child Text 'Change Password'), which is non-functional; either implement the
password-change flow by wiring onPressed to the appropriate handler (e.g., call
your changePassword() method or navigate to ChangePasswordScreen) and perform
validation/UI work, or remove the button; if you want a placeholder, disable the
button (set onPressed null or use Enabled=false) and add a Tooltip explaining
"Coming soon" so users aren’t misled.

Comment thread main.dart
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_buildMetric('Active', '24/7', Colors.emerald),
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 | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify Flutter Colors API to confirm emerald is not available
rg -n "static const.*emerald" --type dart -g "!main.dart"

Repository: Anuj-verse/fundGuard

Length of output: 46


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Find all uses of Colors.emerald in the repo
rg -n "Colors\.emerald" --type dart

# 2) Inspect main.dart around the referenced line (small window)
rg -n "Colors\.emerald" main.dart -n || true
sed -n '380,435p' main.dart || true

# 3) Look for any local "emerald" color definition in main.dart
rg -n "emerald" main.dart --type dart || true

Repository: Anuj-verse/fundGuard

Length of output: 2705


🌐 Web query:

Flutter Colors emerald constant Colors.emerald

💡 Result:

In Flutter’s built-in Material color constants, there is no Colors.emerald constant. Flutter’s Colors class defines named Material Design swatches such as Colors.green, Colors.teal, Colors.lime, etc., plus opacity variants (e.g., black54, white10) and other base colors like Colors.transparent and Colors.white. The Colors documentation and source list these constants, and “emerald” is not among them [1][2]. What to do instead: - Use the closest existing Material swatch, e.g. Colors.green (MaterialColor with shades via indexing like Colors.green) [3]. - Or define your own emerald constant, e.g. const Color(0xFF...) using the Color constructor (ARGB 0xAARRGGBB) [4]. Example custom emerald: const Color emerald = Color(0xFF50C878); // 0xAARRGGBB format per Flutter Color docs [4] If you want a MaterialColor swatch (shades 50..900) like the built-in palette, create a MaterialColor with a map of shades (MaterialColor constructor described in Flutter docs) [5].

Citations:


Fix undefined Flutter color constant Colors.emerald
Colors.emerald isn’t a built-in constant in Flutter’s Colors API and is referenced multiple times in main.dart (407, 525, 528, 540), which will break compilation. Replace it with Colors.green or a custom const Color(...).

🔧 Example replacement
-              _buildMetric('Active', '24/7', Colors.emerald),
+              _buildMetric('Active', '24/7', const Color(0xFF10B981)),

Apply the same replacement to the other Colors.emerald usages in main.dart.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
_buildMetric('Active', '24/7', Colors.emerald),
_buildMetric('Active', '24/7', const Color(0xFF10B981)),
🤖 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 `@main.dart` at line 407, Replace the undefined Colors.emerald usages with a
valid color constant: locate calls to _buildMetric where Colors.emerald is
passed (e.g., the call _buildMetric('Active', '24/7', Colors.emerald) and the
other similar invocations) and change the third argument to either Colors.green
or a custom const Color(0xFF...) value; ensure all occurrences in main.dart are
updated consistently so the code compiles.

Comment on lines +21 to +25
const [isLoggedIn, setIsLoggedIn] = useState(() => {
try {
const stored = localStorage.getItem("isLoggedIn");
return stored === "true";
} catch {
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 | 🟠 Major | 🏗️ Heavy lift

Do not trust auth state sourced only from localStorage.

isLoggedIn can be forged by manually setting localStorage keys, which allows frontend auth bypass.

Use a server-verified session/token check during bootstrap and treat localStorage as cache only.

Also applies to: 37-43

🤖 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 `@services/dashboard/src/context/AuthContext.tsx` around lines 21 - 25, The
AuthContext currently initializes isLoggedIn from localStorage (useState
initializer) which can be forged; instead treat localStorage as a cache only and
perform a server-verified session/token check during bootstrap: update the
AuthContext bootstrap flow (the useEffect or bootstrapAuth routine that calls
setIsLoggedIn) to call your backend session or token verification endpoint
(e.g., verifySession / getCurrentUser) and set isLoggedIn based on that
response, falling back to localStorage only while the network call is pending;
ensure any localStorage read in the isLoggedIn initializer is used as a
temporary optimistic value and that all protected actions rely on the
server-validated state returned to setIsLoggedIn and to clear invalid
localStorage tokens via setIsLoggedIn and localStorage.removeItem when
verification fails.

Comment on lines +111 to +120
// Redirect unauthenticated users to auth page
useEffect(() => {
if (!isLoggedIn) {
const currentPath = window.location.pathname;
if (currentPath !== "/auth" && !currentPath.startsWith("/auth")) {
// Optional: Redirect to auth if not on auth page
// navigate("/auth");
}
}
}, [isLoggedIn]);
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 | 🟠 Major | ⚡ Quick win

Enforce redirect for logged-out users on protected shell routes.

The redirect is currently disabled, so unauthenticated users can stay on protected pages.

🔐 Suggested fix
   useEffect(() => {
     if (!isLoggedIn) {
-      const currentPath = window.location.pathname;
-      if (currentPath !== "/auth" && !currentPath.startsWith("/auth")) {
-        // Optional: Redirect to auth if not on auth page
-        // navigate("/auth");
-      }
+      navigate("/auth", { replace: true });
     }
-  }, [isLoggedIn]);
+  }, [isLoggedIn, navigate]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Redirect unauthenticated users to auth page
useEffect(() => {
if (!isLoggedIn) {
const currentPath = window.location.pathname;
if (currentPath !== "/auth" && !currentPath.startsWith("/auth")) {
// Optional: Redirect to auth if not on auth page
// navigate("/auth");
}
}
}, [isLoggedIn]);
// Redirect unauthenticated users to auth page
useEffect(() => {
if (!isLoggedIn) {
navigate("/auth", { replace: true });
}
}, [isLoggedIn, navigate]);
🤖 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 `@services/dashboard/src/layout/MainLayout.tsx` around lines 111 - 120, The
useEffect in MainLayout currently checks isLoggedIn but leaves the redirect
commented out, allowing unauthenticated users on protected routes; update the
effect to call navigate("/auth") (preferably with navigate("/auth", { replace:
true })) when !isLoggedIn and the path is not "/auth" or starting with "/auth".
Ensure useNavigate() is imported/used in this component and included in the
effect dependencies (e.g., [isLoggedIn, navigate]) so the redirect executes
reliably for the MainLayout shell routes.

Comment on lines +82 to +87
setTimeout(() => {
if (otpCode === "123456") {
// Mock successful verification
login(loginForm.phoneNumber);
navigate("/dashboard");
} else {
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 | 🟠 Major | 🏗️ Heavy lift

Move OTP validation to the backend; client-side hardcoded OTP is insecure.

A fixed client-side OTP (123456) allows trivial bypass and grants authenticated state without server verification.

Also applies to: 233-235

🤖 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 `@services/dashboard/src/pages/Authentication.tsx` around lines 82 - 87, The
client currently hardcodes OTP validation in the setTimeout block by checking
otpCode === "123456" and calling login(loginForm.phoneNumber) then
navigate("/dashboard"); remove this insecure client-side bypass and instead call
the backend verification endpoint (e.g., POST /verify-otp) with the phone number
and otpCode, await the server response, and only on a successful response call
login(...) and navigate(...); update the logic where otpCode is checked
(including the other occurrence at lines ~233-235) to use the server response
for success/failure handling and surface any server error messages to the UI.

return (
<div className="h-screen bg-white dark:bg-[#020817] text-slate-900 dark:text-white flex overflow-hidden">
{/* Sidebar */}
<aside className="hidden lg:flex w-[260px] h-screen sticky top-0 bg-[#050d1d] border-r border-white/5 flex-col px-5 py-6 overflow-y-auto scrollbar-thin scrollbar-thumb-emerald-500/20 scrollbar-track-transparent">
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 | 🟠 Major | ⚡ Quick win

Add a mobile section selector for docs navigation.

The section navigation is desktop-only (hidden lg:flex), so mobile users can’t change activeDoc and are effectively stuck on the default content.

📱 Suggested minimal fix
+  const docItems = sidebarItems.flatMap((section) => section.items);
...
-        {/* Dynamic Documentation Content */}
-        <section className="px-6 lg:px-10 pt-10 pb-12">
+        {/* Dynamic Documentation Content */}
+        <section className="px-6 lg:px-10 pt-10 pb-12">
+          <div className="lg:hidden mb-6">
+            <label htmlFor="doc-section" className="sr-only">Documentation section</label>
+            <select
+              id="doc-section"
+              value={activeDoc}
+              onChange={(e) => setActiveDoc(e.target.value)}
+              className="w-full rounded-xl border border-white/10 bg-[`#071224`] px-3 py-2 text-slate-100"
+            >
+              {docItems.map((item) => (
+                <option key={item.id} value={item.id}>
+                  {item.label}
+                </option>
+              ))}
+            </select>
+          </div>
           {renderContent(activeDoc)}
         </section>

Also applies to: 453-456

🤖 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 `@services/dashboard/src/pages/Documentation.tsx` at line 323, The sidebar
aside with className "hidden lg:flex ..." is desktop-only so mobile users can't
change activeDoc; make a responsive mobile selector (e.g., a top visible
dropdown or horizontal tabs) that is shown when the aside is hidden and wires
into the same state handlers (activeDoc and its setter, e.g., setActiveDoc) used
by the desktop nav; locate the desktop aside element and the corresponding nav
block around the later occurrence (the other nav at lines ~453-456) and
implement a compact mobile UI that calls the same click/change handlers to
update activeDoc so behavior is identical across viewports.

Comment on lines +403 to +407
<input
type="text"
placeholder="Search documentation..."
className="bg-transparent outline-none ml-3 text-sm flex-1 placeholder:text-gray-500 text-slate-900 dark:text-white"
/>
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

Add an explicit accessible label to the search input.

The search field currently relies on placeholder text only.

♿ Suggested fix
             <input
               type="text"
+              aria-label="Search documentation"
               placeholder="Search documentation..."
               className="bg-transparent outline-none ml-3 text-sm flex-1 placeholder:text-gray-500 text-slate-900 dark:text-white"
             />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<input
type="text"
placeholder="Search documentation..."
className="bg-transparent outline-none ml-3 text-sm flex-1 placeholder:text-gray-500 text-slate-900 dark:text-white"
/>
<input
type="text"
aria-label="Search documentation"
placeholder="Search documentation..."
className="bg-transparent outline-none ml-3 text-sm flex-1 placeholder:text-gray-500 text-slate-900 dark:text-white"
/>
🤖 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 `@services/dashboard/src/pages/Documentation.tsx` around lines 403 - 407, The
search input in Documentation.tsx currently uses only placeholder text; add an
accessible name by either adding an associated <label> (e.g., label with
htmlFor="doc-search" and give the input id="doc-search") or by adding an
aria-label attribute (e.g., aria-label="Search documentation") on the input
element so screen readers have an explicit, persistent label.

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.

1 participant