Files
testingcodeantrepo/.gitea/workflows/src/components/Articles.tsx
WDI-Ideas 620ddcaa8e
All checks were successful
CodeAnt AI Review - Stage 1 / codeant-review (pull_request) Successful in 1m4s
chore: touch text files for full CodeAnt coverage test
2026-03-30 01:33:38 +05:30

768 lines
33 KiB
TypeScript

import {
BookOpen,
ChevronLeft,
ChevronRight,
Filter,
Grid,
List,
Search,
X
} from 'lucide-react';
import { useRef, useState, useEffect } from 'react';
import { BlogItem, useGetBlogsQuery } from '../redux/services/blogApi';
import { useGetFaqCategoriesQuery } from '../redux/services/faqApi';
import { ImageWithFallback } from './figma/ImageWithFallback';
import { PrimaryCTAButton } from './PrimaryCTAButton';
import { navigateTo } from './Router';
import { Badge } from './ui/badge';
import { Button } from './ui/button';
import { Card, CardContent } from './ui/card';
import { Input } from './ui/input';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select';
import { FullScreenLoader } from './FullScreenLoader';
import { getSlugWithId } from '../utils/urlHelpers';
// Define category type with ID and name
interface CategoryOption {
id: string;
name: string;
}
type DateRange =
| 'all_time'
| 'last_7_days'
| 'last_30_days'
| 'last_3_months'
| 'last_6_months';
type SortBy =
| 'most_recent'
| 'oldest_first'
| 'title_az';
export function Articles() {
const [searchTerm, setSearchTerm] = useState('');
const [selectedCategory, setSelectedCategory] = useState<CategoryOption>({ id: 'all', name: 'All Categories' });
const [selectedReadTime, setSelectedReadTime] = useState('All Read Times');
const [selectedDateRange, setSelectedDateRange] = useState<DateRange>('all_time');
const [selectedTopic, setSelectedTopic] = useState<{
id: string;
name: string;
}>({
id: 'all',
name: 'All Topics'
});
const [sortBy, setSortBy] = useState<SortBy>('most_recent');
const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid');
const [currentPage, setCurrentPage] = useState(1);
const [debouncedSearch, setDebouncedSearch] = useState('');
const articlesPerPage = 6;
const containerRef = useRef<HTMLDivElement>(null);
const [allTags, setAllTags] = useState<{ id: string; name: string }[]>([]);
// Fetch categories for filter dropdown
const {
data: categoriesData,
isLoading: isLoadingCategories
} = useGetFaqCategoriesQuery({
limit: 100,
offset: 0
});
// Filter categories to only those for blog and create options with id and name
const categories: CategoryOption[] = [
{ id: 'all', name: 'All Categories' },
...(categoriesData?.data?.items
?.filter((category: any) => category.for_blog)
.map((category: any) => ({
id: category.id,
name: category.category_name
})) || [])
];
// Debounce search term
useEffect(() => {
const timer = setTimeout(() => {
setDebouncedSearch(searchTerm);
}, 500);
return () => clearTimeout(timer);
}, [searchTerm]);
// Fetch blogs from API with all parameters
const {
data: blogsData,
isLoading: isLoadingBlogs,
isError: isBlogsError,
refetch: refetchBlogs
} = useGetBlogsQuery({
limit: articlesPerPage,
offset: (currentPage - 1) * articlesPerPage,
search: debouncedSearch || undefined,
content_status: 'publish',
content_type: 'BLOG',
date_range: selectedDateRange !== 'all_time' ? selectedDateRange : undefined,
sort_by: sortBy,
content_category_id: selectedCategory.id !== 'all' ? selectedCategory.id : undefined, // Send UUID
tag_id: selectedTopic.id !== 'all' ? selectedTopic.id : undefined,
});
const sortOptions = [
{ value: 'most_recent', label: 'Most Recent' },
{ value: 'oldest_first', label: 'Oldest First' },
{ value: 'title_az', label: 'Title A-Z' }
];
const readTimes = ['All Read Times', 'Under 5 min', '5-10 min', 'Over 10 min'];
const dateRanges = [
{ value: 'all_time', label: 'All Time' },
{ value: 'last_7_days', label: 'Last 7 days' },
{ value: 'last_30_days', label: 'Last 30 days' },
{ value: 'last_3_months', label: 'Last 3 months' }
];
// Format date function
const formatDate = (dateString: string) => {
return new Date(dateString).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
});
};
// Calculate read time based on content length (approx)
const calculateReadTime = (content: string): string => {
const wordsPerMinute = 200;
const wordCount = content.split(/\s+/).length;
const minutes = Math.ceil(wordCount / wordsPerMinute);
return `${minutes} min read`;
};
// Filter articles by read time (client-side only - API doesn't support this)
const getReadTimeFilteredArticles = () => {
if (selectedReadTime === 'All Read Times' || !blogsData?.data?.items) {
return blogsData?.data?.items || [];
}
return (blogsData?.data?.items || []).filter((blog: BlogItem) => {
const readTimeMinutes = parseInt(calculateReadTime(blog.content).replace(' min read', '')) || 0;
return (selectedReadTime === 'Under 5 min' && readTimeMinutes < 5) ||
(selectedReadTime === '5-10 min' && readTimeMinutes >= 5 && readTimeMinutes <= 10) ||
(selectedReadTime === 'Over 10 min' && readTimeMinutes > 10);
});
};
const finalFilteredArticles = getReadTimeFilteredArticles();
const totalPages = Math.ceil((blogsData?.data?.pagination?.total || 0) / articlesPerPage);
const clearAllFilters = () => {
setSearchTerm('');
setDebouncedSearch('');
setSelectedCategory({ id: 'all', name: 'All Categories' });
setSelectedReadTime('All Read Times');
setSelectedDateRange('all_time');
setSelectedTopic({ id: 'all', name: 'All Topics' });
setSortBy('most_recent');
setCurrentPage(1);
};
const hasActiveFilters = Boolean(
searchTerm ||
selectedCategory.id !== 'all' ||
selectedReadTime !== 'All Read Times' ||
selectedDateRange !== 'all_time' ||
selectedTopic.id !== 'all'
);
useEffect(() => {
if (!blogsData?.data?.items || allTags.length > 0) return;
const tags = Array.from(
new Map(
blogsData.data.items
.flatMap((blog: BlogItem) => blog.blog_tags || [])
.map(tag => [tag.id, { id: tag.id, name: tag.tag_name }])
).values()
);
setAllTags(tags);
}, [blogsData]);
// Handle loading state
if (isLoadingBlogs || isLoadingCategories) {
return (
<div className="min-h-screen flex items-center justify-center bg-white">
<FullScreenLoader text="Loading articles..." />
</div>
);
}
// Handle error state
if (isBlogsError) {
return (
<div className="min-h-screen flex items-center justify-center bg-white">
<div className="text-center">
<div className="w-16 h-16 bg-red-100 rounded-full flex items-center justify-center mx-auto mb-4">
<X className="w-8 h-8 text-red-600" />
</div>
<h2 className="text-h3 mb-2">Failed to load articles</h2>
<p className="text-gray-600 mb-4">Please try again later</p>
<Button onClick={() => refetchBlogs()}>
Retry
</Button>
</div>
</div>
);
}
return (
<div style={{ backgroundColor: '#FFFFFF' }} ref={containerRef}>
{/* Hero Section */}
<section className="relative py-28 overflow-hidden">
<div className="absolute inset-0">
<ImageWithFallback
src="https://images.unsplash.com/photo-1481627834876-b7833e8f5570?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxhcnRpY2xlJTIwYmxvZyUyMGNvbnRlbnQlMjBrbm93bGVkZ2V8ZW58MXx8fHwxNzU1ODU0Mjg2fDA&ixlib=rb-4.1.0&q=80&w=1080"
alt="Knowledge and insights through articles and research"
className="w-full h-full object-cover"
/>
<div className="absolute inset-0 bg-black/60" />
</div>
<div className="relative section-margin-x">
<div className="max-w-4xl mx-auto text-center">
<div className="branded-tag-system-white mb-6 justify-center">
<div className="dot"></div>
<span className="text">INSIGHTS & KNOWLEDGE</span>
</div>
<h1 className="text-h1-white mb-6">
Articles & Research
</h1>
<p className="text-body-lg-white mb-8 max-w-2xl mx-auto">
Discover cutting-edge insights, research findings, and expert perspectives on leadership development, management strategies, and organizational excellence.
</p>
</div>
</div>
{/* Statistics Strip */}
<div className="absolute bottom-0 left-0 right-0">
<div className="bg-black/80 backdrop-blur-sm px-8 py-6">
<div className="section-margin-x">
<div className="grid grid-cols-3 gap-8 text-center">
<div>
<div className="text-h2-white mb-2">{blogsData?.data?.pagination?.total || 0}+</div>
<div className="text-small-white">Expert Articles</div>
</div>
<div>
<div className="text-h2-white mb-2">{categories.length - 1}</div>
<div className="text-small-white">Categories</div>
</div>
<div>
<div className="text-h2-white mb-2">{allTags.length}+</div>
<div className="text-small-white">Topics</div>
</div>
</div>
</div>
</div>
</div>
</section>
{/* Search and Controls Section */}
<section className="py-8" style={{ backgroundColor: '#FFFFFF' }}>
<div className="section-margin-x">
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 mb-6">
{/* Search Bar */}
<div className="relative max-w-md flex-1">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
<Input
type="text"
placeholder="Search articles..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="pl-10 pr-4 py-3 text-body rounded-lg border border-gray-300 focus:border-blue-500 focus:ring-2 focus:ring-blue-200 transition-all duration-200 w-full bg-gray-50"
style={{
fontSize: 'var(--font-body)',
fontFamily: 'var(--font-family-base)',
height: '48px'
}}
/>
</div>
{/* View Toggle and Sort */}
<div className="flex items-center gap-4">
<div className="flex items-center border border-gray-300 rounded-lg overflow-hidden">
<button
onClick={() => setViewMode('grid')}
className={`p-2 transition-colors ${viewMode === 'grid'
? 'text-white'
: 'bg-white text-gray-600 hover:bg-gray-50'
}`}
style={{
backgroundColor: viewMode === 'grid' ? 'var(--color-primary)' : undefined
}}
aria-label="Grid view"
>
<Grid className="w-4 h-4" />
</button>
<button
onClick={() => setViewMode('list')}
className={`p-2 transition-colors ${viewMode === 'list'
? 'text-white'
: 'bg-white text-gray-600 hover:bg-gray-50'
}`}
style={{
backgroundColor: viewMode === 'list' ? 'var(--color-primary)' : undefined
}}
aria-label="List view"
>
<List className="w-4 h-4" />
</button>
</div>
<Select value={sortBy} onValueChange={setSortBy}>
<SelectTrigger className="w-40 text-body">
<SelectValue placeholder="Sort by" />
</SelectTrigger>
<SelectContent>
{sortOptions.map((option) => (
<SelectItem key={option.value} value={option.value}>
{option.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
</div>
</section>
{/* Main Content Section with Sidebar */}
<section className="pb-16" style={{ backgroundColor: '#FFFFFF' }}>
<div className="section-margin-x">
<div className="grid grid-cols-12 gap-8">
{/* Left Sidebar - Sticky Filters */}
<div className="col-span-12 lg:col-span-3">
<div className="sticky top-4">
<Card className="bg-white border border-gray-200 rounded-lg shadow-md overflow-hidden">
{/* Filter Header */}
<div className="bg-gray-50 px-4 py-3 border-b border-gray-200">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<div className="p-1.5 rounded-md" style={{ backgroundColor: 'rgba(4, 4, 91, 0.1)' }}>
<Filter className="w-3.5 h-3.5" style={{ color: 'var(--color-primary)' }} />
</div>
<h3 className="text-body font-semibold text-gray-800">
Filters
</h3>
</div>
{hasActiveFilters && (
<Button
variant="ghost"
size="sm"
onClick={clearAllFilters}
className="text-xs px-2 py-1 rounded-md transition-colors filter-clear-btn"
>
<X className="w-3 h-3 mr-1" />
Clear
</Button>
)}
</div>
</div>
{/* Filter Content */}
<div className="p-4">
<div className="space-y-4">
{/* Category Filter */}
<div className="filter-section">
<label className="block text-small mb-2 font-medium text-gray-700">
Category
</label>
<Select
value={selectedCategory.id}
onValueChange={(value: string) => {
const category = categories.find(c => c.id === value);
if (category) {
setSelectedCategory(category);
}
}}
>
<SelectTrigger className="w-full text-small h-9 border-gray-300 hover:border-gray-400 transition-colors">
<SelectValue placeholder="All Categories">
{selectedCategory.name}
</SelectValue>
</SelectTrigger>
<SelectContent>
{categories.map((category) => (
<SelectItem key={category.id} value={category.id} className="text-small">
{category.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{/* Read Time Filter - Client-side only */}
<div className="filter-section">
<label className="block text-small mb-2 font-medium text-gray-700">
Read Time
</label>
<Select value={selectedReadTime} onValueChange={setSelectedReadTime}>
<SelectTrigger className="w-full text-small h-9 border-gray-300 hover:border-gray-400 transition-colors">
<SelectValue placeholder="All Read Times" />
</SelectTrigger>
<SelectContent>
{readTimes.map((readTime) => (
<SelectItem key={readTime} value={readTime} className="text-small">
{readTime}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{/* Date Range Filter */}
<div className="filter-section">
<label className="block text-small mb-2 font-medium text-gray-700">
Date Range
</label>
<Select value={selectedDateRange} onValueChange={setSelectedDateRange}>
<SelectTrigger className="w-full text-small h-9 border-gray-300 hover:border-gray-400 transition-colors">
<SelectValue placeholder="All Time" />
</SelectTrigger>
<SelectContent>
{dateRanges.map((range) => (
<SelectItem key={range.value} value={range.value}>
{range.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{/* Topics Filter */}
<div className="filter-section">
<label className="block text-small mb-2 font-medium text-gray-700">
Topic
</label>
<Select
value={selectedTopic.id}
onValueChange={(value: string) => {
if (value === 'all') {
setSelectedTopic({ id: 'all', name: 'All Topics' });
return;
}
const topic = allTags.find(t => t.id === value);
if (topic) setSelectedTopic(topic);
}}
>
<SelectTrigger className="w-full text-small h-9 border-gray-300 hover:border-gray-400 transition-colors">
<SelectValue placeholder="All Topics" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">
All Topics
</SelectItem>
{allTags.map((tag) => (
<SelectItem key={tag.id} value={tag.id}>
{tag.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
</div>
</Card>
</div>
</div>
{/* Right Content Area - Scrollable Articles */}
<div className="col-span-12 lg:col-span-9">
<div className="mb-4 text-small text-muted">
Showing {finalFilteredArticles.length} of {blogsData?.data?.pagination?.total || 0} articles
</div>
{/* Articles Results */}
{finalFilteredArticles.length === 0 ? (
<div className="text-center py-12">
<BookOpen className="w-16 h-16 text-gray-400 mx-auto mb-4" />
<p className="text-body-lg text-muted">
No articles found matching your criteria.
</p>
{hasActiveFilters && (
<Button
variant="outline"
onClick={clearAllFilters}
className="mt-4"
>
Clear Filters
</Button>
)}
</div>
) : (
<>
{/* Grid View */}
{viewMode === 'grid' && (
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{finalFilteredArticles.map((article: BlogItem) => (
<Card
key={article.id}
className="overflow-hidden hover:shadow-lg transition-all duration-300 cursor-pointer group"
onClick={() => {
// Use slug_name to create the URL with full UUID
const url = getSlugWithId(article.slug_name, article.id);
navigateTo(`/learning/articles/${url}`);
}}
>
<div className="aspect-video w-full bg-gray-100 overflow-hidden relative">
<ImageWithFallback
src={article.banner_img || 'https://images.unsplash.com/photo-1481627834876-b7833e8f5570?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxhcnRpY2xlJTIwYmxvZyUyMGNvbnRlbnQlMjBrbm93bGVkZ2V8ZW58MXx8fHwxNzU1ODU0Mjg2fDA&ixlib=rb-4.1.0&q=80&w=1080'}
alt={article.title}
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
/>
</div>
<CardContent className="p-6">
<div className="flex items-center gap-2 mb-3">
<Badge variant="outline" className="text-small">
{article.content_category}
</Badge>
<span className="text-small text-muted">
{calculateReadTime(article.content)}
</span>
</div>
<h3 className="text-h4 mb-3 group-hover:text-[#04045B] transition-colors line-clamp-2">
{article.title}
</h3>
<p className="text-small text-muted mb-4 line-clamp-3">
{article.short_description || article.content.substring(0, 150) + '...'}
</p>
<div className="flex items-center justify-end gap-2">
<div className="text-small text-muted">
{formatDate(article.updated_at)}
</div>
</div>
{/* Display tags if available */}
{article.blog_tags && article.blog_tags.length > 0 && (
<div className="flex flex-wrap gap-1 mt-3 pt-3 border-t border-gray-100">
{article.blog_tags.slice(0, 3).map((tag, idx) => (
<Badge key={idx} variant="secondary" className="text-xs">
{tag.tag_name}
</Badge>
))}
{article.blog_tags.length > 3 && (
<span className="text-xs text-gray-500">+{article.blog_tags.length - 3}</span>
)}
</div>
)}
</CardContent>
</Card>
))}
</div>
)}
{/* List View */}
{viewMode === 'list' && (
<div className="space-y-6">
{finalFilteredArticles.map((article: BlogItem) => (
<Card
key={article.id}
className="overflow-hidden hover:shadow-lg transition-all duration-300 cursor-pointer group"
onClick={() => {
// Use slug_name to create the URL with full UUID
const url = getSlugWithId(article.slug_name, article.id);
navigateTo(`/learning/articles/${url}`);
}}
>
<div className="flex flex-col md:flex-row">
<div className="md:w-80 h-48 md:h-auto bg-gray-100 overflow-hidden relative flex-shrink-0">
<ImageWithFallback
src={article.banner_img || 'https://images.unsplash.com/photo-1481627834876-b7833e8f5570?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxhcnRpY2xlJTIwYmxvZyUyMGNvbnRlbnQlMjBrbm93bGVkZ2V8ZW58MXx8fHwxNzU1ODU0Mjg2fDA&ixlib=rb-4.1.0&q=80&w=1080'}
alt={article.title}
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
/>
</div>
<div className="flex-1 p-6">
<div className="flex justify-between items-start">
<div className="flex-1">
<div className="flex items-center gap-2 mb-2">
<Badge variant="outline" className="text-small">
{article.content_category}
</Badge>
<span className="text-small text-muted">
{calculateReadTime(article.content)}
</span>
</div>
<h3 className="text-h4 mb-2 group-hover:text-[#04045B] transition-colors">
{article.title}
</h3>
<p className="text-body text-muted mb-3">
{article.short_description || article.content.substring(0, 200) + '...'}
</p>
<div className="flex items-center justify-between">
<div className="text-small text-muted">
{formatDate(article.updated_at)}
</div>
{/* Display tags if available */}
{article.blog_tags && article.blog_tags.length > 0 && (
<div className="flex flex-wrap gap-1">
{article.blog_tags.slice(0, 2).map((tag, idx) => (
<Badge key={idx} variant="secondary" className="text-xs">
{tag.tag_name}
</Badge>
))}
{article.blog_tags.length > 2 && (
<span className="text-xs text-gray-500">+{article.blog_tags.length - 2}</span>
)}
</div>
)}
</div>
</div>
</div>
</div>
</div>
</Card>
))}
</div>
)}
{/* Pagination */}
{totalPages > 1 && (
<div className="flex items-center justify-center gap-2 mt-8">
<Button
variant="outline"
size="sm"
onClick={() => {
setCurrentPage(prev => Math.max(1, prev - 1));
containerRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' });
}}
disabled={currentPage === 1}
className="flex items-center gap-1 border-gray-300 disabled:opacity-50 disabled:cursor-not-allowed"
>
<ChevronLeft className="w-4 h-4" />
Previous
</Button>
<div className="flex items-center gap-1">
{Array.from({ length: totalPages }, (_, i) => {
const page = i + 1;
// Show limited pages for better UX
if (totalPages > 7) {
const showPage =
page === 1 ||
page === totalPages ||
(page >= currentPage - 1 && page <= currentPage + 1);
if (!showPage) {
if (page === currentPage - 2 || page === currentPage + 2) {
return <span key={page} className="px-2">...</span>;
}
return null;
}
}
return (
<Button
key={page}
variant={currentPage === page ? "default" : "outline"}
size="sm"
onClick={() => {
setCurrentPage(page);
containerRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' });
}}
className={`min-w-10 ${currentPage === page
? 'bg-[#04045B] text-white hover:bg-[#04045B]'
: 'border-gray-300 text-gray-700 hover:bg-gray-50'
}`}
>
{page}
</Button>
);
})}
</div>
<Button
variant="outline"
size="sm"
onClick={() => {
setCurrentPage(prev => Math.min(totalPages, prev + 1));
containerRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' });
}}
disabled={currentPage === totalPages}
className="flex items-center gap-1 border-gray-300 disabled:opacity-50 disabled:cursor-not-allowed"
>
Next
<ChevronRight className="w-4 h-4" />
</Button>
</div>
)}
</>
)}
</div>
</div>
</div>
</section>
{/* CTA Banner Section */}
<section className="relative h-[700px] overflow-hidden">
<div className="absolute inset-0">
<ImageWithFallback
src="https://images.unsplash.com/photo-1753613648191-4771cf76f034?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxvbmxpbmUlMjBsZWFybmluZyUyMGRpZ2l0YWwlMjBlZHVjYXRpb258ZW58MXx8fHwxNzU1ODU0Mjc1fDA&ixlib=rb-4.1.0&q=80&w=1080"
alt="Online learning and digital education environment"
className="w-full h-full object-cover"
/>
<div className="absolute inset-0 bg-black/30" />
<div className="absolute inset-0 bg-gradient-to-r from-black/20 via-transparent to-black/60" />
</div>
<div className="relative h-full flex items-center justify-end section-margin-x">
<div
className="bg-opacity-95 backdrop-blur-sm rounded-lg p-16 max-w-2xl"
style={{
backgroundColor: 'var(--color-brand-primary)'
}}
>
<div className="branded-tag-system-next-steps mb-6 justify-start">
<div className="dot"></div>
<span className="text">NEXT STEPS</span>
</div>
<h2 className="text-h2-white mb-8">
Ready to explore more insights?
<span
className="italic"
style={{ color: 'var(--color-brand-accent)' }}
>
{" "}Discover{" "}
</span>
our complete library of leadership resources.
</h2>
<PrimaryCTAButton
text="Browse All Resources"
onClick={() => navigateTo('/learning/articles')}
ariaLabel="Browse all leadership articles and resources"
className="cta-banner-yellow mb-6"
/>
<p className="text-body-white opacity-90">
Access cutting-edge research, expert insights, and practical guidance to accelerate your leadership journey and organizational success.
</p>
</div>
</div>
</section>
</div>
);
}