-
Notifications
You must be signed in to change notification settings - Fork 515
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve code splitting #8979
Improve code splitting #8979
Conversation
✅ Deploy Preview for care-ohc ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
CARE Run #3827
Run Properties:
|
Project |
CARE
|
Branch Review |
sainak/code-splitting
|
Run status |
Passed #3827
|
Run duration | 05m 03s |
Commit |
a09a3e9daa: Improve code splitting
|
Committer | Aakash Singh |
View all properties for this run ↗︎ |
Test results | |
---|---|
Failures |
0
|
Flaky |
0
|
Pending |
0
|
Skipped |
0
|
Passing |
125
|
View all changes introduced in this branch ↗︎ |
Deploying care-fe with Cloudflare Pages
|
To replicate, perform a hard refresh on the patient consultation page, click on the "Vital" section, and then begin selecting other sections. Similar behavior is noted for most of the components you changed @sainak is this behavior expected in your recent changes? CC: @rithviknishad |
wrapping the components in suspens should fix it, I'll work on it |
WalkthroughThis pull request includes several significant changes across multiple files. The Changes
Poem
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
@sainak once you make the PR is ready for re-review, change the label once again |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 11
🧹 Outside diff range and nitpick comments (15)
src/i18n.ts (1)
24-25
: Performance optimization through selective language loading.The
currentOnly
load strategy combined with dynamicsupportedLngs
is an excellent optimization that ensures only the required language resources are loaded.Consider implementing a preloading strategy for the most common languages in your user base to improve the perceived performance. This could be done using:
// Add preload configuration preload: ['en'], // Preload English as it's the fallbacksrc/Routers/SessionRouter.tsx (2)
1-7
: LGTM! Consider organizing imports by category.The direct imports are better for tree-shaking and code splitting. Consider organizing imports into categories:
- React/framework imports
- Components imports
import { lazy } from "react"; import { useRoutes } from "raviger"; + import SessionExpired from "@/components/ErrorPages/SessionExpired"; import InvalidReset from "@/components/ErrorPages/InvalidReset"; import ResetPassword from "@/components/Auth/ResetPassword"; import Login from "@/components/Auth/Login";
Line range hint
10-24
: Add proper typing for route parameters.The route parameter typing can be improved by replacing
any
with a proper type definition.+type ResetPasswordParams = { + token: string; +}; + const routes = { "/": () => <Login />, "/login": () => <Login />, "/forgot-password": () => <Login forgot={true} />, - "/password_reset/:token": ({ token }: any) => <ResetPassword token={token} />, + "/password_reset/:token": ({ token }: ResetPasswordParams) => <ResetPassword token={token} />, "/session-expired": () => <SessionExpired />, "/licenses": () => <LicensesPage />, "/invalid-reset": () => <InvalidReset />, };src/PluginEngine.tsx (2)
15-15
: LGTM! Consider adding a delay to prevent flash.The Loading component integration looks good and aligns with the code splitting improvements.
Consider adding a minimal delay before showing the loading state to prevent flash of loading content for fast connections:
- <Suspense fallback={<Loading />}> + <Suspense fallback={<DelayedLoading delayMs={300} />}>
Line range hint
9-27
: Address chart component reload issue mentioned in PR comments.While this component properly implements Suspense, the chart component reload issue mentioned in PR comments might require similar treatment in child components.
Consider these recommendations:
- Ensure child plugin components (especially charts) are wrapped in their own Suspense boundaries.
- Implement proper code splitting at the plugin level using
React.lazy
.- Consider using a loading strategy that prevents full page reloads:
// Example plugin loading strategy const ChartPlugin = React.lazy(() => import('./plugins/ChartPlugin')); function PluginWrapper() { return ( <Suspense fallback={<Loading />}> <ChartPlugin /> </Suspense> ); }src/components/Facility/Consultations/components/StackedLinePlot.tsx (1)
Line range hint
7-88
: Add TypeScript interfaces for improved type safety.The component uses 'any' types which makes it harder to maintain and more prone to runtime errors. Consider adding proper TypeScript interfaces.
+interface DataPoint { + name: string; + data: number[]; +} + +interface StackedLinePlotProps { + title: string; + xData: string[]; + yData: DataPoint[]; +} + -export const StackedLinePlot = (props: any) => { +export const StackedLinePlot = (props: StackedLinePlotProps) => { const { title, xData, yData } = props; - const series = yData.map((x: any) => ({ + const series = yData.map((x) => ({ name: x.name, type: "line", stack: x.name, data: x.data.map((d: any) => Math.round(d * 100) / 100), connectNulls: true, }));Also consider moving the
COLORS
array to a shared constants file if it's used by other chart components.src/components/Facility/Consultations/components/LinePlot.tsx (1)
214-217
: Consider performance and type safety improvements.The ReactEcharts implementation looks good, but could benefit from some optimizations:
- The
lazyUpdate
prop is correctly used for waveform types, but consider memoizing the options object to prevent unnecessary re-renders- The component lacks proper TypeScript types
Consider applying these improvements:
+ import { useMemo } from "react"; + import type { EChartsOption } from "echarts"; - const generalOptions: any = { + const generalOptions: EChartsOption = { return ( <> {yData.map((value: any, idx: any) => ( <span id={`${title}-${idx + 1}`} className="sr-only px-1"> {value ? properRoundOf(value) : "NA"} </span> ))} + {useMemo( + () => ( <ReactEcharts option={generalOptions} className={props.classes} lazyUpdate={props.type === "WAVEFORM"} /> + ), + [generalOptions, props.classes, props.type] + )} </> );src/components/Resource/ResourceBoard.tsx (1)
Line range hint
89-146
: Consider optimizing export functionality and filter formatting.A few optimization suggestions:
- Consider memoizing the formatted filters to avoid recalculation
- The export functionality could be extracted and lazy loaded
Example optimization:
+ const memoizedFilters = useMemo( + () => formatFilter(qParams), + [qParams] + ); - const appliedFilters = formatFilter(qParams); + const appliedFilters = memoizedFilters;src/components/Auth/ResetPassword.tsx (3)
Line range hint
124-139
: Move password validation messages to i18n.The password validation rule messages are hardcoded in English. According to the PR objectives, UI text should be managed in I18n files.
Consider updating the validateRule calls to use translated messages:
{validateRule( - form.password?.length >= 8, - "Password should be atleast 8 characters long", + form.password?.length >= 8, + t("password_min_length"), )}
Line range hint
17-24
: Consider performance optimizations.A few suggestions to improve component performance:
- Memoize validation rules to prevent unnecessary re-renders
- Extract token validation logic into a custom hook
Example implementation:
const useResetToken = (token: string) => { useEffect(() => { const checkResetToken = async () => { const { res } = await request(routes.checkResetToken, { body: { token }, }); if (!res || !res.ok) { navigate("/invalid-reset"); } }; if (token) { checkResetToken(); } else { navigate("/invalid-reset"); } }, [token]); };
Line range hint
1-11
: Consider lazy loading heavy dependencies.To align with the PR's code splitting objectives, consider lazy loading heavy dependencies:
- The translation setup could be loaded dynamically
- Common components like ButtonV2 could be split into a separate chunk
Example implementation:
const { useTranslation } = await import('react-i18next'); const { Cancel, Submit } = await import('@/components/Common/components/ButtonV2');This would help reduce the initial bundle size and improve the component's load time.
package.json (1)
132-132
: LGTM! Switching to marked for markdown processing.The addition of
marked
and removal ofreact-markdown
+rehype-raw
aligns with the PR objective of pre-rendering the markdown banner.Consider these architectural benefits of the change:
- Smaller bundle size:
marked
is lighter than thereact-markdown
+rehype-raw
combination- Build-time processing: Pre-rendering markdown at build time reduces runtime overhead
- Better performance: No need to ship markdown processing to the client
src/components/Shifting/ShiftingBoard.tsx (1)
28-31
: Consider additional performance optimizations.While the lazy loading implementation is good, consider these additional optimizations:
- Memoize the board sections configuration to prevent unnecessary recalculations
- Consider implementing virtual scrolling if dealing with large datasets
Example memoization implementation:
+ import { lazy, Suspense, useState, useMemo } from "react"; // ... in the component ... + const boardSections = useMemo( + () => + boardFilter.map((board) => ({ + id: board.text, + title: ( + <h3 className="flex h-8 items-center text-xs"> + {board.label || board.text}{" "} + <ExportButton + action={async () => { + const { data } = await request(routes.downloadShiftRequests, { + query: { + ...formatFilter({ ...qParams, status: board.text }), + csv: true, + }, + }); + return data ?? null; + }} + filenamePrefix={`shift_requests_${board.label || board.text}`} + /> + </h3> + ), + fetchOptions: (id) => ({ + route: routes.listShiftRequests, + options: { + query: formatFilter({ + ...qParams, + status: id, + }), + }, + }), + })), + [boardFilter, qParams] + ); // Then use boardSections in KanbanBoard <KanbanBoard<ShiftingModel> title={<BadgesList {...{ qParams, FilterBadges }} />} - sections={boardFilter.map((board) => ({ - // ... board configuration - }))} + sections={boardSections} // ... other props />src/components/Common/FilePreviewDialog.tsx (1)
149-152
: Consider resetting state when switching files.The page and numPages state is only reset on document load success. Consider also resetting these values when switching between files to prevent potential stale state issues.
onDocumentLoadSuccess={(numPages: number) => { setPage(1); setNumPages(numPages); }} +fileUrl={fileUrl} +key={fileUrl} // Force remount when URL changessrc/components/Auth/Login.tsx (1)
Line range hint
18-418
: Consider splitting into smaller componentsThe Login component is quite large and handles multiple responsibilities (login and forgot password flows). Consider breaking it down into smaller, more focused components for better maintainability and code splitting benefits.
Suggested structure:
LoginForm
ForgotPasswordForm
LoginHeader
LoginFooter
Would you like me to provide a detailed refactoring plan?
🧰 Tools
🪛 Biome
[error] 196-196: Avoid passing content using the dangerouslySetInnerHTML prop.
Setting content using code can expose users to cross-site scripting (XSS) attacks
(lint/security/noDangerouslySetInnerHtml)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
⛔ Files ignored due to path filters (1)
package-lock.json
is excluded by!**/package-lock.json
📒 Files selected for processing (19)
.envrc
(0 hunks)package.json
(3 hunks)src/Locale/TRANSLATION_CONTRIBUTION.md
(0 hunks)src/Locale/update_locale.js
(0 hunks)src/PluginEngine.tsx
(1 hunks)src/Routers/SessionRouter.tsx
(1 hunks)src/components/Auth/Login.tsx
(5 hunks)src/components/Auth/ResetPassword.tsx
(2 hunks)src/components/Auth/index.tsx
(0 hunks)src/components/Common/FilePreviewDialog.tsx
(2 hunks)src/components/Facility/Consultations/components/LinePlot.tsx
(2 hunks)src/components/Facility/Consultations/components/ReactEcharts.tsx
(1 hunks)src/components/Facility/Consultations/components/StackedLinePlot.tsx
(2 hunks)src/components/Kanban/Board.tsx
(1 hunks)src/components/Resource/ResourceBoard.tsx
(2 hunks)src/components/Shifting/ShiftingBoard.tsx
(1 hunks)src/globals.d.ts
(1 hunks)src/i18n.ts
(2 hunks)vite.config.mts
(4 hunks)
💤 Files with no reviewable changes (4)
- .envrc
- src/Locale/TRANSLATION_CONTRIBUTION.md
- src/Locale/update_locale.js
- src/components/Auth/index.tsx
✅ Files skipped from review due to trivial changes (1)
- src/components/Kanban/Board.tsx
🧰 Additional context used
🪛 Biome
src/components/Auth/Login.tsx
[error] 196-196: Avoid passing content using the dangerouslySetInnerHTML prop.
Setting content using code can expose users to cross-site scripting (XSS) attacks
(lint/security/noDangerouslySetInnerHtml)
🔇 Additional comments (19)
src/globals.d.ts (1)
1-1
: LGTM! Type declaration looks good.
The type declaration follows proper naming conventions and provides appropriate type safety for the build-time constant.
Let's verify the HTML sanitization implementation:
Additionally, please verify that the Login component properly handles the undefined case to prevent rendering issues. I can help generate the verification code if needed.
✅ Verification successful
Security review for HTML content handling
Based on the verification results:
- The markdown-to-HTML conversion uses marked v14.1.3, which has built-in XSS protection by default
- The HTML content is generated at build time, not runtime, limiting attack vectors
- The Login component properly handles the undefined case with conditional rendering
- No additional sanitization is needed as marked's default settings are secure
However, for better security documentation:
- Consider adding a comment in vite.config.mts explaining that marked's default sanitization is being relied upon
- Document that
__CUSTOM_DESCRIPTION_HTML__
is pre-rendered and safe for dangerouslySetInnerHTML
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check if marked library configuration includes sanitization
# Expected: Find marked configuration with sanitize option enabled
# Search for marked configuration in vite config
rg -A 5 "marked\.(parse|setOptions)"
# Search for any custom sanitization implementation
rg -g "!node_modules" -A 5 "sanitize.*html"
Length of output: 264
Script:
#!/bin/bash
# Let's verify the Login component's usage of __CUSTOM_DESCRIPTION_HTML__
# and check for any sanitization or error handling
# Search for Login component's usage of __CUSTOM_DESCRIPTION_HTML__
rg -A 5 "__CUSTOM_DESCRIPTION_HTML__"
# Search for marked configuration in vite config with more context
rg -B 5 -A 10 "marked\.parse"
# Look for any DOMPurify or other sanitization utilities
rg -g "!node_modules" "(DOMPurify|sanitize|xss)"
Length of output: 16909
Script:
#!/bin/bash
# Let's check the marked library configuration and version in package files
# to verify if it has built-in sanitization
# Check package.json for marked version
rg -A 5 '"marked":' package.json
# Look for any marked configuration in the entire codebase
rg -g "!{node_modules,dist,build}/*" "marked\.(setOptions|defaults)"
# Check if there's any conditional rendering or error handling for __CUSTOM_DESCRIPTION_HTML__
rg -B 5 -A 5 "customDescriptionHtml" "src/components/Auth/Login.tsx"
Length of output: 1041
src/i18n.ts (1)
4-4
: LGTM: Efficient implementation of lazy-loaded translations.
The switch to i18next-xhr-backend
with dynamic loading aligns well with the PR's code splitting objectives. This change will reduce the initial bundle size by loading translations on demand.
Let's verify the locale files are correctly placed:
Also applies to: 16-22
✅ Verification successful
Locale files are correctly placed and match the supported languages configuration
The verification confirms that translation files exist at the expected location (public/locale/
) with JSON files for all supported languages (en, hi, kn, ml, mr, ta). This matches the XHR backend's loadPath configuration and ensures proper lazy loading functionality.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify locale files exist in the correct location
# Expected: Find JSON files for each supported language
# Check if locale directory exists and contains language files
fd -e json . "public/locale"
Length of output: 160
src/Routers/SessionRouter.tsx (1)
8-8
:
Add Suspense boundary for lazy-loaded component.
The LicensesPage is correctly set up for lazy loading, but it needs to be wrapped in a Suspense boundary to handle the loading state gracefully. This would also address the page reload issue mentioned in the PR comments.
Let's verify if other lazy-loaded components in the codebase use Suspense:
Add Suspense boundary like this:
const LicensesPage = lazy(() => import("@/components/Licenses/LicensesPage"));
+
+const SuspensedLicensesPage = () => (
+ <Suspense fallback={<div>Loading...</div>}>
+ <LicensesPage />
+ </Suspense>
+);
const routes = {
"/": () => <Login />,
"/login": () => <Login />,
"/forgot-password": () => <Login forgot={true} />,
"/password_reset/:token": ({ token }: any) => <ResetPassword token={token} />,
"/session-expired": () => <SessionExpired />,
- "/licenses": () => <LicensesPage />,
+ "/licenses": () => <SuspensedLicensesPage />,
"/invalid-reset": () => <InvalidReset />,
};
src/components/Facility/Consultations/components/ReactEcharts.tsx (1)
1-41
: Verify lazy loading implementation in parent components.
Since this component is part of the code splitting improvements, let's verify its integration in parent components.
✅ Verification successful
ReactEcharts is properly implemented with lazy loading
The component is correctly used with React.lazy() in both parent components:
src/components/Facility/Consultations/components/LinePlot.tsx
src/components/Facility/Consultations/components/StackedLinePlot.tsx
Both components implement dynamic imports using the proper syntax:
const ReactEcharts = lazy(() => import("@/components/Facility/Consultations/components/ReactEcharts"))
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check how this component is imported in parent components
# Expect to see dynamic imports for proper code splitting
echo "Checking for dynamic imports of ReactEcharts:"
rg -l "React\.lazy.*ReactEcharts" src/
echo "\nChecking for direct imports that should be lazy:"
rg -l "from ['\"].*ReactEcharts['\"]" src/
Length of output: 301
Script:
#!/bin/bash
# Let's try a different approach to find imports and usage of ReactEcharts
# First, let's find all files that contain 'ReactEcharts'
echo "Files containing ReactEcharts:"
rg -l "ReactEcharts" src/
# Then, let's see the actual import statements and usage context
echo -e "\nImport patterns and usage context:"
rg -B2 -A2 "ReactEcharts" src/
# Let's also check for any lazy loading patterns in the consultation components
echo -e "\nChecking lazy loading patterns in consultation components:"
rg "React\.lazy|import\(" "src/components/Facility/Consultations/"
Length of output: 4265
src/PluginEngine.tsx (1)
7-7
: Verify Loading component implementation for code splitting.
Since this PR focuses on code splitting improvements, let's ensure the Loading component is properly optimized.
src/components/Facility/Consultations/components/StackedLinePlot.tsx (1)
3-5
: Verify the ReactEcharts implementation.
Let's ensure the ReactEcharts component is properly implemented and exists in the specified path.
✅ Verification successful
Let me gather more information about the ReactEcharts implementation.
ReactEcharts component implementation is properly verified
The ReactEcharts component is correctly implemented with:
- Proper imports from
echarts-for-react
andecharts/core
- Required chart components and renderers registration
- Correct wrapper implementation that passes props to
ReactEchartsCore
- Consistent usage across LinePlot and StackedLinePlot components
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify ReactEcharts component implementation
# Check if the component file exists and contains necessary echarts setup
echo "Checking ReactEcharts component implementation..."
rg -l "ReactEchartsCore|echarts" "src/components/Facility/Consultations/components/ReactEcharts"
# Check for other components using ReactEcharts to ensure consistent implementation
echo "Checking other components using ReactEcharts..."
rg -l "import.*ReactEcharts" "src/components"
Length of output: 655
Script:
#!/bin/bash
# Check the actual implementation of ReactEcharts.tsx
echo "Content of ReactEcharts.tsx:"
cat src/components/Facility/Consultations/components/ReactEcharts.tsx
# Check how it's being used in LinePlot and StackedLinePlot
echo -e "\nUsage in LinePlot.tsx:"
rg -A 5 "ReactEcharts" src/components/Facility/Consultations/components/LinePlot.tsx
echo -e "\nUsage in StackedLinePlot.tsx:"
rg -A 5 "ReactEcharts" src/components/Facility/Consultations/components/StackedLinePlot.tsx
Length of output: 2014
src/components/Facility/Consultations/components/LinePlot.tsx (1)
1-7
: Ensure proper Suspense boundaries are in place for lazy-loaded chart.
The implementation of lazy loading aligns with the code splitting objectives. However, to address the page reload issue mentioned in the PR comments, ensure that:
- A Suspense boundary is properly set up in the parent component
- An error boundary is implemented to handle potential loading failures
Consider implementing a dedicated error boundary for the chart component to gracefully handle loading failures:
// ChartErrorBoundary.tsx
class ChartErrorBoundary extends React.Component {
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
// Log error to monitoring service
console.error('Chart failed to load:', error);
}
render() {
return this.props.children;
}
}
src/components/Resource/ResourceBoard.tsx (2)
Line range hint 1-20
: LGTM! Imports are well organized.
The imports are logically grouped and the addition of lazy
import supports the code splitting objective.
21-24
: Verify lazy loading implementation across the codebase.
Let's verify the consistency of lazy loading implementation for components mentioned in the PR objectives.
✅ Verification successful
Based on the shell script results, I can now generate the final response:
Lazy loading implementation is consistent and properly configured
The codebase shows a consistent pattern of lazy loading implementation with proper Suspense boundaries:
- Lazy loaded components are correctly imported using
lazy(() => import())
- All lazy components are wrapped with
Suspense
fallbacks showing loading states - The new
KanbanBoard
lazy loading follows the same pattern as other components
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check lazy loading implementation patterns across the codebase
# Test 1: Find all lazy-loaded components
echo "Checking lazy loading patterns:"
rg "lazy\(\s*\(\)\s*=>\s*import\(" -A 2
# Test 2: Check for Suspense usage with lazy components
echo -e "\nChecking Suspense boundaries:"
rg "<Suspense" -A 3
# Test 3: Specifically check Chart component implementation
echo -e "\nChecking Chart component implementation:"
rg "import.*Chart" -A 2
Length of output: 4476
src/components/Auth/ResetPassword.tsx (1)
13-13
: LGTM! Verify import statements in dependent files.
The change to default export aligns well with code splitting objectives. Default exports are typically better for code splitting optimization.
Also applies to: 173-173
✅ Verification successful
Import statement is already correctly updated to use default import
The import statement in src/Routers/SessionRouter.tsx
is already using the correct default import syntax: import ResetPassword from "@/components/Auth/ResetPassword"
. No further changes are needed as the codebase is consistent with the export change.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for existing imports of ResetPassword to ensure they're updated
rg "import.*ResetPassword.*from.*Auth/ResetPassword"
Length of output: 145
package.json (1)
83-83
: LGTM! Addition of i18next-xhr-backend for lazy loading locales.
The addition aligns with the PR objective of implementing lazy loading for locales.
Let's verify the i18next setup:
✅ Verification successful
LGTM! i18next-xhr-backend is properly configured for lazy loading locales
The i18next configuration in src/i18n.ts
correctly implements the XHR backend with proper initialization:
- Backend is configured to load locale files from
/locale/{{lng}}.json
- No static imports of locale JSON files found in the codebase
- Proper integration with react-i18next and language detection
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify i18next configuration and usage
# Expected: Find backend configuration in i18n setup and dynamic imports
# Check i18next configuration
rg -l "i18next.*backend" --type ts
# Look for static imports that should be removed
rg "import.*\.json['\"]\s*$" --type ts
Length of output: 91
Script:
#!/bin/bash
# Let's examine the i18n configuration file content
cat src/i18n.ts
# Also check for any dynamic imports of locale files
rg "import\(.*\.json.*\)" --type ts
# Look for i18next initialization and backend usage
rg "i18next" -A 5 --type ts
Length of output: 96761
src/components/Shifting/ShiftingBoard.tsx (2)
14-14
: LGTM: Import statement updated correctly.
The addition of lazy
import from React is appropriate for implementing code splitting.
28-31
: LGTM: Lazy loading implementation is correct.
The implementation follows React's best practices for code splitting:
- Proper type import and assertion
- Correct usage of lazy loading
src/components/Common/FilePreviewDialog.tsx (2)
4-11
: LGTM: Clean import organization.
The addition of lazy
and Suspense
imports is well-organized within the existing React imports.
146-155
: LGTM: Proper Suspense implementation.
The Suspense wrapper with CircularProgress fallback is correctly implemented, which should help prevent the page reload issues mentioned in the PR.
src/components/Auth/Login.tsx (3)
16-18
: LGTM: Import and component declaration changes
The changes to use the Link
component from raviger and switch to a default export align with modern React practices and the PR's code splitting objectives.
419-420
: LGTM: Clean export syntax
The change to a default export is clean and follows modern React practices.
249-256
: Verify Link behavior with target="_blank"
While the change to use the Link
component is good, please verify that the target="_blank"
attribute works as expected with the raviger Link
component.
✅ Verification successful
Based on the gathered information, I can now generate the final response:
Link component with target="_blank" is correctly implemented
The usage of target="_blank"
with rel="noopener noreferrer"
is a consistent pattern throughout the codebase for external links, including in the Login component. The raviger Link component (v4.1.2) supports the target
prop as shown in ButtonV2.tsx implementation, making this a safe and correct implementation.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check if raviger Link supports target="_blank"
rg -A 2 'target="_blank".*Link'
Length of output: 33
Script:
#!/bin/bash
# Let's check the Link component usage in the codebase and raviger documentation imports
rg -A 2 'import.*raviger'
# Also check for any existing Link components with target attribute
rg -B 2 -A 2 '<Link.*target'
Length of output: 21689
Script:
#!/bin/bash
# Let's check the raviger Link component source or documentation in package.json and README
cat package.json | grep raviger
cat README.md
# Also check for any external link handling patterns in the codebase
rg -B 2 -A 2 'rel="noopener noreferrer"'
Length of output: 10972
vite.config.mts (1)
63-63
: Enhance code clarity with explicit type annotation
Adding the explicit type annotation Set<string>
to the dependencies
variable enhances type safety and improves code readability.
src/components/Facility/Consultations/components/ReactEcharts.tsx
Outdated
Show resolved
Hide resolved
src/components/Facility/Consultations/components/ReactEcharts.tsx
Outdated
Show resolved
Hide resolved
src/components/Facility/Consultations/components/ReactEcharts.tsx
Outdated
Show resolved
Hide resolved
src/components/Facility/Consultations/components/ReactEcharts.tsx
Outdated
Show resolved
Hide resolved
src/components/Facility/Consultations/components/StackedLinePlot.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this 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
🧹 Outside diff range and nitpick comments (14)
src/Routers/SessionRouter.tsx (2)
1-8
: Consider lazy loading additional routes for better performance.Since this PR focuses on code splitting improvements, consider lazy loading other non-critical routes like InvalidReset and SessionExpired to further reduce the initial bundle size.
Example implementation:
const InvalidReset = lazy(() => import("@/components/ErrorPages/InvalidReset")); const SessionExpired = lazy(() => import("@/components/ErrorPages/SessionExpired"));
Line range hint
14-14
: Replace 'any' type with a specific type for token parameter.Using
any
type reduces type safety. Consider defining a proper type for the route parameters.- "/password_reset/:token": ({ token }: any) => <ResetPassword token={token} />, + "/password_reset/:token": ({ token }: { token: string }) => <ResetPassword token={token} />,src/components/Facility/Consultations/components/ReactEcharts.tsx (1)
15-17
: Consider adding WebGL renderer for better performance.Currently, only the Canvas renderer is used. For complex charts or when dealing with large datasets, the WebGL renderer could provide better performance.
import * as echarts from "echarts/core"; - import { CanvasRenderer } from "echarts/renderers"; + import { CanvasRenderer, SVGRenderer } from "echarts/renderers";src/components/Facility/Consultations/components/StackedLinePlot.tsx (2)
Line range hint
16-21
: Use more precise decimal rounding method.Using
Math.round
with multiplication/division for decimal places can lead to floating-point precision issues. Consider usingtoFixed()
or a dedicated number formatting library.const series = yData.map((x: any) => ({ name: x.name, type: "line", stack: x.name, - data: x.data.map((d: any) => Math.round(d * 100) / 100), + data: x.data.map((d: number) => Number(d.toFixed(2))), connectNulls: true, }));
Line range hint
23-87
: Consider extracting chart configuration.The chart configuration object is quite large and contains multiple static values. Consider extracting it to a separate configuration file for better maintainability.
Create a new file
chartConfig.ts
and move the configuration there:// src/components/Facility/Consultations/components/chartConfig.ts export const COLORS = ["#B13F3C", "#2F8B35", "#44327A", "#B19D3C"]; export const createChartOptions = (title: string, xData: string[], series: any[]) => ({ grid: { left: "20px", right: "30px", containLabel: true, }, color: COLORS, // ... rest of the configuration });src/components/Facility/Consultations/components/LinePlot.tsx (1)
Line range hint
214-218
: Add TypeScript interfaces for better type safety.The component uses 'any' types extensively and lacks proper type definitions, which could lead to runtime errors.
Add proper TypeScript interfaces:
interface ChartOptions { grid: { top: string; left: string; right: string; containLabel: boolean; }; title: { text: string; textStyle: { fontSize: number; rich: Record<string, unknown>; }; }; // ... other option types } interface LinePlotProps { title: string; name: string; xData: string[]; yData: number[]; low?: number | null; high?: number | null; defaultSpace?: boolean; type?: 'WAVEFORM'; classes?: string; color?: string; yStart?: number; yEnd?: number; } export const LinePlot = (props: LinePlotProps) => { // ... rest of the implementation }Also, consider adding prop validation for the ReactEcharts component:
<ReactEcharts - option={generalOptions} + option={generalOptions as ChartOptions} className={props.classes} lazyUpdate={props.type === "WAVEFORM"} />src/components/Resource/ResourceBoard.tsx (2)
Line range hint
108-124
: Consider extracting export functionality to a reusable component.The export functionality is duplicated in the page title and each board section. This could be extracted into a reusable component to improve maintainability.
Example refactor:
type ExportResourceProps = { filters: typeof appliedFilters; status?: string; filenamePrefix: string; }; const ExportResource = ({ filters, status, filenamePrefix }: ExportResourceProps) => ( <ExportButton action={async () => { const { data } = await request(routes.downloadResourceRequests, { query: { ...filters, ...(status && { status }), csv: true, }, }); return data ?? null; }} filenamePrefix={filenamePrefix} /> );
Line range hint
37-40
: Consider using React Query for state management.The current implementation uses local state for filter management. Consider using React Query to handle the state and caching of filtered data, which could improve performance and reduce complexity.
This would also help with the initial loading issue by providing better loading states and cache management.
src/components/Auth/ResetPassword.tsx (1)
Line range hint
13-171
: Consider security improvements for token handling.While the password validation and error handling are well implemented, storing sensitive tokens in localStorage could be improved. Consider:
- Using more secure storage methods for the reset token
- Adding token expiration checks
- Implementing rate limiting for reset attempts
Would you like me to provide specific implementation suggestions for these security improvements?
src/components/Common/FilePreviewDialog.tsx (2)
16-16
: Consider adding error boundary for lazy-loaded component.While the lazy loading implementation is correct, adding an error boundary would help handle potential loading failures gracefully.
Example implementation:
const PDFViewerErrorBoundary = ({ children }: { children: React.ReactNode }) => { return ( <ErrorBoundary fallback={ <div className="flex flex-col items-center justify-center p-4"> <CareIcon icon="l-exclamation-triangle" className="mb-2 text-error-500" /> <p>{t("failed_to_load_pdf_viewer")}</p> </div> } > {children} </ErrorBoundary> ); };
146-155
: Enhance loading state feedback.While the implementation correctly addresses the page reload issue mentioned in PR comments, consider making the loading state more informative.
Replace the simple CircularProgress with a more descriptive loading state:
-<Suspense fallback={<CircularProgress />}> +<Suspense + fallback={ + <div className="flex flex-col items-center justify-center p-4"> + <CircularProgress /> + <p className="mt-2 text-secondary-700">{t("loading_pdf_viewer")}</p> + </div> + } +>src/components/Auth/Login.tsx (1)
18-18
: LGTM! Consider adding JSDoc documentation.The switch to default export aligns with the code splitting objectives. Consider adding JSDoc documentation to describe the component's purpose and props.
+/** + * Login component handling user authentication and password recovery + * @param {Object} props - Component props + * @param {boolean} [props.forgot] - Whether to show forgot password form + */ const Login = (props: { forgot?: boolean }) => {Also applies to: 419-420
vite.config.mts (2)
205-205
: Fix typo in variable name 'manulaVendorChunks' to 'manualVendorChunks'There's a typo in the variable name
manulaVendorChunks
on line 205. Correcting it tomanualVendorChunks
will improve code readability and prevent potential confusion.Apply this diff to fix the typo:
- const manulaVendorChunks = /tiny-invariant/; + const manualVendorChunks = /tiny-invariant/;
174-224
: Consider adding documentation to the 'manualChunks' function for clarityThe
manualChunks
function contains complex logic involving recursive checks and dynamic chunk naming. Adding comments or documentation explaining the purpose and flow of this function can enhance readability and maintainability.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
⛔ Files ignored due to path filters (1)
package-lock.json
is excluded by!**/package-lock.json
📒 Files selected for processing (19)
.envrc
(0 hunks)package.json
(3 hunks)src/Locale/TRANSLATION_CONTRIBUTION.md
(0 hunks)src/Locale/update_locale.js
(0 hunks)src/PluginEngine.tsx
(1 hunks)src/Routers/SessionRouter.tsx
(1 hunks)src/components/Auth/Login.tsx
(5 hunks)src/components/Auth/ResetPassword.tsx
(2 hunks)src/components/Auth/index.tsx
(0 hunks)src/components/Common/FilePreviewDialog.tsx
(2 hunks)src/components/Facility/Consultations/components/LinePlot.tsx
(2 hunks)src/components/Facility/Consultations/components/ReactEcharts.tsx
(1 hunks)src/components/Facility/Consultations/components/StackedLinePlot.tsx
(2 hunks)src/components/Kanban/Board.tsx
(1 hunks)src/components/Resource/ResourceBoard.tsx
(2 hunks)src/components/Shifting/ShiftingBoard.tsx
(1 hunks)src/globals.d.ts
(1 hunks)src/i18n.ts
(2 hunks)vite.config.mts
(4 hunks)
💤 Files with no reviewable changes (4)
- .envrc
- src/Locale/TRANSLATION_CONTRIBUTION.md
- src/Locale/update_locale.js
- src/components/Auth/index.tsx
✅ Files skipped from review due to trivial changes (2)
- src/components/Kanban/Board.tsx
- src/globals.d.ts
🧰 Additional context used
🪛 Biome
src/components/Auth/Login.tsx
[error] 196-196: Avoid passing content using the dangerouslySetInnerHTML prop.
Setting content using code can expose users to cross-site scripting (XSS) attacks
(lint/security/noDangerouslySetInnerHtml)
🔇 Additional comments (16)
src/Routers/SessionRouter.tsx (1)
1-8
: Verify consistent Suspense usage across lazy-loaded components.
Let's check if other lazy-loaded components in the codebase have proper Suspense wrappers to avoid similar reload issues.
✅ Verification successful
Let me gather more context about the usage of these lazy-loaded components to provide a more complete verification.
Let me check one more thing to verify how these components are wrapped at a higher level, as Suspense might be implemented in parent components.
Lazy-loaded components are properly handled by root Suspense wrapper
The verification shows that all lazy-loaded components, including LicensesPage
, are properly wrapped by a root-level Suspense component in App.tsx
. This root Suspense wrapper with a Loading fallback handles the suspension of all lazy-loaded components in the application, making individual Suspense wrappers unnecessary.
- The root
<Suspense fallback={<Loading />}>
in App.tsx covers all routes and components - FilePreviewDialog, InvestigationsPrintPreview, and LicensesPage are all under this root Suspense boundary
- The implementation follows React's recommended pattern for app-wide Suspense boundaries
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check for lazy imports without corresponding Suspense wrappers
# This helps identify potential sources of the reported page reload issues
echo "Searching for lazy imports..."
rg "lazy\(\s*\(\s*\)\s*=>\s*import" -l | while read -r file; do
echo "Checking $file for Suspense usage..."
if ! rg "Suspense" "$file" > /dev/null; then
echo "WARNING: $file contains lazy imports but no Suspense wrapper"
fi
done
echo "Checking for proper lazy/Suspense patterns..."
ast-grep --pattern 'lazy(() => import($_))' --lang typescript
Length of output: 1459
Script:
#!/bin/bash
# Check how these lazy components are rendered in their parent components
echo "Checking FilePreviewDialog usage..."
rg -A 5 "FilePreviewDialog"
echo "\nChecking InvestigationsPrintPreview usage..."
rg -A 5 "InvestigationsPrintPreview"
echo "\nChecking LicensesPage usage..."
rg -A 5 "LicensesPage"
# Check if Suspense is imported but used elsewhere in these files
echo "\nChecking for Suspense imports..."
rg "import.*Suspense" src/components/Facility/Investigations/InvestigationsPrintPreview.tsx src/Routers/SessionRouter.tsx
Length of output: 3807
Script:
#!/bin/bash
# Check for Suspense usage in parent components and app root
echo "Checking for Suspense in AppRouter..."
rg -B 3 -A 7 "Suspense" src/Routers/AppRouter.tsx
echo "\nChecking for Suspense in root app component..."
rg -B 3 -A 7 "Suspense" src/App.tsx
echo "\nChecking ConsultationRoutes implementation..."
cat src/Routers/routes/ConsultationRoutes.tsx
Length of output: 7394
src/components/Facility/Consultations/components/ReactEcharts.tsx (2)
1-41
: Consider implementing loading optimization strategies.
Based on the PR comments about page reload issues when charts are opened for the first time, consider implementing the following optimizations:
- Wrap the component with Suspense
- Implement progressive loading of chart data
- Add skeleton loading states
Let's check the current bundle size impact:
36-41
:
Avoid props mutation and add error handling.
The current implementation has several issues:
- Directly mutating props is an anti-pattern in React
- Missing error boundaries and loading states that could help with the reported page reload issues
Here's a suggested improvement:
-export default function ReactEcharts(props: ReactEchartsProps) {
+export default function ReactEcharts({ echarts: providedEcharts, ...rest }: ReactEchartsProps) {
- if (!props.echarts) {
- props.echarts = echarts;
- }
- return <ReactEchartsCore {...props} />;
+ return (
+ <ReactEchartsCore
+ echarts={providedEcharts || echarts}
+ {...rest}
+ />
+ );
+}
Let's verify if this component is wrapped in Suspense in its usage:
src/PluginEngine.tsx (2)
7-7
: LGTM! Import of dedicated Loading component.
The addition of a dedicated Loading component aligns well with the PR's objective of improving loading experiences across the application.
15-15
: Verify loading behavior with the new Loading component.
The change to use a dedicated Loading component is good for consistency. However, given the reported performance issues with lazy-loaded components, let's verify the loading behavior.
Let's check for other Suspense usage patterns in the codebase to ensure consistent loading behavior:
✅ Verification successful
Loading component implementation and usage is consistent across the codebase
The verification shows that:
- The Loading component is properly implemented in
src/components/Common/Loading.tsx
with a spinner animation - Suspense with Loading fallback is consistently used in key places:
- Main App wrapper in
src/App.tsx
- Plugin loading in
src/PluginEngine.tsx
- Main App wrapper in
- Other lazy-loaded components use appropriate loading indicators (e.g., CircularProgress for PDF viewer)
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check Suspense usage patterns across the codebase
# Expected: Find other components using Suspense to verify loading behavior consistency
# Search for Suspense usage patterns
echo "Checking Suspense usage patterns:"
rg -A 3 "Suspense.*fallback" --type tsx --type jsx
# Search for Loading component usage
echo -e "\nChecking Loading component usage:"
rg -A 3 "import.*Loading.*from.*Common/Loading" --type tsx --type jsx
Length of output: 340
Script:
#!/bin/bash
# Let's try a different approach using more generic file patterns and ast-grep
# Check Suspense usage patterns
echo "Checking Suspense usage patterns:"
rg -t js -t jsx -t ts -t tsx "Suspense.*fallback" -A 3
# Check Loading component imports and usage
echo -e "\nChecking Loading component imports:"
rg -t js -t jsx -t ts -t tsx "import.*Loading" -A 1
# Check Loading component definition
echo -e "\nChecking Loading component definition:"
fd -e js -e jsx -e ts -e tsx -x ast-grep --pattern 'const Loading = $_'
Length of output: 1487
Script:
#!/bin/bash
# Let's try one more search to find all Suspense usages without the fallback filter
# and check for any other loading patterns
echo "Checking all Suspense usages:"
rg -g "*.{js,jsx,ts,tsx}" "<Suspense" -A 3
echo -e "\nChecking lazy imports:"
rg -g "*.{js,jsx,ts,tsx}" "lazy\(\s*\(\s*\)\s*=>" -A 1
Length of output: 1611
src/components/Facility/Consultations/components/StackedLinePlot.tsx (1)
1-5
:
Add Suspense wrapper to handle loading states.
The lazy loading implementation is good, but it needs to be wrapped in a Suspense component to handle the loading state gracefully. This should address the reported issue where the chart causes page reloads on first render.
Apply this change:
import { lazy } from "react";
+import { Suspense } from "react";
const ReactEcharts = lazy(
() => import("@/components/Facility/Consultations/components/ReactEcharts"),
);
+
+const LoadingFallback = () => (
+ <div className="h-64 w-full animate-pulse bg-gray-100" />
+);
And update the return statement to:
- return <ReactEcharts option={generalOptions} />;
+ return (
+ <Suspense fallback={<LoadingFallback />}>
+ <ReactEcharts option={generalOptions} />
+ </Suspense>
+ );
src/components/Facility/Consultations/components/LinePlot.tsx (1)
5-7
: Verify consistent lazy loading implementation across chart components.
Let's ensure all chart components are consistently implementing lazy loading to prevent similar performance issues.
✅ Verification successful
Lazy loading is consistently implemented for chart components
The verification shows that both chart components (LinePlot.tsx
and StackedLinePlot.tsx
) are already implementing lazy loading for the ReactEcharts
component consistently. Both use the same import path and lazy loading pattern:
const ReactEcharts = lazy(
() => import("@/components/Facility/Consultations/components/ReactEcharts"),
);
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for other chart component implementations
echo "Searching for other chart components..."
rg -l "ReactEchartsCore|ReactEcharts" --type tsx
# Search for lazy loading implementation
echo "\nChecking lazy loading implementation..."
rg "lazy\(" --type tsx -C 2
Length of output: 318
Script:
#!/bin/bash
# Search for other chart components implementations
echo "Searching for other chart components..."
rg -l "ReactEchartsCore|ReactEcharts"
# Search for lazy loading implementation
echo -e "\nChecking lazy loading implementation..."
rg "lazy\(" -C 2
Length of output: 3792
src/components/Resource/ResourceBoard.tsx (2)
Line range hint 1-20
: LGTM! Imports are well-organized.
The imports include all necessary dependencies for the component's functionality, including the lazy
import required for code splitting.
21-24
: LGTM! Proper implementation of lazy loading.
The KanbanBoard component is correctly set up for code splitting with proper type safety.
src/components/Auth/ResetPassword.tsx (1)
173-173
: LGTM! Export change aligns with code splitting objectives.
The change to default export supports the PR's goal of improving code splitting and enabling lazy loading of components.
Let's verify the import changes in other files:
✅ Verification successful
Export change is safe and correctly imported
The verification shows that ResetPassword
is already imported as a default import in SessionRouter.tsx
, which aligns with the export change. No named imports were found that would need updating.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for any remaining named imports of ResetPassword that need updating
rg "import.*\{.*ResetPassword.*\}.*from.*components/Auth"
Length of output: 59
Script:
#!/bin/bash
# Let's try a broader search for ResetPassword imports
rg "import.*ResetPassword.*from" -A 1
# Also search for any usage of ResetPassword component
rg "ResetPassword" --type tsx --type ts
Length of output: 273
package.json (1)
132-132
: Verify security implications of marked package.
The addition of marked for build-time markdown processing is a good performance optimization. However, since this is a healthcare application, we should verify the security implications.
Let's check for known vulnerabilities and sanitization options:
#!/bin/bash
# Check for known vulnerabilities in marked
npm audit marked
# Search for markdown sanitization configuration in the codebase
rg -A 5 "marked\.(parse|options)"
src/components/Shifting/ShiftingBoard.tsx (2)
14-14
: LGTM! Import statement is properly structured.
The combined import of lazy
and useState
from React is well-organized and supports the code splitting implementation.
28-31
:
Add Suspense boundary for lazy-loaded KanbanBoard component.
While the lazy loading implementation is correct and aligns with the code splitting objectives, the component needs to be wrapped in a Suspense boundary to handle the loading state properly. This might be related to the performance issue mentioned in the PR comments about page reloading when the chart component is first opened.
Let's verify if other components are properly wrapped with Suspense:
Apply this change to fix the loading behavior:
import { lazy, useState, Suspense } from "react";
// ... other imports
export default function BoardView() {
// ... other code
return (
<div className="flex-col px-2 pb-2">
{/* ... other JSX */}
+ <Suspense fallback={<div>Loading...</div>}>
<KanbanBoard<ShiftingModel>
title={<BadgesList {...{ qParams, FilterBadges }} />}
sections={boardFilter.map((board) => ({
// ... props
}))}
/>
+ </Suspense>
{/* ... other JSX */}
</div>
);
}
src/components/Common/FilePreviewDialog.tsx (2)
4-11
: LGTM: Import changes align with code splitting strategy.
The addition of lazy
and Suspense
imports from React is appropriate for implementing code splitting.
16-16
: Verify code splitting configuration.
Let's ensure the PDF viewer is properly configured for code splitting across the codebase.
Also applies to: 146-155
✅ Verification successful
Code splitting implementation is correctly configured
The lazy loading of PDFViewer is properly implemented:
- PDFViewer is only imported lazily in FilePreviewDialog.tsx
- No direct imports of PDFViewer exist elsewhere in the codebase
- The pattern follows the same lazy loading strategy used for other components (Loading, LicensesPage)
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify PDF viewer imports and bundle configuration
# Check for any direct imports of PDFViewer that should be lazy loaded
echo "Checking for direct PDFViewer imports..."
rg -l "import.*PDFViewer.*from.*@/components/Common/PDFViewer"
# Look for vite.config.ts to verify build configuration
echo "Checking build configuration..."
rg -l "build.*rollupOptions.*manualChunks" "vite.config.ts"
Length of output: 343
Script:
#!/bin/bash
# Let's try a broader search for build configuration and PDF viewer usage
# Check for any build config files
echo "Checking for build configuration files..."
fd -e ts -e js "vite.config" -e "rollup.config"
# Look for any PDF viewer imports with a broader pattern
echo "Checking for PDF viewer imports..."
rg "PDFViewer"
# Check for lazy loading patterns
echo "Checking for lazy loading patterns..."
rg "lazy\(\s*\(\s*\)\s*=>\s*import"
Length of output: 975
src/components/Auth/Login.tsx (1)
249-256
: Verify Link component behavior with target="_blank"
The change from anchor to Link component is good for client-side routing. However, let's verify that the raviger Link component properly handles target="_blank"
.
✅ Verification successful
Link component from raviger supports target="_blank" properly
Based on the codebase analysis:
- The ButtonV2 component demonstrates that raviger's Link component properly handles the
target
prop by passing it through to the rendered element - The codebase shows consistent patterns of using both Link and anchor tags with
target="_blank"
andrel="noopener noreferrer"
for external links - The implementation in Login.tsx follows the established pattern correctly
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for other usages of Link with target="_blank" to confirm behavior
rg -A 2 'Link.*target="_blank"'
Length of output: 33
Script:
#!/bin/bash
# Let's first check the imports to identify which Link component is being used
rg -p "import.*Link.*from" src/components/Auth/Login.tsx
# Let's also search for Link usage patterns in the codebase
rg -p "import.*Link.*from.*raviger"
# And check for any documentation or type definitions related to Link
fd -e ts -e tsx -e md -H "raviger" --exec grep -l "Link"
Length of output: 2313
Script:
#!/bin/bash
# Let's check how Link is used in other components, particularly looking for target="_blank" patterns
rg -p "Link.*target.*" src/components/
# Also check for any existing external link patterns in the codebase
rg -p '<Link[^>]*rel="noopener noreferrer"' src/components/
Length of output: 278
Script:
#!/bin/bash
# Let's examine the ButtonV2 component more closely since it shows Link with target support
cat src/components/Common/components/ButtonV2.tsx
# Also check if there are any regular anchor tags with target="_blank" in the codebase for comparison
rg -p '<a[^>]*target="_blank"' src/components/
Length of output: 7274
src/components/Facility/Consultations/components/ReactEcharts.tsx
Outdated
Show resolved
Hide resolved
src/components/Facility/Consultations/components/ReactEcharts.tsx
Outdated
Show resolved
Hide resolved
@coderabbitai review |
✅ Actions performedReview triggered.
|
@nihal467 now it only shows loading state for the specific component |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Outside diff range and nitpick comments (15)
src/i18n.ts (1)
24-25
: Well-implemented lazy loading strategy!The configuration effectively optimizes locale loading:
load: "currentOnly"
ensures only the needed language is loadedsupportedLngs
maintains consistency with available translationsThis implementation aligns well with the PR's code-splitting objectives and should help reduce the initial bundle size.
src/components/Facility/Consultations/components/ReactEcharts.tsx (1)
1-48
: Consider implementing a loading boundary strategy.To address the page reload issues mentioned in the PR comments, consider the following architectural recommendations:
- Implement a shared loading boundary at the page level to prevent multiple Suspense fallbacks from triggering simultaneously
- Consider using a loading skeleton that matches the chart's dimensions to prevent layout shifts
- Implement a caching strategy for frequently accessed chart data
Example implementation:
// pages/PatientConsultation.tsx const ChartLoadingBoundary: React.FC = ({ children }) => ( <Suspense fallback={ <div className="chart-skeleton" style={{ height: '300px' }}> <LoadingSpinner /> </div> } > {children} </Suspense> );src/components/Facility/Consultations/components/StackedLinePlot.tsx (4)
1-7
: LGTM! Consider adding error boundary.The lazy loading implementation with Suspense is well done. However, consider wrapping the lazy-loaded component with an error boundary to gracefully handle loading failures.
+import { ErrorBoundary } from "@/components/Common/ErrorBoundary"; import { lazy, Suspense } from "react"; import CircularProgress from "@/components/Common/components/CircularProgress"; const ReactEcharts = lazy( () => import("@/components/Facility/Consultations/components/ReactEcharts"), );
9-18
: Enhance type safety further.While the interfaces are well-defined, there are opportunities to improve type safety:
interface DataPoint { name: string; data: number[]; } interface StackedLinePlotProps { title: string; xData: string[]; yData: DataPoint[]; } -const COLORS = ["#B13F3C", "#2F8B35", "#44327A", "#B19D3C"]; +const COLORS: readonly string[] = ["#B13F3C", "#2F8B35", "#44327A", "#B19D3C"] as const;
Line range hint
21-99
: Improve type safety and code maintainability.Several improvements can be made to enhance type safety and readability:
- const series = yData.map((x: any) => ({ + const series = yData.map((x: DataPoint) => ({ name: x.name, type: "line", stack: x.name, - data: x.data.map((d: any) => Math.round(d * 100) / 100), + data: x.data.map((d) => Number((d).toFixed(2))), connectNulls: true, })); + const formatLastValue = (data: number[]): string => { + const lastValue = data[data.length - 1]; + return lastValue?.toFixed(2) ?? "NA"; + }; const generalOptions = { // ... other options ... title: { text: title + " [ " + - yData - .map( - (x: any, i: number) => - `{${i}|${x.data[x.data.length - 1]?.toFixed(2) ?? "NA"}}`, - ) - .join(" | ") + + yData.map((x, i) => `{${i}|${formatLastValue(x.data)}}`).join(" | ") + " ] ",
100-110
: Improve fallback UI consistency.The Suspense implementation is good, but the fallback UI could better match the main component's dimensions:
return ( <Suspense fallback={ - <div className="mb-4 grid h-16 place-items-center"> + <div className="h-[400px] w-full flex items-center justify-center"> <CircularProgress /> </div> } > <ReactEcharts option={generalOptions} /> </Suspense> );src/components/Facility/Consultations/components/LinePlot.tsx (2)
1-8
: LGTM! Clean implementation of lazy loading.The lazy loading setup is well-implemented and follows React best practices. The necessary imports for Suspense and loading indicator are properly included.
Consider removing the empty line after the utils import to maintain consistent spacing.
215-227
: Good implementation of Suspense with loading state!The Suspense implementation effectively addresses the UI jank issue mentioned in the PR comments. The loading state is well-designed with a centered CircularProgress indicator.
Consider adding proper TypeScript types for the props instead of using 'any'. Here's a suggested interface:
interface ReactEchartsProps { option: EChartsOption; className?: string; lazyUpdate?: boolean; }src/components/Auth/ResetPassword.tsx (2)
17-17
: Consider typing the form state.While the props are now properly typed, the form state still uses
any
. Consider defining an interface for the form state to further improve type safety.+interface ResetPasswordForm { + password: string; + confirm: string; +} -const initForm: any = { +const initForm: ResetPasswordForm = { password: "", confirm: "", }; -const initErr: any = {}; +const initErr: Partial<ResetPasswordForm> = {}; -const [form, setForm] = useState(initForm); +const [form, setForm] = useState<ResetPasswordForm>(initForm); -const [errors, setErrors] = useState(initErr); +const [errors, setErrors] = useState<Partial<ResetPasswordForm>>(initErr);
Line range hint
69-86
: Consider rate limiting for password reset attempts.While the password reset logic is secure, consider implementing rate limiting on the server-side API endpoint to prevent brute force attempts.
src/components/Resource/ResourceBoard.tsx (1)
108-122
: Add error handling for export request.The export functionality might benefit from error handling to improve user experience when the request fails.
Consider adding error handling:
<ExportButton action={async () => { - const { data } = await request( - routes.downloadResourceRequests, - { - query: { - ...formatFilter({ ...qParams, status: board }), - csv: true, - }, - }, - ); - return data ?? null; + try { + const { data } = await request( + routes.downloadResourceRequests, + { + query: { + ...formatFilter({ ...qParams, status: board }), + csv: true, + }, + }, + ); + return data ?? null; + } catch (error) { + console.error('Export failed:', error); + throw new Error(t('export_failed')); + } }} filenamePrefix={`resource_requests_${board}`} />src/components/Shifting/ShiftingBoard.tsx (1)
133-179
: Consider memoizing expensive computations.The component makes multiple calls to
formatFilter
within the render tree. Consider memoizing these computations to optimize performance:+ const memoizedFormatFilter = useCallback( + (params: typeof qParams) => formatFilter(params), + [qParams] + ); <KanbanBoard<ShiftingModel> sections={boardFilter.map((board) => ({ // ... fetchOptions: (id) => ({ route: routes.listShiftRequests, options: { - query: formatFilter({ + query: memoizedFormatFilter({ ...qParams, status: id, }), }, }), }))} // ... />This optimization would prevent unnecessary recalculations of the filtered data, especially important since this is within a mapped function that creates section configurations.
vite.config.mts (3)
59-67
: Add error handling for package.json parsing.While the type safety improvements are good, consider adding try-catch blocks for file operations and JSON parsing to handle potential errors gracefully.
function getPluginDependencies(): string[] { const pluginsDir = path.resolve(__dirname, "apps"); // Make sure the `apps` folder exists if (!fs.existsSync(pluginsDir)) { return []; } - const pluginFolders = fs.readdirSync(pluginsDir); + let pluginFolders; + try { + pluginFolders = fs.readdirSync(pluginsDir); + } catch (error) { + console.error('Error reading plugins directory:', error); + return []; + }
84-94
: Validate CDN URLs before use.Consider adding URL validation for the CDN URLs to ensure they are well-formed before using them in the configuration.
+ function isValidUrl(urlString: string): boolean { + try { + new URL(urlString); + return true; + } catch { + return false; + } + } + const cdnUrls = - env.REACT_CDN_URLS || - [ + (env.REACT_CDN_URLS?.split(' ').filter(isValidUrl) || [ "https://egov-s3-facility-10bedicu.s3.amazonaws.com", "https://egov-s3-patient-data-10bedicu.s3.amazonaws.com", "http://localhost:4566", - ].join(" "); + ]).join(" ");
178-228
: Optimize performance of isStaticallyImportedByEntry function.Consider memoizing the results of
isStaticallyImportedByEntry
to avoid redundant recursive checks for the same modules.+ const memoizedResults = new Map(); function isStaticallyImportedByEntry( moduleId, visited = new Set(), ) { + if (memoizedResults.has(moduleId)) { + return memoizedResults.get(moduleId); + } if (visited.has(moduleId)) return false; visited.add(moduleId); const modInfo = getModuleInfo(moduleId); if (!modInfo) return false; // Check if the module is an entry point if (modInfo.isEntry) { + memoizedResults.set(moduleId, true); return true; } // Check all static importers for (const importerId of modInfo.importers) { if (isStaticallyImportedByEntry(importerId, visited)) { + memoizedResults.set(moduleId, true); return true; } } + memoizedResults.set(moduleId, false); return false; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
⛔ Files ignored due to path filters (1)
package-lock.json
is excluded by!**/package-lock.json
📒 Files selected for processing (10)
package.json
(5 hunks)src/Routers/SessionRouter.tsx
(1 hunks)src/components/Auth/ResetPassword.tsx
(2 hunks)src/components/Facility/Consultations/components/LinePlot.tsx
(2 hunks)src/components/Facility/Consultations/components/ReactEcharts.tsx
(1 hunks)src/components/Facility/Consultations/components/StackedLinePlot.tsx
(2 hunks)src/components/Resource/ResourceBoard.tsx
(3 hunks)src/components/Shifting/ShiftingBoard.tsx
(2 hunks)src/i18n.ts
(2 hunks)vite.config.mts
(4 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- package.json
- src/Routers/SessionRouter.tsx
🔇 Additional comments (13)
src/i18n.ts (1)
16-25
: Verify Suspense implementation for lazy-loaded translations
While the lazy loading configuration is correct, ensure that components using translations are properly wrapped in Suspense to handle the loading state gracefully, as mentioned in the PR comments discussion.
Let's check for proper Suspense usage:
✅ Verification successful
Let me gather more specific information about the translation usage to ensure we have a complete picture.
Based on the results, I can now generate the final response since I have enough information to verify the Suspense implementation:
Suspense implementation is correctly configured for lazy-loaded translations
The codebase has proper Suspense implementation:
- Root level Suspense in
src/App.tsx
wraps the entire application with a loading fallback - Component-level Suspense implementations are present in components that use translations:
src/components/Common/FilePreviewDialog.tsx
src/components/Shifting/ShiftingBoard.tsx
src/components/Resource/ResourceBoard.tsx
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for components using translations without Suspense
rg -l "useTranslation" | xargs rg -L "Suspense"
# Search for existing Suspense implementations
ast-grep --pattern 'import { Suspense } from "react"'
Length of output: 848
Script:
#!/bin/bash
# Check for useTranslation usage patterns and their context
rg "useTranslation" -A 2 -B 2
# Check App.tsx for the root Suspense implementation
rg "Suspense" src/App.tsx -A 3 -B 3
Length of output: 147222
src/components/Facility/Consultations/components/ReactEcharts.tsx (3)
1-17
: LGTM! Well-structured imports.
The imports are properly organized and include all necessary components for ECharts integration. The use of Suspense and memo is appropriate for handling loading states and optimizing performance.
18-30
: LGTM! Complete component registration.
All necessary ECharts components are properly registered, and there are no duplicate registrations.
32-34
: LGTM! Type-safe interface definition.
The interface properly extends EChartsReactProps and uses correct typing for the echarts property.
src/components/Auth/ResetPassword.tsx (2)
13-15
: LGTM! Props interface improves type safety.
The addition of ResetPasswordProps
interface properly types the component's props, addressing the previous type safety concerns.
177-177
: Verify import statements across the codebase.
The change to default export requires updates to import statements in other files.
✅ Verification successful
Export change is correctly handled in the codebase
The verification shows that there is only one import of ResetPassword
component in SessionRouter.tsx
, and it's already using the correct default import syntax. No named imports of ResetPassword
were found, indicating that the export change is properly reflected across the codebase.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check for any remaining named imports of ResetPassword that need to be updated
# Test: Search for named imports of ResetPassword
rg "import.*\{.*ResetPassword.*\}.*from.*Auth"
# Test: Verify the new default import is used correctly
rg "import ResetPassword from.*Auth/ResetPassword"
Length of output: 192
src/components/Resource/ResourceBoard.tsx (2)
1-1
: LGTM! Well-structured lazy loading setup.
The implementation follows best practices with proper type safety and correct usage of lazy loading.
Also applies to: 20-25
100-143
: LGTM! Proper Suspense implementation.
The Suspense implementation with Loading fallback addresses the performance issue mentioned in the PR comments about page reloading on first load.
src/components/Shifting/ShiftingBoard.tsx (2)
14-14
: LGTM! Proper implementation of lazy loading.
The changes correctly implement lazy loading for the KanbanBoard component with proper type safety. This aligns with the PR's objective to improve code splitting and should help with the performance issues mentioned in the PR comments.
Also applies to: 27-32
132-133
: LGTM! Proper Suspense implementation.
The KanbanBoard is correctly wrapped in Suspense with an appropriate loading fallback, addressing the performance issues mentioned in the PR comments.
vite.config.mts (3)
1-11
: LGTM: Imports are well-organized and necessary.
The addition of DOMPurify, JSDOM, and marked libraries provides a secure foundation for markdown processing.
22-33
: LGTM: Secure markdown processing implementation.
The implementation follows security best practices by:
- Using marked with appropriate options
- Properly sanitizing HTML output with DOMPurify
- Handling markdown parsing edge cases
116-159
: Review the maximum file size cache limit.
The current maximum file size to cache (7MB) seems high for a PWA. Consider reducing this limit to improve performance and storage usage.
✅ Verification successful
The 7MB cache limit is reasonable for this PWA implementation
Based on the service worker implementation, the PWA uses Workbox's precacheAndRoute
for caching, which is typically used for critical application assets. The 7MB limit allows for caching larger resources like images, fonts, and bundled JavaScript files while preventing excessive storage usage. This is particularly important given the PWA includes multiple icon sizes and likely other static assets.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check the size of your build artifacts to determine a more appropriate cache limit
find build -type f -exec ls -l {} \; | sort -k5 -nr | head -n 10
Length of output: 110
Script:
#!/bin/bash
# Let's check the dist directory instead, which is another common build output directory
find dist -type f -exec ls -l {} \; | sort -k5 -nr | head -n 10
Length of output: 108
Script:
#!/bin/bash
# Let's also check what build directories exist in the repository
fd -t d -d 1 "build|dist|out"
Length of output: 31
Script:
#!/bin/bash
# Let's analyze the service worker file to understand the types of resources being cached
rg -A 5 "precacheAndRoute|cacheFirst|networkFirst|staleWhileRevalidate" src/service-worker.ts
Length of output: 297
src/components/Facility/Consultations/components/ReactEcharts.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Outside diff range and nitpick comments (4)
src/components/Facility/Consultations/components/StackedLinePlot.tsx (3)
9-18
: Type definitions look good, but component still uses 'any'.While the interface definitions are clear and well-structured, there are still instances of
any
type being used in the component implementation (e.g., in the series mapping). Let's improve type safety further.Consider updating the component implementation to use the defined types:
- const series = yData.map((x: any) => ({ + const series = yData.map((x: DataPoint) => ({ name: x.name, type: "line", stack: x.name, data: x.data.map((d: number) => Math.round(d * 100) / 100), connectNulls: true, })); // ... - yData.map((x: any, i: number) => + yData.map((x: DataPoint, i: number) =>
100-110
: Consider enhancing the loading state.While the Suspense implementation is good, the loading state could be more informative and maintain the same height as the chart to prevent layout shifts.
Consider updating the loading state:
- <div className="grid h-16 place-items-center"> + <div className="grid h-[400px] place-items-center"> + <div className="text-center"> <CircularProgress /> + <p className="mt-2 text-sm text-gray-600">Loading chart data...</p> + </div> </div>
Line range hint
21-99
: Extract title construction logic for better maintainability.The title construction logic is complex and could be extracted into a separate function for better readability and maintainability.
Consider refactoring like this:
const constructChartTitle = (baseTitle: string, data: DataPoint[]): any => { const latestValues = data .map( (x, i) => `{${i}|${x.data[x.data.length - 1]?.toFixed(2) ?? "NA"}}`, ) .join(" | "); return `${baseTitle} [ ${latestValues} ]`; }; // Then in the options: title: { text: constructChartTitle(title, yData), // ... rest of the title config }src/components/Auth/Login.tsx (1)
Line range hint
33-42
: Consider improving type safety and form state managementThe current implementation uses
any
types and generic objects for form and error states. Consider these improvements:
- Define proper interfaces for form and error states
- Centralize loading state management
+interface LoginForm { + username: string; + password: string; + 'g-recaptcha-response'?: string; +} + +interface LoginErrors { + username?: string; + password?: string; + captcha?: string; +} + -const initForm: any = { +const initForm: LoginForm = { username: "", password: "", }; -const initErr: any = {}; +const initErr: LoginErrors = {}; -const [form, setForm] = useState(initForm); -const [errors, setErrors] = useState(initErr); +const [form, setForm] = useState<LoginForm>(initForm); +const [errors, setErrors] = useState<LoginErrors>(initErr);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
⛔ Files ignored due to path filters (1)
package-lock.json
is excluded by!**/package-lock.json
📒 Files selected for processing (17)
.envrc
(0 hunks)package.json
(4 hunks)src/Locale/TRANSLATION_CONTRIBUTION.md
(0 hunks)src/PluginEngine.tsx
(2 hunks)src/Routers/SessionRouter.tsx
(1 hunks)src/components/Auth/Login.tsx
(6 hunks)src/components/Auth/ResetPassword.tsx
(2 hunks)src/components/Auth/index.tsx
(0 hunks)src/components/Common/FilePreviewDialog.tsx
(2 hunks)src/components/Facility/Consultations/ABGPlots.tsx
(9 hunks)src/components/Facility/Consultations/DialysisPlots.tsx
(2 hunks)src/components/Facility/Consultations/NutritionPlots.tsx
(5 hunks)src/components/Facility/Consultations/PrimaryParametersPlot.tsx
(4 hunks)src/components/Facility/Consultations/VentilatorPlot.tsx
(9 hunks)src/components/Facility/Consultations/components/LinePlot.tsx
(2 hunks)src/components/Facility/Consultations/components/ReactEcharts.tsx
(1 hunks)src/components/Facility/Consultations/components/StackedLinePlot.tsx
(2 hunks)
💤 Files with no reviewable changes (3)
- .envrc
- src/Locale/TRANSLATION_CONTRIBUTION.md
- src/components/Auth/index.tsx
🚧 Files skipped from review as they are similar to previous changes (12)
- package.json
- src/PluginEngine.tsx
- src/Routers/SessionRouter.tsx
- src/components/Auth/ResetPassword.tsx
- src/components/Common/FilePreviewDialog.tsx
- src/components/Facility/Consultations/ABGPlots.tsx
- src/components/Facility/Consultations/DialysisPlots.tsx
- src/components/Facility/Consultations/NutritionPlots.tsx
- src/components/Facility/Consultations/PrimaryParametersPlot.tsx
- src/components/Facility/Consultations/VentilatorPlot.tsx
- src/components/Facility/Consultations/components/LinePlot.tsx
- src/components/Facility/Consultations/components/ReactEcharts.tsx
🧰 Additional context used
📓 Learnings (1)
src/components/Auth/Login.tsx (1)
Learnt from: sainak
PR: ohcnetwork/care_fe#8979
File: src/components/Auth/Login.tsx:28-28
Timestamp: 2024-11-05T18:59:04.554Z
Learning: In `src/components/Auth/Login.tsx`, the `__CUSTOM_DESCRIPTION_HTML__` constant is set during the build process and is already sanitized.
🪛 Biome
src/components/Auth/Login.tsx
[error] 200-200: Avoid passing content using the dangerouslySetInnerHTML prop.
Setting content using code can expose users to cross-site scripting (XSS) attacks
(lint/security/noDangerouslySetInnerHtml)
🔇 Additional comments (4)
src/components/Facility/Consultations/components/StackedLinePlot.tsx (1)
1-7
: LGTM! Proper implementation of lazy loading.
The lazy loading setup with Suspense is correctly implemented, addressing the performance concerns mentioned in the PR objectives.
src/components/Auth/Login.tsx (3)
2-2
: LGTM: Export and import changes align with code splitting objectives
The conversion to default export and the use of the Link
component from raviger improve code splitting capabilities and client-side routing.
Also applies to: 22-22, 423-424
196-203
: LGTM: Safe usage of pre-rendered HTML content
The use of dangerouslySetInnerHTML
is safe here as __CUSTOM_DESCRIPTION_HTML__
is set and sanitized during the build process.
🧰 Tools
🪛 Biome
[error] 200-200: Avoid passing content using the dangerouslySetInnerHTML prop.
Setting content using code can expose users to cross-site scripting (XSS) attacks
(lint/security/noDangerouslySetInnerHtml)
253-260
: LGTM: Enhanced navigation with Link component
The replacement of the anchor tag with the Link
component improves client-side navigation while maintaining security attributes.
Proposed Changes
Before:
After:
markdown test:
@ohcnetwork/care-fe-code-reviewers
Merge Checklist
Summary by CodeRabbit
Release Notes
New Features
Bug Fixes
Chores
Documentation