diff --git a/src/components/InsightsSection.tsx b/src/components/InsightsSection.tsx index 366d467..3e92e34 100644 --- a/src/components/InsightsSection.tsx +++ b/src/components/InsightsSection.tsx @@ -3,47 +3,36 @@ import { ImageWithFallback } from "./figma/ImageWithFallback"; import { BrandedTag } from "./about/BrandedTag"; import { StandardCTAButton } from "./StandardCTAButton"; import { navigateTo } from "./Router"; +import { useGetFeaturedBlogsQuery } from "../redux/services/homepageApi"; +import { FullScreenLoader } from "./FullScreenLoader"; +import { getSlugWithId } from "../utils/urlHelpers"; -interface InsightCard { - id: number; +// Interface for featured blog items from API +interface FeaturedBlog { + id: string; + title: string; + short_description: string | null; + slug_name: string; + banner_img: string | null; + updated_at: string; + content_category: string; + content_type: string; + tags: string[]; + featured: boolean; + featured_order: number; +} + +// Insight Card Component Interface +interface InsightCardData { + id: string; title: string; description?: string; date: string; tags: string[]; image: string; - slug?: string; + slug: string; } -const insightCards: InsightCard[] = [ - { - id: 1, - title: "A New Lens on Leadership", - description: "Your leadership calls, and how you interpret opportunities and threats, are influenced by your lenses, which are unique and personal to you.", - date: "16-08-2016", - tags: ["Leadership Lens", "Perspective"], - image: "https://images.unsplash.com/photo-1560550900-5c10828c40aa?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxsZWFkZXJzaGlwJTIwbGVucyUyMHBlcnNwZWN0aXZlfGVufDF8fHx8MTc1OTk5NTg0N3ww&ixlib=rb-4.1.0&q=80&w=1080", - slug: "new-lens-on-leadership" - }, - { - id: 2, - title: "Putting Psychometry in perspective", - description: "An in-depth exploration of the limitations and appropriate use of psychometric tools in leadership assessment and talent selection.", - date: "17-12-2016", - tags: ["Psychometry", "Assessment"], - image: "https://images.unsplash.com/photo-1460925895917-afdab827c52f?w=600&h=400&fit=crop", - slug: "putting-psychometry-in-perspective" - }, - { - id: 3, - title: "The Busby Way to Talent Management: The Making of \"Busby's Babes\" – Part 1", - description: "How Matt Busby transformed Manchester United through visionary talent management and youth development, creating a legacy that shaped modern football leadership.", - date: "05-12-2013", - tags: ["Talent Management", "Youth Development"], - image: "https://images.unsplash.com/photo-1574629810360-7efbbe195018?w=600&h=400&fit=crop", - slug: "busby-way-talent-management-part-1" - } -]; - // Insight Tag Component function InsightTag({ text }: { text: string }) { return ( @@ -78,7 +67,7 @@ function CalendarIcon() { ); } -// Explore All Button Component - Updated to redirect to articles page +// Explore All Button Component function ExploreAllButton() { return ( { - if (card.slug) { - navigateTo(`/learning/articles/${card.slug}`); + if (card.slug && card.id) { + const url = getSlugWithId(card.slug, card.id); + navigateTo(`/learning/articles/${url}`); } }; - const hasLink = !!card.slug; + const hasLink = !!(card.slug && card.id); return (
{/* Top section with tags and arrow */}
-
- {card.tags.map((tag, index) => ( +
+ {card.tags.slice(0, 2).map((tag, index) => ( ))}
@@ -158,14 +148,15 @@ function LargeInsightCard({ card }: { card: InsightCard }) { } // Small Insight Card Component -function SmallInsightCard({ card }: { card: InsightCard }) { +function SmallInsightCard({ card }: { card: InsightCardData }) { const handleClick = () => { - if (card.slug) { - navigateTo(`/learning/articles/${card.slug}`); + if (card.slug && card.id) { + const url = getSlugWithId(card.slug, card.id); + navigateTo(`/learning/articles/${url}`); } }; - const hasLink = !!card.slug; + const hasLink = !!(card.slug && card.id); return (
{/* Top section with tags and arrow */}
-
- {card.tags.map((tag, index) => ( +
+ {card.tags.slice(0, 2).map((tag, index) => ( { + return new Date(dateString).toLocaleDateString('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric' + }); +}; + export function InsightsSection() { + // Fetch featured blogs from API + const { data: featuredBlogs, isLoading, isError } = useGetFeaturedBlogsQuery({ limit: 3 }); + + // Transform API data to match InsightCardData format + const transformToInsightCard = (blog: FeaturedBlog): InsightCardData => ({ + id: blog.id, + title: blog.title, + description: blog.short_description || undefined, + date: formatDate(blog.updated_at), + tags: blog.tags || [], + image: blog.banner_img || 'https://images.unsplash.com/photo-1481627834876-b7833e8f5570?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&w=1080', + slug: blog.slug_name, + }); + + // Handle loading state + if (isLoading) { + return ( +
+
+
+ +
+
+
+ ); + } + + // Handle error or no data state + if (isError || !featuredBlogs || featuredBlogs.length === 0) { + return ( +
+
+ + +
+ {/* Header */} +
+

+ Leadership Insights & Ideas +

+ +
+ + {/* Show message when no featured blogs available */} +
+

No featured insights available at the moment. Check back soon!

+
+
+
+
+ ); + } + + // Ensure we have at least 3 blogs for the layout (use placeholders if needed) + const cards = featuredBlogs.map(transformToInsightCard); + + // The layout expects: first card (large) + two small cards + const largeCard = cards[0]; + const smallCards = cards.slice(1, 3); + + // If we don't have enough cards, we can still render with what we have + const hasLargeCard = !!largeCard; + const hasSmallCards = smallCards.length > 0; + return (
- {/* Branded Tag */}
@@ -261,15 +341,20 @@ export function InsightsSection() { {/* Main Grid Layout */}
{/* Large Card - Takes full height on left */} -
- -
+ {hasLargeCard && ( +
+ +
+ )} {/* Small Cards - Stack on right */} -
- - -
+ {hasSmallCards && ( +
+ {smallCards.map((card:any, index:any) => ( + + ))} +
+ )}
diff --git a/src/components/LearningOnline.tsx b/src/components/LearningOnline.tsx index 558cedb..5eeef8e 100644 --- a/src/components/LearningOnline.tsx +++ b/src/components/LearningOnline.tsx @@ -53,7 +53,7 @@ export function LearningOnline() { const [selectedPriceRange, setSelectedPriceRange] = useState('All Prices'); const [selectedDuration, setSelectedDuration] = useState('All Durations'); const [selectedRating, setSelectedRating] = useState('All Ratings'); - const [sortBy, setSortBy] = useState('Most Popular'); + const [sortBy, setSortBy] = useState('most_popular'); // ✅ Changed to match API value const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid'); const [currentPage, setCurrentPage] = useState(1); const coursesPerPage = 9; @@ -68,14 +68,15 @@ export function LearningOnline() { offset: 0 }); + // ✅ Updated sort options to match backend API values const sortOptions = [ { value: 'most_popular', label: 'Most Popular' }, { value: 'newest', label: 'Newest First' }, { value: 'title_asc', label: 'Title A-Z' }, - { value: 'price_asc', label: 'Price: Low to High' }, - { value: 'price_desc', label: 'Price: High to Low' }, - { value: 'rating_desc', label: 'Highest Rated' }, - { value: 'duration_asc', label: 'Duration' } + { value: 'price_low', label: 'Price: Low to High' }, + { value: 'price_high', label: 'Price: High to Low' }, + { value: 'highest_rated', label: 'Highest Rated' }, + { value: 'duration', label: 'Duration' } ]; const priceRanges = [ @@ -112,33 +113,33 @@ export function LearningOnline() { return cats; }, [categoriesData]); - // Helper function to convert UI price range to API format + // ✅ Helper function to convert UI price range to API format const getPriceRangeForApi = useCallback((priceRange: string): string | undefined => { switch (priceRange) { case 'Under ₹20,000': - return '0-20000'; + return 'under_20000'; case '₹20,000 - ₹35,000': - return '20000-35000'; + return '20000_35000'; case '₹35,000 - ₹50,000': - return '35000-50000'; + return '35000_50000'; case 'Over ₹50,000': - return '50000-999999'; + return 'above_50000'; default: return undefined; } }, []); - // Helper function to convert UI duration to API format + // ✅ Helper function to convert UI duration to API format const getDurationForApi = useCallback((duration: string): string | undefined => { switch (duration) { case 'Under 6 hours': - return '0-6'; + return 'under_6'; case '6-10 hours': - return '6-10'; + return '6_10'; case '10-15 hours': - return '10-15'; + return '10_15'; case 'Over 15 hours': - return '15-999'; + return 'above_15'; default: return undefined; } @@ -158,26 +159,11 @@ export function LearningOnline() { } }, []); - // Helper function to convert sort option to API format + // ✅ Helper function to convert sort option to API format const getSortByForApi = useCallback((sort: string): string | undefined => { - switch (sort) { - case 'Most Popular': - return 'popular'; - case 'newest': - return 'newest'; - case 'title': - return 'title_asc'; - case 'price_low': - return 'price_asc'; - case 'price_high': - return 'price_desc'; - case 'rating': - return 'rating_desc'; - case 'duration': - return 'duration_asc'; - default: - return undefined; - } + // sort is already in API format (most_popular, newest, title_asc, etc.) + // Just return it as is + return sort; }, []); // Build API filters based on current UI state @@ -304,7 +290,7 @@ export function LearningOnline() { setSelectedPriceRange('All Prices'); setSelectedDuration('All Durations'); setSelectedRating('All Ratings'); - setSortBy('Most Popular'); + setSortBy('most_popular'); // ✅ Updated to match API value }; const hasActiveFilters = searchTerm || @@ -361,7 +347,7 @@ export function LearningOnline() { return (
- {/* Hero Banner (keep as is) */} + {/* Hero Banner */}
- {/* Search and Controls Section (keep as is) */} + {/* Search and Controls Section */}
@@ -453,7 +439,7 @@ export function LearningOnline() { - {sortOptions.map((option: any) => ( + {sortOptions.map((option) => ( {option.label} @@ -597,6 +583,15 @@ export function LearningOnline() {

No courses found matching your criteria.

+ {hasActiveFilters && ( + + )}
) : ( <> @@ -693,18 +688,17 @@ export function LearningOnline() {
); -} +} \ No newline at end of file diff --git a/src/redux/services/courseApi.ts b/src/redux/services/courseApi.ts index 490fac0..4f6b3a9 100644 --- a/src/redux/services/courseApi.ts +++ b/src/redux/services/courseApi.ts @@ -321,8 +321,8 @@ export const courseApi = createApi({ const queryString = searchParams.toString(); return queryString - ? `admin/course/list?${queryString}` - : `admin/course/list`; + ? `admin/course/public/list?${queryString}` + : `admin/course/public/list`; }, providesTags: (result) => diff --git a/src/redux/services/homepageApi.ts b/src/redux/services/homepageApi.ts index 3bf35b8..03d3682 100644 --- a/src/redux/services/homepageApi.ts +++ b/src/redux/services/homepageApi.ts @@ -112,9 +112,22 @@ export const homepageApi = createApi({ providesTags: [{ type: "Homepage", id: "LIST" }], }), + getFeaturedBlogs: builder.query({ + query: ({ limit = 3 }) => ({ + url: `/admin/blogs/featured?limit=${limit}`, + method: 'GET', + }), + transformResponse: (response: any) => { + if (response?.success && response?.data) { + return response.data; + } + return []; + }, + }), + }), }); /* ================= HOOKS ================= */ -export const { useGetHomepageQuery } = homepageApi; \ No newline at end of file +export const { useGetHomepageQuery, useGetFeaturedBlogsQuery } = homepageApi; \ No newline at end of file