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: General Maintenance and code base enhancements #2413

Merged
merged 8 commits into from
Jan 27, 2025

Conversation

OchiengPaul442
Copy link
Contributor

@OchiengPaul442 OchiengPaul442 commented Jan 26, 2025

Summary of Changes (What does this PR do?)

  • Fix for the broken links on the resources page
  • Created a central config to adjust website layout and padding
  • Refactor and code base clean up
  • Refactored the SWR config to only try api requests incase of error (max of 3 time only).

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

Release Notes

  • New Features

    • Introduced a centralized configuration management system with mainConfig.
    • Added a new custom hook useResourceFilter for managing resource filtering and pagination.
    • Created a new ResourceCard component for displaying resource information.
  • Improvements

    • Enhanced layout flexibility by dynamically applying container classes across multiple components.
    • Streamlined navigation in components using Link instead of imperative routing.
    • Improved type safety with new TypeScript interfaces and type definitions.
  • Refactoring

    • Simplified state management in various components.
    • Updated routing and navigation logic in multiple views.
    • Standardized layout configurations across the application.
  • Technical Enhancements

    • Added module path resolution for configurations in TypeScript.
    • Implemented more declarative component structures.
    • Improved pagination and resource filtering mechanisms.

Copy link

coderabbitai bot commented Jan 26, 2025

📝 Walkthrough

Walkthrough

This pull request introduces a comprehensive refactoring of layout configurations across the website, primarily focusing on centralizing styling through a new mainConfig configuration. The changes involve replacing hardcoded maximum width and centering classes with a dynamic, configurable approach. A new mainConfig.ts file has been created to manage global layout settings, and multiple components have been updated to leverage this centralized configuration.

Changes

File/Path Change Summary
website2/src/configs/mainConfigs.ts Added new Config type and mainConfig with template, homepage, and container class settings
Multiple Page Components Replaced max-w-5xl mx-auto with dynamic mainConfig.containerClass
Layout Components Integrated mainConfig for consistent styling across different sections
website2/tsconfig.json Added path mapping for @/configs/*
website2/src/hooks/useResourceFilter.ts Introduced a custom hook for filtering and pagination of resources
website2/src/types/index.ts Added a new Resource interface and ResourceCategory type alias
website2/src/utils/string-utils.ts Added a new formatString function for string processing
website2/src/components/ui/Pagination.tsx Updated pagination logic with new optional properties for configuration

Suggested reviewers

  • Baalmart
  • Codebmk

Possibly related PRs

Poem

Code flows like rivers, config in hand,
Tailwind classes dance across the land.
Flexibility blooms with each new line,
Styling now dances by design divine! 🌈✨

✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

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

🔭 Outside diff range comments (2)
website2/src/app/legal/layout.tsx (1)

Replace placeholder 'yourdomain.com' URLs across all page metadata

Found placeholder URLs in metadata across multiple pages that need to be updated with the actual production domain for proper SEO and social sharing:

  • website2/src/app/page.tsx (home page)
  • website2/src/app/clean-air-network/layout.tsx
  • website2/src/app/solutions/layout.tsx
  • website2/src/app/partners/layout.tsx
  • website2/src/app/products/layout.tsx
  • website2/src/app/legal/layout.tsx
  • website2/src/app/explore-data/layout.tsx
  • website2/src/app/contact/layout.tsx
  • website2/src/app/(about)/about-us/page.tsx
  • website2/src/app/(about)/careers/layout.tsx
  • website2/src/app/(about)/resources/page.tsx
  • website2/src/app/(about)/press/page.tsx
  • website2/src/app/(about)/events/layout.tsx

Each file contains placeholder URLs for page URLs, OG images, and canonical links that need to be updated.

🔗 Analysis chain

Line range hint 19-24: Update placeholder URLs in metadata.

The metadata contains placeholder URLs (yourdomain.com) which should be replaced with actual production URLs for proper SEO.

Run this script to check for other placeholder URLs in metadata:

Also applies to: 31-34, 42-44

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for placeholder URLs in metadata configurations
rg -g '*.tsx' -g '*.ts' "yourdomain.com" 

Length of output: 3984

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

Line range hint 61-90: Enhance carousel accessibility.

The carousel implementation could benefit from additional accessibility features for better screen reader support.

 <div
-  className="flex transition-transform duration-500 ease-in-out"
+  className="flex transition-transform duration-500 ease-in-out"
+  role="region"
+  aria-roledescription="carousel"
+  aria-label="Featured highlights"
   style={{ transform: `translateX(-${currentIndex * 100}%)` }}
 >
   {highlights.map((item: any) => (
     <div
       key={item.id}
+      role="group"
+      aria-roledescription="slide"
       className="w-full flex-shrink-0 flex flex-col md:flex-row gap-8 md:gap-16"
     >
🧹 Nitpick comments (35)
website2/src/utils/string-utils.ts (1)

1-6: Consider handling edge cases in formatString

The function looks clean and follows TypeScript best practices. However, it might benefit from handling edge cases.

Consider this enhanced implementation:

 export const formatString = (category: string): string => {
+  if (!category) return '';
   return category
     .split('_')
+    .filter(Boolean)  // Handle consecutive underscores
     .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
     .join(' ');
 };
website2/src/views/cleanairforum/resources/ResourceCard.tsx (1)

11-58: Enhance accessibility with ARIA attributes

The component could benefit from improved accessibility support.

Consider adding these attributes:

-  <div className="bg-white rounded-lg p-5 lg:p-8 shadow">
+  <div 
+    className="bg-white rounded-lg p-5 lg:p-8 shadow"
+    role="article"
+    aria-labelledby={`resource-title-${resource.id}`}
+  >
     <p className="text-blue-500 text-lg font-semibold mb-2">
       {formatString(resource.resource_category)}
     </p>
-    <h3 className="text-2xl lg:text-4xl font-semibold mb-2">
+    <h3 
+      id={`resource-title-${resource.id}`}
+      className="text-2xl lg:text-4xl font-semibold mb-2"
+    >
website2/src/views/cleanairforum/resources/ResourcePage.tsx (3)

21-27: Move categories to configuration file

Consider moving the categories array to a dedicated configuration file for better maintainability.

This would align with the PR's goal of centralizing configurations. Consider creating a new file:

// src/configs/resourceConfigs.ts
export const resourceCategories = [
  'All',
  'Toolkit',
  'Technical Report',
  'Workshop Report',
  'Research Publication',
] as const;

export type ResourceCategory = typeof resourceCategories[number];

Line range hint 62-67: Optimize image loading with proper dimensions

While the priority attribute is good for above-the-fold images, consider optimizing the image dimensions.

   <Image
     src="https://res.cloudinary.com/dbibjvyhm/image/upload/v1728132390/website/cleanAirForum/images/resource_apztbz.webp"
     alt="Air Quality Management Banner"
-    width={800}
-    height={400}
+    width={1200}
+    height={600}
+    sizes="(max-width: 768px) 100vw, 1200px"
     className="rounded-lg object-contain w-full"
     priority
   />

101-125: Implement virtualization for better performance

For better performance with large datasets, consider implementing virtualization.

You could use react-window or react-virtualized to render only the visible resources. This would be especially beneficial as the resource list grows.

Example implementation pattern:

import { FixedSizeList } from 'react-window';

const Row = ({ index, style }: { index: number; style: object }) => (
  <div style={style}>
    <ResourceCard resource={paginatedResources[index]} />
  </div>
);

// In render:
<FixedSizeList
  height={800}
  itemCount={paginatedResources.length}
  itemSize={200}
  width="100%"
>
  {Row}
</FixedSizeList>
website2/src/configs/mainConfigs.ts (1)

7-11: Consider environment-specific configuration values.

While the configuration centralization is good, consider:

  1. Making these values environment-specific
  2. Using a more specific templateName (e.g., 'AirQo Public Website')
website2/src/hooks/useResourceFilter.ts (2)

12-19: Enhance category comparison robustness.

The string manipulation for category comparison could be improved:

  1. Multiple spaces in category names aren't handled
  2. Consider using a more robust transformation method
-        selectedCategory.toLowerCase().replace(' ', '_'),
+        selectedCategory.toLowerCase().trim().replace(/\s+/g, '_'),

5-41: Consider adding error handling for edge cases.

The hook could benefit from:

  1. Validation for negative itemsPerPage
  2. Handling empty resource arrays
  3. Boundary checking for currentPage
website2/src/views/cleanairforum/RegisterBanner.tsx (1)

Line range hint 13-20: Move image URL to configuration.

Consider moving the hardcoded Cloudinary URL to the configuration system for better maintainability.

website2/src/views/legal/Tabsection.tsx (1)

14-19: Consider enhancing tab configuration.

The tabs array could be moved to a separate configuration file for better maintainability.

-const tabs: Tab[] = [
-  { label: 'Terms of Service', value: 'terms-of-service' },
-  { label: 'Privacy Policy', value: 'privacy-policy' },
-  { label: 'AirQo Data', value: 'airqo-data' },
-  { label: 'Payment Terms & Refund Policy', value: 'payment-refund-policy' },
-];

Create a new file configs/tabConfigs.ts:

export const legalTabs: Tab[] = [
  { label: 'Terms of Service', value: 'terms-of-service' },
  { label: 'Privacy Policy', value: 'privacy-policy' },
  { label: 'AirQo Data', value: 'airqo-data' },
  { label: 'Payment Terms & Refund Policy', value: 'payment-refund-policy' },
];
website2/src/app/clean-air-forum/layout.tsx (1)

32-55: Well-structured semantic HTML layout.

Good use of semantic HTML elements (header, main, footer) and proper integration of mainConfig for container styling.

Consider adding aria-labels for better accessibility:

-<header className="sticky top-0 z-50">
+<header className="sticky top-0 z-50" aria-label="Main navigation">
website2/src/components/layouts/NotificationBanner.tsx (2)

26-44: Consider accessibility improvements for the notification banner.

While the implementation is good, consider adding ARIA attributes for better accessibility.

-<Link href={CLEAN_AIR_NETWORK_ROUTE}>
+<Link 
+  href={CLEAN_AIR_NETWORK_ROUTE}
+  aria-label="Join the CLEAN-AIR Network"
+>

31-37: Consider performance impact of infinite animation.

The infinite animation might impact performance on low-end devices. Consider adding a prefers-reduced-motion media query check.

const shouldAnimate = typeof window !== 'undefined' 
  ? !window.matchMedia('(prefers-reduced-motion: reduce)').matches 
  : false;

<motion.span
  className="text-blue-600 hover:text-blue-800 flex items-center"
  animate={shouldAnimate ? { x: [0, 4, 0] } : {}}
  transition={shouldAnimate ? { duration: 1.5, repeat: Number.POSITIVE_INFINITY } : {}}
>
website2/src/components/layouts/ActionButtons2.tsx (1)

Line range hint 15-19: Consider moving external URLs to configuration.

The hardcoded Google Form URLs should be moved to a configuration file for better maintainability and easier updates.

Example implementation:

+ import { externalLinks } from '@/configs/links';
  // ...
- window.open('https://docs.google.com/forms/d/e/1FAIpQLScIPz7VrhfO2ifMI0dPWIQRiGQ9y30LoKUCT-DDyorS7sAKUA/viewform');
+ window.open(externalLinks.joinNetworkForm);
  // ...
- window.open('https://docs.google.com/forms/d/14jKDs2uCtMy2a_hzyCiJnu9i0GbxITX_DJxVB4GGP5c/edit');
+ window.open(externalLinks.registerEventForm);

Also applies to: 36-40

website2/src/components/layouts/Highlight.tsx (1)

Line range hint 14-20: Consider moving Cloudinary URLs to configuration.

The hardcoded Cloudinary image URLs should be moved to a configuration file for better maintainability and easier updates.

Example implementation:

+ import { images } from '@/configs/images';
  // ...
- src="https://res.cloudinary.com/dbibjvyhm/image/upload/v1728132440/website/photos/highlights/engineer_byss3s.webp"
+ src={images.highlights.engineer}
  // ...
- src="https://res.cloudinary.com/dbibjvyhm/image/upload/v1728132441/website/photos/highlights/google-org_clia2b.svg"
+ src={images.highlights.googleOrg}

Also applies to: 29-35

website2/src/views/cleanairforum/PaginatedSection.tsx (1)

Line range hint 10-82: Consider extracting pagination logic to a custom hook.

The pagination logic could be reused across components. Consider creating a custom hook like usePagination:

function usePagination<T>(items: T[], itemsPerPage: number) {
  const [currentPage, setCurrentPage] = useState(1);
  const totalPages = Math.ceil(items.length / itemsPerPage);
  
  const paginatedItems = useMemo(() => {
    const startIndex = (currentPage - 1) * itemsPerPage;
    return items.slice(startIndex, startIndex + itemsPerPage);
  }, [items, currentPage, itemsPerPage]);

  return {
    currentPage,
    setCurrentPage,
    totalPages,
    paginatedItems,
  };
}
website2/src/views/events/EventCardsSection.tsx (1)

Line range hint 46-48: Consider using CSS Grid's auto-fit for better responsiveness.

The current grid layout could be more responsive. Consider this improvement:

-<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
+<div className="grid grid-cols-repeat(auto-fit, minmax(300px, 1fr)) gap-8">

This would:

  1. Automatically adjust columns based on available space
  2. Eliminate the need for explicit breakpoints
  3. Maintain consistent card sizes
website2/src/views/home/AppDownloadSection.tsx (1)

23-25: Consider extracting common padding classes to mainConfig.

The padding classes (px-4 sm:px-6 lg:px-8 py-16 lg:py-24) appear to be layout-related. Consider moving these to mainConfig for consistency with the centralization approach.

-        className={`${mainConfig.containerClass} relative px-4 sm:px-6 lg:px-8 py-16 lg:py-24`}
+        className={`${mainConfig.containerClass} ${mainConfig.sectionPadding} relative`}
website2/src/views/careers/DetailsPage.tsx (2)

18-18: Consider extracting padding to mainConfig.

The padding class (p-8) could be moved to mainConfig for consistency.

-      <div className={`${mainConfig.containerClass} p-8`}>
+      <div className={`${mainConfig.containerClass} ${mainConfig.contentPadding}`}>

60-62: Consider extracting common styling classes to mainConfig.

The styling classes (bg-white border border-gray-200 rounded-lg) could be moved to mainConfig as a reusable card style.

-      className={`${mainConfig.containerClass} w-full p-8 bg-white border border-gray-200 rounded-lg mt-6`}
+      className={`${mainConfig.containerClass} ${mainConfig.cardStyle} w-full mt-6`}
website2/src/components/layouts/NewsLetter.tsx (1)

49-51: Consider extracting flex layout patterns to mainConfig.

The flex layout classes could be moved to mainConfig as they might be reused across components.

-        className={`${mainConfig.containerClass} flex flex-col lg:flex-row items-center lg:items-start lg:justify-between gap-12`}
+        className={`${mainConfig.containerClass} ${mainConfig.flexColumnToRow} gap-12`}
website2/src/views/legal/PRP_Page.tsx (1)

102-102: Consider extracting flex layout patterns to mainConfig.

The flex layout classes could be moved to mainConfig for consistency with other components.

-    <div className="w-full flex flex-col md:flex-row">
+    <div className={`${mainConfig.flexColumnToRow}`}>
website2/src/views/press/PressPage.tsx (1)

Line range hint 59-64: Consider memoizing the skeleton loader component.

Since ArticleSkeleton is a static component, it could benefit from memoization to prevent unnecessary re-renders.

-  const ArticleSkeleton = () => (
+  const ArticleSkeleton = React.memo(() => (
     <div className="p-8 lg:px-16 lg:py-12 space-y-8 rounded-lg shadow-sm bg-gray-200 animate-pulse">
       {/* ... skeleton content ... */}
     </div>
-  );
+  ));
website2/src/views/solutions/AfricanCities/AfricanCities.tsx (1)

Line range hint 41-45: Consider enhancing error feedback.

The error message could be more specific to help users understand what went wrong and what they can do about it.

-      <div className="text-center py-20 text-red-500">
-        Failed to load data. Please try again later.
+      <div className="text-center py-20 space-y-4">
+        <p className="text-red-500">Unable to load African countries data.</p>
+        <p className="text-sm text-gray-600">
+          Please check your connection and try refreshing the page.
+        </p>
       </div>
website2/src/views/home/FeaturedCarousel.tsx (1)

Line range hint 124-143: Add keyboard navigation support.

Consider adding keyboard navigation support for better accessibility.

+  useEffect(() => {
+    const handleKeyDown = (e: KeyboardEvent) => {
+      if (e.key === 'ArrowLeft') prevSlide();
+      if (e.key === 'ArrowRight') nextSlide();
+    };
+    window.addEventListener('keydown', handleKeyDown);
+    return () => window.removeEventListener('keydown', handleKeyDown);
+  }, []);
website2/src/views/cleanairforum/FeaturedEvent.tsx (2)

Line range hint 48-68: Consider extracting skeleton and error components.

These components could be reused across the application. Consider moving them to a shared components directory.

+// In @/components/ui/LoadingSkeleton.tsx
+export const EventSkeleton = () => (
+  <section
+    className={`${mainConfig.containerClass} w-full mt-16 bg-blue-50 rounded-lg shadow-sm p-6 lg:px-8 lg:py-12 flex flex-col lg:flex-row items-center gap-6 animate-pulse`}
+  >
+    {/* Current skeleton content */}
+  </section>
+);

+// In @/components/ui/ErrorMessage.tsx
+export const EventError = ({ message }: { message: string }) => (
+  <section
+    className={`${mainConfig.containerClass} w-full mt-16 bg-blue-50 rounded-lg shadow-sm p-6 lg:px-8 lg:py-12 flex flex-col lg:flex-row items-center gap-6`}
+  >
+    {/* Current error content */}
+  </section>
+);

Line range hint 95-108: Consider using a date formatting library.

Using a library like date-fns or dayjs would provide more consistent date formatting and better localization support.

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

-    const formattedDate = end_date
-      ? new Date(end_date).toLocaleDateString('en-US', {
-          day: '2-digit',
-          month: 'long',
-          year: 'numeric',
-        })
-      : 'Date TBA';
+    const formattedDate = end_date
+      ? format(new Date(end_date), 'MMMM dd, yyyy')
+      : 'Date TBA';

-    const formattedTime =
-      start_time && end_time
-        ? `${new Date(`1970-01-01T${start_time}`).toLocaleTimeString([], {
-            hour: '2-digit',
-            minute: '2-digit',
-          })} - ${new Date(`1970-01-01T${end_time}`).toLocaleTimeString([], {
-            hour: '2-digit',
-            minute: '2-digit',
-          })}`
-        : 'Time TBA';
+    const formattedTime =
+      start_time && end_time
+        ? `${format(new Date(`1970-01-01T${start_time}`), 'h:mm a')} - ${format(
+            new Date(`1970-01-01T${end_time}`),
+            'h:mm a'
+          )}`
+        : 'Time TBA';
website2/src/views/publications/ResourcePage.tsx (1)

7-7: Consider moving padding configuration to mainConfig.

While the container class is now centralized, the padding classes (px-4 lg:px-8) are still hardcoded. Consider moving these to mainConfig for consistent padding across components.

-<div className={`${mainConfig.containerClass} px-4 lg:px-8`}>
+<div className={`${mainConfig.containerClass} ${mainConfig.paddingClass}`}>

Also applies to: 70-71

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

119-124: Consider enhancing loading skeleton accessibility.

While the loading skeleton implementation is good, consider adding aria-label or role="status" to improve accessibility for screen readers.

 <div
   key={idx}
-  className="flex items-center justify-between p-4 lg:p-6 bg-[#E0E0E0] rounded-lg animate-pulse"
+  className="flex items-center justify-between p-4 lg:p-6 bg-[#E0E0E0] rounded-lg animate-pulse"
+  role="status"
+  aria-label="Loading job listing"
 >
website2/src/app/products/api/ApiPage.tsx (1)

89-89: Consider extracting common layout patterns.

Multiple sections share similar layout patterns. Consider extracting these into reusable layout components to reduce duplication and improve maintainability.

Example of a reusable section layout:

interface SectionLayoutProps {
  children: React.ReactNode;
  className?: string;
}

const SectionLayout: React.FC<SectionLayoutProps> = ({ children, className = '' }) => (
  <div className={`${mainConfig.containerClass} ${className}`}>
    {children}
  </div>
);

Also applies to: 120-122, 167-169

website2/src/app/products/mobile-app/MobilePage.tsx (1)

52-54: Well-structured layout configuration.

The mainConfig integration maintains the responsive design while centralizing the layout configuration. The existing flex and grid arrangements are preserved appropriately.

However, consider adding JSDoc comments for the motion variants to improve code documentation.

+/**
+ * Animation variant for container elements
+ * Applies a staggered fade-in effect to children
+ */
const containerVariants = {
  hidden: { opacity: 0 },
  visible: {
    opacity: 1,
    transition: {
      staggerChildren: 0.1,
      duration: 0.5,
      ease: 'easeOut',
    },
  },
};

Also applies to: 91-91, 121-123, 181-183, 237-239

website2/src/components/dialogs/EngagementDialog.tsx (1)

Line range hint 141-165: Consider enhancing form validation.

The form validation could be improved by:

  1. Adding client-side email format validation
  2. Implementing a more robust terms acceptance check
 const handleSubmit = async (e: React.FormEvent) => {
   e.preventDefault();
   setLoading(true);
   setSubmissionError(null);
 
+  // Email validation
+  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+  if (!emailRegex.test(formData.email)) {
+    setSubmissionError('Please enter a valid email address');
+    setLoading(false);
+    return;
+  }
+
+  // Terms acceptance validation
+  if (!formData.termsAccepted) {
+    setSubmissionError('Please accept the terms and conditions');
+    setLoading(false);
+    return;
+  }
 
   if (formData.termsAccepted && selectedCategory) {
website2/src/views/cleanairforum/events/SingleEvent.tsx (1)

280-288: Consider simplifying date formatting.

The date formatting logic could be simplified using a helper function to reduce code duplication.

+const formatTime = (time: string) => format(new Date(`1970-01-01T${time}`), 'h:mm a');
+
 <span className="text-sm text-gray-500">
-  {format(
-    new Date(`1970-01-01T${session.start_time}`),
-    'h:mm a',
-  )}{' '}
-  -{' '}
-  {format(
-    new Date(`1970-01-01T${session.end_time}`),
-    'h:mm a',
-  )}
+  {formatTime(session.start_time)} - {formatTime(session.end_time)}
 </span>
website2/src/views/cleanairforum/TabNavigation.tsx (2)

9-14: Good move on extracting the tabs array! 🎯

Moving the tabs configuration outside the component prevents unnecessary re-creation on each render. Consider defining this as a constant using as const for better type inference:

-const tabs = [
+const tabs = [
   { label: 'About', value: '/clean-air-network' },
   { label: 'Membership', value: '/clean-air-network/membership' },
   { label: 'Events', value: '/clean-air-network/events' },
   { label: 'Resources', value: '/clean-air-network/resources' },
-];
+] as const;

27-45: Clean and semantic implementation! ♿

Great use of semantic HTML with the nav element and proper styling transitions. Consider adding ARIA labels for better accessibility:

-    <nav className="border-y pt-4 overflow-x-auto bg-white border-gray-200">
+    <nav 
+      className="border-y pt-4 overflow-x-auto bg-white border-gray-200"
+      aria-label="Clean Air Network navigation"
+    >
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 1e1846c and 26b03ad.

📒 Files selected for processing (58)
  • website2/src/app/clean-air-forum/glossary/page.tsx (1 hunks)
  • website2/src/app/clean-air-forum/layout.tsx (2 hunks)
  • website2/src/app/clean-air-forum/logistics/page.tsx (2 hunks)
  • website2/src/app/clean-air-forum/partners/page.tsx (5 hunks)
  • website2/src/app/clean-air-forum/program-committee/page.tsx (2 hunks)
  • website2/src/app/clean-air-forum/resources/page.tsx (2 hunks)
  • website2/src/app/clean-air-forum/schedule/page.tsx (4 hunks)
  • website2/src/app/clean-air-forum/speakers/page.tsx (3 hunks)
  • website2/src/app/legal/layout.tsx (2 hunks)
  • website2/src/app/products/analytics/AnalyticsPage.tsx (6 hunks)
  • website2/src/app/products/api/ApiPage.tsx (6 hunks)
  • website2/src/app/products/calibrate/CalibratePage.tsx (6 hunks)
  • website2/src/app/products/mobile-app/MobilePage.tsx (6 hunks)
  • website2/src/app/products/monitor/MonitorPage.tsx (7 hunks)
  • website2/src/components/dialogs/EngagementDialog.tsx (2 hunks)
  • website2/src/components/layouts/ActionButtons.tsx (2 hunks)
  • website2/src/components/layouts/ActionButtons2.tsx (1 hunks)
  • website2/src/components/layouts/Footer.tsx (2 hunks)
  • website2/src/components/layouts/Highlight.tsx (1 hunks)
  • website2/src/components/layouts/Navbar.tsx (2 hunks)
  • website2/src/components/layouts/NewsLetter.tsx (2 hunks)
  • website2/src/components/layouts/NotificationBanner.tsx (1 hunks)
  • website2/src/components/ui/Pagination.tsx (1 hunks)
  • website2/src/configs/mainConfigs.ts (1 hunks)
  • website2/src/hooks/useResourceFilter.ts (1 hunks)
  • website2/src/types/index.ts (1 hunks)
  • website2/src/utils/string-utils.ts (1 hunks)
  • website2/src/views/about/AboutPage.tsx (5 hunks)
  • website2/src/views/careers/CareerPage.tsx (3 hunks)
  • website2/src/views/careers/DetailsPage.tsx (3 hunks)
  • website2/src/views/cleanairforum/FeaturedEvent.tsx (3 hunks)
  • website2/src/views/cleanairforum/PaginatedSection.tsx (2 hunks)
  • website2/src/views/cleanairforum/RegisterBanner.tsx (1 hunks)
  • website2/src/views/cleanairforum/TabNavigation.tsx (1 hunks)
  • website2/src/views/cleanairforum/events/EventsPage.tsx (3 hunks)
  • website2/src/views/cleanairforum/events/SingleEvent.tsx (5 hunks)
  • website2/src/views/cleanairforum/membership/MemberPage.tsx (1 hunks)
  • website2/src/views/cleanairforum/resources/ResourceCard.tsx (1 hunks)
  • website2/src/views/cleanairforum/resources/ResourcePage.tsx (3 hunks)
  • website2/src/views/events/EventCardsSection.tsx (2 hunks)
  • website2/src/views/events/EventPage.tsx (6 hunks)
  • website2/src/views/events/SingleEvent.tsx (4 hunks)
  • website2/src/views/home/AnalyticsContentSection.tsx (2 hunks)
  • website2/src/views/home/AppDownloadSection.tsx (2 hunks)
  • website2/src/views/home/FeaturedCarousel.tsx (3 hunks)
  • website2/src/views/home/HomePlayerSection.tsx (2 hunks)
  • website2/src/views/home/HomeStatsSection.tsx (2 hunks)
  • website2/src/views/legal/AirQoDataPage.tsx (1 hunks)
  • website2/src/views/legal/PP_Page.tsx (1 hunks)
  • website2/src/views/legal/PRP_Page.tsx (1 hunks)
  • website2/src/views/legal/TOSPage.tsx (1 hunks)
  • website2/src/views/legal/Tabsection.tsx (2 hunks)
  • website2/src/views/press/PressPage.tsx (5 hunks)
  • website2/src/views/publications/ResourcePage.tsx (4 hunks)
  • website2/src/views/solutions/AfricanCities/AfricanCities.tsx (2 hunks)
  • website2/src/views/solutions/AfricanCities/AfricanCityPage.tsx (3 hunks)
  • website2/src/views/solutions/communities/CommunityPage.tsx (6 hunks)
  • website2/tsconfig.json (1 hunks)
✅ Files skipped from review due to trivial changes (6)
  • website2/src/app/clean-air-forum/speakers/page.tsx
  • website2/src/views/legal/AirQoDataPage.tsx
  • website2/src/app/clean-air-forum/partners/page.tsx
  • website2/src/app/clean-air-forum/program-committee/page.tsx
  • website2/src/app/clean-air-forum/schedule/page.tsx
  • website2/src/app/clean-air-forum/resources/page.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
website2/src/views/cleanairforum/events/SingleEvent.tsx

[error] 253-253: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)


[error] 301-301: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

website2/src/views/events/SingleEvent.tsx

[error] 180-180: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)


[error] 228-228: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build-push-website2-deploy-preview
🔇 Additional comments (64)
website2/src/configs/mainConfigs.ts (1)

1-5: Well-structured type definition.

The Config type clearly defines the expected shape of the configuration object with appropriate types.

website2/src/types/index.ts (2)

32-32: Good use of type alias for resource categories.

Using a type alias derived from the Resource interface ensures type consistency and reduces duplication.


13-30: Comprehensive Resource interface with good type safety.

The interface is well-structured with appropriate types. However:

  1. resource_link allows null - ensure proper null checking in UI components
  2. Consider adding JSDoc comments for better documentation
✅ Verification successful

Proper null handling confirmed for resource_link

The codebase already implements thorough null checking for resource_link in components, with both TypeScript type safety and runtime validation. The implementation in ResourceCard.tsx uses a belt-and-suspenders approach with conditional rendering and explicit null checks.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for null handling of resource_link in components
rg "resource_link" -A 3 -g "*.tsx"

Length of output: 1007

website2/src/hooks/useResourceFilter.ts (1)

23-30: Good use of useMemo for pagination optimization.

The pagination logic is well-implemented with proper performance optimization using useMemo.

website2/src/views/cleanairforum/RegisterBanner.tsx (2)

9-9: Good adoption of centralized configuration.

The component correctly uses mainConfig for consistent layout styling.


Line range hint 33-37: Implement onClick handler for register button.

The register button has an empty onClick handler. Please implement the registration functionality or add a TODO comment if it's pending.

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

15-16: Verify mainConfig integration for layout consistency.

The hardcoded max-width and centering classes have been removed. To maintain layout consistency, ensure that these styles are now being applied through the mainConfig configuration.

Also applies to: 33-33

✅ Verification successful

✓ Layout configuration is properly integrated

The removed max-width and centering classes are correctly managed through mainConfig.containerClass at the layout level, ensuring consistent styling across all forum pages.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if mainConfig is properly imported and used in the parent layout
rg -l "mainConfig.containerClass" "website2/src/app/clean-air-forum/"

Length of output: 111


Script:

#!/bin/bash
# Check the layout implementation
cat website2/src/app/clean-air-forum/layout.tsx

Length of output: 1685

website2/src/views/legal/Tabsection.tsx (2)

9-12: Well-structured type definition for tabs.

Good addition of TypeScript interface for improved type safety.


37-47: Good use of Next.js Link component.

Replacing programmatic navigation with Link components improves performance and follows Next.js best practices.

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

20-20: Good loading state management.

Proper handling of loading state improves user experience.

Also applies to: 25-29

website2/src/components/layouts/NotificationBanner.tsx (1)

11-11: Good route management practice.

Extracting route as a constant improves maintainability.

website2/src/components/layouts/ActionButtons.tsx (1)

5-5: Clean integration of mainConfig for container styling.

The implementation correctly integrates the centralized layout configuration while preserving the existing flex layout classes.

Also applies to: 15-17

website2/src/components/layouts/ActionButtons2.tsx (1)

4-5: Clean integration of mainConfig for container styling.

The implementation correctly integrates the centralized layout configuration while preserving the existing flex layout classes.

Also applies to: 10-12

website2/src/app/legal/layout.tsx (1)

5-5: Clean integration of mainConfig for container styling.

The implementation correctly integrates the centralized layout configuration while maintaining the legal-page-content class.

Also applies to: 55-57

website2/src/components/layouts/Highlight.tsx (1)

5-6: Clean integration of mainConfig for container styling.

The implementation correctly integrates the centralized layout configuration while preserving the existing styling classes.

Also applies to: 9-11

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

4-4: Clean implementation of centralized layout configuration!

The changes effectively integrate the centralized layout configuration while maintaining the component's existing styling utilities.

Also applies to: 32-34

website2/src/components/ui/Pagination.tsx (4)

9-10: Well-structured prop additions with good defaults!

The new optional props enhance component flexibility while maintaining backward compatibility.

Also applies to: 17-18


20-28: Nice performance optimization with useCallback!

The page change handler is properly memoized with the correct dependencies.


30-44: Consider edge case handling in pagination logic.

While the pagination logic is sound, it might benefit from additional validation:

  1. Ensure maxVisiblePages is always positive
  2. Handle the case when maxVisiblePages > totalPages

50-53: Great accessibility improvements!

The addition of semantic HTML (nav element) and ARIA attributes enhances accessibility.

Also applies to: 72-73, 83-83

website2/src/views/cleanairforum/PaginatedSection.tsx (1)

5-5: Clean integration of centralized layout configuration.

The mainConfig integration preserves existing padding utilities while standardizing the container width.

Also applies to: 38-38

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

9-9: Clean integration of centralized layout configuration.

The mainConfig integration maintains existing spacing utilities while standardizing the container width.

Also applies to: 39-41

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

4-4: LGTM! Import aligns with centralized configuration approach.

The addition of mainConfig import supports the PR's objective of centralizing layout configurations.

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

9-9: LGTM! Import aligns with centralized configuration approach.

The addition of mainConfig import supports the PR's objective of centralizing layout configurations.

website2/src/components/layouts/NewsLetter.tsx (1)

5-5: LGTM! Import aligns with centralized configuration approach.

The addition of mainConfig import supports the PR's objective of centralizing layout configurations.

website2/src/views/press/PressPage.tsx (2)

8-8: Clean implementation of centralized styling!

The integration of mainConfig for container classes enhances maintainability and consistency across the application.

Also applies to: 47-47, 56-56


113-113: Good UX enhancement with scroll-to-top functionality!

Adding the scrollToTop prop improves user experience when navigating through paginated content.

website2/src/views/solutions/AfricanCities/AfricanCities.tsx (1)

4-4: Well-structured responsive container implementation!

The combination of mainConfig.containerClass with responsive padding classes provides a clean and maintainable solution.

Also applies to: 53-53

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

163-163: Great UX improvement!

Adding scrollToTop={true} to the Pagination component ensures users are taken to the top of the content when changing pages, improving navigation experience.

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

10-10: Well-structured layout separation!

Good job keeping component-specific layout classes (flex-col-reverse lg:flex-row items-center justify-between) separate from the centralized container configuration.

Also applies to: 69-71

website2/src/views/solutions/AfricanCities/AfricanCityPage.tsx (1)

9-9: Clean layout implementation!

Good separation between the centralized container configuration and section-specific styling (text-center).

Also applies to: 50-50

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

6-6: Excellent skeleton loader implementation!

The skeleton loader's layout perfectly mirrors the actual content's layout while maintaining proper separation between centralized container configuration and component-specific spacing classes.

Also applies to: 13-15

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

9-9: LGTM! Import of mainConfig for centralized layout configuration.

The addition of mainConfig import aligns with the PR's objective of centralizing layout configurations.


55-57: Consistent application of containerClass across all sections.

The changes effectively implement the centralized layout configuration across loading states, error states, featured events section, tabs section, and event cards loading state. This enhances maintainability and ensures consistent layout behavior.

Also applies to: 74-76, 87-89, 155-157, 184-186

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

9-9: LGTM! Import of mainConfig for centralized layout configuration.

The addition of mainConfig import aligns with the PR's objective of centralizing layout configurations.


66-66: Enhanced spacing with margin-bottom addition.

The addition of mb-16 to the header section improves the visual hierarchy and spacing between sections.


84-110: Well-structured categories section with improved loading state.

The categories section has been effectively restructured with:

  • Proper spacing and padding
  • Responsive flex layout
  • Enhanced loading skeleton implementation
website2/src/components/layouts/Footer.tsx (1)

11-11: LGTM! Consistent layout configuration in footer.

The changes appropriately implement the centralized layout configuration in the footer component, maintaining consistency with other components.

Also applies to: 22-22

website2/src/app/products/api/ApiPage.tsx (2)

8-8: LGTM! Import of mainConfig for centralized layout configuration.

The addition of mainConfig import aligns with the PR's objective of centralizing layout configurations.


52-54: Consistent application of containerClass across all sections.

The changes effectively implement the centralized layout configuration across all major sections of the API page, ensuring consistent layout behavior and improved maintainability.

Also applies to: 89-89, 120-122, 167-169, 216-216

website2/src/components/layouts/Navbar.tsx (1)

189-191: LGTM! Clean implementation of centralized styling.

The change effectively implements the centralized layout configuration using mainConfig.containerClass.

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

128-128: LGTM! Consistent implementation of container class.

The banner section correctly implements the centralized layout configuration.


155-157: LGTM! Clean implementation of filter section layout.

The filter section properly utilizes the centralized container class while maintaining the existing padding and flex layout.

website2/src/app/products/calibrate/CalibratePage.tsx (3)

52-54: LGTM! Clean implementation of hero section layout.

The grid layout and responsive design are preserved while implementing the centralized container class.


90-90: LGTM! Effective implementation in Why Calibrate section.

The section maintains its grid structure while adopting the centralized styling approach.


123-125: LGTM! Consistent implementation across remaining sections.

The Cost Effective, Calibrate Your Data, and Simple User Interface sections all correctly implement the centralized container class while preserving their unique layouts and responsive behaviors.

Also applies to: 177-179, 227-227

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

141-141: LGTM! Clean implementation of stats section layout.

The section effectively implements the centralized container class while maintaining the spacing and nested layout structure.

website2/src/views/legal/PP_Page.tsx (1)

228-228: LGTM! Clean integration of mainConfig.

The change aligns with the PR's objective of centralizing layout configuration.

website2/src/app/products/analytics/AnalyticsPage.tsx (2)

9-9: Import placement looks good.

The mainConfig import is properly placed with other imports.


53-54: Consistent application of mainConfig across sections.

The layout configuration has been standardized effectively across all sections while maintaining the specific grid and flex arrangements for each section.

Also applies to: 91-91, 123-124, 197-198, 245-245

website2/src/app/products/monitor/MonitorPage.tsx (2)

53-55: Clean integration of mainConfig across all sections.

The layout configuration has been standardized effectively while preserving the specific layout requirements for each section.

Also applies to: 95-97, 154-156, 201-203, 244-244, 273-275


Line range hint 122-134: Verify documentation links accessibility.

The component includes links to various documentation PDFs. Let's verify their accessibility.

Also applies to: 282-297

✅ Verification successful

Documentation links are accessible and properly hosted

Both the Maintenance Manual and User Guide PDFs are accessible and returning successful HTTP 200 responses.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the accessibility of documentation links

# List of documentation URLs to check
urls=(
  "https://res.cloudinary.com/dbibjvyhm/image/upload/v1716038904/website/docs/Binos-Maintenance-Manual_agusuh.pdf"
  "https://res.cloudinary.com/dbibjvyhm/image/upload/v1716038903/website/docs/AirQoMonitorUserGuideV04_ogbfjs.pdf"
)

# Check each URL
for url in "${urls[@]}"; do
  echo "Checking $url"
  status_code=$(curl -s -o /dev/null -w "%{http_code}" "$url")
  if [ "$status_code" -eq 200 ]; then
    echo "✓ Accessible (HTTP $status_code)"
  else
    echo "✗ Not accessible (HTTP $status_code)"
  fi
done

Length of output: 1284

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

14-14: LGTM! Clean layout configuration.

The use of mainConfig.containerClass for consistent layout styling is a good practice. It centralizes the layout configuration and makes it easier to maintain.

Also applies to: 83-84, 137-138, 209-210

website2/src/views/solutions/communities/CommunityPage.tsx (1)

11-11: LGTM! Consistent layout configuration.

The integration of mainConfig.containerClass across various sections maintains layout consistency while preserving the animation configurations.

Also applies to: 54-54, 81-81, 160-160, 317-317

website2/src/components/dialogs/EngagementDialog.tsx (1)

8-8: LGTM! Clean dialog layout configuration.

The use of mainConfig.containerClass for dialog content maintains consistency with the project's layout standards.

Also applies to: 353-355

website2/src/views/about/AboutPage.tsx (3)

9-9: LGTM! Good move centralizing the layout configuration.

The addition of mainConfig import aligns with the PR's objective of improving maintainability through centralized styling.


172-180: LGTM! Clean and semantic markup structure.

The introduction section's markup is well-structured with appropriate spacing and typography classes.


189-193: Verify image dimensions and optimize loading strategy.

The images are now served from Cloudinary CDN, which is great for performance. However, let's verify a few things:

  1. Ensure the image dimensions (1024x480) are optimal for the layout
  2. Consider using responsive image sizes with srcSet
  3. Verify if eager loading is necessary for all images

Also applies to: 197-201

✅ Verification successful

Current image implementation is well-optimized 🎯

The current setup is actually quite solid:

  • Images are appropriately placed in a vision section with hover effects
  • Eager loading is justified for these above-fold, interactive elements
  • Dimensions are optimal for the layout (1024x480 for main, 1024x49 for overlay)
  • Cloudinary CDN + responsive classes already provide good optimization
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if these images are used above the fold
rg -A 5 "teamImage_ganc1y|Frame_ugwgqr" src/

Length of output: 91


Script:

#!/bin/bash
# Search for the image references and surrounding context
rg -B 5 -A 10 "teamImage_ganc1y|Frame_ugwgqr"

# Look for similar image implementations and srcSet usage
ast-grep --pattern 'src="https://res.cloudinary.com/dbibjvyhm/image/upload/$$$"'

# Find all Image components with their props
ast-grep --pattern '<Image $$$>'

Length of output: 2288

website2/src/views/legal/TOSPage.tsx (1)

601-601: LGTM! Simplified container class.

The container class has been simplified to use w-full, which aligns with the PR's objective of standardizing layout approaches across the website.

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

16-17: LGTM! Consistent layout approach.

The container and divider styling changes maintain consistency with other pages while preserving the layout structure.

website2/tsconfig.json (1)

29-30: LGTM! Well-structured path configuration.

The addition of @/configs/* path alias supports the new centralized configuration approach and maintains consistency with the existing path structure.

website2/src/views/cleanairforum/TabNavigation.tsx (3)

1-8: Clean and well-organized imports! 👌

The imports are properly structured with a clear separation of Next.js components, types, and local configurations. The 'use client' directive is correctly placed at the top.


19-23: Smart handling of the root path edge case! 🧠

The isActiveTab function elegantly handles the special case for the root path while using startsWith for sub-paths. This prevents false positives in path matching.


Line range hint 1-49: Excellent refactoring of the navigation component! 🌟

The changes have significantly improved the component by:

  • Using semantic HTML
  • Centralizing styles through mainConfig
  • Optimizing performance by moving static data outside
  • Simplifying the active tab logic
  • Using proper TypeScript types

This aligns perfectly with the PR's objectives of enhancing code maintainability and user experience.

Comment on lines +27 to +35
onClick={() => {
if (resource.resource_link) {
window.open(
resource.resource_link,
'_blank',
'noopener,noreferrer',
);
}
}}
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 URL validation before opening external links

While the link handling includes security attributes, it would be safer to validate URLs before opening them.

Consider this enhancement:

   onClick={() => {
-    if (resource.resource_link) {
+    if (resource.resource_link && /^https?:\/\//i.test(resource.resource_link)) {
       window.open(
         resource.resource_link,
         '_blank',
         'noopener,noreferrer',
       );
+    } else {
+      console.error('Invalid or missing resource URL');
     }
   }}
📝 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
onClick={() => {
if (resource.resource_link) {
window.open(
resource.resource_link,
'_blank',
'noopener,noreferrer',
);
}
}}
onClick={() => {
if (resource.resource_link && /^https?:\/\//i.test(resource.resource_link)) {
window.open(
resource.resource_link,
'_blank',
'noopener,noreferrer',
);
} else {
console.error('Invalid or missing resource URL');
}
}}

Comment on lines +179 to +183
className="prose lg:prose-xl"
dangerouslySetInnerHTML={{
__html: eventDetailsHtml || 'Event details coming soon.',
}}
></div>
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Confirmed XSS vulnerability in HTML rendering implementation

The current implementation in website2/src/utils/quillUtils.ts lacks HTML sanitization, which could allow malicious script injection. The convertDeltaToHtml function:

  • Accepts and returns raw HTML without sanitization
  • Uses QuillDeltaToHtmlConverter without security options
  • Lacks protection against script injection

Recommended fixes:

  • Install and implement DOMPurify or a similar HTML sanitization library
  • Apply sanitization before returning HTML in convertDeltaToHtml
  • Never accept raw HTML input without sanitization
🔗 Analysis chain

Potential XSS vulnerability with dangerouslySetInnerHTML.

The use of dangerouslySetInnerHTML could expose users to cross-site scripting (XSS) attacks. Please ensure that convertDeltaToHtml properly sanitizes the HTML output.

Let's verify the HTML sanitization in the utility function:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for sanitization in the utility function
rg -A 5 "convertDeltaToHtml" src/utils/

Length of output: 90


Script:

#!/bin/bash
# Search for convertDeltaToHtml in the entire website2 directory
rg -l "convertDeltaToHtml" website2/

# Find imports in SingleEvent.tsx
ast-grep --pattern 'import { $$$ } from "$_"' website2/src/views/events/SingleEvent.tsx

# Search for the function definition
ast-grep --pattern 'function convertDeltaToHtml($_) { $$$ }' website2/
ast-grep --pattern 'const convertDeltaToHtml = $_' website2/

Length of output: 3618


Script:

#!/bin/bash
# Check for isHtml function implementation
ast-grep --pattern 'function isHtml($_) { $$$ }' website2/
ast-grep --pattern 'const isHtml = $_' website2/

# Look for sanitization-related imports
rg -A 1 "import.*sanitize|import.*DOMPurify|import.*QuillDeltaToHtmlConverter" website2/src/utils/quillUtils.ts

Length of output: 880

🧰 Tools
🪛 Biome (1.9.4)

[error] 180-180: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

Comment on lines +252 to +256
className="prose lg:prose-xl"
dangerouslySetInnerHTML={{
__html: eventDetailsHtml || 'Event details coming soon.',
}}
></div>
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Confirmed XSS vulnerability in HTML content handling

The review comment is valid. The convertDeltaToHtml function in quillUtils.ts either:

  • Passes through raw HTML content directly if detected
  • Converts Delta format without subsequent sanitization

To fix this:

  • Install and implement DOMPurify: npm install dompurify @types/dompurify
  • Modify quillUtils.ts to sanitize HTML output before returning
  • Apply the fix to both SingleEvent components
🔗 Analysis chain

Potential XSS vulnerability with dangerouslySetInnerHTML.

Similar to the other event component, ensure that the HTML content is properly sanitized before rendering.

Let's verify the sanitization:

Also applies to: 301-304

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for HTML sanitization in the codebase
rg -A 5 "sanitize.*html|html.*sanitize" src/

Length of output: 91


Script:

#!/bin/bash
# Check package.json for sanitization libraries
cat website2/package.json | jq '.dependencies + .devDependencies'

# Search for sanitization-related code and DOMPurify usage
rg -i "(sanitize|purify|xss|dompurify)" website2/src/

# Look for eventDetailsHtml assignment or processing
rg "eventDetailsHtml" website2/src/

Length of output: 2229


Script:

#!/bin/bash
# Check for quill-delta-to-html usage
rg -A 5 "quill-delta-to-html" website2/src/

# Check event_details processing
rg -A 5 "event_details" website2/src/

Length of output: 1562


Script:

#!/bin/bash
# Check the implementation of convertDeltaToHtml
cat website2/src/utils/quillUtils.ts

Length of output: 2807

🧰 Tools
🪛 Biome (1.9.4)

[error] 253-253: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

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

🧹 Nitpick comments (3)
website2/src/views/about/AboutPage.tsx (3)

44-47: Consider adding type safety to partner filtering.

The current implementation uses any type for partners, which could lead to runtime errors. Let's add proper typing:

-  const filteredPartners = partners.filter(
-    (partner: any) => partner.website_category !== 'cleanair',
-  );
+  interface Partner {
+    id: string | number;
+    website_category: string;
+    partner_logo_url?: string;
+  }
+  const filteredPartners = partners.filter(
+    (partner: Partner) => partner.website_category !== 'cleanair',
+  );

314-337: Enhance semantic structure of values list.

The values list could benefit from better semantic markup and accessibility improvements.

-  <ul className="space-y-6 text-lg text-gray-700 leading-relaxed">
+  <ul className="space-y-6 text-lg text-gray-700 leading-relaxed" role="list">
     <li>
-      <span className="font-bold">Citizen Focus:</span>
+      <h3 className="text-xl font-bold mb-2">Citizen Focus</h3>
       <br /> At AirQo, we believe that the main beneficiary of our
       work should be the citizen.
     </li>

390-396: Consider adding analytics tracking to partner CTA.

The "Partner with Us" button is a key conversion point but lacks analytics tracking.

   <CustomButton
     onClick={() => dispatch(openModal())}
+    data-analytics="partner-cta-click"
     className="bg-transparent p-0 m-0 flex items-center text-blue-700"
   >
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 26b03ad and 81f89f4.

📒 Files selected for processing (1)
  • website2/src/views/about/AboutPage.tsx (6 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build-push-website2-deploy-preview

Comment on lines +399 to +404
<PaginatedSection
logos={filteredPartners.map((partner: any) => ({
id: partner.id,
logoUrl: partner.partner_logo_url || '',
}))}
/>
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 error handling for missing partner logos.

The partner logo mapping doesn't handle cases where partner_logo_url is undefined or invalid.

   <PaginatedSection
-    logos={filteredPartners.map((partner: any) => ({
-      id: partner.id,
-      logoUrl: partner.partner_logo_url || '',
-    }))}
+    logos={filteredPartners
+      .filter((partner: Partner) => partner.partner_logo_url)
+      .map((partner: Partner) => ({
+        id: partner.id,
+        logoUrl: partner.partner_logo_url!,
+      }))}
   />

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

Comment on lines +211 to +236
<div className="relative w-full h-[400px] flex items-center justify-center">
{/* Background Abstract Shape Image */}
<Image
src="https://res.cloudinary.com/dbibjvyhm/image/upload/v1728294910/website/photos/about/about_us_vector_3_wiw2ie.png"
alt="Abstract Background"
width={800}
height={400}
className="absolute w-[300px] h-[200px] lg:w-[410px] lg:h-[225px]"
loading="eager"
/>

{/* Outline Abstract Shape Image */}
<Image
src="https://res.cloudinary.com/dbibjvyhm/image/upload/v1728294910/website/photos/about/about-us-vector-2_mioxcy.png"
alt="Abstract Outline"
width={800}
height={400}
className="absolute w-[350px] h-[230px] lg:w-[450px] lg:h-[300px] object-contain"
loading="eager"
/>

{/* Text */}
<span className="text-gray-800 text-lg lg:text-[48px] relative">
Our vision
</span>
</div>
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 accessibility of the vision section.

The vision section uses decorative images but lacks proper ARIA attributes.

-  <div className="relative w-full h-[400px] flex items-center justify-center">
+  <div 
+    className="relative w-full h-[400px] flex items-center justify-center"
+    role="presentation"
+  >
   <Image
     src="https://res.cloudinary.com/dbibjvyhm/image/upload/v1728294910/website/photos/about/about_us_vector_3_wiw2ie.png"
     alt="Abstract Background"
+    aria-hidden="true"
     width={800}
     height={400}
     className="absolute w-[300px] h-[200px] lg:w-[410px] lg:h-[225px]"
     loading="eager"
   />
📝 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
<div className="relative w-full h-[400px] flex items-center justify-center">
{/* Background Abstract Shape Image */}
<Image
src="https://res.cloudinary.com/dbibjvyhm/image/upload/v1728294910/website/photos/about/about_us_vector_3_wiw2ie.png"
alt="Abstract Background"
width={800}
height={400}
className="absolute w-[300px] h-[200px] lg:w-[410px] lg:h-[225px]"
loading="eager"
/>
{/* Outline Abstract Shape Image */}
<Image
src="https://res.cloudinary.com/dbibjvyhm/image/upload/v1728294910/website/photos/about/about-us-vector-2_mioxcy.png"
alt="Abstract Outline"
width={800}
height={400}
className="absolute w-[350px] h-[230px] lg:w-[450px] lg:h-[300px] object-contain"
loading="eager"
/>
{/* Text */}
<span className="text-gray-800 text-lg lg:text-[48px] relative">
Our vision
</span>
</div>
<div
className="relative w-full h-[400px] flex items-center justify-center"
role="presentation"
>
{/* Background Abstract Shape Image */}
<Image
src="https://res.cloudinary.com/dbibjvyhm/image/upload/v1728294910/website/photos/about/about_us_vector_3_wiw2ie.png"
alt="Abstract Background"
aria-hidden="true"
width={800}
height={400}
className="absolute w-[300px] h-[200px] lg:w-[410px] lg:h-[225px]"
loading="eager"
/>
{/* Outline Abstract Shape Image */}
<Image
src="https://res.cloudinary.com/dbibjvyhm/image/upload/v1728294910/website/photos/about/about-us-vector-2_mioxcy.png"
alt="Abstract Outline"
width={800}
height={400}
className="absolute w-[350px] h-[230px] lg:w-[450px] lg:h-[300px] object-contain"
loading="eager"
/>
{/* Text */}
<span className="text-gray-800 text-lg lg:text-[48px] relative">
Our vision
</span>
</div>

Comment on lines +194 to +206
src="https://res.cloudinary.com/dbibjvyhm/image/upload/v1728295735/website/photos/about/teamImage_ganc1y.png"
alt="Abstract Outline"
width={1024}
height={480}
className="object-cover w-auto h-auto"
loading="eager"
/>

{/* Outline Abstract Shape Image */}
<Image
src="https://res.cloudinary.com/dbibjvyhm/image/upload/v1728294910/website/photos/about/about-us-vector-2_mioxcy.png"
src="https://res.cloudinary.com/dbibjvyhm/image/upload/v1728295909/website/photos/about/Frame_ugwgqr.png"
alt="Abstract Outline"
width={800}
height={400}
className="absolute w-[350px] h-[230px] lg:w-[450px] lg:h-[300px] object-contain"
width={1024}
height={49}
className="absolute w-auto h-auto object-cover"
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

🎨 Critical: Large unoptimized image impacts page performance

The teamImage (12MB PNG) is significantly impacting page load performance. Here are the key issues:

  • teamImage_ganc1y.png is 12MB in size and eagerly loaded
  • No Cloudinary optimization parameters are being used
  • Hardcoded dimensions might not match actual image dimensions

Quick fixes:

-  src="https://res.cloudinary.com/dbibjvyhm/image/upload/v1728295735/website/photos/about/teamImage_ganc1y.png"
+  src="https://res.cloudinary.com/dbibjvyhm/image/upload/f_auto,q_auto,w_1024/v1728295735/website/photos/about/teamImage_ganc1y.png"
-  loading="eager"
+  loading="lazy"
🔗 Analysis chain

Verify image dimensions and optimize loading strategy.

The images use hardcoded dimensions which might not match the actual image sizes from Cloudinary. This could affect the Cumulative Layout Shift (CLS) metric.

Consider using Cloudinary's transformation parameters to serve optimized images:

-  src="https://res.cloudinary.com/dbibjvyhm/image/upload/v1728295735/website/photos/about/teamImage_ganc1y.png"
+  src="https://res.cloudinary.com/dbibjvyhm/image/upload/f_auto,q_auto/v1728295735/website/photos/about/teamImage_ganc1y.png"
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify actual dimensions of the images from Cloudinary
# Note: Using curl to fetch image metadata without downloading the full image

for url in \
  "https://res.cloudinary.com/dbibjvyhm/image/upload/v1728295735/website/photos/about/teamImage_ganc1y.png" \
  "https://res.cloudinary.com/dbibjvyhm/image/upload/v1728295909/website/photos/about/Frame_ugwgqr.png"
do
  echo "Checking: $url"
  curl -sI "$url" | grep -i "content-length\|content-type"
done

Length of output: 1684


Script:

#!/bin/bash
# Search for other usages of these images and any image optimization configs
rg -l "teamImage_ganc1y|Frame_ugwgqr" 

# Check for image optimization related configs
fd -e config.js -e next.config.js -e next.config.mjs

Length of output: 464

Copy link
Contributor

New Website2 changes available for preview here

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

@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

🧹 Nitpick comments (2)
website2/src/hooks/swrConfig.ts (2)

18-20: LGTM! Consider monitoring failed requests.

The retry configuration is well-documented and reasonable. The 5-second retry interval provides a good balance between quick recovery and avoiding network congestion.

Consider adding monitoring for failed requests to ensure the reduced retry count (3 total attempts) is sufficient for your use case.


21-28: Consider extracting retry configuration to constants.

The custom retry function works correctly but duplicates values from the config. Consider making it more maintainable:

+// At the top of the file
+const MAX_RETRY_COUNT = 2;
+const RETRY_DELAY = 5000;
+
 export const swrOptions: SWRConfiguration = {
   // ... other config
-  errorRetryCount: 2,
-  errorRetryInterval: 5000,
+  errorRetryCount: MAX_RETRY_COUNT,
+  errorRetryInterval: RETRY_DELAY,
   
   onErrorRetry: (error, key, config, revalidate, { retryCount }) => {
-    if (retryCount >= 2) return;
+    if (retryCount >= MAX_RETRY_COUNT) return;
     
-    setTimeout(() => revalidate({ retryCount: retryCount + 1 }), 5000);
+    setTimeout(() => revalidate({ retryCount: retryCount + 1 }), RETRY_DELAY);
   },
 };
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 81f89f4 and 9c76dd0.

📒 Files selected for processing (2)
  • website2/src/configs/mainConfigs.ts (1 hunks)
  • website2/src/hooks/swrConfig.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • website2/src/configs/mainConfigs.ts
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build-push-website2-deploy-preview
🔇 Additional comments (1)
website2/src/hooks/swrConfig.ts (1)

1-1: Verify the impact of disabling automatic revalidation.

Disabling revalidateOnFocus and revalidateOnReconnect means that data won't automatically refresh when users return to the tab or reconnect to the network. This could lead to stale data being displayed.

Please confirm if this aligns with the application's requirements, especially for time-sensitive data like air quality metrics.

Also applies to: 3-3, 5-5, 8-8

Copy link
Contributor

New Website2 changes available for preview here

@Baalmart Baalmart merged commit b5ac692 into staging Jan 27, 2025
31 checks passed
@Baalmart Baalmart deleted the website-feedback-updates branch January 27, 2025 08:50
@Baalmart Baalmart mentioned this pull request Jan 27, 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