Skip to content
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

Analytics: Updates to display locations by groups #2311

Merged
merged 3 commits into from
Dec 10, 2024
Merged

Conversation

OchiengPaul442
Copy link
Contributor

@OchiengPaul442 OchiengPaul442 commented Dec 10, 2024

Summary of Changes (What does this PR do?)

  • Fetch locations based on selected group
  • Updated tooltip descriptions on trend

Status of maturity (all need to be checked before merging):

  • I've tested this locally
  • I consider this code done
  • This change ready to hit production in its current state

Summary by CodeRabbit

  • New Features

    • Enhanced site summary fetching based on selected organization in the AddLocations component.
    • Improved site selection management and error handling in the DataDownload component.
    • Added support for grouping parameters in site summary API requests.
    • Updated data download functionality to reflect "calibrated" data.
  • Bug Fixes

    • Improved error handling for various scenarios in API calls and component submissions.
  • Chores

    • Simplified control flow by removing unnecessary data fetching logic in the AuthenticatedHomePage component.
    • Enhanced proxy handler to forward query parameters and provide standardized error messages.
    • Updated styles to improve layout and visual behavior across components.

image

Copy link

coderabbitai bot commented Dec 10, 2024

📝 Walkthrough

Walkthrough

The pull request introduces several enhancements across multiple components related to site summary fetching and data handling. Key changes include the addition of a fetchSitesSummary import in the AddLocations component, which now retrieves data based on the selected organization. The DataDownload component has improved state management and error handling, while the getSitesSummaryApi function in Analytics.js now accepts a grouping parameter. Additionally, the proxy handler for sites has been updated to forward query parameters, enhancing its functionality.

Changes

File Change Summary
platform/src/common/components/Modal/dataDownload/modules/AddLocations.jsx Added fetchSitesSummary import, updated local state for error handling, and modified handleSubmit for site selection limits.
platform/src/common/components/Modal/dataDownload/modules/DataDownload.jsx Introduced useEffect for network initialization, memoized ORGANIZATION_OPTIONS, and added handleToggleSite for site selection.
platform/src/core/apis/Analytics.js Updated getSitesSummaryApi to include a group parameter and enhanced error handling for various scenarios.
platform/src/lib/store/services/sitesSummarySlice/index.js Modified fetchSitesSummary to accept a group parameter and updated API calls accordingly.
platform/src/pages/analytics/index.jsx Removed useEffect that dispatched fetchSitesSummary on component mount, simplifying control flow.
platform/src/pages/api/proxy/sites.js Enhanced handler to include query parameter handling and improved error response messages.
platform/src/common/components/AQNumberCard/index.jsx Renamed getTrendData to generateTrendData, updated tooltip content, and improved accessibility features.
platform/src/common/components/Modal/dataDownload/modules/MoreInsights.jsx Changed datatype from 'raw' to 'calibrated' in the download function and updated tooltip content.
platform/src/common/styles/global.scss Added styles for scrollbar visibility, overflow management, and new keyframe animations for UI elements.

Possibly related PRs

Suggested labels

ready for review

Suggested reviewers

  • Baalmart
  • Codebmk

Poem

In the land of code where changes flow,
New features sprout, and improvements grow.
With sites summarized and errors tamed,
The user’s journey is now well-framed.
So let’s celebrate this code delight,
A brighter UI, shining so bright! 🌟


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?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Experiment)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai or @coderabbitai title anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

New next-platform changes available for preview here

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (4)
platform/src/common/components/Modal/dataDownload/modules/DataDownload.jsx (3)

103-116: Refactor duplicated logic for selecting the default network

The logic for determining the default network, prioritizing "airqo" if available, is implemented both in the useEffect hook (lines 107-114) and the handleClearSelection function (lines 134-140). To enhance maintainability and reduce code duplication, consider extracting this logic into a separate helper function.

Suggested refactor:

+// Helper function to get the default network
+const getDefaultNetwork = () => {
+  const airqoNetwork = NETWORK_OPTIONS.find(
+    (network) => network.name.toLowerCase() === 'airqo',
+  );
+  return airqoNetwork || NETWORK_OPTIONS[0] || { id: '', name: 'Default Network' };
+};

...

// In useEffect hook
useEffect(() => {
  if (NETWORK_OPTIONS.length > 0 && !formData.network) {
    setFormData((prevData) => ({
      ...prevData,
-     network: airqoNetwork || NETWORK_OPTIONS[0],
+     network: getDefaultNetwork(),
    }));
  }
}, [NETWORK_OPTIONS, formData.network]);

...

// In handleClearSelection function
const handleClearSelection = useCallback(() => {
  ...
  setFormData({
    title: { name: 'Untitled Report' },
-   network: airqoNetwork ||
-     NETWORK_OPTIONS[0] || { id: '', name: 'Default Network' },
+   network: getDefaultNetwork(),
    ...
  });
  ...
}, [NETWORK_OPTIONS]);

Also applies to: 134-140


215-217: Use accurate date calculations for duration validation

The current validation uses fixed month durations (30 days), which could lead to inaccuracies due to varying month lengths. To improve accuracy, consider using the differenceInMonths function from date-fns.

Suggested change:

-import { format } from 'date-fns';
+import { format, differenceInMonths } from 'date-fns';

...

const validateDuration = (frequency, startDate, endDate) => {
-  const sixMonthsInMs = 6 * 30 * 24 * 60 * 60 * 1000; // 6 months in milliseconds
-  const oneYearInMs = 12 * 30 * 24 * 60 * 60 * 1000; // 1 year in milliseconds
-  const durationMs = endDate - startDate;
+  const durationMonths = differenceInMonths(endDate, startDate);

-  if (frequency === 'hourly' && durationMs > sixMonthsInMs) {
+  if (frequency === 'hourly' && durationMonths > 6) {
     return 'For hourly frequency, the duration cannot exceed 6 months.';
   }
-  if (frequency === 'daily' && durationMs > oneYearInMs) {
+  if (frequency === 'daily' && durationMonths > 12) {
     return 'For daily frequency, the duration cannot exceed 1 year.';
   }
   return null;
};

This adjustment accounts for the actual number of days in each month, providing a more precise validation.


299-302: Enhance user feedback in error handling

When setting the form error (lines 299-302), consider providing user-friendly error messages that guide the user on how to resolve the issue. Tailoring error messages based on specific exceptions can significantly improve the user experience.

platform/src/common/components/Modal/dataDownload/modules/AddLocations.jsx (1)

65-72: Consider enhancing the useEffect implementation.

While the basic functionality is there, consider these improvements for better error handling and performance:

  1. Add a guard clause for undefined organizationName
  2. Handle loading state
  3. Add error handling within the effect

Here's a suggested implementation:

 useEffect(() => {
-  if (chartData.organizationName) {
+  if (!chartData.organizationName) return;
+  
+  const fetchData = async () => {
+    try {
+      setLoading(true);
       dispatch(fetchSitesSummary({ group: chartData.organizationName }));
+    } catch (error) {
+      console.error('Failed to fetch sites:', error);
+    } finally {
+      setLoading(false);
+    }
   }
+  
+  fetchData();
 }, [dispatch, chartData.organizationName]);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between c8bf181 and a7ba045.

📒 Files selected for processing (6)
  • platform/src/common/components/Modal/dataDownload/modules/AddLocations.jsx (3 hunks)
  • platform/src/common/components/Modal/dataDownload/modules/DataDownload.jsx (10 hunks)
  • platform/src/core/apis/Analytics.js (1 hunks)
  • platform/src/lib/store/services/sitesSummarySlice/index.js (3 hunks)
  • platform/src/pages/analytics/index.jsx (0 hunks)
  • platform/src/pages/api/proxy/sites.js (1 hunks)
💤 Files with no reviewable changes (1)
  • platform/src/pages/analytics/index.jsx
🔇 Additional comments (4)
platform/src/core/apis/Analytics.js (1)

25-34: Inclusion of 'group' parameter enhances API flexibility

Adding the group parameter to the getSitesSummaryApi function allows for more specific data retrieval based on user group selection. The implementation correctly integrates the parameter into the Axios request.

platform/src/lib/store/services/sitesSummarySlice/index.js (1)

8-21: Integration of 'group' parameter improves data specificity

By introducing the group parameter in the fetchSitesSummary thunk, the application can now fetch site summaries tailored to specific user groups. This change enhances data relevance and aligns with user selections.

Also applies to: 31-31

platform/src/common/components/Modal/dataDownload/modules/AddLocations.jsx (2)

10-10: LGTM! Clean integration of site summary fetching.

The addition of fetchSitesSummary and chartData selector aligns well with the PR's objective of enabling group-based location fetching.

Also applies to: 38-38


Line range hint 1-266: Verify group-based filtering and consider architectural improvements.

While the component works as intended, here are some architectural considerations:

  1. Consider extracting the site fetching and filtering logic into a custom hook for better reusability and testing
  2. The filteredSites memo could be optimized to include group-based filtering

Let's verify the group-based filtering implementation:

✅ Verification successful

Group-based filtering and online status filtering are properly implemented.

The verification shows that:

  1. Sites are correctly filtered by organization through fetchSitesSummary({ group: chartData.organizationName }) in the Redux slice
  2. Online status filtering is implemented using site.isOnline === true in a memoized function
  3. The filtering chain is properly connected:
    • Organization selection triggers API fetch with group parameter
    • Results are filtered for online sites
    • UI components receive and display filtered data
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the implementation of group-based filtering across the codebase

# Check for any existing group-based filtering logic
echo "Checking for group-based filtering implementations..."
rg -l "group.*filter|filter.*group" --type js --type jsx

# Verify the API integration
echo "Verifying API integration..."
ast-grep --pattern 'fetchSitesSummary({ $$ })' 

# Check for potential edge cases in filtering
echo "Checking for edge case handling..."
rg "isOnline.*true" --type js --type jsx -C 2

Length of output: 474


Script:

#!/bin/bash
# Let's modify our approach to handle the file extensions correctly

# Check for group-based filtering implementations
echo "Checking for group-based filtering implementations..."
rg -t js "group.*filter|filter.*group"

# Verify the API integration for fetchSitesSummary
echo "Verifying API integration..."
rg "fetchSitesSummary" -t js -C 2

# Check for online status filtering implementation
echo "Checking online status filtering..."
rg "isOnline" -t js -C 2

# Check the sites summary slice implementation
echo "Checking sites summary slice..."
fd -e js -e jsx sitesSummarySlice

Length of output: 66460


Script:

#!/bin/bash
# Let's try a more focused approach to verify the implementation

# Check the sites summary slice implementation
echo "Checking sites summary slice implementation..."
fd sitesSummarySlice -t js -t jsx -x cat {}

# Check fetchSitesSummary usage
echo "Checking fetchSitesSummary usage..."
rg "fetchSitesSummary" -t js -t jsx -A 2 -B 2

# Check online status filtering
echo "Checking online status filtering..."
rg "isOnline.*true" -t js -t jsx -A 2 -B 2

# Check organization-based filtering
echo "Checking organization-based filtering..."
rg "organizationName.*group" -t js -t jsx -A 2 -B 2

Length of output: 802


Script:

#!/bin/bash
# Let's try without file type restrictions since the extensions might be mixed

# Check the sites summary slice implementation
echo "Checking sites summary slice implementation..."
fd sitesSummarySlice -x cat {}

# Check fetchSitesSummary usage
echo "Checking fetchSitesSummary usage..."
rg "fetchSitesSummary" -A 2 -B 2

# Check online status filtering
echo "Checking online status filtering..."
rg "isOnline.*true" -A 2 -B 2

# Check organization-based filtering
echo "Checking organization-based filtering..."
rg "chartData\.organizationName" -A 2 -B 2

Length of output: 10156

Comment on lines 23 to 26
// Handle errors and respond appropriately
res.status(error.response?.status || 500).json({
error: 'Error while proxying request',
message: error.response?.data?.message || error.message,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Prevent exposure of internal error details to the client

Returning error.response?.data?.message or error.message directly to the client may inadvertently reveal sensitive internal information. It's prudent to send a generic error message to the client while logging detailed errors on the server for debugging.

Suggested change:

res.status(error.response?.status || 500).json({
  error: 'Error while proxying request',
- message: error.response?.data?.message || error.message,
+ message: 'An unexpected error occurred. Please try again later.',
});

Also, log the detailed error server-side:

console.error('Proxy error:', error);

Comment on lines 12 to 17
// Pass query parameters from the request to the target URL
const response = await axios.get(targetUrl, {
headers: {
Authorization: req.headers.authorization,
Authorization: req.headers.authorization, // Pass the authorization header
},
params: req.query, // Include query parameters from the request
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Validate query parameters before forwarding

Passing req.query directly as params (line 17) without validation might introduce security risks, such as injection attacks. It's advisable to validate or sanitize query parameters to ensure they meet expected formats and values before forwarding them.

Suggested approach:

  • Define allowed query parameters and their expected formats.
  • Use a validation library like joi or manually check each parameter.

Example:

const allowedParams = ['group'];
const params = {};

allowedParams.forEach((param) => {
  if (req.query[param]) {
    params[param] = req.query[param];
  }
});

// Then pass `params` instead of `req.query`
...
params,

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Outside diff range and nitpick comments (2)
platform/src/common/components/Modal/dataDownload/modules/AddLocations.jsx (2)

38-38: Consider destructuring specific chart properties.

Instead of selecting the entire chart state, consider destructuring only the required organizationName property to minimize unnecessary re-renders.

-const chartData = useSelector((state) => state.chart);
+const { organizationName } = useSelector((state) => state.chart);

Line range hint 38-72: Consider architectural improvements for better maintainability.

The component handles multiple responsibilities and complex state management. Consider these architectural improvements:

  1. Extract site selection logic into a custom hook (e.g., useSiteSelection)
  2. Create a separate hook for data fetching logic (e.g., useSitesFetching)
  3. Consider adding TypeScript for better type safety
  4. Add unit tests for the complex selection and submission logic

Example custom hook structure:

const useSitesFetching = (organizationName) => {
  const dispatch = useDispatch();
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  useEffect(() => {
    // ... fetching logic
  }, [organizationName, dispatch]);

  return { loading, error };
};
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between c8bf181 and a7ba045.

📒 Files selected for processing (6)
  • platform/src/common/components/Modal/dataDownload/modules/AddLocations.jsx (3 hunks)
  • platform/src/common/components/Modal/dataDownload/modules/DataDownload.jsx (10 hunks)
  • platform/src/core/apis/Analytics.js (1 hunks)
  • platform/src/lib/store/services/sitesSummarySlice/index.js (3 hunks)
  • platform/src/pages/analytics/index.jsx (0 hunks)
  • platform/src/pages/api/proxy/sites.js (1 hunks)
💤 Files with no reviewable changes (1)
  • platform/src/pages/analytics/index.jsx
🔇 Additional comments (3)
platform/src/core/apis/Analytics.js (1)

25-34: Inclusion of 'group' Parameter Enhances Functionality

The addition of the group parameter and its incorporation into the API call appropriately extends the function's capability to fetch data based on group selection.

platform/src/lib/store/services/sitesSummarySlice/index.js (1)

3-8: Async Thunk Updated Correctly to Accept 'group' Parameter

The modifications to fetchSitesSummary to include the group parameter are well implemented, allowing for more dynamic data fetching based on group context.

platform/src/common/components/Modal/dataDownload/modules/AddLocations.jsx (1)

10-10: LGTM! Clean import addition.

The import follows the established pattern and uses the alias path convention correctly.

Comment on lines +299 to +302
setFormError(
error.message ||
'An error occurred while downloading. Please try again.',
);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid Displaying Technical Error Messages to Users

In setFormError, using error.message directly can expose technical details to the user. It's better to display a generic error message to enhance user experience and security.

Apply this diff to implement the change:

-setFormError(
-  error.message ||
-    'An error occurred while downloading. Please try again.',
-);
+setFormError('An error occurred while downloading. Please try again.');
📝 Committable suggestion

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

Suggested change
setFormError(
error.message ||
'An error occurred while downloading. Please try again.',
);
setFormError('An error occurred while downloading. Please try again.');

Comment on lines 23 to 26
// Handle errors and respond appropriately
res.status(error.response?.status || 500).json({
error: 'Error while proxying request',
message: error.response?.data?.message || error.message,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Prevent Exposing Internal Error Details in API Responses

Currently, the error response may include internal error messages from error.response.data.message or error.message, which could reveal sensitive information. Consider returning a generic error message to the client and logging the detailed error server-side.

Apply this diff to address the issue:

-    res.status(error.response?.status || 500).json({
-      error: 'Error while proxying request',
-      message: error.response?.data?.message || error.message,
-    });
+    console.error('Error while proxying request:', error);
+    res.status(error.response?.status || 500).json({
+      error: 'Error while proxying request',
+      message: 'An error occurred while processing your request.',
+    });
📝 Committable suggestion

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

Suggested change
// Handle errors and respond appropriately
res.status(error.response?.status || 500).json({
error: 'Error while proxying request',
message: error.response?.data?.message || error.message,
// Handle errors and respond appropriately
console.error('Error while proxying request:', error);
res.status(error.response?.status || 500).json({
error: 'Error while proxying request',
message: 'An error occurred while processing your request.',

Comment on lines +65 to +72
/**
* Fetch sites summary whenever the selected organization changes.
*/
useEffect(() => {
if (chartData.organizationName) {
dispatch(fetchSitesSummary({ group: chartData.organizationName }));
}
}, [dispatch, chartData.organizationName]);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider enhancing the fetch implementation.

While the basic functionality is correct, consider these improvements:

  1. Add loading state management to prevent multiple concurrent requests
  2. Implement error handling for failed fetches
  3. Consider debouncing the fetch operation if organization changes are frequent
 useEffect(() => {
+  const controller = new AbortController();
+  
   if (chartData.organizationName) {
-    dispatch(fetchSitesSummary({ group: chartData.organizationName }));
+    dispatch(fetchSitesSummary({ 
+      group: chartData.organizationName,
+      signal: controller.signal 
+    })).catch(error => {
+      if (!error.name === 'AbortError') {
+        console.error('Failed to fetch sites:', error);
+      }
+    });
   }
+  
+  return () => controller.abort();
 }, [dispatch, chartData.organizationName]);

Committable suggestion skipped: line range outside the PR's diff.

Copy link
Contributor

New next-platform changes available for preview here

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (5)
platform/src/common/components/AQNumberCard/index.jsx (3)

30-60: Nice improvements to the trend data generation! 👍

The renaming to generateTrendData and the enhanced tooltip messages make the code more maintainable and user-friendly. The JSDoc documentation is clear and helpful.

Consider adding units to the tooltip messages for better clarity:

-    trendTooltip = `AQI has increased by ${percentageDifference}% compared to the previous week, indicating deteriorating air quality.`;
+    trendTooltip = `Air Quality Index (AQI) has increased by ${percentageDifference}% compared to the previous week, indicating deteriorating air quality.`;

155-159: Good tooltip enhancements! 💡

The placement and width control improvements enhance the user experience.

Consider making the tooltip width responsive:

-            className="w-52"
+            className="w-52 lg:w-64"

253-256: Nice UX improvements to the Add Location button! ✨

The enhanced styling and accessibility attributes make the button more user-friendly and accessible.

Consider adding a color transition to make the hover effect smoother:

-    className="border-dashed border-2 border-blue-400 bg-blue-50 rounded-xl px-4 py-6 h-[220px] flex justify-center items-center text-blue-500 transition-transform transform hover:scale-95 focus:outline-none focus:ring-2 focus:ring-blue-500"
+    className="border-dashed border-2 border-blue-400 bg-blue-50 rounded-xl px-4 py-6 h-[220px] flex justify-center items-center text-blue-500 transition-all duration-200 transform hover:scale-95 hover:bg-blue-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
platform/src/common/components/Modal/dataDownload/modules/DataDownload.jsx (2)

80-87: Add error handling for user groups mapping.

The current implementation assumes userInfo.groups is always valid. Consider adding null checks and providing a default empty array.

  const ORGANIZATION_OPTIONS = useMemo(
    () =>
-     userInfo?.groups?.map((group) => ({
+     (userInfo?.groups || []).map((group) => ({
        id: group._id,
        name: group.grp_title,
      })) || [],
    [userInfo],
  );

106-116: Optimize organization initialization.

The current implementation checks ORGANIZATION_OPTIONS on every render. Consider moving the airqo network finding logic into the useMemo hook.

+ const defaultOrganization = useMemo(() => {
+   if (!ORGANIZATION_OPTIONS.length) return null;
+   return (
+     ORGANIZATION_OPTIONS.find(
+       (group) => group.name.toLowerCase() === 'airqo',
+     ) || ORGANIZATION_OPTIONS[0]
+   );
+ }, [ORGANIZATION_OPTIONS]);

  useEffect(() => {
-   if (ORGANIZATION_OPTIONS.length > 0 && !formData.organization) {
-     const airqoNetwork = ORGANIZATION_OPTIONS.find(
-       (group) => group.name.toLowerCase() === 'airqo',
-     );
+   if (defaultOrganization && !formData.organization) {
      setFormData((prevData) => ({
        ...prevData,
-       organization: airqoNetwork || ORGANIZATION_OPTIONS[0],
+       organization: defaultOrganization,
      }));
    }
- }, [ORGANIZATION_OPTIONS, formData.organization]);
+ }, [defaultOrganization, formData.organization]);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between a7ba045 and cefc5ac.

📒 Files selected for processing (5)
  • platform/src/common/components/AQNumberCard/index.jsx (9 hunks)
  • platform/src/common/components/Modal/dataDownload/modules/DataDownload.jsx (11 hunks)
  • platform/src/common/components/Modal/dataDownload/modules/MoreInsights.jsx (2 hunks)
  • platform/src/common/styles/global.scss (1 hunks)
  • platform/src/pages/api/proxy/sites.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • platform/src/pages/api/proxy/sites.js
🔇 Additional comments (8)
platform/src/common/components/AQNumberCard/index.jsx (3)

68-81: Great accessibility improvements! 🎯

The enhanced aria-labels and tooltip placement provide better user experience and accessibility. The PropTypes update properly reflects the new data structure.

Also applies to: 106-106


144-144: LGTM: Consistent function name update

The function call has been correctly updated to match the renamed utility function.


220-223: Excellent accessibility consideration! ♿

Adding aria-hidden="true" to the decorative icon is a great accessibility practice, as it prevents screen readers from announcing non-essential content.

platform/src/common/styles/global.scss (1)

13-13: Consider alternative approaches to handle horizontal overflow.

While overflow-x: hidden on html, body prevents horizontal scrolling, it's a global solution that might mask underlying layout issues. Consider:

  1. Identifying specific components causing horizontal overflow
  2. Using container-level overflow control
  3. Implementing responsive design patterns
platform/src/common/components/Modal/dataDownload/modules/DataDownload.jsx (2)

164-171: LGTM! Clean implementation of site toggle functionality.

The use of useCallback and efficient state updates is well done.


299-302: Avoid exposing technical error messages to users.

platform/src/common/components/Modal/dataDownload/modules/MoreInsights.jsx (2)

364-364: LGTM! Clear and accurate tooltip content.

The tooltip content accurately reflects the data type being downloaded.


231-231: Verify if 'calibrated' is the only valid datatype value.

The hardcoded value 'calibrated' might need to be more flexible. Consider making it configurable or validating against allowed values.

✅ Verification successful

Let me search for more context about how this datatype is used.


Let me check how this dataType is being configured in the form data.


Let me search for the DATA_TYPE_OPTIONS constant which seems to define the valid values.


'calibrated' and 'raw' are the only valid datatype values

Based on the codebase analysis:

  • DATA_TYPE_OPTIONS defines two options: 'Calibrated Data' and 'Raw Data'
  • In DataDownload.jsx, these are converted to lowercase values 'calibrated' and 'raw'
  • This is consistent with the dataTypeOptions in ExportData/index.js which also defines only these two values

The hardcoded value 'calibrated' is valid and there's no need for additional configuration as the system already handles both possible values.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Search for other datatype values in the codebase

# Search for datatype assignments
rg "datatype:\s*['\"](\w+)['\"]" --type js

Length of output: 155


Script:

#!/bin/bash
# Search for any references to datatype in the codebase
rg "datatype" -B 2 -A 2 --type js

# Search for any type validation or configuration related to datatype
ast-grep --pattern 'datatype'

Length of output: 1648


Script:

#!/bin/bash
# Search for dataType configuration in forms or initial data
rg "dataType" -B 3 -A 3 --type js

# Look for any enum or constant definitions related to data types
rg "const.*dataType|const.*DATA_TYPE" --type js

Length of output: 75604


Script:

#!/bin/bash
# Search for DATA_TYPE_OPTIONS definition
rg "DATA_TYPE_OPTIONS\s*=" -B 2 -A 2 --type js

# Also search for any other datatype related constants
rg "const.*[Dd]ata[Tt]ype.*=" -B 2 -A 2 --type js

Length of output: 66095

Copy link
Contributor

New next-platform changes available for preview here

@Baalmart Baalmart merged commit 120af45 into staging Dec 10, 2024
30 of 31 checks passed
@Baalmart Baalmart deleted the analytics-updates branch December 10, 2024 21:05
@Baalmart Baalmart mentioned this pull request Dec 10, 2024
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants