-
Notifications
You must be signed in to change notification settings - Fork 33
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
Netmanager nextjs project setup #2364
Conversation
- added sites registry overview
📝 WalkthroughWalkthroughThis pull request introduces a comprehensive Next.js application for network management, establishing a robust frontend architecture with modern web development practices. The project sets up a complete ecosystem including authentication, state management, UI components, routing, and permission-based access control. Key technologies integrated include React, Redux, React Query, Tailwind CSS, and TypeScript, creating a scalable and type-safe application structure. Changes
Suggested Labels
Suggested Reviewers
Poem
Sequence DiagramsequenceDiagram
participant User
participant LoginPage
participant AuthService
participant Redux Store
participant Dashboard
User->>LoginPage: Enter credentials
LoginPage->>AuthService: Authenticate
AuthService->>Redux Store: Store user details
Redux Store-->>Dashboard: Redirect if authenticated
Dashboard->>User: Display personalized content
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 (
|
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: 3
🧹 Nitpick comments (28)
netmanager-app/package.json (2)
11-28
: Consider additional dependencies and version managementThe current dependencies provide a solid foundation for UI and form handling. However, consider:
For network management functionality, you might need:
- Data fetching utilities (e.g.,
@tanstack/react-query
)- Date/time handling (e.g.,
date-fns
)- Network visualization (e.g.,
@visx/network
orreact-flow
)Consider using exact versions instead of caret ranges for better reproducibility:
- "next": "14.2.16", + "next": "14.2.16", - "react": "^18", + "react": "18.2.0", - "react-dom": "^18", + "react-dom": "18.2.0",
29-38
: Enhance development toolingConsider adding these development dependencies to improve code quality and development experience:
- Testing utilities:
"devDependencies": { + "@testing-library/react": "^14.0.0", + "@testing-library/jest-dom": "^6.1.0", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0",
- Code quality tools:
"devDependencies": { + "prettier": "^3.0.0", + "eslint-config-prettier": "^9.0.0", + "husky": "^8.0.0", + "lint-staged": "^15.0.0",netmanager-app/components/ui/collapsible.tsx (1)
3-11
: Exports are carefully organized and straightforward.
Re-exporting Radix UI primitives provides a clean API surface, enhancing reusability. You might consider adding short doc comments or TypeScript definitions for discoverability, but otherwise this is well structured.netmanager-app/app/types/sites.ts (2)
1-1
: Consider adding comments or JSDoc for the interface.
Providing a high-level description of theSite
interface properties can make the code more readable and maintainable.
20-28
: Consider using a string enum.
While the typed union fordeploymentStatus
is acceptable, using a TypeScript enum might improve clarity and maintainability if this field is widely used or likely to expand further.netmanager-app/components/ui/input.tsx (1)
5-19
: Consider accessibility attributes.
Adding standard HTML input attributes (e.g.,aria-label
,aria-describedby
, orautocomplete
) may enhance the form’s usability and accessibility.netmanager-app/app/page.tsx (3)
2-2
: Consider removing unused import or integratingLayout
.
TheLayout
import isn't used in this file. If you intended to wrap theAnalytics
component withLayout
, you should do so. Otherwise, remove this import to keep the code base tidy.
5-8
: Name and display text mismatch.
The component is namedAnalytics
, but the visible heading says "Dashboard". It's not necessarily problematic, but you might want to rename either the component or the heading to avoid confusion.
9-33
: Hardcoded data in the UI.
All the numeric stats (156, 42, and 1.2M) are hardcoded. You may consider passing these values as props or fetching dynamic data to make the component more reusable.netmanager-app/components/ui/badge.tsx (1)
30-33
: Consider using a semantic element if interactivity is not needed.
While using a<div>
for the badge is fine, if the badge is just for display, a<span>
might be more semantic. On the other hand, if interactivity is planned, a<button>
could be considered.netmanager-app/app/components/top-nav.tsx (2)
11-23
: Add accessibility features to the inline search.
The search input is missing a form element and accessible labels. If you intend to make this a fully functional search form, consider wrapping it in a<form>
and adding an accessible label oraria-label
. This ensures better screen reader support.
26-28
: Check for accessibility on the notification button.
The notification button uses an icon without a textual label. For better accessibility, consider adding anaria-label
describing the purpose of the button.netmanager-app/components/ui/button.tsx (2)
36-40
: Interface clarity is well-handled.
UsingasChild
withinButtonProps
provides flexibility when rendering the component. Just remember to add or update inline documentation for new teammates unfamiliar with the property.
42-54
: Forward ref approach is appropriate for a UI library.
It’s good to see thatforwardRef
is used, allowing library consumers to easily find and manipulate DOM elements in advanced scenarios. Additionally, consider adding test coverage for this component’s ref usage to ensure no unexpected regressions.netmanager-app/components/ui/card.tsx (1)
20-29
: Consider ARIA roles or semantic elements for accessibility.
For instance, theCardHeader
orCardTitle
might benefit from being a<header>
or<h2>
element for better semantic structure and accessibility.netmanager-app/tailwind.config.js (1)
9-16
: Container configuration looks great for a responsive layout.
Setting the container to center with generous padding is a good default for most uses.netmanager-app/components/ui/table.tsx (1)
9-15
: Consider adding table accessibility attributes.
You might consider anaria-label
oraria-describedby
on the table element for improved screen reader support, especially if the table’s purpose or data set is not entirely obvious from surrounding context.netmanager-app/app/components/topbar.tsx (2)
60-85
: Encourage discoverability of application links.
The dropdown approach is convenient, but consider whether you need a more prominent top-level navigation for these essential apps. If discoverability is essential, you might want to also provide direct links or a visible menu.
87-118
: Consider logout confirmation.
Adding a confirmation step for logout can prevent accidental sign-outs.netmanager-app/components/ui/dialog.tsx (1)
32-55
: Avoid nested portals if not necessary.
DialogPortal
is nested withinDialogContent
, which is fairly standard, but be mindful not to host multiple portals in deeper nesting, as it may complicate the DOM structure and debugging experience.netmanager-app/app/sites/page.tsx (2)
26-52
: Data source and refresh mechanism
You're relying on a localsites
constant as sample data. Once you transition to actual data retrieval (e.g., from an API), ensure the component re-renders correctly when new data arrives.
54-63
: Efficient filtering
Your filter approach is straightforward but runs each time the component renders. For very large datasets, consider more efficient search patterns or a library for fuzzy searching.netmanager-app/components/ui/form.tsx (2)
18-18
: Exporting Form as FormProvider might cause confusion.
While this approach is succinct, it could be confusing to others who might expect Form to be a custom wrapper with added logic. A clear naming convention (e.g.FormProvider as Form
) or an inline comment may help maintain clarity.
44-65
: Potential error check improvement.
You throw an error ifuseFormField
is used outside<FormField>
(line 52). Given you also referenceitemContext
(line 46), consider a similar validation ifitemContext
is required for correct usage. Otherwise, ensure fallback behavior ifFormItem
context is absent.netmanager-app/app/sites/[id]/page.tsx (1)
25-43
: Consider loading real data in future iterations.
Currently,siteData
anddevices
are static (line 25, 45). For production, you might fetch real data with an API call or query to keep the UI aligned with dynamic content.netmanager-app/app/components/sidebar.tsx (3)
18-18
: Rename the importedMap
to avoid overshadowing the global property.
UsingMap
fromlucide-react
can be confusing sinceMap
is also a global object in JavaScript. Consider renaming it to something likeMapIcon
orMapSVG
to avoid collisions.-import { Map } from "lucide-react"; +import { Map as MapIcon } from "lucide-react";🧰 Tools
🪛 Biome (1.9.4)
[error] 18-18: Do not shadow the global "Map" property.
Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.
(lint/suspicious/noShadowRestrictedNames)
46-46
: Potential dynamic organization loading.
Currently, theorganizations
array is hard-coded. Consider fetching organizations dynamically if the application requires real data.
343-348
: Implement or remove the placeholder logout logic.
The logout button exists but doesn’t perform any action. Users may be confused if it does nothing. Consider routing them to a logout flow or removing the placeholder until it’s ready.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (4)
netmanager-app/app/favicon.ico
is excluded by!**/*.ico
netmanager-app/app/fonts/GeistMonoVF.woff
is excluded by!**/*.woff
netmanager-app/app/fonts/GeistVF.woff
is excluded by!**/*.woff
netmanager-app/package-lock.json
is excluded by!**/package-lock.json
📒 Files selected for processing (34)
netmanager-app/.eslintrc.json
(1 hunks)netmanager-app/.gitignore
(1 hunks)netmanager-app/README.md
(1 hunks)netmanager-app/app/components/layout.tsx
(1 hunks)netmanager-app/app/components/sidebar.tsx
(1 hunks)netmanager-app/app/components/top-nav.tsx
(1 hunks)netmanager-app/app/components/topbar.tsx
(1 hunks)netmanager-app/app/globals.css
(1 hunks)netmanager-app/app/layout.tsx
(1 hunks)netmanager-app/app/page.tsx
(1 hunks)netmanager-app/app/sites/[id]/devices.tsx
(1 hunks)netmanager-app/app/sites/[id]/page.tsx
(1 hunks)netmanager-app/app/sites/page.tsx
(1 hunks)netmanager-app/app/sites/site-form.tsx
(1 hunks)netmanager-app/app/types/layout.ts
(1 hunks)netmanager-app/app/types/sites.ts
(1 hunks)netmanager-app/components.json
(1 hunks)netmanager-app/components/ui/badge.tsx
(1 hunks)netmanager-app/components/ui/button.tsx
(1 hunks)netmanager-app/components/ui/card.tsx
(1 hunks)netmanager-app/components/ui/collapsible.tsx
(1 hunks)netmanager-app/components/ui/dialog.tsx
(1 hunks)netmanager-app/components/ui/dropdown-menu.tsx
(1 hunks)netmanager-app/components/ui/form.tsx
(1 hunks)netmanager-app/components/ui/input.tsx
(1 hunks)netmanager-app/components/ui/label.tsx
(1 hunks)netmanager-app/components/ui/table.tsx
(1 hunks)netmanager-app/lib/utils.ts
(1 hunks)netmanager-app/next.config.mjs
(1 hunks)netmanager-app/package.json
(1 hunks)netmanager-app/postcss.config.mjs
(1 hunks)netmanager-app/tailwind.config.js
(1 hunks)netmanager-app/tailwind.config.ts
(1 hunks)netmanager-app/tsconfig.json
(1 hunks)
✅ Files skipped from review due to trivial changes (7)
- netmanager-app/next.config.mjs
- netmanager-app/.eslintrc.json
- netmanager-app/postcss.config.mjs
- netmanager-app/README.md
- netmanager-app/tsconfig.json
- netmanager-app/.gitignore
- netmanager-app/components.json
🧰 Additional context used
🪛 Biome (1.9.4)
netmanager-app/app/components/sidebar.tsx
[error] 18-18: Do not shadow the global "Map" property.
Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.
(lint/suspicious/noShadowRestrictedNames)
🔇 Additional comments (44)
netmanager-app/package.json (1)
1-4
: Verify project name alignment with organization standardsThe project name "netmanager-app" appears to be descriptive of its purpose. However, let's ensure it aligns with the organization's naming conventions, especially since this is part of the AirQo platform.
✅ Verification successful
Project naming follows platform conventions - no changes needed
Looking at other package.json files in the repository, we can see that the naming is consistent with the platform's conventions:
- Simple, descriptive names are used:
platform
,website
,reports
,calibrate
,docs
- The new
netmanager-app
follows this pattern with a clear, descriptive name- It's actually more specific than some other packages (like
frontend
), which is good for clarity🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for other package.json files to verify naming patterns fd package.json --type f --exec sh -c 'echo "=== {} ==="; cat {} | jq -r ".name"'Length of output: 415
netmanager-app/app/types/layout.ts (2)
1-1
: Import usage looks good.
ImportingReactNode
is the correct approach when defining props for React children.
3-5
: Interface definition is straightforward and appropriate.
Definingchildren
asReactNode
ensures flexibility for rendering any valid React element. Nicely done!netmanager-app/lib/utils.ts (1)
4-6
: Concise utility function for merging class names.
The use ofclsx
combined withtwMerge
is a neat pattern. It promotes clear, conflict-free Tailwind class manipulations.netmanager-app/components/ui/collapsible.tsx (1)
1-2
: Client-side declaration is correct for interactive UI elements.
Marking this file as"use client"
ensures these collapsible components are rendered on the client side. No issues here.netmanager-app/app/layout.tsx (2)
7-11
: RootLayout approach aligns with Next.js standards.
Exporting the layout as the default entry point is a good pattern, and typingchildren
properly retains clarity and maintainability.
13-17
: Usage of the Inter font and structured HTML is well-done.
This enhances readability, sets a strong foundation for consistent styling, and keeps your layout definition minimal yet effective.netmanager-app/app/types/sites.ts (2)
2-18
: Ensure naming consistency for location-related fields.
Currently, fields likedistrict
,region
,parish
, andsubCounty
appear as simple strings. For a large-scale system, it might be beneficial to standardize naming (e.g.,subCounty
→sub_county
) or confirm it matches usage in other modules for consistent data modeling.
14-17
: Validate optional property usage.
Fields likegreenness
andmobileAppDescription
are optional, which is sensible. Double-check that any usage in the code handles these fields being possibly undefined.netmanager-app/components/ui/label.tsx (2)
9-11
: Nice approach withclass-variance-authority
(CVA).
This succinctly organizes label styling variations. Good job!
13-23
: Forward ref usage looks clean.
The implementation is straightforward, and applyingclassName
throughcn(labelVariants(), className)
is an elegant approach.netmanager-app/app/components/layout.tsx (1)
16-26
: Overall layout structure is coherent.
TheSidebar
andTopbar
usage combined with dark mode toggling offers a good user experience. The organizational approach is clear and straightforward.netmanager-app/components/ui/badge.tsx (1)
6-24
: Proper use ofclass-variance-authority
.
ThebadgeVariants
object is well-defined, and the variant logic is clearly separated. Good job!netmanager-app/tailwind.config.ts (2)
3-9
: Content paths appear properly defined.
Good job specifying the paths to pages, components, and the app directory. Tailwind will scan these files correctly for class usage.
61-61
: Confirm plugin usage.
You are requiringtailwindcss-animate
. Just ensure that plugin is installed and is truly needed for your project to reduce bundle size overhead.netmanager-app/components/ui/button.tsx (1)
7-34
: Fantastic usage of class-variance-authority for style variants.
The variants and sizing are thoughtfully named and appear to cover common use cases. This structure will help maintain a clean and consistent UI throughout your application.netmanager-app/components/ui/card.tsx (2)
5-18
: Consistent structure with forwardRef is excellent.
All card sub-components maintain a similar shape, which makes the API clean and predictable. Keep up the consistent naming conventions!
32-85
: Nice separation of card sections.
Each exported component is logically separated (header, content, footer). This modular pattern encourages reusability and clarity. Great job!netmanager-app/app/sites/[id]/devices.tsx (2)
14-16
: Prop typing is clear and straightforward.
DefiningSiteDevicesProps
with an array ofDevice
objects is simple and effective. This fosters a strongly typed API for the component.
18-66
: Map logic is tidy; consider fallback checks on data.
The mapping logic here is well-structured. However, it might be beneficial to handle edge cases ifdevices
is empty or undefined. This ensures the table remains stable and avoids any runtime errors.netmanager-app/tailwind.config.js (2)
1-8
: Good coverage of Next.js directories in content paths.
Including allts
andtsx
files across pages, components,app
, andsrc
ensures Tailwind’s purging mechanism is effective and keeps the CSS bundle lean.
18-75
: Impressive theme extension with HSL-based variables.
Using dynamic HSL variables keeps your design system flexible, especially when theming for dark mode. Also, nice addition of accordion animations for a polished user experience.netmanager-app/components/ui/table.tsx (3)
5-16
: Ensure forwardRef usage meets your expectations.
UsingReact.forwardRef
is an excellent choice for customizing table elements, but verify that the parent components actually utilize and pass refs down to these table elements if needed for focusing, scrolling, or measuring. Otherwise, consider removing it to simplify the code.
54-68
: Verify hover styling considerations.
The row hover effect applieshover:bg-muted/50
; ensure that this visual feedback aligns with the overall design system and doesn't conflict with any interactive children elements like buttons or links within the row.
96-106
: Take advantage of semantic HTML for captions.
TableCaption
is used correctly here. Great job ensuring semantic table structure!netmanager-app/app/components/topbar.tsx (1)
23-36
: Confirm default dark mode settings.
Your code conditionally toggles dark mode based on local state, but confirm whether the default should be dark or light when the user first visits. Some projects prefer system-level defaults or local storage persistence of user’s last preference.netmanager-app/components/ui/dialog.tsx (2)
17-31
: Overlay styling complements transitions effectively.
The animated overlay with different states is a nice addition. Be sure to confirm that the transition speeds and durations align with the rest of the design system.
84-109
: Promote consistent typography.
DialogTitle
andDialogDescription
utilize distinct text classes. Ensure consistency with existing heading and body text patterns across the app.netmanager-app/app/sites/page.tsx (1)
64-85
: Dialog usage
The creation dialog is well implemented, but ensure the form displays error messages or loading states if you are planning to integrate a backend.netmanager-app/components/ui/form.tsx (3)
31-42
: Good use of context for form fields.
This structure neatly wraps theController
and facilitates passing down context to child components. It simplifies both usage and future extensibility.
75-86
: Forward ref usage is well-applied.
Forwarding refs allows flexibility and enhances composability with external libraries. This implementation ensures developers can easily extend or style<FormItem>
from parent contexts.
146-168
: Provides clear user feedback for validation errors.
Displaying errors throughFormMessage
is a good practice, and the logic of conditionally displaying error messages (line 151-154) simplifies debugging for end-users.netmanager-app/app/sites/[id]/page.tsx (2)
61-91
: Dialog usage for "Edit Site" is straightforward and user-friendly.
Toggling the dialog with<DialogTrigger>
is a clean approach. The structure inside<DialogContent>
(lines 79-89) is also intuitive for users. Good job on using separate components (SiteForm
) to maintain modularity.
94-185
: Good layout for site details.
Organizing fields in cards ensures clarity for each data category. Maintaining consistent styling (e.g.,CardHeader
,CardTitle
,CardContent
) also improves user experience. This approach is well-aligned with typical Next.js best practices.netmanager-app/components/ui/dropdown-menu.tsx (3)
9-9
: Straightforward re-export of Radix primitives.
The direct re-export ofDropdownMenuPrimitive.Root
asDropdownMenu
is convenient and preserves naming clarity for other developers.
59-75
: Portal usage ensures proper layering.
By wrapping<DropdownMenuPrimitive.Content>
in a<DropdownMenuPrimitive.Portal>
(line 63), you safeguard the dropdown menu from parent overflow restrictions, improving UI consistency across different layouts.
95-115
: Checkbox menu item is a neat solution.
Showing a check mark (lines 109-111) when checked is a clear and intuitive indication for users. The code remains concise and reusable within various menu contexts.netmanager-app/app/sites/site-form.tsx (3)
17-39
: Effective schema definition using zod.
Defining constraints (e.g.,min(2)
onname
, regex for latitude and longitude) ensures robust client-side input validation. This approach prevents many invalid inputs early on.
47-56
: Good default values for organization and empty strings.
Providing default forms data improves user experience and avoids uncontrolled field warnings inreact-hook-form
. This also ensures minimal friction when users load the form.
250-284
: Separation of mobile app details aids maintainability.
Structuring the form with a separate section for mobile app details (line 251-284) maintains clarity and aligns with the rest of the form’s layout. This makes future expansions less cumbersome.netmanager-app/app/components/sidebar.tsx (2)
1-3
: Confirm client-side compatibility.
Using"use client";
is required for client components in Next.js 13. Ensure that this component indeed needs client-side features (like state, effects, etc.). Otherwise, remove the directive to reduce overhead.
51-57
: ValidateuseEffect
dependencies.
The effect toggles the devices section based on pathname and collapsed state. This logic appears sound, but keep an eye on potential race conditions or over-rendering if the dependencies change frequently.netmanager-app/app/globals.css (2)
18-18
: Confirm the muted foreground color for accessibility.
The--muted-foreground
hue is set to a relatively low contrast. Verify that the color remains readable for all users, especially in low-light conditions.
34-59
: Validate the dark mode palette for consistent branding.
It’s great to see a thorough set of custom properties for dark mode. Ensure these colors match your project’s branding guidelines and remain harmonious for extended viewing.
"scripts": { | ||
"dev": "next dev", | ||
"build": "next build", | ||
"start": "next start", | ||
"lint": "next lint" | ||
}, |
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.
🛠️ Refactor suggestion
Add test script configuration
The PR checklist mentions the need for unit or end-to-end tests, but there's no test script configured. Consider adding a test script using Jest or your preferred testing framework.
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
- "lint": "next lint"
+ "lint": "next lint",
+ "test": "jest",
+ "test:watch": "jest --watch"
}
Committable suggestion skipped: line range outside the PR's diff.
useEffect(() => { | ||
const isDarkMode = localStorage.getItem("darkMode") === "true"; | ||
setDarkMode(isDarkMode); | ||
document.documentElement.classList.toggle("dark", isDarkMode); | ||
}, []); |
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.
🛠️ Refactor suggestion
Check for SSR compatibility.
Reading from localStorage
inside useEffect
is fine for client-side only, but consider safeguarding if you ever render this component server-side.
netmanager-app/app/sites/page.tsx
Outdated
<div className="border rounded-lg"> | ||
<Table> | ||
<TableHeader> | ||
<TableRow> | ||
<TableHead>Name</TableHead> | ||
<TableHead>Site ID</TableHead> | ||
<TableHead>Description</TableHead> | ||
<TableHead>Country</TableHead> | ||
<TableHead>District</TableHead> | ||
<TableHead>Region</TableHead> | ||
<TableHead>Actions</TableHead> | ||
</TableRow> | ||
</TableHeader> | ||
<TableBody> | ||
{filteredSites.map((site) => ( | ||
<TableRow | ||
key={site.id} | ||
className="cursor-pointer" | ||
onClick={() => router.push(`/sites/${site.id}`)} | ||
> | ||
<TableCell>{site.name}</TableCell> | ||
<TableCell>{site.id}</TableCell> | ||
<TableCell>{site.description}</TableCell> | ||
<TableCell>{site.country}</TableCell> | ||
<TableCell>{site.district}</TableCell> | ||
<TableCell>{site.region}</TableCell> | ||
<TableCell> | ||
<Button | ||
variant="ghost" | ||
size="icon" | ||
className="text-destructive hover:text-destructive" | ||
> | ||
<Trash2 className="h-4 w-4" /> | ||
</Button> | ||
</TableCell> | ||
</TableRow> | ||
))} | ||
</TableBody> | ||
</Table> | ||
</div> | ||
</div> |
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.
🛠️ Refactor suggestion
Prevent accidental navigation
Clicking a row transitions the user away from the page. If you eventually include row-level inline actions (like the trash can button), consider ways to prevent the entire row from being clickable or make the row-clicking optional.
- added redux toolkit - use tan stack query for server side mgt - added login and logout hooks
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: 3
🧹 Nitpick comments (9)
netmanager-app/app/providers.tsx (1)
8-29
: Consider centralizing configurations for the Query Client and conditional usage of ReactQueryDevtools.
You might centralize React Query's default options (e.g.,staleTime
,refetchOnWindowFocus
) into environment-based configs for uniform, environment-appropriate behavior. Additionally, consider enabling ReactQueryDevtools only in development to keep production builds minimal.netmanager-app/package.json (3)
18-21
: Consider potential state management complexityThe project includes both Redux (@reduxjs/toolkit, react-redux) and React Query (@tanstack/react-query) for state management. While this combination can be powerful, it might introduce unnecessary complexity.
Consider:
- Using React Query for server state
- Using Redux only for complex client state
- Document the state management strategy
Also applies to: 30-31
12-17
: Optimize bundle size with UI componentsThe project includes multiple Radix UI components and utility libraries (class-variance-authority, clsx, tailwind-merge). Consider:
- Tree-shaking optimization
- Lazy loading for larger components
- Component library strategy documentation
Also applies to: 23-26, 32-33
36-46
: Consider adding additional development toolsThe development dependencies look good, but consider adding:
prettier
for consistent code formattinghusky
for git hookslint-staged
for pre-commit linting@testing-library/react
for component testingnetmanager-app/core/apis/axiosConfig.ts (1)
23-23
: Avoid using thedelete
operator for performance.Static analysis warns that using
delete
can degrade performance. Consider setting the header toundefined
or an empty string instead.- delete config.headers["Authorization"]; + config.headers["Authorization"] = undefined;🧰 Tools
🪛 Biome (1.9.4)
[error] 23-23: Avoid the delete operator which can impact performance.
Unsafe fix: Use an undefined assignment instead.
(lint/performance/noDelete)
netmanager-app/app/types/users.ts (2)
10-14
: Role naming consistency.Currently, you have
role_name
androle_permissions
. This is fine, but consider normalizing field names (e.g.,name
,permissions
) for naming consistency across the codebase. That tends to make data structures more intuitive.
16-23
: Enum or string literal for userType.
userType
is typed as a string, but consider using a string literal union or an enum for improved maintainability and to reduce the likelihood of typos.netmanager-app/core/hooks/users.ts (1)
22-28
: Check error boundaries on login.A robust error-handling strategy is crucial. Consider wrapping your POST request with a try/catch for more consistent error manipulation instead of relying on
.then(...).catch(...)
.netmanager-app/core/apis/users.ts (1)
5-9
: Centralized error handling strategy.You are currently returning
response.data
. This is okay, but you might want to handle or funnel all errors into a unified error shape if you plan to display them consistently in the UI.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
netmanager-app/package-lock.json
is excluded by!**/package-lock.json
📒 Files selected for processing (12)
netmanager-app/app/layout.tsx
(1 hunks)netmanager-app/app/providers.tsx
(1 hunks)netmanager-app/app/types/users.ts
(1 hunks)netmanager-app/components.json
(1 hunks)netmanager-app/core/apis/axiosConfig.ts
(1 hunks)netmanager-app/core/apis/users.ts
(1 hunks)netmanager-app/core/hooks/users.ts
(1 hunks)netmanager-app/core/redux/slices/userSlice.ts
(1 hunks)netmanager-app/core/redux/store.ts
(1 hunks)netmanager-app/core/urls.tsx
(1 hunks)netmanager-app/lib/utils.ts
(1 hunks)netmanager-app/package.json
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- netmanager-app/components.json
- netmanager-app/app/layout.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
netmanager-app/core/apis/axiosConfig.ts
[error] 23-23: Avoid the delete operator which can impact performance.
Unsafe fix: Use an undefined assignment instead.
(lint/performance/noDelete)
🔇 Additional comments (23)
netmanager-app/app/providers.tsx (2)
1-1
: No issues with the "use client" directive.
This ensures that this module is a client component in Next.js. It looks consistent with the migration goals.
2-6
: Imports look standard and aligned with Next.js client components.
All imported libraries are relevant to the usage of Redux, React Query, and DevTools. No immediate concerns or security issues spotted.netmanager-app/package.json (1)
5-10
: Add test script configurationThe PR checklist mentions the need for unit or end-to-end tests, but there's no test script configured.
netmanager-app/core/redux/store.ts (3)
1-2
: Solid import and usage of Redux toolkit.The import statements correctly reference
@reduxjs/toolkit
and the local slice. No issues spotted.
4-8
: Correct store configuration.Defining the store with
user: userReducer
aligns well with typical Redux Toolkit usage and keeps the naming consistent. Looks great!
10-11
: Helpful type exports.Exposing
RootState
andAppDispatch
is a best practice in TypeScript when using Redux Toolkit. This approach ensures type safety across the codebase.netmanager-app/core/urls.tsx (2)
1-2
: Ensure environment variable is properly set.Using
stripTrailingSlash
prevents trailing slashes inREACT_APP_API_URL
, which is neat. Please verify thatprocess.env.REACT_APP_API_URL
is correctly configured for all environments (development, staging, production).
4-7
: Centralized API endpoint management.Defining these URLs in a single place improves maintainability. Looking good!
netmanager-app/core/apis/axiosConfig.ts (3)
1-2
: Good use of axios import.The import is straightforward and correct. Nothing to change here.
3-6
: Local storage usage.Retrieving a JWT token from local storage is common. However, ensure that sensitive tokens are adequately protected, as local storage can be vulnerable to XSS attacks.
11-35
: Flexible interceptor based on authentication type.This design allows toggling between JWT and token-based approaches seamlessly. Great structure for future flexibility.
🧰 Tools
🪛 Biome (1.9.4)
[error] 23-23: Avoid the delete operator which can impact performance.
Unsafe fix: Use an undefined assignment instead.
(lint/performance/noDelete)
netmanager-app/core/redux/slices/userSlice.ts (6)
1-3
: Neat slice import and setup.Using
createSlice
and typedPayloadAction
from Redux Toolkit is a standard pattern. The typed imports forNetwork
,UserDetails
, etc., keep the code strongly typed.
5-11
: Well-defined user state interface.
UserState
clearly separates user details, networks, and roles. This clarity helps maintain a consistent state shape.
25-29
: Accurate user details update logic.Setting
isAuthenticated
totrue
upon receiving user details and populating available networks is logical. No issues here.
30-38
: Role mapping logic.Mapping role permissions into a simpler structure is convenient. Consider verifying that
role_permissions
is always defined to avoid potential runtime errors.
39-45
: Logout reset is well-handled.Resetting all relevant fields to their initial state is appropriate and ensures a clean logout.
49-50
: Clean slice exports.Exporting both actions and reducer in one go is tidy. No further improvements needed.
netmanager-app/app/types/users.ts (3)
1-8
: Interfaces are well-structured.Your
Permission
interface is clearly defined with optional properties for flexibility. This is a solid approach for handling partially available data.
44-74
: Highly detailed interface.Your
UserDetails
interface covers an extensive set of fields. Verify each field’s source and usage to ensure correct typing and avoid accidentalnull
/undefined
usage where not expected.
99-102
: Align naming for permissions.While
Role
usesrole_permissions
of typePermission[]
,CurrentRole
usespermissions
of typestring[]
. Confirm whether these types are indeed different so that permission-handling logic remains consistent across the application.netmanager-app/core/hooks/users.ts (1)
91-95
: Verify mutation state.
loginMutation.isPending
might not exist in your React Query version. Typically, it’sloginMutation.isLoading
. Ensure you’re using a currently supported property to avoid unexpected UI behavior.netmanager-app/lib/utils.ts (2)
4-6
: Class name merging approach is efficient.Your usage of
clsx
andtwMerge
is a commendable setup for handling complex Tailwind class merges. This approach keeps everything neat and consistent.
8-10
: Simple yet effective utility function.
stripTrailingSlash
is short, clear, and does what it says. Great job! It’s always good to have small utilities like this to avoid repeated pattern usage throughout the code.
netmanager-app/package.json
Outdated
"dependencies": { | ||
"@hookform/resolvers": "^3.9.1", | ||
"@radix-ui/react-collapsible": "^1.1.2", | ||
"@radix-ui/react-dialog": "^1.1.4", | ||
"@radix-ui/react-dropdown-menu": "^2.1.4", | ||
"@radix-ui/react-label": "^2.1.1", | ||
"@radix-ui/react-slot": "^1.1.1", | ||
"@reduxjs/toolkit": "^2.5.0", | ||
"@tanstack/react-query": "^5.62.11", | ||
"@tanstack/react-query-devtools": "^5.62.11", | ||
"@types/react-redux": "^7.1.34", | ||
"axios": "^1.7.9", | ||
"class-variance-authority": "^0.7.1", | ||
"clsx": "^2.1.1", | ||
"jwt-decode": "^4.0.0", | ||
"lucide-react": "^0.469.0", | ||
"next": "14.2.16", | ||
"react": "^18", | ||
"react-dom": "^18", | ||
"react-hook-form": "^7.54.2", | ||
"react-redux": "^9.2.0", | ||
"tailwind-merge": "^2.6.0", | ||
"tailwindcss-animate": "^1.0.7", | ||
"zod": "^3.24.1" | ||
}, |
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.
💡 Codebase verification
Update Dependencies: Security Vulnerabilities Found 🚨
The security scan revealed critical vulnerabilities in two major dependencies:
-
axios@^1.7.9
:- HIGH severity: Server-Side Request Forgery vulnerability in versions <= 1.7.3
- Update to version 1.7.4 or later
-
- HIGH severity: Authorization bypass vulnerability in versions < 14.2.15
- HIGH severity: Cache Poisoning in versions < 14.2.10
- MODERATE severity: DoS in image optimization < 14.2.7
- Current version (14.2.16) is secure
Recommended actions:
- Update axios to a version >= 1.7.4
- Lock down the version ranges using exact versions or tilde (~) instead of caret (^) for better security control
Other scanned dependencies (@reduxjs/toolkit, @tanstack/react-query, jwt-decode, react-redux) show no known vulnerabilities.
🔗 Analysis chain
Verify security of dependencies
Several dependencies are using caret (^) version ranges, which could potentially introduce breaking changes or security vulnerabilities. Let's verify the security status of critical dependencies.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check for known vulnerabilities in dependencies
# Check for known vulnerabilities using GitHub's security advisories
for pkg in "@reduxjs/toolkit" "@tanstack/react-query" "axios" "jwt-decode" "next" "react-redux"; do
echo "Checking $pkg..."
gh api graphql -f query='{
securityVulnerabilities(first: 5, ecosystem: NPM, package: "'$pkg'") {
nodes {
advisory {
summary
severity
publishedAt
}
vulnerableVersionRange
firstPatchedVersion {
identifier
}
}
}
}'
done
Length of output: 5330
@@ -0,0 +1,98 @@ | |||
import { useMutation } from "@tanstack/react-query"; | |||
import { useDispatch } from "react-redux"; | |||
import { jwtDecode } from "jwt-decode"; |
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.
Correct import link for jwt-decode.
jwtDecode
is typically a default export from the jwt-decode
package. This can cause runtime errors if not imported correctly. Verify that your bundler supports named imports for jwt-decode
; otherwise, use the default import:
-import { jwtDecode } from "jwt-decode";
+import jwtDecode from "jwt-decode";
📝 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.
import { jwtDecode } from "jwt-decode"; | |
import jwtDecode from "jwt-decode"; |
netmanager-app/core/apis/users.ts
Outdated
getUserDetails: async (userID: string) => { | ||
return await createAxiosInstance() | ||
.get(`${USERS_MGT_URL}/users/${userID}`) | ||
.then((response) => response.data); | ||
}, |
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.
🛠️ Refactor suggestion
Add robust type annotations.
It’s good practice to annotate the return type of your async methods explicitly to maintain clarity and consistency for future contributors.
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: 8
♻️ Duplicate comments (1)
netmanager-app/core/hooks/users.ts (1)
3-3
:⚠️ Potential issueCorrect import link for jwt-decode
The named import from jwt-decode could cause runtime errors.
-import { jwtDecode } from "jwt-decode"; +import jwtDecode from "jwt-decode";
🧹 Nitpick comments (23)
netmanager-app/package.json (1)
37-47
: Enhance development toolingConsider adding these commonly used dev dependencies for a better development experience:
prettier
- For consistent code formattinghusky
- For git hooks to enforce quality checkslint-staged
- For running linters on staged files@testing-library/react
- For component testing@testing-library/jest-dom
- For DOM testing utilitiesHere's a suggested addition:
"devDependencies": { + "@testing-library/jest-dom": "^6.1.4", + "@testing-library/react": "^14.0.0", + "husky": "^8.0.3", + "lint-staged": "^15.0.2", + "prettier": "^3.0.3", ...existing devDependencies... }netmanager-app/app/(authenticated)/analytics/page.tsx (4)
4-5
: Consider renaming or clarifying component scope.The function name “Analytics” might be slightly misleading given the rendered title is “Dashboard.” Aligning the component name and title prevents confusion between “analytics” and “dashboard.”
6-8
: Ensure consistent layout semantics and accessibility.The usage of utility classes (e.g.,
p-6
) is fine, but wrapping the content in more semantic tags (like<main>
or<section>
) could improve structure and accessibility.
9-32
: Leverage dynamic data over hard-coded stats.Currently, the values (
156
,42
,1.2M
) are hard-coded. In production, consider fetching these metrics dynamically from an API. This approach ensures accurate, up-to-date information whenever the dashboard is rendered.
9-32
: Consolidate repetitive card patterns.You may factor out a smaller “StatCard” component to handle the repeated heading-content pattern, making the code more maintainable and reducing duplication.
netmanager-app/app/(authenticated)/sites/[id]/devices.tsx (1)
53-59
: Recommendation: Use an enum or constant for deployment statuses
Currently, the checks and string literals for"Deployed"
can be error-prone if the statuses proliferate. Consider using a typed enum or union for a more robust approach.netmanager-app/app/(authenticated)/sites/page.tsx (2)
26-52
: Replace placeholder data fetching with real API calls
Sample data is useful for development, but plan to retrieve it from an API or appropriate data source in production code.
59-62
: Heads-up on potential performance issue with large datasets
Filtering on every keystroke can become expensive if thesites
array grows significantly. Consider adding client-side pagination or debouncing the input for better performance.netmanager-app/app/(authenticated)/sites/[id]/page.tsx (2)
58-59
:isEditing
state is not actively used
TheisEditing
state is defined but not used elsewhere. If not needed, consider removing it to reduce clutter and reflect a more accurate state of the component.
25-56
: Ensure that sample data is replaced with real fetched data
HardcodingsiteData
anddevices
helps with UI development, but they must be fetched from persistent storage or an API in production. This ensures the details stay in sync with the source of truth.netmanager-app/core/redux/hooks.ts (1)
4-6
: Encourage consistent usage.
Make sure every component in the codebase usesuseAppDispatch
anduseAppSelector
to maintain consistency and avoid type mismatches.netmanager-app/components/ui/alert.tsx (1)
35-46
: AlertTitle alignment.
The naming and usage follow a readable pattern. Everything looks good, especially the well-chosen heading element.netmanager-app/app/forgot-password/page.tsx (1)
47-55
: Submission flow clarity.
TheonSubmit
function sets status to "idle" before performing the async operation. It’s a good pattern to keep the user interface updated.netmanager-app/app/login/page.tsx (4)
3-7
: Consider lazy-loading or grouping imports if build performance is hindered.
Although these imports are straightforward and organized, if the application grows rapidly, you might want to explore code-splitting for heavier dependencies or shared library code.
32-37
: Validate multi-factor or advanced password constraints if applicable.
Currently, the validation ensures a minimum length of 8 and valid email format. If your application requires stronger security, consider more robust constraints such as uppercase letters, numbers, or multi-factor authentication.
52-70
: Guard against repeated login attempts.
The try/catch is well-handled. For high-security scenarios, consider implementing rate limiting or a cooldown after multiple failed attempts to mitigate brute-force attacks.
114-120
: Expand error handling for accessibility and analytics.
When displaying an error message, consider logging it (in an observable or analytics platform) to detect repeated login failures, which might hint at user confusion or malicious attempts.netmanager-app/app/page.tsx (1)
12-18
: Consider a brief loading transition or skeleton.
The immediate redirect is fine, but it might be worth including a short transitional state or skeleton if the redirect occasionally slows. This can enhance user experience.netmanager-app/core/apis/axiosConfig.ts (1)
12-39
: Consolidate interceptors for further maintainability.
Currently, the interceptor logic is straightforward. As more complex logic arises (e.g., token refresh flows), consider abstracting out each scenario for better testability.🧰 Tools
🪛 Biome (1.9.4)
[error] 24-24: Avoid the delete operator which can impact performance.
Unsafe fix: Use an undefined assignment instead.
(lint/performance/noDelete)
[error] 27-27: A character class cannot match a joined character sequence.
A zero width joiner composes several emojis into a new one. Replace the character class with an alternation.
(lint/suspicious/noMisleadingCharacterClass)
netmanager-app/app/providers.tsx (1)
20-40
: Optimize query client options for large-scale data.
The default stale time and refetch strategies might need fine-tuning if your app will handle large data sets or multiple queries. Tweaking these can enhance performance consistency.netmanager-app/core/redux/slices/userSlice.ts (1)
19-26
: Consider usingas const
for type safety in initial state arraysThe empty arrays in the initial state could benefit from explicit typing to prevent accidental mutations.
- availableNetworks: [], - userGroups: [], + availableNetworks: [] as Network[], + userGroups: [] as Group[],netmanager-app/core/hooks/users.ts (1)
96-114
: Add type safety to localStorage operationsThe session restoration logic should include type checking for parsed JSON data.
const restoreSession = () => { const token = localStorage.getItem("token"); const storedUserDetails = localStorage.getItem("userDetails"); const storedActiveNetwork = localStorage.getItem("activeNetwork"); if (token && storedUserDetails) { - const userDetails = JSON.parse(storedUserDetails) as UserDetails; + try { + const userDetails = JSON.parse(storedUserDetails) as UserDetails; + if (!userDetails._id) throw new Error("Invalid user details"); + dispatch(setUserDetails(userDetails)); if (storedActiveNetwork) { - const activeNetwork = JSON.parse(storedActiveNetwork) as Network; + const activeNetwork = JSON.parse(storedActiveNetwork) as Network; + if (!activeNetwork.net_name) throw new Error("Invalid network"); dispatch(setActiveNetwork(activeNetwork)); } + } catch (error) { + console.error("Failed to restore session:", error); + handleLogout(); + } } else { handleLogout(); } };netmanager-app/core/apis/users.ts (1)
16-130
: Consider implementing commented endpoints incrementallyThe large number of commented endpoints suggests future functionality. Consider implementing these endpoints incrementally as needed, with proper typing and error handling.
Create separate files for different API domains (e.g.,
roleApi.ts
,networkApi.ts
,teamApi.ts
) to improve maintainability.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
netmanager-app/package-lock.json
is excluded by!**/package-lock.json
📒 Files selected for processing (20)
netmanager-app/app/(authenticated)/analytics/page.tsx
(1 hunks)netmanager-app/app/(authenticated)/layout.tsx
(1 hunks)netmanager-app/app/(authenticated)/sites/[id]/devices.tsx
(1 hunks)netmanager-app/app/(authenticated)/sites/[id]/page.tsx
(1 hunks)netmanager-app/app/(authenticated)/sites/page.tsx
(1 hunks)netmanager-app/app/(authenticated)/sites/site-form.tsx
(1 hunks)netmanager-app/app/forgot-password/page.tsx
(1 hunks)netmanager-app/app/layout.tsx
(1 hunks)netmanager-app/app/login/page.tsx
(1 hunks)netmanager-app/app/page.tsx
(1 hunks)netmanager-app/app/providers.tsx
(1 hunks)netmanager-app/app/types/users.ts
(1 hunks)netmanager-app/components/ui/alert.tsx
(1 hunks)netmanager-app/core/apis/axiosConfig.ts
(1 hunks)netmanager-app/core/apis/users.ts
(1 hunks)netmanager-app/core/hooks/users.ts
(1 hunks)netmanager-app/core/redux/hooks.ts
(1 hunks)netmanager-app/core/redux/slices/userSlice.ts
(1 hunks)netmanager-app/core/urls.tsx
(1 hunks)netmanager-app/package.json
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- netmanager-app/app/layout.tsx
- netmanager-app/app/types/users.ts
🧰 Additional context used
🪛 Biome (1.9.4)
netmanager-app/core/apis/axiosConfig.ts
[error] 24-24: Avoid the delete operator which can impact performance.
Unsafe fix: Use an undefined assignment instead.
(lint/performance/noDelete)
[error] 27-27: A character class cannot match a joined character sequence.
A zero width joiner composes several emojis into a new one. Replace the character class with an alternation.
(lint/suspicious/noMisleadingCharacterClass)
🔇 Additional comments (26)
netmanager-app/package.json (2)
5-10
: Add test script configurationThe PR checklist mentions the need for unit or end-to-end tests, but there's no test script configured. Consider adding a test script using Jest or your preferred testing framework.
11-36
: Review dependency versioning strategyI notice several dependencies using very recent versions with caret (^) ranges. This could lead to stability issues:
@reduxjs/toolkit@^2.5.0
- Major version 2 was recently released@tanstack/react-query@^5.62.11
- Version 5 includes breaking changesreact-redux@^9.2.0
- Major version 9 has significant changesConsider:
- Using tilde (~) for patch updates or exact versions for better stability
- Testing thoroughly with these newer major versions
- Documenting any breaking changes that required updates in your migration
Would you like me to help create a dependency update strategy that balances security and stability?
netmanager-app/app/(authenticated)/analytics/page.tsx (2)
1-2
: Good use of third-party UI components.Imports from the
@/components/ui/card
library are clear and well-structured. This approach facilitates consistent UI styling across the application.
34-36
: Looks solid for a first pass.Returning the JSX from this component is straightforward, with no extraneous logic needed in this file.
netmanager-app/app/(authenticated)/sites/[id]/devices.tsx (2)
18-19
: Looks good and straightforward!
The function signature and prop definition align well, making it clear how to pass the devices data.
35-35
: Consider using a more reliable key property
Relying ondevice.name
as the unique key can be risky if device names are not guaranteed to be unique. If possible, use a stable, unique identifier (e.g., a UUID).netmanager-app/app/(authenticated)/sites/site-form.tsx (1)
23-28
: Longitude/latitude regex might be oversimplified
Latitude and longitude can be hard to validate with a single regex (e.g., checking bounds of -90 to 90 for lat and -180 to 180 for long). Consider more comprehensive checks or specialized validation libraries if needed.netmanager-app/app/(authenticated)/layout.tsx (4)
1-2
: Good practice to enforce client-side rendering.
Using"use client";
at the top ensures the code is treated as a client component in Next.js, which is essential if you're dealing with client-side hooks or states. Keep it up!
3-5
: Global style import check.
Importingglobals.css
here is fine. However, confirm you won't have conflicting styles with other global imports.
6-10
: Clear, concise prop declaration.
Defining thechildren
prop type fosters clarity and maintainability. Good job on usingReact.ReactNode
.
11-12
: Layout rendering is straightforward.
Wrapping child components with theLayout
container keeps the authenticated routes tidy. No issues spotted.netmanager-app/core/redux/hooks.ts (1)
1-2
: Typed redux imports.
Doing a typed import foruseDispatch
anduseSelector
helps leverage TypeScript’s safety. Nicely done.netmanager-app/components/ui/alert.tsx (5)
1-5
: Imports aligned with the design system.
Bringing inclass-variance-authority
and utility methods is a good approach to maintain consistent styling across UI components.
6-20
: Variant-driven styling.
Thecva
usage shows a clear separation of variants (default
anddestructive
). This helps keep UI consistent.
22-34
: Forward ref usage for Alert.
UsingReact.forwardRef
is beneficial for future expansions, e.g., focusing or measuring the alert element. Nicely done!
47-58
: AlertDescription modernization.
Applying a text utility class automatically to children’s<p>
elements is a clever approach. Good use of class merges.
59-59
: Clean exports.
Exporting all alert items in a single statement is consistent and easy to import in other files.netmanager-app/app/forgot-password/page.tsx (4)
1-2
: Client-side rendering for form interactions.
This directive is essential in Next.js for using hooks likeuseState
anduseEffect
.
3-7
: Libraries for form management and validation.
Usingreact-hook-form
pluszod
is an excellent combination for strongly typed form validation.
32-34
: Schema definition best practice.
Defining the.email()
ensures a robust email validation right at the schema level.
57-114
: Comprehensive user feedback.
Providing success or error alerts is a great user experience. The page structure is intuitive and well organized.netmanager-app/core/apis/axiosConfig.ts (1)
27-27
: Validate zero-width joiner usage in regex if used.
For clarity, review your approach to removing zero-width characters. If these characters compose emojis or advanced glyphs, a more targeted solution than a simple regex might be required.🧰 Tools
🪛 Biome (1.9.4)
[error] 27-27: A character class cannot match a joined character sequence.
A zero width joiner composes several emojis into a new one. Replace the character class with an alternation.
(lint/suspicious/noMisleadingCharacterClass)
netmanager-app/app/providers.tsx (1)
9-18
: Add error boundary for session restoration.
IfrestoreSession
faces network failures or invalid tokens, consider handling those errors gracefully (e.g., logging out the user) to avoid partial states.netmanager-app/core/redux/slices/userSlice.ts (2)
57-58
: LGTM! Clean and standard Redux exportsThe exports follow Redux best practices by explicitly exporting both actions and reducer.
38-46
: Add error handling for role permissions mappingThe role permissions mapping assumes the role object structure is always valid. Consider adding error handling.
netmanager-app/core/apis/users.ts (1)
6-15
: 🛠️ Refactor suggestionAdd return type annotations and error handling
The API methods should have explicit return types and error handling.
- loginUser: async (data: LoginCredentials) => { + loginUser: async (data: LoginCredentials): Promise<LoginResponse> => { return await createAxiosInstance() .post(`${USERS_MGT_URL}/loginUser`, data) - .then((response) => response.data); + .then((response) => response.data) + .catch((error) => { + console.error('Login failed:', error); + throw error; + }); }, - getUserDetails: async (userID: string) => { + getUserDetails: async (userID: string): Promise<UserDetailsResponse> => { return await createAxiosInstance() .get(`${USERS_MGT_URL}/${userID}`) - .then((response) => response.data); + .then((response) => response.data) + .catch((error) => { + console.error('Failed to fetch user details:', error); + throw error; + }); },Likely invalid or redundant comment.
import { stripTrailingSlash } from "@/lib/utils"; | ||
export const BASE_API_URL = stripTrailingSlash( | ||
process.env.NEXT_PUBLIC_API_URL || "" | ||
); |
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.
🛠️ Refactor suggestion
Consider enhancing error handling and type safety for the base URL configuration
The current implementation could be improved in several ways:
- The empty string fallback might lead to silent failures
- Missing runtime validation for malformed URLs
- No type safety for the environment variable
Consider applying these improvements:
+ import { z } from 'zod';
+ const envSchema = z.object({
+ NEXT_PUBLIC_API_URL: z.string().url().min(1),
+ });
+ const env = envSchema.safeParse({
+ NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL
+ });
+ if (!env.success) {
+ throw new Error(
+ `Invalid environment variables: ${env.error.flatten().fieldErrors.NEXT_PUBLIC_API_URL?.join(', ')}`
+ );
+ }
- export const BASE_API_URL = stripTrailingSlash(
- process.env.NEXT_PUBLIC_API_URL || ""
- );
+ export const BASE_API_URL = stripTrailingSlash(env.data.NEXT_PUBLIC_API_URL);
Committable suggestion skipped: line range outside the PR's diff.
export const USERS_MGT_URL = `${BASE_API_URL}/users`; | ||
export const DEVICES_MGT_URL = `${BASE_API_URL}/devices`; | ||
export const SITES_MGT_URL = `${BASE_API_URL}/devices/sites`; | ||
export const ANALYTICS_MGT_URL = `${BASE_API_URL}/analytics`; |
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.
💡 Codebase verification
Sites should be a top-level resource, not nested under devices
Based on the codebase analysis:
-
The
SITES_MGT_URL
is inconsistently nested under/devices/sites
while other endpoints like/users
,/devices
, and/analytics
are top-level resources. -
Evidence shows that sites are treated as a primary resource throughout the codebase:
- Multiple independent site-related URLs exist in different services
- Sites have their own management pages and components
- Sites are referenced directly in analytics, data exports, and user preferences
Recommended change:
- export const SITES_MGT_URL = `${BASE_API_URL}/devices/sites`;
+ export const SITES_MGT_URL = `${BASE_API_URL}/sites`;
Regarding the other suggestions in the review:
- API versioning: Not currently used in the codebase's API URLs
- TypeScript types: Good practice but not critical for URL constants
🔗 Analysis chain
Consider standardizing API path structure and adding type safety
A few observations about the endpoint structure:
SITES_MGT_URL
is nested underdevices/
, which seems inconsistent with other top-level resources- Missing API version prefix (e.g.,
/v1/
) - No TypeScript types for the URL constants
Consider these improvements:
+ const API_VERSION = 'v1';
+
+ type ApiEndpoint = `${typeof BASE_API_URL}/${typeof API_VERSION}/${string}`;
+
- export const USERS_MGT_URL = `${BASE_API_URL}/users`;
+ export const USERS_MGT_URL: ApiEndpoint = `${BASE_API_URL}/${API_VERSION}/users`;
- export const DEVICES_MGT_URL = `${BASE_API_URL}/devices`;
+ export const DEVICES_MGT_URL: ApiEndpoint = `${BASE_API_URL}/${API_VERSION}/devices`;
- export const SITES_MGT_URL = `${BASE_API_URL}/devices/sites`;
+ export const SITES_MGT_URL: ApiEndpoint = `${BASE_API_URL}/${API_VERSION}/sites`;
- export const ANALYTICS_MGT_URL = `${BASE_API_URL}/analytics`;
+ export const ANALYTICS_MGT_URL: ApiEndpoint = `${BASE_API_URL}/${API_VERSION}/analytics`;
Let's verify if this API structure aligns with the backend:
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check if the backend API follows a versioned structure
curl -I "${process.env.NEXT_PUBLIC_API_URL}/v1/users" 2>/dev/null | head -n 1
Length of output: 144
Script:
#!/bin/bash
# Let's check the API structure by searching for API calls in the codebase
rg -g '*.{ts,tsx,js,jsx}' "fetch.*${BASE_API_URL}" -A 2
# Also search for any existing version prefixes
rg -g '*.{ts,tsx,js,jsx}' "/v[0-9]+/" -A 2
# Check if sites are accessed through devices elsewhere
rg -g '*.{ts,tsx,js,jsx}' "devices/sites" -A 2
# Look for API-related configuration
fd -e ts -e js -e json "api|config" --type f --exec cat {}
Length of output: 67911
Script:
#!/bin/bash
# Let's check if sites are accessed independently in the codebase
rg -g '*.{ts,tsx,js,jsx}' "sites['\"]?\s*[:=]|/sites['\"]?" -A 2
# Check for any API version prefixes in configuration files
fd -g "*.{ts,tsx,js,jsx,json}" -E "node_modules" -E "dist" -E "build" --exec grep -l "v[0-9]" {} \;
# Look for API route definitions
rg -g '*.{ts,tsx,js,jsx}' "const.*_URL.*=.*${BASE_API_URL}" -A 2
Length of output: 50446
{filteredSites.map((site) => ( | ||
<TableRow | ||
key={site.id} | ||
className="cursor-pointer" | ||
onClick={() => router.push(`/sites/${site.id}`)} | ||
> |
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.
🛠️ Refactor suggestion
Prevent accidental navigation when clicking action buttons
Making the entire row clickable might interfere with the trash button’s onClick event. Consider stopping event propagation on the button click to avoid conflicting navigation.
function onSubmit(values: SiteFormValues) { | ||
console.log(values); | ||
// Here you would typically send this data to your API | ||
} |
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.
🛠️ Refactor suggestion
Handle form submission gracefully
Right now, the form logs values to the console. As next steps, consider calling an API endpoint and handling success/error states, adding appropriate user feedback (e.g., notifications).
config.params = { ...config.params }; | ||
} else { | ||
// Remove the JWT header and use a query parameter | ||
delete config.headers["Authorization"]; |
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.
🛠️ Refactor suggestion
Avoid using delete operator for performance.
As flagged by static analysis, using delete
can degrade performance in key-heavy objects. Instead, set the property to undefined
if you want to remove it from headers.
- delete config.headers["Authorization"];
+ config.headers["Authorization"] = undefined;
📝 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.
delete config.headers["Authorization"]; | |
config.headers["Authorization"] = undefined; |
🧰 Tools
🪛 Biome (1.9.4)
[error] 24-24: Avoid the delete operator which can impact performance.
Unsafe fix: Use an undefined assignment instead.
(lint/performance/noDelete)
setUserDetails(state: UserState, action: PayloadAction<UserDetails>) { | ||
state.userDetails = action.payload; | ||
state.isAuthenticated = true; | ||
state.availableNetworks = action.payload.networks || []; | ||
state.userGroups = action.payload.groups || []; | ||
}, |
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.
🛠️ Refactor suggestion
Add null checks for optional payload properties
The current implementation assumes networks
and groups
exist in the payload. Add null checks to handle cases where these properties might be undefined.
- state.availableNetworks = action.payload.networks || [];
- state.userGroups = action.payload.groups || [];
+ state.availableNetworks = action.payload?.networks ?? [];
+ state.userGroups = action.payload?.groups ?? [];
📝 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.
setUserDetails(state: UserState, action: PayloadAction<UserDetails>) { | |
state.userDetails = action.payload; | |
state.isAuthenticated = true; | |
state.availableNetworks = action.payload.networks || []; | |
state.userGroups = action.payload.groups || []; | |
}, | |
setUserDetails(state: UserState, action: PayloadAction<UserDetails>) { | |
state.userDetails = action.payload; | |
state.isAuthenticated = true; | |
state.availableNetworks = action.payload?.networks ?? []; | |
state.userGroups = action.payload?.groups ?? []; | |
}, |
netmanager-app/core/hooks/users.ts
Outdated
const airqoNetwork = userInfo.networks?.find( | ||
(network: Network) => network.net_name.toLowerCase() === "airqo" | ||
); | ||
if (airqoNetwork) { | ||
dispatch(setActiveNetwork(airqoNetwork)); | ||
localStorage.setItem("activeNetwork", JSON.stringify(airqoNetwork)); | ||
} else if (userInfo?.networks && userInfo.networks.length > 0) { | ||
dispatch(setActiveNetwork(userInfo.networks[0])); | ||
localStorage.setItem( | ||
"activeNetwork", | ||
JSON.stringify(userInfo.networks[0]) | ||
); | ||
} |
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.
🛠️ Refactor suggestion
Add error handling for network selection
The network selection logic should handle potential type mismatches and network availability more gracefully.
- const airqoNetwork = userInfo.networks?.find(
+ const networks = userInfo.networks ?? [];
+ const airqoNetwork = networks.find(
(network: Network) => network.net_name.toLowerCase() === "airqo"
);
+ const defaultNetwork = airqoNetwork ?? networks[0];
+
+ if (defaultNetwork) {
+ dispatch(setActiveNetwork(defaultNetwork));
+ localStorage.setItem("activeNetwork", JSON.stringify(defaultNetwork));
+ }
- if (airqoNetwork) {
- dispatch(setActiveNetwork(airqoNetwork));
- localStorage.setItem("activeNetwork", JSON.stringify(airqoNetwork));
- } else if (userInfo?.networks && userInfo.networks.length > 0) {
- dispatch(setActiveNetwork(userInfo.networks[0]));
- localStorage.setItem(
- "activeNetwork",
- JSON.stringify(userInfo.networks[0])
- );
- }
📝 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.
const airqoNetwork = userInfo.networks?.find( | |
(network: Network) => network.net_name.toLowerCase() === "airqo" | |
); | |
if (airqoNetwork) { | |
dispatch(setActiveNetwork(airqoNetwork)); | |
localStorage.setItem("activeNetwork", JSON.stringify(airqoNetwork)); | |
} else if (userInfo?.networks && userInfo.networks.length > 0) { | |
dispatch(setActiveNetwork(userInfo.networks[0])); | |
localStorage.setItem( | |
"activeNetwork", | |
JSON.stringify(userInfo.networks[0]) | |
); | |
} | |
const networks = userInfo.networks ?? []; | |
const airqoNetwork = networks.find( | |
(network: Network) => network.net_name.toLowerCase() === "airqo" | |
); | |
const defaultNetwork = airqoNetwork ?? networks[0]; | |
if (defaultNetwork) { | |
dispatch(setActiveNetwork(defaultNetwork)); | |
localStorage.setItem("activeNetwork", JSON.stringify(defaultNetwork)); | |
} |
netmanager-app/core/hooks/users.ts
Outdated
const loginMutation = useMutation({ | ||
mutationFn: async (credentials: LoginCredentials) => { | ||
// 1. Login and get token | ||
const loginResponse = (await users.loginUser( | ||
credentials | ||
)) as LoginResponse; | ||
const token = loginResponse.token; | ||
|
||
// 2. Store token | ||
localStorage.setItem("token", token); | ||
|
||
// 3. Decode token | ||
const decoded = jwtDecode<DecodedToken>(token); | ||
|
||
// 4. Create userDetails from decoded token | ||
const userDetails: UserDetails = { | ||
_id: decoded._id, | ||
firstName: decoded.firstName, | ||
lastName: decoded.lastName, | ||
userName: decoded.userName, | ||
email: decoded.email, | ||
organization: decoded.organization, | ||
long_organization: decoded.long_organization, | ||
privilege: decoded.privilege, | ||
country: decoded.country, | ||
profilePicture: decoded.profilePicture, | ||
phoneNumber: decoded.phoneNumber, | ||
createdAt: decoded.createdAt, | ||
updatedAt: decoded.updatedAt, | ||
rateLimit: decoded.rateLimit, | ||
lastLogin: decoded.lastLogin, | ||
iat: decoded.iat, | ||
}; | ||
|
||
// 5. Store user details | ||
localStorage.setItem("userDetails", JSON.stringify(userDetails)); | ||
|
||
// 7. Get user info store in redux | ||
const userDetailsResponse = (await users.getUserDetails( | ||
loginResponse._id | ||
)) as UserDetailsResponse; | ||
const userInfo = userDetailsResponse.users[0]; | ||
dispatch(setUserDetails(userDetails)); | ||
|
||
// 8. Set AirQo as default network | ||
const airqoNetwork = userInfo.networks?.find( | ||
(network: Network) => network.net_name.toLowerCase() === "airqo" | ||
); | ||
if (airqoNetwork) { | ||
dispatch(setActiveNetwork(airqoNetwork)); | ||
localStorage.setItem("activeNetwork", JSON.stringify(airqoNetwork)); | ||
} else if (userInfo?.networks && userInfo.networks.length > 0) { | ||
dispatch(setActiveNetwork(userInfo.networks[0])); | ||
localStorage.setItem( | ||
"activeNetwork", | ||
JSON.stringify(userInfo.networks[0]) | ||
); | ||
} | ||
|
||
return userInfo; | ||
}, |
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.
🛠️ Refactor suggestion
Refactor login mutation for better separation of concerns
The login mutation handles too many responsibilities. Consider extracting token handling and user info fetching into separate functions.
+ const handleTokenStorage = (token: string) => {
+ localStorage.setItem("token", token);
+ return jwtDecode<DecodedToken>(token);
+ };
+
+ const createUserDetails = (decoded: DecodedToken): UserDetails => ({
+ _id: decoded._id,
+ firstName: decoded.firstName,
+ // ... other fields
+ });
+
const loginMutation = useMutation({
mutationFn: async (credentials: LoginCredentials) => {
const loginResponse = await users.loginUser(credentials) as LoginResponse;
- const token = loginResponse.token;
- localStorage.setItem("token", token);
- const decoded = jwtDecode<DecodedToken>(token);
+ const decoded = handleTokenStorage(loginResponse.token);
+ const userDetails = createUserDetails(decoded);
Committable suggestion skipped: line range outside the PR's diff.
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: 4
🧹 Nitpick comments (9)
netmanager-app/core/redux/slices/userSlice.ts (1)
7-8
: Consider enhancing type safety for userGroupsThe
Group
type is imported but theuserGroups
array in the state isn't leveraging its type safety effectively in the reducers. Consider adding type validation or transformation logic when setting user groups.Also applies to: 16-16
netmanager-app/app/components/sidebar.tsx (5)
3-43
: Consider optimizing imports and fixing Map shadowing
- The
Map
import from lucide-react shadows the globalMap
object. Consider renaming it toMapIcon
for clarity.- Consider grouping related imports together (e.g., all lucide-react icons in a separate file) to improve maintainability.
- import { Map } from "lucide-react"; + import { Map as MapIcon } from "lucide-react"; // Later in the code: - <Map size={18} /> + <MapIcon size={18} />🧰 Tools
🪛 Biome (1.9.4)
[error] 18-18: Do not shadow the global "Map" property.
Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.
(lint/suspicious/noShadowRestrictedNames)
55-55
: Remove console.log statementRemove debugging console.log statement before deploying to production.
- console.log(availableNetworks);
61-67
: Optimize useEffect dependenciesThe useEffect hook includes
userCollapsed
in its dependency array but also sets it within the effect. This could lead to unnecessary re-renders. Consider removing it from the dependency array since it's only used to set its own state.useEffect(() => { if (isDevicesActive && !userCollapsed) { setIsDevicesOpen(true); } else if (!isDevicesActive) { setUserCollapsed(false); } - }, [pathname, isDevicesActive, userCollapsed]); + }, [pathname, isDevicesActive]);
98-107
: Add empty state handling for networks listThe network dropdown menu should handle the case when
availableNetworks
is empty. Consider adding a placeholder message when no networks are available.<DropdownMenuLabel>Switch Network</DropdownMenuLabel> <DropdownMenuSeparator /> + {availableNetworks.length === 0 && ( + <DropdownMenuItem disabled> + No networks available + </DropdownMenuItem> + )} {availableNetworks.map((network) => (
124-128
: Extract repeated className patternsConsider creating a utility function or custom hook to generate the common className patterns. This will improve maintainability and reduce code duplication.
const getNavLinkClassName = (isActive: boolean) => { return `flex items-center gap-2 text-sm text-foreground hover:bg-accent hover:text-accent-foreground p-2 rounded-md ${ isActive ? "bg-accent text-accent-foreground" : "" }`; };Then use it like:
className={getNavLinkClassName(isActive("/analytics"))}Also applies to: 137-141, 150-154, 173-176, 186-189, 199-202, 227-230, 247-251, 259-262, 269-273, 284-286, 296-299, 309-310, 320-323, 342-345
netmanager-app/app/components/topbar.tsx (3)
24-34
: Consider persisting dark mode preferenceThe dark mode state currently only exists in memory. Consider persisting the user's preference in localStorage to maintain consistency across page refreshes.
const [darkMode, setDarkMode] = useState(false); + +useEffect(() => { + const savedMode = localStorage.getItem('darkMode') === 'true'; + setDarkMode(savedMode); +}, []); useEffect(() => { if (darkMode) { document.documentElement.classList.add("dark"); + localStorage.setItem('darkMode', 'true'); } else { document.documentElement.classList.remove("dark"); + localStorage.setItem('darkMode', 'false'); } }, [darkMode]);
40-52
: Add TypeScript interface and consider environment-specific URLsDefine a proper interface for the app configuration and consider using environment variables for URLs.
+interface AppConfig { + name: string; + url: string; + description: string; +} + -const apps = [ +const apps: AppConfig[] = [ { name: "Calibrate", - url: "/calibrate", + url: process.env.NEXT_PUBLIC_CALIBRATE_URL || "/calibrate", description: "Device Calibration Tool", }, // ... rest of the apps ];
24-125
: Add component documentation and TypeScript definitionsThe component is well-implemented, but would benefit from proper documentation and TypeScript definitions.
+interface TopbarProps { + className?: string; +} + +/** + * Topbar component that provides navigation and user controls + * @component + * @example + * return ( + * <Topbar className="custom-class" /> + * ) + */ -const Topbar = () => { +const Topbar: React.FC<TopbarProps> = ({ className }) => { // ... component implementation };
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
netmanager-app/app/components/sidebar.tsx
(1 hunks)netmanager-app/app/components/topbar.tsx
(1 hunks)netmanager-app/core/hooks/users.ts
(1 hunks)netmanager-app/core/redux/slices/userSlice.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- netmanager-app/core/hooks/users.ts
🧰 Additional context used
🪛 Biome (1.9.4)
netmanager-app/app/components/sidebar.tsx
[error] 18-18: Do not shadow the global "Map" property.
Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.
(lint/suspicious/noShadowRestrictedNames)
🔇 Additional comments (4)
netmanager-app/core/redux/slices/userSlice.ts (3)
19-26
: Well-structured initial stateThe initial state is properly typed and initialized with appropriate default values for all fields.
32-37
: Add null checks for optional payload propertiesThe current implementation assumes
networks
andgroups
exist in the payload.
60-66
: Clean and well-organized exportsThe exports follow Redux best practices by making individual actions available and exporting the reducer as default.
netmanager-app/app/components/topbar.tsx (1)
1-23
: Well-structured imports with clear organization!The imports are logically grouped and the "use client" directive is correctly placed for client-side interactivity.
logout(state: UserState) { | ||
state.isAuthenticated = false; | ||
state.userDetails = null; | ||
state.activeNetwork = null; | ||
state.availableNetworks = []; | ||
state.currentRole = null; | ||
}, |
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.
Reset userGroups in logout reducer
The logout reducer should reset all state fields to their initial values, but userGroups
is not being reset.
Apply this diff:
logout(state: UserState) {
state.isAuthenticated = false;
state.userDetails = null;
state.activeNetwork = null;
state.availableNetworks = [];
state.currentRole = null;
+ state.userGroups = [];
},
📝 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.
logout(state: UserState) { | |
state.isAuthenticated = false; | |
state.userDetails = null; | |
state.activeNetwork = null; | |
state.availableNetworks = []; | |
state.currentRole = null; | |
}, | |
logout(state: UserState) { | |
state.isAuthenticated = false; | |
state.userDetails = null; | |
state.activeNetwork = null; | |
state.availableNetworks = []; | |
state.currentRole = null; | |
state.userGroups = []; | |
}, |
setActiveNetwork(state: UserState, action: PayloadAction<Network>) { | ||
state.activeNetwork = action.payload; | ||
// Set current role based on active network | ||
const roleInNetwork = action.payload.role; | ||
state.currentRole = { | ||
role_name: roleInNetwork.role_name, | ||
permissions: roleInNetwork.role_permissions.map((p) => p.permission), | ||
}; | ||
}, |
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.
Add null checks for role properties
The role mapping logic could throw runtime errors if the role object or its properties are undefined.
Apply this diff to add proper null checks:
setActiveNetwork(state: UserState, action: PayloadAction<Network>) {
state.activeNetwork = action.payload;
// Set current role based on active network
- const roleInNetwork = action.payload.role;
+ const roleInNetwork = action.payload?.role;
+ if (!roleInNetwork) {
+ state.currentRole = null;
+ return;
+ }
state.currentRole = {
- role_name: roleInNetwork.role_name,
- permissions: roleInNetwork.role_permissions.map((p) => p.permission),
+ role_name: roleInNetwork.role_name ?? '',
+ permissions: roleInNetwork.role_permissions?.map((p) => p.permission) ?? [],
};
},
📝 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.
setActiveNetwork(state: UserState, action: PayloadAction<Network>) { | |
state.activeNetwork = action.payload; | |
// Set current role based on active network | |
const roleInNetwork = action.payload.role; | |
state.currentRole = { | |
role_name: roleInNetwork.role_name, | |
permissions: roleInNetwork.role_permissions.map((p) => p.permission), | |
}; | |
}, | |
setActiveNetwork(state: UserState, action: PayloadAction<Network>) { | |
state.activeNetwork = action.payload; | |
// Set current role based on active network | |
const roleInNetwork = action.payload?.role; | |
if (!roleInNetwork) { | |
state.currentRole = null; | |
return; | |
} | |
state.currentRole = { | |
role_name: roleInNetwork.role_name ?? '', | |
permissions: roleInNetwork.role_permissions?.map((p) => p.permission) ?? [], | |
}; | |
}, |
const handleNetworkChange = (network: Network) => { | ||
dispatch(setActiveNetwork(network)); | ||
localStorage.setItem("activeNetwork", JSON.stringify(network)); | ||
}; |
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.
🛠️ Refactor suggestion
Add error handling for network operations
The handleNetworkChange
function should include error handling for localStorage operations and dispatch failures. Consider wrapping it in a try-catch block and showing a user-friendly error message if the operation fails.
const handleNetworkChange = (network: Network) => {
+ try {
dispatch(setActiveNetwork(network));
localStorage.setItem("activeNetwork", JSON.stringify(network));
+ } catch (error) {
+ console.error('Failed to switch network:', error);
+ // TODO: Show user-friendly error message
+ }
};
📝 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.
const handleNetworkChange = (network: Network) => { | |
dispatch(setActiveNetwork(network)); | |
localStorage.setItem("activeNetwork", JSON.stringify(network)); | |
}; | |
const handleNetworkChange = (network: Network) => { | |
try { | |
dispatch(setActiveNetwork(network)); | |
localStorage.setItem("activeNetwork", JSON.stringify(network)); | |
} catch (error) { | |
console.error('Failed to switch network:', error); | |
// TODO: Show user-friendly error message | |
} | |
}; |
return ( | ||
<div className="h-16 border-b bg-background px-4 flex items-center justify-between"> | ||
{/* Left side - can be used for breadcrumbs or other navigation */} | ||
<div></div> | ||
|
||
{/* Right side - Apps and Profile */} | ||
<div className="flex items-center gap-4"> | ||
{/* Apps Navigation */} | ||
<DropdownMenu> | ||
<DropdownMenuTrigger asChild> | ||
<Button | ||
variant="ghost" | ||
className="flex items-center gap-2 text-foreground" | ||
> | ||
<GridIcon size={20} /> | ||
<span className="text-sm font-medium">Apps</span> | ||
</Button> | ||
</DropdownMenuTrigger> | ||
<DropdownMenuContent align="end" className="w-60"> | ||
{apps.map((app) => ( | ||
<DropdownMenuItem key={app.name} asChild> | ||
<Link href={app.url} className="flex items-center gap-3 p-2"> | ||
<div className="flex-1"> | ||
<div className="text-sm font-medium">{app.name}</div> | ||
<div className="text-xs text-muted-foreground"> | ||
{app.description} | ||
</div> | ||
</div> | ||
<ExternalLink size={16} className="text-muted-foreground" /> | ||
</Link> | ||
</DropdownMenuItem> | ||
))} | ||
</DropdownMenuContent> | ||
</DropdownMenu> | ||
|
||
{/* Profile Dropdown */} | ||
<DropdownMenu> | ||
<DropdownMenuTrigger asChild> | ||
<Button variant="ghost" size="icon" className="text-foreground"> | ||
<UserCircle size={24} /> | ||
</Button> | ||
</DropdownMenuTrigger> | ||
<DropdownMenuContent align="end"> | ||
<DropdownMenuLabel>My Account</DropdownMenuLabel> | ||
<DropdownMenuSeparator /> | ||
<DropdownMenuItem asChild> | ||
<Link href="/profile" className="flex items-center gap-2"> | ||
<UserCircle size={18} /> | ||
Profile Settings | ||
</Link> | ||
</DropdownMenuItem> | ||
<DropdownMenuItem onClick={toggleDarkMode}> | ||
{darkMode ? ( | ||
<Sun size={18} className="mr-2" /> | ||
) : ( | ||
<Moon size={18} className="mr-2" /> | ||
)} | ||
{darkMode ? "Light Mode" : "Dark Mode"} | ||
</DropdownMenuItem> | ||
<DropdownMenuSeparator /> | ||
<DropdownMenuItem className="text-destructive" onClick={logout}> | ||
<LogOut size={18} /> | ||
<span>Log out</span> | ||
</DropdownMenuItem> | ||
</DropdownMenuContent> | ||
</DropdownMenu> | ||
</div> | ||
</div> |
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.
🛠️ Refactor suggestion
Enhance accessibility and async operation handling
The UI implementation looks clean, but consider these improvements:
- Add ARIA labels for better accessibility
- Handle loading states during logout
- Add error handling for failed operations
<Button
variant="ghost"
className="flex items-center gap-2 text-foreground"
+ aria-label="Open applications menu"
>
// ... and for the logout operation:
-<DropdownMenuItem className="text-destructive" onClick={logout}>
+<DropdownMenuItem
+ className="text-destructive"
+ onClick={async () => {
+ try {
+ await logout();
+ } catch (error) {
+ // Handle error (e.g., show toast notification)
+ console.error('Logout failed:', error);
+ }
+ }}
+ disabled={isLoggingOut}
+>
<LogOut size={18} />
- <span>Log out</span>
+ <span>{isLoggingOut ? 'Logging out...' : 'Log out'}</span>
</DropdownMenuItem>
Committable suggestion skipped: line range outside the PR's diff.
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: 1
♻️ Duplicate comments (2)
netmanager-app/core/redux/slices/userSlice.ts (1)
52-58
:⚠️ Potential issueReset userGroups in logout
This was previously suggested in a past review. Thelogout
reducer should also resetuserGroups
to match the initial state.logout(state: UserState) { state.isAuthenticated = false; state.userDetails = null; state.activeNetwork = null; state.availableNetworks = []; state.currentRole = null; + state.userGroups = []; },
netmanager-app/core/hooks/users.ts (1)
3-3
:⚠️ Potential issueImport jwtDecode correctly
As noted in a previous review,jwt-decode
typically exports a default function. Using named imports may lead to errors unless your build pipeline specifically supports it.- import { jwtDecode } from "jwt-decode"; + import jwtDecode from "jwt-decode";
🧹 Nitpick comments (14)
netmanager-app/components/top-nav.tsx (3)
11-16
: Enhance accessibility for search input.
Currently, the search input lacks an explicit label. Screen readers may not recognize the intent of this field based solely on the placeholder text. Providing an accessible label or usingaria-label
would improve usability, especially for visually impaired users.<input + aria-label="Search" className="border-2 border-gray-300 bg-white h-10 px-5 pr-16 rounded-lg text-sm focus:outline-none" type="search" ... />
17-23
: Use a form element for better semantics and accessibility.
Since the button is of type "submit", wrapping the search input and button in a<form>
element would allow the browser and assistive technologies to treat them as a complete searchable form.<div className="flex-shrink-0 flex items-center"> - <input ... /> - <button type="submit">...</button> + <form className="relative"> + <input ... /> + <button type="submit">...</button> + </form> </div>
26-28
: Avoid using ambiguous icons without descriptive text.
The bell icon is used for notifications, but there is no accompanying label or accessible text for screen readers. Addingaria-label="Notifications"
ensures better accessibility.<button className="p-1 rounded-full text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" + aria-label="Notifications" > <Bell className="h-6 w-6" /> </button>netmanager-app/core/hooks/usePermissions.ts (1)
3-4
: Consider defining a type or interface forcurrentRole
.
Currently,currentRole
is accessed dynamically. Defining a type (e.g.,interface Role { permissions: string[] }
) would improve code clarity, static analysis, and maintainability.netmanager-app/components/permission-guard.tsx (2)
4-9
: Expand the fallback type if dynamic content is needed.
Right now,fallback
isReactNode | null
. If you anticipate rendering dynamic or asynchronous content, consider supporting a callback function for more flexibility.
26-28
: Potential accessibility improvement.
When access is denied,fallback
is returned. Consider rendering a component with an ARIA role or an accessible message to communicate restricted access for screen readers.netmanager-app/components/layout.tsx (1)
10-14
: Consider SSR/ hydration nuances for dark mode.
Since dark mode is read fromlocalStorage
, the UI might briefly flash in the wrong mode before hydration. For a smoother UX, consider server-side or inline scripts to mitigate flicker.netmanager-app/components/route-guard.tsx (2)
31-35
: Check for possibility of merging.
hasAccess
logic here closely matchesPermissionGuard
. If code duplication grows, consider centralizing this permission logic in a single component to reduce maintenance overhead.
37-47
: Clear user feedback for insufficient permissions.
Displaying a warning message with an icon and descriptive text is user-friendly. Consider providing a link or action for users to navigate or request additional permissions.netmanager-app/components/topbar.tsx (3)
25-34
: Persist dark mode preference across sessions
Consider persisting the user's dark mode preference in local storage (or similar) so that it persists across browser sessions, enhancing the user experience.
36-38
: Leverage React’s built-in state toggling
A succinct way to toggle the boolean value is:setDarkMode((prev) => !prev);
This can reduce potential mistakes if multiple toggles happen in quick succession.- const toggleDarkMode = () => { - setDarkMode(!darkMode); - }; + const toggleDarkMode = () => { + setDarkMode((prev) => !prev); + };
73-85
: Enhance accessibility for screen readers
When rendering dropdown items with dynamic elements, consider addingaria-label
or visually hidden text to provide better context for assistive technologies.netmanager-app/components/sidebar.tsx (2)
56-56
: Remove or reduce console logs
There is an activeconsole.log(availableNetworks);
which may clutter production logs. Recommend removing or converting it to a debug log.- console.log(availableNetworks); + // console.log(availableNetworks);
245-253
: Consider using icons that better represent device management
Although the radio icon might be acceptable, a device-themed icon might make it more intuitive to the user.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (12)
netmanager-app/app/(authenticated)/layout.tsx
(1 hunks)netmanager-app/app/(authenticated)/sites/page.tsx
(1 hunks)netmanager-app/components.json
(1 hunks)netmanager-app/components/layout.tsx
(1 hunks)netmanager-app/components/permission-guard.tsx
(1 hunks)netmanager-app/components/route-guard.tsx
(1 hunks)netmanager-app/components/sidebar.tsx
(1 hunks)netmanager-app/components/top-nav.tsx
(1 hunks)netmanager-app/components/topbar.tsx
(1 hunks)netmanager-app/core/hooks/usePermissions.ts
(1 hunks)netmanager-app/core/hooks/users.ts
(1 hunks)netmanager-app/core/redux/slices/userSlice.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- netmanager-app/components.json
- netmanager-app/app/(authenticated)/layout.tsx
- netmanager-app/app/(authenticated)/sites/page.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
netmanager-app/components/sidebar.tsx
[error] 18-18: Do not shadow the global "Map" property.
Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.
(lint/suspicious/noShadowRestrictedNames)
🔇 Additional comments (11)
netmanager-app/components/top-nav.tsx (2)
1-2
: Well-structured import statements.
The imports from React and lucide-react are clearly separated and straightforward.
31-38
: Implement user menu toggle behavior.
Currently, the button for opening the user menu only displays the icon and includes a placeholder label for screen readers. Consider implementing a toggle or dropdown with an accessible action.Offer to generate a script or open an issue to confirm there’s a plan for user menu interactions.
netmanager-app/core/hooks/usePermissions.ts (2)
6-7
: Smart use of optional chaining for safe access.
The nullish coalescing operator ensures that the code won't throw an error ifcurrentRole
is undefined.
10-11
: Function naming is intuitive.
hasAnyPermission
clearly communicates its intention, making it straightforward for other developers to integrate.netmanager-app/components/permission-guard.tsx (1)
20-24
: Straightforward conditional logic.
UsinghasAnyPermission
andhasAllPermissions
clarifies permission checks. Good job balancing user experience with maintainability!netmanager-app/components/layout.tsx (2)
1-2
: Designating this as a client component is appropriate.
Explicitly using"use client"
in Next.js ensures that the code can manipulatelocalStorage
and browser-specific APIs. Good approach!
16-26
: Well-structured layout.
Organizing the sidebar, topbar, and main container in this manner keeps responsibilities clear and scales well for future expansions.netmanager-app/components/route-guard.tsx (2)
14-18
: Consistent permission checking approach.
ReusingusePermissions
helps maintain consistency with the rest of the codebase and ensures a single source of truth for permission logic.
23-29
: Thoughtful loading state handling.
Displaying a spinner while the user’s state is initialized avoids rendering incorrect fallback components prematurely.netmanager-app/components/sidebar.tsx (1)
70-75
: Review userCollapsed logic
There is a conditional that toggles userCollapsed in response to devices being active. Verify that the user experience is clear and does not inadvertently expand or collapse sections in unexpected scenarios.netmanager-app/core/hooks/users.ts (1)
75-87
: Handle network selection more gracefully
Consider adding null checks in case no networks are returned, to avoid potential errors. This logic was flagged before for clarity and robustness.
Activity, | ||
UserCircle, | ||
Download, | ||
Map, |
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.
Rename the 'Map' import to avoid shadowing the global Map object
Renaming this icon import to something like MapIcon
or similar helps avoid confusion and potential naming conflicts, as flagged by the lint rule.
- Map,
+ MapIcon,
📝 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.
Map, | |
MapIcon, |
🧰 Tools
🪛 Biome (1.9.4)
[error] 18-18: Do not shadow the global "Map" property.
Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.
(lint/suspicious/noShadowRestrictedNames)
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: 3
🧹 Nitpick comments (11)
netmanager-app/core/apis/sites.ts (1)
7-18
: Consider typing forresponse.data
You might consider specifying a return type or leveraging generics, such as
axiosInstance.get<YourType>()
to ensure stronger type checking and better autocompletion in your codebase. This can reduce runtime surprises and provide clarity when consuming the method downstream.netmanager-app/core/redux/slices/sitesSlice.ts (2)
30-54
: Maintain consistent naming and cross-field checksInterfaces such as
Site
are well-organized. Keep an eye on naming to ensure naming reflects domain logic accurately. For instance, ifapproximate_latitude
is always required, ensure the type truly can't be null or undefined.
62-66
: Centralize initial loading checksYou're setting
isLoading
to false on initialization. If you need advanced or dynamic loading states in more complex flows, consider leveraging additional flags or statuses (like an enum) for verbose clarity.netmanager-app/core/redux/store.ts (1)
5-10
: Enhance debugging experience with Redux DevTools configurationIf needed, provide a configuration object to
configureStore
enabling or disabling DevTools based on environment. This helps in debugging in development and optimizes production builds.netmanager-app/core/redux/slices/userSlice.ts (1)
42-50
: Strengthen role existence checks
Ifrole
is missing or incomplete inaction.payload
, this mapping could break. Consider applying robust null checks and fallback logic forrole_name
androle_permissions
.netmanager-app/core/hooks/users.ts (2)
119-127
: Enhance error handling with specific error types.The error handling could be improved by:
- Using specific error types instead of the generic
error
- Providing user-friendly error messages
- Implementing retry logic for network failures
- onError: (error) => { + onError: (error: Error) => { + let errorMessage = "An unexpected error occurred"; + if (error instanceof NetworkError) { + errorMessage = "Network error. Please check your connection."; + } else if (error instanceof AuthenticationError) { + errorMessage = "Invalid credentials"; + } console.error("Login failed:", error); + console.error("User friendly message:", errorMessage); localStorage.removeItem("token"); // ... other cleanup },
130-139
: Consider enhancing the logout flow.The logout implementation could be improved by:
- Adding a confirmation dialog to prevent accidental logouts
- Handling any pending operations before logout
- Implementing proper cleanup for async tasks
- const handleLogout = () => { + const handleLogout = async () => { + if (window.confirm("Are you sure you want to logout?")) { + // Cancel any pending operations + await Promise.all([ + // Cancel pending mutations + loginMutation.reset(), + // Other cleanup tasks + ]); + localStorage.removeItem("token"); // ... other cleanup dispatch(logout()); router.push("/login"); + } };netmanager-app/components/sidebar.tsx (2)
58-58
: Extract path matching logic into a utility function.The
isActive
function is used repeatedly with similar patterns. Consider extracting it into a more robust utility.+const getNavItemClassName = (isActive: boolean) => { + return `flex items-center gap-2 text-sm text-foreground hover:bg-accent + hover:text-accent-foreground p-2 rounded-md ${ + isActive ? "bg-accent text-accent-foreground" : "" + }`; +}; const isActive = (path: string) => pathname?.startsWith(path);
76-79
: Memoize network change handler.The
handleNetworkChange
function is recreated on every render. Consider usinguseCallback
to memoize it.- const handleNetworkChange = (network: Network) => { + const handleNetworkChange = useCallback((network: Network) => { dispatch(setActiveNetwork(network)); localStorage.setItem("activeNetwork", JSON.stringify(network)); - }; + }, [dispatch]);netmanager-app/app/(authenticated)/sites/page.tsx (2)
70-92
: Consider using memoization for repeated sorting operations.
When sorting large arrays repeatedly, especially if the function re-renders often, usinguseMemo
to cache the sorted sites can improve performance.const sortedSites = React.useMemo(() => { return sortSites(filteredSites); }, [filteredSites, sortField, sortOrder]);
303-303
: Optional: Implementation for the trash button
You have a placeholder comment indicating a future deletion handler. If removing sites is part of the MVP, consider implementing or at least referencing an issue that tracks this.Do you want me to open a follow-up card for Site deletion logic and confirm the route or API method?
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
netmanager-app/app/(authenticated)/sites/page.tsx
(1 hunks)netmanager-app/components/sidebar.tsx
(1 hunks)netmanager-app/components/ui/pagination.tsx
(1 hunks)netmanager-app/core/apis/sites.ts
(1 hunks)netmanager-app/core/hooks/useSites.ts
(1 hunks)netmanager-app/core/hooks/users.ts
(1 hunks)netmanager-app/core/redux/slices/sitesSlice.ts
(1 hunks)netmanager-app/core/redux/slices/userSlice.ts
(1 hunks)netmanager-app/core/redux/store.ts
(1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
netmanager-app/components/sidebar.tsx
[error] 18-18: Do not shadow the global "Map" property.
Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.
(lint/suspicious/noShadowRestrictedNames)
🔇 Additional comments (29)
netmanager-app/core/redux/slices/sitesSlice.ts (3)
4-15
: Great usage of typed interfacesDefining clear interfaces for
SiteCategory
with typed properties helps maintain clarity and future-proofs the code, especially as your data evolves. Good job.
17-24
: Validategroup
fields when populatingDevice
If
authRequired
,groups
, or other properties can be undefined in certain payloads, consider defaulting them to safe values in order to avoid potential runtime errors.
71-85
: Kudos on the straightforward reducersThese reducers are well-structured and follow Redux Toolkit practices. Keep it up.
netmanager-app/core/redux/slices/userSlice.ts (2)
36-41
: Add null checks for optional payload properties
This was mentioned in a previous review. Reiterating that you should handle cases where these properties might be undefined to prevent runtime errors.
54-60
: ResetuserGroups
on logout
Past comments noted thatuserGroups
isn't reset on logout. This can lead to stale group data if a different user logs in afterward.netmanager-app/core/hooks/users.ts (2)
3-3
: Correct import link for jwt-decode.The
jwt-decode
package typically exports a default export, not a named export.-import { jwtDecode } from "jwt-decode"; +import jwtDecode from "jwt-decode";
29-128
: 🛠️ Refactor suggestionRefactor login mutation for better separation of concerns.
The login mutation handles too many responsibilities. Consider extracting token handling, user info fetching, and network/group selection into separate functions for better maintainability.
+const handleTokenStorage = (token: string) => { + localStorage.setItem("token", token); + return jwtDecode<DecodedToken>(token); +}; + +const createUserDetails = (decoded: DecodedToken): UserDetails => ({ + _id: decoded._id, + firstName: decoded.firstName, + // ... other fields +}); + +const handleNetworkAndGroupSelection = (userInfo: UserDetailsResponse['users'][0]) => { + const networks = userInfo.networks || []; + const groups = userInfo.groups || []; + + const airqoNetwork = networks.find( + (network: Network) => network.net_name.toLowerCase() === "airqo" + ); + const defaultNetwork = airqoNetwork || networks[0]; + + if (defaultNetwork) { + dispatch(setActiveNetwork(defaultNetwork)); + localStorage.setItem("activeNetwork", JSON.stringify(defaultNetwork)); + + if (airqoNetwork) { + const airqoGroup = groups.find( + (group: Group) => group.grp_title.toLowerCase() === "airqo" + ); + if (airqoGroup) { + dispatch(setActiveGroup(airqoGroup)); + localStorage.setItem("activeGroup", JSON.stringify(airqoGroup)); + } + } + } +};Likely invalid or redundant comment.
netmanager-app/components/sidebar.tsx (1)
18-18
: Rename the 'Map' import to avoid shadowing.The
Map
import shadows the globalMap
object, which could lead to confusion.- Map, + MapIcon,🧰 Tools
🪛 Biome (1.9.4)
[error] 18-18: Do not shadow the global "Map" property.
Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.
(lint/suspicious/noShadowRestrictedNames)
netmanager-app/core/hooks/useSites.ts (3)
1-5
: Imports appear neatly organized and relevant.
No issues found with these imports, and they align well with the React-Redux and React Query patterns.
18-19
: Ensure error handling also captures network timeouts and connectivity issues.
At times, theerror.message
might be empty or generic for connectivity issues. Consider logging additional info or dispatching more granular error states to help debugging.
23-27
: Return structure is concise and clear.
Your return shape (sites, isLoading, error) is straightforward and intuitive for consumers. Good job!netmanager-app/components/ui/pagination.tsx (7)
7-14
: ARIA attributes and semantic HTML usage look spot on.
Great job leveraging<nav>
and setting the correctaria-*
attributes for pagination. This approach improves accessibility.
17-27
: PaginationContent design is simple and flexible.
Forwarding refs like this can make the component more versatile. The usage offlex
layout is appropriate for pagination items.
42-60
: PaginationLink'sprops
usage is well-structured.
UsingbuttonVariants
ensures consistent styling with your design system. TheisActive
logic is also well-implemented.
62-77
: Using specialized Pagination components.
PaginationPrevious
component improves readability and reusability. ARIA labels are well-thought-out for screen readers.
78-93
:PaginationNext
matches the pattern nicely.
No changes needed here; the approach is consistent and accessible.
94-108
:PaginationEllipsis
correctly handles hidden pages scenario.
Including a visually hidden label for screen readers is a great accessibility enhancement.
109-117
: Exports are well-organized.
All pagination components are succinctly exported for easy imports across the project. Good structure!netmanager-app/app/(authenticated)/sites/page.tsx (11)
8-45
: Imports and constant declarations are neatly arranged.
Everything relevant to the page (icons, components, hooks) is present. No concerns here.
46-50
: ITEMS_PER_PAGE and sort field types are straightforward.
Defining these up front clarifies your pagination boundaries and maintainable sorting approach.
94-102
: Searching is thorough and well-structured.
Multi-field searching with.includes()
is a good user experience approach. The code is easy to read and free of obvious issues.
112-147
: Pagination logic is well thought out.
Your approach to dynamic ellipsis and page boundaries is user-friendly. Good to see a max visible pages limit.
272-276
: Avoid accidental navigation conflicts when clicking row child elements.
This was noted in a previous review: child elements (like buttons) may also need event handling that won't trigger navigation. Consider stopping event propagation on child elements to prevent user confusion.
150-166
: Error UI is user-friendly.
Displaying a descriptive alert message is a nice touch, ensuring issues are more transparent.
170-180
: Tidy usage of theDialog
This section sets up clearly for adding a new Site. Good usage of the Headless UI approach.
193-228
: Search & sort UI is clean
UsingDropdownMenu
for sort options is a pleasant user experience. No issues found.
230-307
: Table structure is robust and readable
Each column is well-defined, and the user flow to navigate by row click is consistent. Good layout.
320-369
: Pagination UI is properly integrated.
Tying the pagination logic to these custom components is cohesive. Setting disabled states for first/last pages is handled well.
1-373
: Overall, this file offers a solid, feature-complete SitesPage.
The approach to routing, sorting, searching, and pagination is well-structured. Minor performance optimizations (memoization) and event-handling adjustments can be addressed in subsequent iterations. Great job!
const restoreSession = () => { | ||
try { | ||
const token = localStorage.getItem("token"); | ||
const storedUserDetails = localStorage.getItem("userDetails"); | ||
const storedActiveNetwork = localStorage.getItem("activeNetwork"); | ||
const storedAvailableNetworks = localStorage.getItem("availableNetworks"); | ||
const storedActiveGroup = localStorage.getItem("activeGroup"); | ||
const storedUserGroups = localStorage.getItem("userGroups"); | ||
|
||
if (token && storedUserDetails) { | ||
const userDetails = JSON.parse(storedUserDetails) as UserDetails; | ||
dispatch(setUserDetails(userDetails)); | ||
|
||
if (storedActiveNetwork) { | ||
const activeNetwork = JSON.parse(storedActiveNetwork) as Network; | ||
dispatch(setActiveNetwork(activeNetwork)); | ||
} | ||
if (storedAvailableNetworks) { | ||
const availableNetworks = JSON.parse( | ||
storedAvailableNetworks | ||
) as Network[]; | ||
dispatch(setAvailableNetworks(availableNetworks)); | ||
} | ||
if (storedUserGroups) { | ||
const userGroups = JSON.parse(storedUserGroups) as Group[]; | ||
dispatch(setUserGroups(userGroups)); | ||
} | ||
if (storedActiveGroup) { | ||
const activeGroup = JSON.parse(storedActiveGroup) as Group; | ||
dispatch(setActiveGroup(activeGroup)); | ||
} | ||
} else { | ||
handleLogout(); | ||
} | ||
} finally { | ||
dispatch(setInitialized()); | ||
} | ||
}; |
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.
🛠️ Refactor suggestion
Add robust error handling for session restoration.
The session restoration logic should handle corrupted local storage data and provide appropriate fallbacks.
const restoreSession = () => {
try {
const token = localStorage.getItem("token");
const storedUserDetails = localStorage.getItem("userDetails");
- const storedActiveNetwork = localStorage.getItem("activeNetwork");
+ let parsedUserDetails: UserDetails | null = null;
+ try {
+ parsedUserDetails = storedUserDetails
+ ? (JSON.parse(storedUserDetails) as UserDetails)
+ : null;
+ } catch (e) {
+ console.error("Failed to parse user details:", e);
+ handleLogout();
+ return;
+ }
- if (token && storedUserDetails) {
- const userDetails = JSON.parse(storedUserDetails) as UserDetails;
+ if (token && parsedUserDetails) {
+ // Validate token expiration
+ try {
+ const decoded = jwtDecode<DecodedToken>(token);
+ if (decoded.exp && decoded.exp < Date.now() / 1000) {
+ handleLogout();
+ return;
+ }
+ } catch (e) {
+ console.error("Failed to decode token:", e);
+ handleLogout();
+ return;
+ }
Committable suggestion skipped: line range outside the PR's diff.
<div className="p-4 border-b"> | ||
<DropdownMenu> | ||
<DropdownMenuTrigger asChild> | ||
<Button | ||
variant="outline" | ||
className="w-full justify-between text-foreground uppercase" | ||
> | ||
{activeNetwork?.net_name || "Select Network"} | ||
<ChevronDown size={16} /> | ||
</Button> | ||
</DropdownMenuTrigger> | ||
<DropdownMenuContent className="w-56"> | ||
<DropdownMenuLabel>Switch Network</DropdownMenuLabel> | ||
<DropdownMenuSeparator /> | ||
{availableNetworks.map((network) => ( | ||
<DropdownMenuItem | ||
key={network._id} | ||
onClick={() => handleNetworkChange(network)} | ||
className="flex items-center justify-between uppercase" | ||
> | ||
{network.net_name} | ||
{activeNetwork?._id === network._id && <Check size={16} />} | ||
</DropdownMenuItem> | ||
))} | ||
</DropdownMenuContent> | ||
</DropdownMenu> | ||
</div> |
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.
🛠️ Refactor suggestion
Enhance accessibility for network switcher.
Add proper ARIA labels and roles to improve accessibility for the network switcher dropdown.
<div className="p-4 border-b">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
className="w-full justify-between text-foreground uppercase"
+ aria-label="Select network"
>
📝 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.
<div className="p-4 border-b"> | |
<DropdownMenu> | |
<DropdownMenuTrigger asChild> | |
<Button | |
variant="outline" | |
className="w-full justify-between text-foreground uppercase" | |
> | |
{activeNetwork?.net_name || "Select Network"} | |
<ChevronDown size={16} /> | |
</Button> | |
</DropdownMenuTrigger> | |
<DropdownMenuContent className="w-56"> | |
<DropdownMenuLabel>Switch Network</DropdownMenuLabel> | |
<DropdownMenuSeparator /> | |
{availableNetworks.map((network) => ( | |
<DropdownMenuItem | |
key={network._id} | |
onClick={() => handleNetworkChange(network)} | |
className="flex items-center justify-between uppercase" | |
> | |
{network.net_name} | |
{activeNetwork?._id === network._id && <Check size={16} />} | |
</DropdownMenuItem> | |
))} | |
</DropdownMenuContent> | |
</DropdownMenu> | |
</div> | |
<div className="p-4 border-b"> | |
<DropdownMenu> | |
<DropdownMenuTrigger asChild> | |
<Button | |
variant="outline" | |
className="w-full justify-between text-foreground uppercase" | |
aria-label="Select network" | |
> | |
{activeNetwork?.net_name || "Select Network"} | |
<ChevronDown size={16} /> | |
</Button> | |
</DropdownMenuTrigger> | |
<DropdownMenuContent className="w-56"> | |
<DropdownMenuLabel>Switch Network</DropdownMenuLabel> | |
<DropdownMenuSeparator /> | |
{availableNetworks.map((network) => ( | |
<DropdownMenuItem | |
key={network._id} | |
onClick={() => handleNetworkChange(network)} | |
className="flex items-center justify-between uppercase" | |
> | |
{network.net_name} | |
{activeNetwork?._id === network._id && <Check size={16} />} | |
</DropdownMenuItem> | |
))} | |
</DropdownMenuContent> | |
</DropdownMenu> | |
</div> |
const { data, isLoading, error } = useQuery({ | ||
queryKey: ["sites", activeNetwork?.net_name], | ||
queryFn: () => sites.getSitesSummary(activeNetwork?.net_name || ""), | ||
enabled: !!activeNetwork?.net_name, | ||
onSuccess: (data: any) => { | ||
dispatch(setSites(data.sites)); | ||
}, | ||
onError: (error: Error) => { | ||
dispatch(setError(error.message)); | ||
}, | ||
}); |
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.
🛠️ Refactor suggestion
Consider providing a more explicit type for the query's response.
Currently, the query callback and onSuccess callbacks pass data
as any
. Using a well-defined interface or type for data
can improve type safety and reduce future bugs.
const { data, isLoading, error } = useQuery({
queryKey: ["sites", activeNetwork?.net_name],
- queryFn: () => sites.getSitesSummary(activeNetwork?.net_name || ""),
+ queryFn: (): SitesSummaryResponse => sites.getSitesSummary(activeNetwork?.net_name || ""),
...
});
Committable suggestion skipped: line range outside the PR's diff.
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: 3
🧹 Nitpick comments (15)
netmanager-app/package.json (1)
11-39
: Consider using stricter version rangesUsing caret (^) for version ranges could lead to unexpected breaking changes. Consider using exact versions or tilde (~) for better stability.
- "@hookform/resolvers": "^3.9.1", + "@hookform/resolvers": "~3.9.1", // Apply similar changes to other dependenciesnetmanager-app/.gitignore (4)
71-74
: Ensure comprehensive environment variable protectionGood inclusion of various .env files. However, consider adding more specific Next.js environment patterns:
.env .env.test .env.local +.env.development +.env.production +.env.development.local +.env.production.local
107-109
: Consider expanding Cypress configurationGood inclusion of Cypress test artifacts. Consider adding additional Cypress-specific patterns:
cypress/videos cypress/screenshots +cypress/downloads +cypress/reports +cypress/results
117-119
: Enhance IDE configuration patternsThe VS Code ignore pattern could be more specific:
.vscode/* +!.vscode/extensions.json +!.vscode/launch.json +!.vscode/settings.jsonThis modification allows sharing of consistent VS Code workspace settings while ignoring user-specific files.
1-119
: Overall assessment: Well-structured .gitignore fileThe .gitignore file is well-organized and covers most essential patterns for a Next.js application. It properly excludes build artifacts, dependencies, and sensitive files while maintaining a clean repository structure.
However, consider adding these commonly overlooked patterns:
+# macOS system files +.DS_Store + +# Debug logs +debug.log* + +# Local SSL certificates +*.pem + +# Storybook +storybook-static/ + +# Bundle analyzer +analyze-*.jsonnetmanager-app/core/redux/slices/sitesSlice.ts (4)
53-53
: Consider defining a proper type forairqlouds
Using
unknown[]
suggests incomplete type definition. Consider defining anAirqloud
interface to make the code more type-safe and self-documenting.
41-42
: Add validation constraints for geographic coordinatesConsider adding validation or documentation for the acceptable ranges of latitude (-90 to 90) and longitude (-180 to 180) values.
latitude: number; // -90 to 90 longitude: number; // -180 to 180Also applies to: 44-45
74-78
: Consider implementing optimistic updatesThe
setSites
reducer could benefit from implementing optimistic updates pattern for better user experience, especially in slow network conditions.
82-85
: Consider using a custom error typeInstead of using a simple string for errors, consider creating a custom error type to handle different error scenarios more effectively.
type SiteError = { code: string; message: string; details?: unknown; };netmanager-app/app/(authenticated)/sites/create-site-form.tsx (4)
51-55
: Consider hosting marker icon locallyLoading the marker icon from an external CDN could lead to reliability issues. Consider:
- Hosting the marker icon locally with your assets
- Implementing a fallback icon
154-158
: Improve error handling granularityThe current error handling is generic. Consider implementing specific error handling for different scenarios:
- Network connectivity issues
- Validation errors
- Server-side errors
} catch (error: any) { + if (!navigator.onLine) { + setError("Network connection lost. Please check your internet connection."); + } else if (error.response?.status === 422) { + setError("Invalid data. Please check your inputs."); + } else { setError(error.message || "An error occurred while creating the site."); + } }
92-97
: Enhance map initializationConsider improving the map's initial state:
- Center on user's current location (with permission)
- Adjust zoom level based on context (city/country view)
- Add a button to recenter map on current location
204-269
: Remove unnecessary fragmentThe fragment wrapping the form fields is redundant as it contains only one child element.
- <> <div className="space-y-4"> {/* ... form fields ... */} </div> - </>🧰 Tools
🪛 Biome (1.9.4)
[error] 204-269: Avoid using unnecessary Fragment.
A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment(lint/complexity/noUselessFragments)
netmanager-app/app/(authenticated)/sites/page.tsx (2)
63-91
: Optimize sorting for large datasetsThe current client-side sorting implementation might impact performance with large datasets. Consider:
- Implementing server-side sorting
- Adding pagination parameters to API calls
- Caching sorted results
93-101
: Optimize search functionalityThe current search implementation could be improved:
- Add debouncing to prevent excessive filtering on each keystroke
- Consider server-side search for large datasets
- Implement search highlighting for matched terms
+import { useMemo, useCallback } from 'react'; +import debounce from 'lodash/debounce'; - const filteredSites = sites.filter((site: Site) => { + const debouncedSearch = useCallback( + debounce((query: string) => { + setSearchQuery(query); + }, 300), + [] + ); + + const filteredSites = useMemo(() => sites.filter((site: Site) => { const searchLower = searchQuery.toLowerCase(); return ( site.name?.toLowerCase().includes(searchLower) || site.location_name?.toLowerCase().includes(searchLower) || site.generated_name?.toLowerCase().includes(searchLower) || site.formatted_name?.toLowerCase().includes(searchLower) ); - }); + }), [sites, searchQuery]);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
netmanager-app/package-lock.json
is excluded by!**/package-lock.json
📒 Files selected for processing (7)
netmanager-app/.gitignore
(1 hunks)netmanager-app/app/(authenticated)/sites/create-site-form.tsx
(1 hunks)netmanager-app/app/(authenticated)/sites/page.tsx
(1 hunks)netmanager-app/core/apis/sites.ts
(1 hunks)netmanager-app/core/redux/slices/sitesSlice.ts
(1 hunks)netmanager-app/next-env.d.ts
(1 hunks)netmanager-app/package.json
(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- netmanager-app/next-env.d.ts
🧰 Additional context used
🪛 Biome (1.9.4)
netmanager-app/app/(authenticated)/sites/create-site-form.tsx
[error] 204-269: Avoid using unnecessary Fragment.
A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment
(lint/complexity/noUselessFragments)
🔇 Additional comments (9)
netmanager-app/package.json (4)
1-4
: LGTM! Project metadata is well-configured.The project is properly named and marked as private to prevent accidental publishing.
5-10
: Add test script configurationThe PR checklist mentions the need for unit or end-to-end tests, but there's no test script configured.
11-39
: Update Dependencies: Security Vulnerabilities Found 🚨The security scan revealed critical vulnerabilities in the axios dependency.
40-50
: Development environment is well configuredThe TypeScript and ESLint setup looks good. Let's verify the TypeScript configuration.
✅ Verification successful
TypeScript configuration verified successfully ✓
The TypeScript setup is properly configured with strict type checking, Next.js integration, and correct module resolution settings.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify TypeScript configuration # Check for tsconfig.json and its contents if [ -f "tsconfig.json" ]; then echo "Found tsconfig.json:" cat tsconfig.json else echo "Missing tsconfig.json" fiLength of output: 79
Script:
#!/bin/bash # Search for any tsconfig files fd tsconfig # Check for Next.js config file that might contain TypeScript settings if [ -f "next.config.js" ]; then echo -e "\nFound next.config.js:" cat next.config.js elif [ -f "next.config.mjs" ]; then echo -e "\nFound next.config.mjs:" cat next.config.mjs fiLength of output: 144
Script:
#!/bin/bash # Check the contents of tsconfig.json in netmanager-app cat netmanager-app/tsconfig.jsonLength of output: 608
netmanager-app/.gitignore (1)
79-81
: Verify Next.js output directory configurationThe
.next/
ignore pattern is correctly included. However, the Gatsby-related comment about thepublic
directory needs clarification for Next.js context.Since this is a Next.js migration, please verify if the public directory should be tracked. Next.js uses the
public
directory for static assets that should be version controlled.Also applies to: 88-91
✅ Verification successful
Current .gitignore configuration is correct for Next.js
The current setup correctly handles the public directory for Next.js:
- The
public
directory is intentionally not ignored, which is the correct behavior for Next.js projects- The comment accurately explains the difference between Next.js and Gatsby handling
- This ensures static assets in the
public
directory are properly version controlled🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check if public directory exists and contains static assets if [ -d "netmanager-app/public" ]; then echo "Public directory contents:" ls -la netmanager-app/public fiLength of output: 34
Script:
#!/bin/bash # Check if this is a Next.js project and look for configuration echo "=== Package.json Next.js dependency ===" cat netmanager-app/package.json | grep -A 2 "next" echo -e "\n=== Current .gitignore patterns ===" cat netmanager-app/.gitignore | grep -i "public" echo -e "\n=== Next.js config file ===" if [ -f "netmanager-app/next.config.js" ]; then cat netmanager-app/next.config.js fiLength of output: 822
netmanager-app/core/redux/slices/sitesSlice.ts (3)
58-68
: Well-structured state managementClean implementation of state interface and initial state following Redux best practices. The error handling and loading states are properly typed and initialized.
1-90
: Verify Redux integration with Next.jsSince this is part of a Next.js migration, ensure proper integration with Next.js app router and other Redux slices.
✅ Verification successful
Redux integration is properly implemented
The sitesSlice implementation follows Redux best practices and is correctly integrated with Next.js app router through the providers setup.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check for proper Redux provider setup in Next.js rg -A 10 "Provider.*store" # Check for other Redux slice imports rg -A 5 "createSlice|configureStore"Length of output: 66161
17-24
: Review security implications of Device interfaceThe
authRequired
field suggests authentication logic. Ensure this is properly handled in the authentication flow and consider documenting the security implications.✅ Verification successful
Security implementation appears robust and well-structured
The
authRequired
field in the Device interface is part of a proper security architecture where:
- Authentication is enforced at the route level via the
(authenticated)
route group- Access control is implemented through the
RouteGuard
component with specific permissions- UI and API interfaces are properly separated, with sensitive fields only present in the API interface
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for usage of authRequired field to verify proper handling rg -A 5 "authRequired"Length of output: 397
Script:
#!/bin/bash # Search for Device interface usage ast-grep --pattern 'interface Device' -l # Search for files containing Device type annotations rg ': Device[\[\]\s{};]' -l # Search for authentication-related files fd -e ts -e js auth # Search for API handlers or middleware using Device rg -l "Device.*auth|auth.*Device"Length of output: 592
Script:
#!/bin/bash # Check devices.tsx implementation cat "netmanager-app/app/(authenticated)/sites/[id]/devices.tsx" # Look for middleware or guards in authenticated route group fd -e ts -e js middleware fd -e ts -e js guard # Check for auth checks in the authenticated route group rg -l "middleware|guard" "netmanager-app/app/(authenticated)"Length of output: 2254
Script:
#!/bin/bash # Check the Device interface from types cat "netmanager-app/app/types/sites.ts" # Check the sites page implementation cat "netmanager-app/app/(authenticated)/sites/page.tsx" # Look for any imports of sitesSlice rg -l "from.*sitesSlice"Length of output: 13539
netmanager-app/app/(authenticated)/sites/page.tsx (1)
259-263
: Prevent accidental navigation when clicking action buttonsMaking the entire row clickable might interfere with the trash button's onClick event. Consider stopping event propagation on the button click to avoid conflicting navigation.
# Yarn add output | ||
.pnp.cjs | ||
.pnp.loader.mjs | ||
.yarn/ | ||
.yarnrc.yml | ||
|
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.
💡 Codebase verification
Remove unnecessary Yarn-specific patterns from .gitignore
The project is using npm (evidenced by package-lock.json) rather than Yarn. The following Yarn-specific patterns should be removed from .gitignore as they're not applicable:
- .pnp.cjs
- .pnp.loader.mjs
- .yarn/
- .yarnrc.yml
🔗 Analysis chain
Validate Yarn configuration for Next.js project
The Yarn-related ignore patterns are comprehensive. However, since this is a Next.js migration:
Please verify the package manager configuration:
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check for package manager configuration
echo "Checking package manager configuration..."
if [ -f "netmanager-app/package.json" ]; then
jq '.packageManager' netmanager-app/package.json
fi
Length of output: 187
Script:
#!/bin/bash
# Check for package manager related files
echo "Checking for package manager files in netmanager-app/..."
for file in yarn.lock package-lock.json .yarnrc.yml .npmrc; do
if [ -f "netmanager-app/$file" ]; then
echo "Found: $file"
if [ "$file" = ".yarnrc.yml" ]; then
echo "Content of .yarnrc.yml:"
cat "netmanager-app/$file"
fi
fi
done
# Check for PnP files
if [ -d "netmanager-app/.yarn" ]; then
echo "Found .yarn directory"
ls -la netmanager-app/.yarn
fi
Length of output: 660
const siteFormSchema = z.object({ | ||
name: z.string().min(2, { | ||
message: "Site name must be at least 2 characters.", | ||
}), | ||
}); |
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.
🛠️ Refactor suggestion
Enhance form validation schema for coordinates
The current schema only validates the site name. Consider adding validation for latitude (-90 to 90) and longitude (-180 to 180) to prevent invalid coordinates.
const siteFormSchema = z.object({
name: z.string().min(2, {
message: "Site name must be at least 2 characters.",
}),
+ latitude: z.string().refine(
+ (val) => {
+ const num = parseFloat(val);
+ return !isNaN(num) && num >= -90 && num <= 90;
+ },
+ { message: "Latitude must be between -90 and 90 degrees" }
+ ),
+ longitude: z.string().refine(
+ (val) => {
+ const num = parseFloat(val);
+ return !isNaN(num) && num >= -180 && num <= 180;
+ },
+ { message: "Longitude must be between -180 and 180 degrees" }
+ ),
});
📝 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.
const siteFormSchema = z.object({ | |
name: z.string().min(2, { | |
message: "Site name must be at least 2 characters.", | |
}), | |
}); | |
const siteFormSchema = z.object({ | |
name: z.string().min(2, { | |
message: "Site name must be at least 2 characters.", | |
}), | |
latitude: z.string().refine( | |
(val) => { | |
const num = parseFloat(val); | |
return !isNaN(num) && num >= -90 && num <= 90; | |
}, | |
{ message: "Latitude must be between -90 and 90 degrees" } | |
), | |
longitude: z.string().refine( | |
(val) => { | |
const num = parseFloat(val); | |
return !isNaN(num) && num >= -180 && num <= 180; | |
}, | |
{ message: "Longitude must be between -180 and 180 degrees" } | |
), | |
}); |
export const sites = { | ||
getSitesSummary: async (networkId: string) => { | ||
try { | ||
const response = await axiosInstance.get( | ||
`${SITES_MGT_URL}/summary?network=${networkId}` | ||
); | ||
return response.data; | ||
} catch (error: any) { | ||
throw new Error( | ||
error.response?.data?.message || "Failed to fetch sites summary" | ||
); | ||
} | ||
}, | ||
|
||
createSite: async (data: { | ||
name: string; | ||
latitude: string; | ||
longitude: string; | ||
network: string; | ||
}) => { | ||
try { | ||
const response = await axiosInstance.post(`${SITES_MGT_URL}`, data); | ||
return response.data; | ||
} catch (error: any) { | ||
throw new Error(error.response?.data?.message || "Failed to create site"); | ||
} | ||
}, | ||
}; |
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.
🛠️ Refactor suggestion
Enhance API integration robustness
Consider the following improvements to the API integration:
- Add request timeout
- Implement retry logic for transient failures
- Define response type interfaces
+interface SiteResponse {
+ id: string;
+ name: string;
+ latitude: string;
+ longitude: string;
+ network: string;
+ createdAt: string;
+}
export const sites = {
getSitesSummary: async (networkId: string) => {
try {
- const response = await axiosInstance.get(
+ const response = await axiosInstance.get<SiteResponse[]>(
`${SITES_MGT_URL}/summary?network=${networkId}`,
+ {
+ timeout: 5000,
+ retry: 3,
+ retryDelay: 1000
+ }
);
return response.data;
} catch (error: any) {
+ if (error.code === 'ECONNABORTED') {
+ throw new Error('Request timed out. Please try again.');
+ }
throw new Error(
error.response?.data?.message || "Failed to fetch sites summary"
);
}
},
// Apply similar changes to createSite method
Committable suggestion skipped: line range outside the PR's diff.
Screenshots (optional)
Summary by CodeRabbit
New Features
Configuration
UI Components
Authentication
User Management