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: UI updates in relation to backend changes #2304

Merged
merged 1 commit into from
Dec 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 31 additions & 25 deletions website2/src/app/(about)/events/[id]/SingleEvent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import { format, isSameMonth, parse } from 'date-fns';
import Image from 'next/image';
import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { FiCalendar, FiClock } from 'react-icons/fi';
import {
FiCalendar,
FiClock,
FiDownload,
FiExternalLink,
} from 'react-icons/fi';

import { Accordion, CustomButton, NoData } from '@/components/ui';
import { getEventDetails } from '@/services/apiService';
Expand Down Expand Up @@ -342,33 +347,34 @@ const SingleEvent: React.FC<any> = ({ id }) => {
{event.resources.map((resource: any) => (
<div
key={resource.id}
className="p-4 bg-white rounded-lg shadow hover:shadow-md transition-shadow duration-300 flex justify-between items-center"
className="flex justify-between items-center p-4 bg-gray-50 rounded-lg shadow-md hover:shadow-lg transition-shadow duration-200"
>
<div>
<h3 className="text-lg font-medium">{resource.title}</h3>
{resource.link && (
<a
href={resource.link}
target="_blank"
rel="noopener noreferrer"
className="text-blue-500 hover:underline"
>
View Link
</a>
)}
{resource.resource && (
<a
href={resource.resource}
download
className="text-blue-500 hover:underline ml-4"
>
Download
</a>
)}
<h3 className="text-xl font-semibold mb-2 text-gray-800">
{resource.title}
</h3>
<div className="flex space-x-4">
{resource.link && (
<a
href={resource.link}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center text-blue-600 hover:underline font-medium"
>
<FiExternalLink className="mr-2" /> View Link
</a>
)}
{resource.resource_url && (
<a
href={resource.resource_url}
download
className="inline-flex items-center text-blue-600 hover:underline font-medium"
>
<FiDownload className="mr-2" /> Download
</a>
)}
</div>
</div>
<span className="text-sm text-gray-500">
Order: {resource.order}
</span>
</div>
))}
</div>
Expand Down
121 changes: 68 additions & 53 deletions website2/src/app/clean-air-forum/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Metadata } from 'next';
import React, { ReactNode } from 'react';
'use client';

// import { Metadata } from 'next';
import React, { ReactNode, useEffect, useState } from 'react';

import Footer from '@/components/layouts/Footer';
import Navbar from '@/components/layouts/Navbar';
Expand All @@ -8,65 +10,76 @@ import BannerSection from '@/components/sections/Forum/BannerSection';
import { ForumDataProvider } from '@/context/ForumDataContext';
import { getForumEvents } from '@/services/apiService';

export const metadata: Metadata = {
title: 'Clean Air Forum | AirQo Africa',
description:
'Join the Clean Air Forum by AirQo to explore air quality initiatives, innovations, and discussions aimed at improving air quality in Africa.',
keywords:
'Clean Air Forum, AirQo Africa, air quality forum, air pollution, clean air Africa, environmental innovation, air quality initiatives',
openGraph: {
title: 'Clean Air Forum - AirQo Africa',
description:
'Discover AirQo’s Clean Air Forum, a platform to discuss innovations, strategies, and actions to improve air quality across Africa.',
url: 'https://yourdomain.com/clean-air-forum',
siteName: 'AirQo',
images: [
{
url: 'https://yourdomain.com/static/clean-air-forum-og-image.jpg',
width: 1200,
height: 630,
alt: 'AirQo Clean Air Forum - Improving Air Quality in Africa',
},
],
locale: 'en_US',
type: 'website',
},
twitter: {
card: 'summary_large_image',
site: '@AirQo',
title: 'Clean Air Forum - AirQo Africa',
description:
'Explore the Clean Air Forum by AirQo to participate in discussions about improving air quality in Africa.',
},
robots: {
index: true,
follow: true,
},
alternates: {
canonical: 'https://yourdomain.com/clean-air-forum',
},
};
import Loading from '../loading';

// export const metadata: Metadata = {
// title: 'Clean Air Forum | AirQo Africa',
// description:
// 'Join the Clean Air Forum by AirQo to explore air quality initiatives, innovations, and discussions aimed at improving air quality in Africa.',
// keywords:
// 'Clean Air Forum, AirQo Africa, air quality forum, air pollution, clean air Africa, environmental innovation, air quality initiatives',
// openGraph: {
// title: 'Clean Air Forum - AirQo Africa',
// description:
// 'Discover AirQo’s Clean Air Forum, a platform to discuss innovations, strategies, and actions to improve air quality across Africa.',
// url: 'https://yourdomain.com/clean-air-forum',
// siteName: 'AirQo',
// images: [
// {
// url: 'https://yourdomain.com/static/clean-air-forum-og-image.jpg',
// width: 1200,
// height: 630,
// alt: 'AirQo Clean Air Forum - Improving Air Quality in Africa',
// },
// ],
// locale: 'en_US',
// type: 'website',
// },
// twitter: {
// card: 'summary_large_image',
// site: '@AirQo',
// title: 'Clean Air Forum - AirQo Africa',
// description:
// 'Explore the Clean Air Forum by AirQo to participate in discussions about improving air quality in Africa.',
// },
// robots: {
// index: true,
// follow: true,
// },
// alternates: {
// canonical: 'https://yourdomain.com/clean-air-forum',
// },
// };

type CleanAirLayoutProps = {
children: ReactNode;
};

// Fetch data server-side
export default async function CleanAirLayout({
children,
}: CleanAirLayoutProps) {
let data = null;
const CleanAirLayout: React.FC<CleanAirLayoutProps> = ({ children }) => {
const [data, setData] = useState<any>(null); // Replace `any` with your actual data type
const [loading, setLoading] = useState<boolean>(true);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Specify the exact data type instead of using 'any'

Using any reduces the benefits of TypeScript's type safety. Replacing it with the actual data type of data will enhance reliability and maintainability.


useEffect(() => {
const fetchForumEvents = async () => {
try {
const res = await getForumEvents();
setData(res ? res[0] : null);
} catch (error) {
console.error('Failed to fetch forum events:', error);
setData(null);
} finally {
setLoading(false);
}
};

try {
const res = await getForumEvents();
data = res ? res[0] : null;
} catch (error) {
console.error('Failed to fetch forum events:', error);
data = null;
fetchForumEvents();
}, []);

if (loading) {
return <Loading />;
}

return (
// Wrap the entire layout with ForumDataProvider
<ForumDataProvider data={data}>
<div className="min-h-screen w-full flex flex-col">
{/* Navbar */}
Expand All @@ -92,4 +105,6 @@ export default async function CleanAirLayout({
</div>
</ForumDataProvider>
);
}
};

export default CleanAirLayout;
8 changes: 4 additions & 4 deletions website2/src/app/clean-air-forum/partners/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,30 @@ const Page = () => {
?.filter((partner: any) => partner.category === 'Co-Convening Partner')
.map((partner: any) => ({
id: partner.id,
logoUrl: partner.partner_logo,
logoUrl: partner.partner_logo_url,
}));

// Filter Host Partners
const hostPartners = data.partners
?.filter((partner: any) => partner.category === 'Host Partner')
.map((partner: any) => ({
id: partner.id,
logoUrl: partner.partner_logo,
logoUrl: partner.partner_logo_url,
}));

const sponsorPartner = data.partners
?.filter((partner: any) => partner.category === 'Sponsor Partner')
.map((partner: any) => ({
id: partner.id,
logoUrl: partner.partner_logo,
logoUrl: partner.partner_logo_url,
}));

// Filter Funding Partners (if available)
const fundingPartners = data.partners
?.filter((partner: any) => partner.category === 'Funding Partner')
.map((partner: any) => ({
id: partner.id,
logoUrl: partner.partner_logo,
logoUrl: partner.partner_logo_url,
}));

return (
Expand Down
10 changes: 8 additions & 2 deletions website2/src/app/clean-air-forum/resources/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,14 @@ import { Divider } from '@/components/ui';
import { useForumData } from '@/context/ForumDataContext';

// Helper function to extract the file name from the URL
const getFileNameFromUrl = (url: string) => {
return url.split('/').pop();
const getFileNameFromUrl = (url: string | null | undefined): string | null => {
if (!url || typeof url !== 'string') {
console.error('Invalid URL:', url);
return null;
}

const segments = url.split('/');
return segments.pop() || null;
};

// Accordion Item Component (for each session inside a resource)
Expand Down
56 changes: 31 additions & 25 deletions website2/src/app/clean-air-network/events/[id]/SingleEvent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import { format, isSameMonth, parse } from 'date-fns';
import Image from 'next/image';
import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { FiCalendar, FiClock } from 'react-icons/fi';
import {
FiCalendar,
FiClock,
FiDownload,
FiExternalLink,
} from 'react-icons/fi';

import { Accordion, CustomButton } from '@/components/ui';
import { getEventDetails } from '@/services/apiService';
Expand Down Expand Up @@ -338,33 +343,34 @@ const SingleEvent: React.FC<any> = ({ id }) => {
{event.resources.map((resource: any) => (
<div
key={resource.id}
className="p-4 bg-white rounded-lg shadow hover:shadow-md transition-shadow duration-300 flex justify-between items-center"
className="flex justify-between items-center p-4 bg-gray-50 rounded-lg shadow-md hover:shadow-lg transition-shadow duration-200"
>
<div>
<h3 className="text-lg font-medium">{resource.title}</h3>
{resource.link && (
<a
href={resource.link}
target="_blank"
rel="noopener noreferrer"
className="text-blue-500 hover:underline"
>
View Link
</a>
)}
{resource.resource && (
<a
href={resource.resource}
download
className="text-blue-500 hover:underline ml-4"
>
Download
</a>
)}
<h3 className="text-xl font-semibold mb-2 text-gray-800">
{resource.title}
</h3>
<div className="flex space-x-4">
{resource.link && (
<a
href={resource.link}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center text-blue-600 hover:underline font-medium"
>
<FiExternalLink className="mr-2" /> View Link
</a>
)}
{resource.resource_url && (
<a
href={resource.resource_url}
download
className="inline-flex items-center text-blue-600 hover:underline font-medium"
>
<FiDownload className="mr-2" /> Download
</a>
)}
</div>
Comment on lines +346 to +372
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Consider enhancing security for external links and downloads

While the UI improvements look great, there are some security considerations:

  1. External links should include additional security attributes
  2. Download links should validate file types

Apply this diff to enhance security:

-                        target="_blank"
-                        rel="noopener noreferrer"
+                        target="_blank"
+                        rel="noopener noreferrer nofollow"
+                        className="inline-flex items-center text-blue-600 hover:underline font-medium"
-                        download
+                        download
+                        onClick={(e) => {
+                          const fileExtension = resource.resource_url.split('.').pop()?.toLowerCase();
+                          const allowedTypes = ['pdf', 'doc', 'docx', 'xls', 'xlsx'];
+                          if (!allowedTypes.includes(fileExtension)) {
+                            e.preventDefault();
+                            alert('Invalid file type');
+                          }
+                        }}

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

</div>
<span className="text-sm text-gray-500">
Order: {resource.order}
</span>
</div>
))}
</div>
Expand Down
2 changes: 1 addition & 1 deletion website2/src/components/sections/Forum/BannerSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const BannerSection: React.FC<BannerSectionProps> = ({ data }) => {
{/* Image Section */}
<div className="flex justify-center items-center md:w-1/2 h-[350px] w-full">
<Image
src={data.background_image}
src={data.background_image_url}
alt="Forum Image"
width={600}
height={350}
Expand Down
2 changes: 1 addition & 1 deletion website2/src/components/ui/MemberCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const MemberCard: React.FC<MemberCardProps> = ({
{/* Image and hover effect with default placeholder */}
<div className="w-auto h-[390px] overflow-hidden rounded-lg">
<Image
src={member.picture_url || member.picture || PlaceholderImage}
src={member.picture_url || PlaceholderImage}
alt={member.name}
width={295}
height={390}
Expand Down
Loading