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

Website: Added Error handling and UI adjustments #2427

Merged
merged 4 commits into from
Jan 29, 2025

Conversation

OchiengPaul442
Copy link
Contributor

@OchiengPaul442 OchiengPaul442 commented Jan 28, 2025

Summary of Changes (What does this PR do?)

  • Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change.

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

  • I've tested this locally
  • I consider this code done

Screenshots (optional)

Summary by CodeRabbit

  • New Features

    • Introduced an interactive Accordion component for dynamic content display.
    • Enhanced Home Player Section with modular video and modal components.
    • Improved Home Stats Section with more structured and dynamic content.
  • Refactor

    • Restructured Home Player and Home Stats sections for better code organization.
    • Optimized component performance using React hooks and memoization.
    • Centralized data fetching logic through a new custom hook.
    • Updated destructuring of data from various hooks for consistency.
  • Documentation

    • Added new data module with partner logos, accordion items, and statistical information.

@OchiengPaul442 OchiengPaul442 self-assigned this Jan 28, 2025
Copy link

coderabbitai bot commented Jan 28, 2025

Warning

Rate limit exceeded

@OchiengPaul442 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 9 minutes and 30 seconds before requesting another review.

⌛ How to resolve this issue?

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

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 0c14caa and cb58d96.

📒 Files selected for processing (1)
  • website2/src/views/solutions/AfricanCities/AfricanCities.tsx (2 hunks)
📝 Walkthrough

Walkthrough

This pull request introduces significant refactoring and improvements to the home page components in the website2 project. The changes focus on modularizing code, improving state management, and enhancing component structure across multiple files. Key modifications include creating a new Accordion component, restructuring the HomePlayerSection and HomeStatsSection, and introducing a centralized data.ts file to manage static content like partner logos and accordion items.

Changes

File Change Summary
website2/src/views/home/Accordion.tsx New React functional component for dynamic accordion with state management and smooth transitions
website2/src/views/home/FeaturedCarousel.tsx Removed error handling, added fallback image logic
website2/src/views/home/HomePlayerSection.tsx Refactored into modular components, improved state management with VideoState, added memoization
website2/src/views/home/HomeStatsSection.tsx Restructured into smaller components, removed inline accordion, introduced new sections
website2/src/views/home/data.ts Added constants for partner logos, accordion items, and statistics
website2/src/app/clean-air-forum/layout.tsx Updated destructuring of useForumEvents to extract data as forumEvents
website2/src/app/partners/[id]/page.tsx Updated destructuring of usePartnerDetails to extract data as partner
website2/src/components/layouts/Navbar.tsx Changed logo link to use dynamic homepage URL from mainConfig
website2/src/hooks/useApiHooks.tsx Introduced useFetch hook to centralize data fetching logic, refactored existing hooks
website2/src/services/apiService/index.tsx Refactored API functions to use centralized getRequest for improved error handling
website2/src/services/externalService/index.tsx Enhanced API request structure and error handling with new request handler functions
website2/src/views/about/AboutPage.tsx Updated destructuring of hooks to access data property for members
website2/src/views/careers/CareerPage.tsx Updated destructuring of hooks to access data property for departments and careers
website2/src/views/careers/DetailsPage.tsx Updated destructuring of useCareerDetails to extract data as careerDetails
website2/src/views/cleanairforum/events/EventsPage.tsx Updated destructuring of useCleanAirEvents to extract data as cleanAirEvents
website2/src/views/cleanairforum/membership/MemberPage.tsx Updated destructuring of usePartners to extract data as partners
website2/src/views/cleanairforum/resources/ResourcePage.tsx Updated destructuring of useCleanAirResources to extract data as cleanAirResources
website2/src/views/events/EventPage.tsx Updated destructuring of useAirQoEvents to extract data as airQoEvents
website2/src/views/events/SingleEvent.tsx Updated destructuring of useEventDetails to extract data as eventDetails
website2/src/views/press/PressPage.tsx Updated destructuring of usePressArticles to extract data as pressArticles
website2/src/views/publications/ResourcePage.tsx Updated destructuring of usePublications to extract data as publications
website2/src/views/solutions/AfricanCities/AfricanCities.tsx Updated destructuring of useAfricanCountries to extract data as africanCountries

Suggested labels

ready for review

Suggested reviewers

  • Baalmart
  • Codebmk

Poem

🌐 Code transforms, components dance light,
Modular magic takes elegant flight.
Accordion opens, stats shine bright,
Refactoring's art, a developer's delight!
🚀 Innovation's rhythm, pure and clean.


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. (Beta)
  • @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

@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

🧹 Nitpick comments (13)
website2/src/views/home/HomeStatsSection.tsx (4)

2-7: Streamline import statements
You appear to be importing some components and hooks from multiple locations. Ensure that any shared imports are consistently declared for better modularity, and consider consolidating them in index files when the codebase grows.


15-29: High-level approach is coherent
The HomeStatsSection function-level structure is clear, hooking into useImpactNumbers gracefully. Adding the subcomponents fosters modular design.

A small improvement: consider how you handle potential loading or error states from useImpactNumbers if network latency or an error arises.


32-56: PartnerLogosSection clarity
Rendering partner logos from a data file keeps the component lean. However, consider adding alt text per partner to improve accessibility.

- alt={`Partner ${index + 1}`}
+ alt={partner ? `Partner: ${partner}` : `Partner ${index + 1}`}

122-148: StatisticsSection usage of impactNumbers
Your approach to mapping over statItems is concise. If the data from impactNumbers might be undefined or of unexpected shape, consider default values or robust checks.

website2/src/views/home/HomePlayerSection.tsx (3)

48-75: Memoized TextSection
Wrapping TextSection in React.memo is beneficial for performance. To further optimize, confirm that the props are stable references to avoid unexpected re-renders.


113-179: Modal with AnimatePresence
Neat usage of Framer Motion transitions. Double-check that no additional items on the page become inaccessible while the modal is open for improved accessibility.


249-250: Consistent naming
HomePlayerSection.displayName is a helpful detail for debugging. Ensure consistency across other memoized or forwardRef components.

website2/src/views/home/Accordion.tsx (2)

9-11: AccordionProps structure
Storing items in AccordionProps keeps the component flexible. In the future, you could extend it to accept custom renderers for advanced layouts.


13-48: Accordion toggle logic
Your toggle approach with a single open item is easy to read. If multi-expand is ever needed, consider arrays for openItem. The transition classes are also well done.

website2/src/views/home/data.ts (2)

13-13: Consider enhancing type safety and accessibility for partner logos.

While the array structure is clean, consider using an interface to include additional metadata like alt text for accessibility.

interface PartnerLogo {
  logo: typeof import('*.svg');
  name: string;
  alt: string;
}

export const partnerLogos: PartnerLogo[] = [
  { logo: Google, name: 'Google', alt: 'Google logo' },
  { logo: UsMission, name: 'US Mission', alt: 'US Mission Uganda logo' },
  // ... other partners
];

52-83: Standardize key naming convention and add type safety.

The statItems array uses inconsistent key naming (e.g., african_cities vs potential camelCase). Let's standardize this and add proper typing.

interface StatItem {
  label: string;
  key: 'africanCities' | 'champions' | 'deployedMonitors' | 'dataRecords' | 'researchPapers' | 'partners';
  icon: typeof import('*.svg');
}

export const statItems: StatItem[] = [
  {
    label: 'African Cities',
    key: 'africanCities',
    icon: Network,
  },
  // ... other items
];
website2/src/views/home/FeaturedCarousel.tsx (2)

Line range hint 69-74: Enhance image loading strategy and accessibility.

While the fallback image is a good addition, the image component could benefit from optimized loading attributes and better accessibility.

 <Image
   src={item.image_url || '/placeholder.svg'}
   alt={item.title}
   fill
   className="object-cover w-full h-full transition-transform duration-500 ease-in-out transform hover:scale-110 cursor-pointer"
   priority
+  sizes="(max-width: 768px) 100vw, 50vw"
+  loading={index === currentIndex ? "eager" : "lazy"}
+  aria-hidden={index !== currentIndex}
 />

Line range hint 104-134: Enhance carousel accessibility features.

The navigation controls are keyboard accessible, but we can improve the overall accessibility of the carousel.

+  // Add at the top of the component
+  useEffect(() => {
+    const handleKeyDown = (e: KeyboardEvent) => {
+      if (e.key === 'ArrowLeft') prevSlide();
+      if (e.key === 'ArrowRight') nextSlide();
+    };
+    window.addEventListener('keydown', handleKeyDown);
+    return () => window.removeEventListener('keydown', handleKeyDown);
+  }, []);

   <div className="mt-8 flex items-center gap-4">
+    <div className="sr-only" role="status" aria-live="polite">
+      Showing slide {currentIndex + 1} of {highlights.length}
+    </div>
     <div className="flex items-center gap-2">
       <span className="font-medium">
         {String(currentIndex + 1).padStart(2, '0')}
       </span>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ae7c3bc and a161ace.

📒 Files selected for processing (5)
  • website2/src/views/home/Accordion.tsx (1 hunks)
  • website2/src/views/home/FeaturedCarousel.tsx (4 hunks)
  • website2/src/views/home/HomePlayerSection.tsx (2 hunks)
  • website2/src/views/home/HomeStatsSection.tsx (1 hunks)
  • website2/src/views/home/data.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build-push-website2-deploy-preview
🔇 Additional comments (13)
website2/src/views/home/HomeStatsSection.tsx (4)

11-12: Great separation for Accordion and data
Splitting Accordion logic and data into separate files is an excellent move for maintainability. This improves clarity and reusability.


58-75: HeadingSection tab toggling
The tab switch logic is straightforward, but remember to visually highlight the active tab for accessibility (e.g., ARIA attributes).

Would you like a follow-up script to verify proper ARIA usage across the codebase?


78-94: AccordionAndImageSection - single responsibility
Nesting the accordion next to an image is well-executed. If these grow in complexity, consider splitting them to reduce future coupling.


96-120: Dynamic image sources
Switching the image URL based on activeTab is a nice touch. Ensure these remote images are always valid, or fallback gracefully if the URLs change.

website2/src/views/home/HomePlayerSection.tsx (7)

8-8: Optimized import for React
Using useCallback, useEffect, useRef, and useState from React is standard. Make sure nothing is re-exported unnecessarily, to keep the bundle small.


16-27: ForwardRef usage for ReactPlayer
Great strategy for controlling the player and ensuring stable references. This pattern is neat and fosters advanced interactions.


43-46: VideoState interface
Introducing a dedicated interface for modal and video states strengthens readability. Ensure naming is consistent across the codebase.


78-110: VideoSection handles background video
The usage of videoRef and a play button overlay is clean. Consider adding a fallback image or handling potential network slowdowns.


180-214: HomePlayerSection state management
The consolidated videoState object is a good approach. If scale grows, you might consider a more robust store or context to manage cross-component states.
[approve]


215-222: Callbacks for router and dispatch
Using useCallback around handleExploreData and handleGetInvolved is optimal for performance. Great job.


228-242: Conditional modal rendering
The AnimatePresence check for videoState.isModalOpen is succinct. This ensures dynamic addition/removal doesn’t break the rest of the page.

website2/src/views/home/Accordion.tsx (1)

4-7: Clarity in type definitions
Defining a clear AccordionItem type with title and content is straightforward and improves maintainability.

website2/src/views/home/data.ts (1)

1-12: Well-organized imports with clear grouping!

The imports are nicely structured, separating partner logos from impact number icons. Using absolute imports with the @ alias is a good practice for maintainability.

Comment on lines +15 to +50
export const accordionItems = {
cities: [
{
title: 'High Resolution Network',
content:
'We want cleaner air in all African cities. We leverage our understanding of the African context.',
},
{
title: 'Digital air quality platforms',
content:
'We empower decision-makers in African cities We increase access to air quality data evidence.',
},
{
title: 'Policy engagement',
content:
'We engage city authorities and government agencies We empower local leaders with air quality information.',
},
],
communities: [
{
title: 'AirQommunity Champions',
content:
'A growing network of individual change makers Championing local leaders and demand action.',
},
{
title: 'Free Access To Air Quality Information',
content:
'We train individuals and communities Facilitating access to air quality information.',
},
{
title: 'AirQo Hosts',
content:
'We engage locals host our deployment activities We involve locals in our maintenance drives.',
},
],
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve content formatting and add type safety to accordion items.

The content structure needs attention in two areas:

  1. Content strings contain multiple sentences without proper punctuation
  2. The object structure lacks type definitions
interface AccordionItem {
  title: string;
  content: string;
}

interface AccordionCategories {
  cities: AccordionItem[];
  communities: AccordionItem[];
}

export const accordionItems: AccordionCategories = {
  cities: [
    {
      title: 'High Resolution Network',
      content: 'We want cleaner air in all African cities. We leverage our understanding of the African context.',
    },
    // ... other items
  ],
  // ... other categories
};

Also, consider reformatting content strings to use proper punctuation and sentence structure.

@@ -7,7 +9,7 @@ import mainConfig from '@/configs/mainConfigs';
import { useHighlights } from '@/hooks/useApiHooks';

const FeaturedCarousel = () => {
const { highlights, isLoading, isError } = useHighlights();
const { highlights, isLoading } = useHighlights();
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Restore error handling for better user experience.

The removal of error handling is concerning. When the API call fails, users won't know why the content isn't displaying. Consider implementing a user-friendly error state.

 const FeaturedCarousel = () => {
-  const { highlights, isLoading } = useHighlights();
+  const { highlights, isLoading, error } = useHighlights();
   const [currentIndex, setCurrentIndex] = useState(0);

   // ... other code ...

+  if (error) {
+    return (
+      <section className="w-full bg-[#F0F4FA] py-16 md:py-24 overflow-hidden">
+        <div className={`${mainConfig.containerClass} px-4 sm:px-6 lg:px-8`}>
+          <div className="text-center">
+            <p className="text-gray-600">Unable to load highlights. Please try again later.</p>
+          </div>
+        </div>
+      </section>
+    );
+  }

   if (!highlights || highlights.length === 0) {
     return null;
   }

Also applies to: 48-48

Copy link
Contributor

New Website2 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: 4

🔭 Outside diff range comments (1)
website2/src/views/events/EventPage.tsx (1)

Line range hint 18-24: Add null check for airQoEvents array!

The component assumes airQoEvents is always an array, but it could be undefined when the API call hasn't completed or fails. This could lead to runtime errors.

Add null checks before filtering:

- const upcomingEvents = airQoEvents.filter(
+ const upcomingEvents = (airQoEvents || []).filter(
    (event: any) => new Date(event.end_date) > new Date(),
  );
- const pastEvents = airQoEvents.filter(
+ const pastEvents = (airQoEvents || []).filter(
    (event: any) => new Date(event.end_date) <= new Date(),
  );
- const featuredEvents = airQoEvents.filter(
+ const featuredEvents = (airQoEvents || []).filter(
    (event: any) => event.event_tag === 'Featured',
  );
♻️ Duplicate comments (1)
website2/src/views/home/FeaturedCarousel.tsx (1)

48-48: ⚠️ Potential issue

Restore error handling for better user experience.

The removal of error handling is concerning. When the API call fails, users won't know why the content isn't displaying.

 const FeaturedCarousel = () => {
-  const { data: highlights, isLoading } = useHighlights();
+  const { data: highlights, isLoading, isError } = useHighlights();
   const [currentIndex, setCurrentIndex] = useState(0);

   // ... other code ...

+  if (isError) {
+    return (
+      <section className="w-full bg-[#F0F4FA] py-16 md:py-24 overflow-hidden">
+        <div className={`${mainConfig.containerClass} px-4 sm:px-6 lg:px-8`}>
+          <div className="text-center">
+            <p className="text-gray-600">Unable to load highlights. Please try again later.</p>
+          </div>
+        </div>
+      </section>
+    );
+  }

   if (!highlights || highlights.length === 0) {
     return null;
   }
🧹 Nitpick comments (11)
website2/src/components/layouts/Navbar.tsx (1)

Line range hint 1-350: Consider these architectural improvements for better maintainability.

While the current implementation is solid, here are some suggestions to enhance the codebase:

  1. Move the menuItems configuration to a separate file, similar to how you're using mainConfig. This would make it easier to maintain and reuse.
  2. Consider memoizing the DropdownMenuContent component and callbacks using useMemo and useCallback to optimize performance.
  3. Extract the animation logic into a custom hook (e.g., useNavbarAnimation) to improve code organization.

Would you like me to provide example implementations for any of these suggestions?

website2/src/services/externalService/index.tsx (4)

1-20: Consider enhancing type safety and configuration robustness.

The configuration setup is well-structured, but could benefit from the following improvements:

+ // Types for environment variables
+ type EnvConfig = {
+   API_URL: string;
+   API_TOKEN: string;
+ };
+
+ // Common headers
+ const DEFAULT_HEADERS = {
+   'Content-Type': 'application/json',
+ } as const;
+
- const API_BASE_URL = `${removeTrailingSlash(process.env.NEXT_PUBLIC_API_URL || '')}/api/v2`;
- const API_TOKEN = process.env.NEXT_PUBLIC_API_TOKEN || '';
+ // Validate environment variables
+ const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL
+   ? `${removeTrailingSlash(process.env.NEXT_PUBLIC_API_URL)}/api/v2`
+   : throw new Error('NEXT_PUBLIC_API_URL is not defined');
+
+ const API_TOKEN = process.env.NEXT_PUBLIC_API_TOKEN
+   ?? throw new Error('NEXT_PUBLIC_API_TOKEN is not defined');

const apiClient: AxiosInstance = axios.create({
  baseURL: API_BASE_URL,
-  headers: {
-    'Content-Type': 'application/json',
-  },
+  headers: DEFAULT_HEADERS,
});

23-59: Enhance request handlers with better type safety and configuration.

The generic handlers are well-implemented, but consider these improvements:

+ // Define generic types for API responses
+ type ApiResponse<T> = {
+   data: T;
+   message: string;
+   status: number;
+ };
+
+ // Add request timeout and interceptors
+ apiClient.interceptors.request.use((config) => {
+   // Add common headers or tokens if needed
+   return {
+     timeout: 5000, // 5 seconds
+     ...config,
+   };
+ });
+
- const getRequest = async (endpoint: string, params?: any): Promise<any | null>
+ const getRequest = async <T>(
+   endpoint: string,
+   params?: Record<string, unknown>
+ ): Promise<T | null>

- const postRequest = async (endpoint: string, body: any): Promise<any | null>
+ const postRequest = async <T>(
+   endpoint: string,
+   body: Record<string, unknown>
+ ): Promise<T | null>

61-75: Enhance error handling with custom types and user-friendly messages.

The error handling is comprehensive, but consider these improvements:

+ // Define custom error types
+ type ApiError = {
+   code: string;
+   message: string;
+   userMessage: string;
+ };
+
+ // Error messages mapping
+ const ERROR_MESSAGES: Record<string, string> = {
+   'network-error': 'Please check your internet connection',
+   'not-found': 'The requested resource was not found',
+   'server-error': 'Something went wrong on our end',
+ };
+
- const handleError = (error: unknown, context: string) => {
+ const handleError = (error: unknown, context: string): ApiError => {
   if (axios.isAxiosError(error)) {
     const axiosError = error as AxiosError;
-    console.error(`Error in ${context}:`, axiosError.message);
+    // Add error tracking (e.g., Sentry)
+    // captureException(error);
+    
+    const code = axiosError.response?.status === 404 ? 'not-found' : 'server-error';
+    
+    return {
+      code,
+      message: axiosError.message,
+      userMessage: ERROR_MESSAGES[code],
+    };
   } else {
-    console.error(`Unexpected error in ${context}:`, error);
+    return {
+      code: 'unknown-error',
+      message: String(error),
+      userMessage: 'An unexpected error occurred',
+    };
   }
 };

77-96: Add type definitions for newsletter and contact form data.

The API functions could benefit from proper type definitions:

+ // Define types for request/response data
+ type NewsletterData = {
+   email: string;
+   name?: string;
+ };
+
+ type ContactFormData = {
+   name: string;
+   email: string;
+   message: string;
+   subject: string;
+ };
+
- export const subscribeToNewsletter = async (body: any): Promise<any | null>
+ export const subscribeToNewsletter = async (
+   body: NewsletterData
+ ): Promise<{ success: boolean; message: string } | null>

- export const postContactUs = async (body: any): Promise<any | null>
+ export const postContactUs = async (
+   body: ContactFormData
+ ): Promise<{ success: boolean; message: string } | null>
website2/src/services/apiService/index.tsx (2)

16-26: Consider enhancing error types and handling.

While the centralized error handling is a good improvement, consider:

  1. Creating custom error types for different API failures
  2. Adding retry logic for transient failures
  3. Including request metadata in error logs
 const getRequest = async (endpoint: string): Promise<any> => {
   try {
     const response = await apiClient.get(endpoint);
     return response.data;
   } catch (error) {
     const axiosError = error as AxiosError;
-    console.error(`Error fetching data from ${endpoint}:`, axiosError.message);
+    console.error(`Error fetching data from ${endpoint}:`, {
+      message: axiosError.message,
+      status: axiosError.response?.status,
+      data: axiosError.response?.data,
+    });
     throw axiosError;
   }
 };

28-114: Consider using specific return types instead of Promise<any>.

While standardizing return types is good, using any loses type safety. Consider:

  1. Creating interfaces for each response type
  2. Using those interfaces in the return type declarations

Example:

interface PressArticle {
  id: string;
  title: string;
  content: string;
  // ... other fields
}

export const getPressArticles = async (): Promise<PressArticle[]> => {
  return getRequest('/press/');
};
website2/src/hooks/useApiHooks.tsx (1)

27-49: Well-structured implementation of the centralized fetch hook!

The implementation is clean and follows SWR best practices. Consider adding generic type parameters to improve type safety:

-const useFetch = (
+const useFetch = <T = any>(
   key: string | null,
-  fetcher: () => Promise<any>,
+  fetcher: () => Promise<T>,
-  initialData: any = [],
+  initialData: T | null = null,
 ) => {
   const { data, error, mutate } = useSWR(key, fetcher, swrOptions);
   const isLoading = !data && !error;
 
   return {
-    data: data ?? initialData,
+    data: (data ?? initialData) as T,
     isLoading,
     isError: error,
     mutate,
   };
 };
website2/src/views/home/HomeStatsSection.tsx (2)

45-50: Improve image accessibility with descriptive alt text.

While the image handling includes good practices like fallback and blend mode, the alt text could be more descriptive than just "Partner X".

 <Image
   src={partner || '/placeholder.svg'}
-  alt={`Partner ${index + 1}`}
+  alt={`${partner.split('/').pop()?.split('.')[0]?.replace(/-/g, ' ')} logo`}
   width={120}
   height={50}
   className="mix-blend-multiply w-auto h-auto transition-transform duration-500 ease-in-out transform hover:scale-110 cursor-pointer"
 />

132-133: Simplify null coalescing with optional chaining.

The current implementation can be simplified while maintaining the same functionality.

-{impactNumbers?.[stat.key] ?? 0}+
+{impactNumbers?.[stat.key] || 0}+
website2/src/views/about/AboutPage.tsx (1)

38-42: LGTM! Consider enhancing type safety.

The explicit destructuring of the data property aligns well with common data fetching patterns. This change improves code clarity and maintainability.

Consider adding TypeScript interfaces for the hook return types to make the data structure more explicit:

interface BoardMember {
  // add relevant fields
}

interface UseQueryResult<T> {
  data: T[];
  isLoading: boolean;
}

const { data: boardMembers, isLoading: loadingBoard }: UseQueryResult<BoardMember> = useBoardMembers();
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a161ace and 8175f01.

📒 Files selected for processing (19)
  • website2/src/app/clean-air-forum/layout.tsx (1 hunks)
  • website2/src/app/partners/[id]/page.tsx (1 hunks)
  • website2/src/components/layouts/Navbar.tsx (1 hunks)
  • website2/src/hooks/useApiHooks.tsx (1 hunks)
  • website2/src/services/apiService/index.tsx (1 hunks)
  • website2/src/services/externalService/index.tsx (1 hunks)
  • website2/src/views/about/AboutPage.tsx (1 hunks)
  • website2/src/views/careers/CareerPage.tsx (1 hunks)
  • website2/src/views/careers/DetailsPage.tsx (1 hunks)
  • website2/src/views/cleanairforum/events/EventsPage.tsx (1 hunks)
  • website2/src/views/cleanairforum/membership/MemberPage.tsx (1 hunks)
  • website2/src/views/cleanairforum/resources/ResourcePage.tsx (1 hunks)
  • website2/src/views/events/EventPage.tsx (1 hunks)
  • website2/src/views/events/SingleEvent.tsx (1 hunks)
  • website2/src/views/home/FeaturedCarousel.tsx (4 hunks)
  • website2/src/views/home/HomeStatsSection.tsx (1 hunks)
  • website2/src/views/press/PressPage.tsx (1 hunks)
  • website2/src/views/publications/ResourcePage.tsx (1 hunks)
  • website2/src/views/solutions/AfricanCities/AfricanCities.tsx (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build-push-website2-deploy-preview
🔇 Additional comments (16)
website2/src/components/layouts/Navbar.tsx (1)

193-197: Simplify the href value and maintain consistent formatting.

Good move on using the configured URL instead of a hardcoded value! However, a few suggestions:

  1. The template literal is unnecessary here since we're not combining strings. You can directly use mainConfig.homePageUrl.
  2. The Link props could be formatted more consistently with the rest of the file (single line vs multi-line).
-          <Link
-            href={`${mainConfig.homePageUrl}`}
-            passHref
-            className="flex items-center"
-          >
+          <Link href={mainConfig.homePageUrl} passHref className="flex items-center">

Let's verify the configured home page URL:

✅ Verification successful

Confirmed: Template literal is unnecessary and homePageUrl is correctly configured.

The review comment is spot-on! I've verified that:

  • mainConfig.homePageUrl is properly defined as '/home' in the configs
  • This is the only instance of unnecessary template literal usage with mainConfig
  • The suggested formatting improvement aligns with the codebase style

The original suggestion to simplify to href={mainConfig.homePageUrl} is the right way to go.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check the mainConfig file for the homePageUrl value
rg "homePageUrl.*=.*['\"].*['\"]" -g "mainConfigs*"

Length of output: 57


Script:

#!/bin/bash
# Find files containing mainConfig
echo "=== Files containing mainConfig ==="
rg -l "mainConfig" --type-add 'config:*.{js,ts,jsx,tsx,json}' -t config

echo -e "\n=== mainConfig declarations/imports ==="
rg "(?:export|import).*mainConfig" --type-add 'config:*.{js,ts,jsx,tsx,json}' -t config

echo -e "\n=== homePageUrl usage ==="
rg "homePageUrl" --type-add 'config:*.{js,ts,jsx,tsx,json}' -t config

Length of output: 6657


Script:

#!/bin/bash
# Check for other similar template literal usage with mainConfig
echo "=== Similar template literal usage ==="
rg "\`\$\{mainConfig\.[^}]+\}\`" --type-add 'config:*.{js,ts,jsx,tsx}' -t config

echo -e "\n=== Other Link href patterns ==="
ast-grep --pattern 'href={`$_`}'

Length of output: 282

website2/src/app/clean-air-forum/layout.tsx (1)

20-20: Nice standardization of hook destructuring!

The change to data: forumEvents aligns with the codebase-wide standardization of hook return values, improving consistency and maintainability.

website2/src/services/apiService/index.tsx (1)

1-14: Good improvement in type safety!

The addition of proper type imports and explicit typing of apiClient as AxiosInstance enhances type safety and maintainability.

website2/src/views/careers/DetailsPage.tsx (1)

13-13: Well-structured component with proper state handling!

The hook destructuring change aligns with the standardization effort, and the component demonstrates good practices with comprehensive loading, error, and empty states.

website2/src/app/partners/[id]/page.tsx (1)

13-13: Clean implementation with attention to detail!

The hook destructuring change maintains consistency with the codebase standards. The component demonstrates excellent practices:

  • Comprehensive state handling
  • Responsive skeleton loader
  • Proper image optimization with Next.js Image component
  • Meaningful alt texts for accessibility
website2/src/hooks/useApiHooks.tsx (1)

90-150: Great organization with clear section separation!

The consistent implementation pattern and clear section comments make the code very maintainable.

website2/src/views/cleanairforum/resources/ResourcePage.tsx (1)

44-48: Clean implementation of the new data fetching pattern!

The destructuring aligns well with the centralized useFetch implementation.

website2/src/views/publications/ResourcePage.tsx (1)

12-12: Clean refactor of hook destructuring!

The change to use data: prefix in destructuring aligns with standard practices for handling API responses and maintains consistency across the codebase.

website2/src/views/cleanairforum/membership/MemberPage.tsx (1)

38-38: LGTM - Hook destructuring refactor!

The update to use data: prefix maintains consistency with the API response structure while preserving the existing categorization logic.

website2/src/views/careers/CareerPage.tsx (1)

15-23: Clean hook destructuring updates!

Both useDepartments and useCareers hooks have been consistently updated to use the data: prefix, maintaining a uniform pattern across the application.

website2/src/views/events/EventPage.tsx (1)

15-15: Hook destructuring refactor looks good!

The update to use data: prefix aligns with the codebase-wide changes for consistent API response handling.

website2/src/views/cleanairforum/events/EventsPage.tsx (1)

49-49: LGTM! Consistent hook data structure.

The destructuring pattern aligns with the standardized hook response structure across the codebase.

website2/src/views/events/SingleEvent.tsx (1)

20-20: LGTM! Consistent hook data structure.

The destructuring pattern aligns with the standardized hook response structure across the codebase.

website2/src/views/home/FeaturedCarousel.tsx (2)

12-12: LGTM! Consistent hook data structure.

The destructuring pattern aligns with the standardized hook response structure across the codebase.


69-69: LGTM! Good fallback for missing images.

Adding a fallback image source is a good practice to handle cases where the image URL is undefined.

website2/src/views/home/HomeStatsSection.tsx (1)

15-15: LGTM! Consistent hook data structure.

The destructuring pattern aligns with the standardized hook response structure across the codebase.

Comment on lines +102 to 119
/**
* Fetch grids summary data. Requires API token for authentication.
*/
export const getGridsSummary = async (): Promise<any | null> => {
try {
const response = await apiClient.get('/devices/grids/summary', {
params: {
token: API_TOKEN,
const response: AxiosResponse<any> = await apiClient.get(
'/devices/grids/summary',
{
params: {
token: API_TOKEN,
},
},
});

);
return response.data;
} catch (error) {
console.error('Error fetching grids summary data:', error);
handleError(error, 'GET /devices/grids/summary');
return null;
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Refactor getGridsSummary to use generic handler.

The getGridsSummary function should use the generic getRequest handler for consistency:

+ type GridsSummaryResponse = {
+   total: number;
+   active: number;
+   inactive: number;
+ };
+
- export const getGridsSummary = async (): Promise<any | null> => {
-   try {
-     const response: AxiosResponse<any> = await apiClient.get(
-       '/devices/grids/summary',
-       {
-         params: {
-           token: API_TOKEN,
-         },
-       },
-     );
-     return response.data;
-   } catch (error) {
-     handleError(error, 'GET /devices/grids/summary');
-     return null;
-   }
- };
+ export const getGridsSummary = async (): Promise<GridsSummaryResponse | null> => {
+   return getRequest<GridsSummaryResponse>('/devices/grids/summary', {
+     token: API_TOKEN,
+   });
+ };

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

Comment on lines +71 to +72
export const useEventDetails = (id: string | null) =>
useFetch(id ? `eventDetails/${id}` : null, () => getEventDetails(id!), null);
Copy link

@coderabbitai coderabbitai bot Jan 28, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider safer error handling for ID-based hooks.

The use of the non-null assertion operator (!) with IDs could lead to runtime errors if null is passed. Consider handling the error case explicitly:

 export const useEventDetails = (id: string | null) =>
-  useFetch(id ? `eventDetails/${id}` : null, () => getEventDetails(id!), null);
+  useFetch(
+    id ? `eventDetails/${id}` : null,
+    () => id ? getEventDetails(id) : Promise.reject(new Error('Invalid ID')),
+    null
+  );

Also applies to: 84-89

Copy link
Collaborator

Choose a reason for hiding this comment

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

reviewed this? @OchiengPaul442

Copy link

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

@@ -9,7 +9,7 @@ import mainConfig from '@/configs/mainConfigs';
import { usePressArticles } from '@/hooks/useApiHooks';

const PressPage: React.FC = () => {
const { pressArticles, isLoading, isError } = usePressArticles();
const { data: pressArticles, isLoading, isError } = usePressArticles();
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add safety checks for array operations.

The component assumes pressArticles is always an array. Add safety checks to prevent runtime errors:

-  const totalPages = Math.ceil(pressArticles.length / itemsPerPage);
+  const totalPages = Math.ceil((pressArticles?.length ?? 0) / itemsPerPage);
-  const displayedArticles = pressArticles.slice(
+  const displayedArticles = pressArticles?.slice(
     (currentPage - 1) * itemsPerPage,
     currentPage * itemsPerPage,
-  );
+  ) ?? [];

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

Comment on lines 8 to 10
const { data: africanCountries, isLoading, isError } = useAfricanCountries();
const [selectedCountry, setSelectedCountry] = useState<any | null>(null);
const [selectedCity, setSelectedCity] = useState<any | null>(null);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add proper TypeScript interfaces for better type safety.

Replace any types with proper interfaces:

interface City {
  id: string;
  city_name: string;
  content: Array<{
    title: string;
    description: Array<{ id: string; paragraph: string }>;
    image: Array<{ id: string; image_url: string }>;
  }>;
}

interface Country {
  id: string;
  country_name: string;
  country_flag_url: string;
  city: City[];
}

const [selectedCountry, setSelectedCountry] = useState<Country | null>(null);
const [selectedCity, setSelectedCity] = useState<City | null>(null);

@OchiengPaul442 OchiengPaul442 changed the title Website: Added Error handling and home page UI adjustments Website: Added Error handling and UI adjustments Jan 28, 2025
Copy link
Contributor

New Website2 changes available for preview here

1 similar comment
Copy link
Contributor

New Website2 changes available for preview here

Copy link
Contributor

New Website2 changes available for preview here

Copy link
Collaborator

@Baalmart Baalmart left a comment

Choose a reason for hiding this comment

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

Thanks @OchiengPaul442 , just highlighted one interested AI bot review comment. Please check it out whenever you get time soon

Comment on lines +71 to +72
export const useEventDetails = (id: string | null) =>
useFetch(id ? `eventDetails/${id}` : null, () => getEventDetails(id!), null);
Copy link
Collaborator

Choose a reason for hiding this comment

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

reviewed this? @OchiengPaul442

@Baalmart Baalmart merged commit c0b3e49 into staging Jan 29, 2025
31 checks passed
@Baalmart Baalmart deleted the website-update-codebase branch January 29, 2025 03:29
@Baalmart Baalmart mentioned this pull request Jan 29, 2025
1 task
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