import React, { useEffect, useState } from "react"; import "./frontend-style.scss"; import { UrlData } from "@app/models/api"; import CTACard from "@app/components/CTACard/CTACard"; import { convertToEasternArabic, mapTaxonomyToName, renderType, } from "@app/services/utilities"; import ResultCard from "@app/components/result-card/ResultCard"; import { CTACardProps, CustomizableCardProps, ResultCardProps, } from "@app/models/components"; import CustomizableCard from "@app/components/customizableCard/CustomizableCard"; import { getFilter, checkUrlDataType } from "@app/services/api"; import InfiniteScroll from "@app/components/InfiniteScroll/InfiniteScroll"; import SkeletonCard from "@app/components/skeleton-card/SkeletonCard"; let frontendData: any; if (topPostsFrontendData) { frontendData = topPostsFrontendData; } else { frontendData = topPostsFrontendWidgetData; } function App() { const [filteredPosts, setFilteredPosts] = useState>([]); const [posts, setPosts] = useState>([]); const [isFetching, setIsFetching] = useState(false); const skeletonItems = Array.from({ length: 6 }, (_, index) => index + 1); const applyFilterLogic = (posts: any[]) => { const filterByLanguage = frontendData?.filter?.language ?? ""; let filterByScope = frontendData?.filter?.scope ?? ""; return posts.filter((post) => { // Check language independently const isLanguageValid = filterByLanguage === "" || post.language === filterByLanguage; // Check scope independently const isScopeValid = filterByScope.length === 0 || (filterByScope.length === 1 && filterByScope[0] === "") || filterByScope.some((s: any) => s === post.type); // Return true only if both conditions are satisfied return isLanguageValid && isScopeValid; }); }; const fetchFilter = async () => { const currentFilterID = frontendData?.filter?.filter_id; if (!currentFilterID) { return; } let checkUrlDataTypeResp; checkUrlDataTypeResp = frontendData?.filter?.filterPosts || []; if (!!checkUrlDataTypeResp.length) { if ( frontendData?.filter?.language || frontendData?.filter?.scope?.filter(Boolean).length > 0 ) { const dataResponse = applyFilterLogic(checkUrlDataTypeResp); setFilteredPosts(dataResponse); setPosts(dataResponse); } else { setFilteredPosts(checkUrlDataTypeResp); setPosts(checkUrlDataTypeResp); } } else { setIsFetching(true); } const getFilterResp = await getFilter( frontendData?.apiKey || "", frontendData?.filterID, frontendData?.filter?.lastModified || "" ); if (getFilterResp?.error) { setIsFetching(false); return; } if (getFilterResp?.status === 304) { checkUrlDataTypeResp = frontendData?.filter?.filterPosts || []; } else { checkUrlDataTypeResp = await checkUrlDataType( getFilterResp.data, "" //scope ); } if ( frontendData?.filter?.language || frontendData?.filter?.scope?.filter(Boolean).length > 0 ) { const dataResponse = applyFilterLogic(checkUrlDataTypeResp); setFilteredPosts(dataResponse); setPosts(dataResponse); } else { setFilteredPosts(checkUrlDataTypeResp); setPosts(checkUrlDataTypeResp); } setIsFetching(false); }; useEffect(() => { fetchFilter(); }, []); useEffect(() => { let updatedPosts = [...posts]; updatedPosts = updatedPosts.map((post) => ({ ...post, excluded: frontendData.filter.excluded?.includes(post?.id) ?? false, pinned: frontendData.filter.pinned?.includes(post.id) ?? false, })); setFilteredPosts(updatedPosts); }, [frontendData.filter, posts]); const shouldDisplay = (prop: string) => { return frontendData.display_options.includes(prop); }; const renderCard = (post: UrlData, index: number) => { const pinnedPostsArray = frontendData?.filter?.pinned; const pinned = pinnedPostsArray?.includes(post?.id) || false; const isPostOrCPT = post?.type !== "category" && post?.type !== "post_tag" && post?.type !== "page"; const isPostOrPage = isPostOrCPT || post?.type === "page"; const hasDate = isPostOrPage && post?.date; const date = hasDate && post?.language === "ar" ? convertToEasternArabic( new Date(post.date).toLocaleDateString("ar", { day: "2-digit", month: "short", year: "numeric", }) ) : hasDate ? new Date(post.date).toLocaleDateString("en-GB", { day: "2-digit", month: "short", year: "numeric", }) : ""; const tags = isPostOrCPT && post?.tags ? post?.tags.map((tag) => ({ label: tag.name, href: tag.link, })) ?? [] : []; const title = (isPostOrCPT ? post?.title : post?.name) ?? ""; const body = (isPostOrCPT ? post?.excerpt : post?.description) ?? ""; const category = isPostOrCPT ? post?.category[0] : mapTaxonomyToName(post?.type); let props: CTACardProps | ResultCardProps | null = null; const style = frontendData?.results_style || "search_results_card"; const getVariant = () => { if (isPostOrPage && !pinned) { return "PRIMARY"; } else if (isPostOrPage && pinned) { return "PRIMARY_VERTICAL"; } else if (!isPostOrPage && !pinned) { return "SECONDARY"; } else if (!isPostOrPage && pinned) { return "TERTIARY"; } }; if (style === "fancy_card") { props = { variant: getVariant(), imageSrc: shouldDisplay("image") ? post?.image : "", title, tag: shouldDisplay("category") ? category : "", meta: shouldDisplay("date") ? date : "", description: shouldDisplay("excerpt") ? body : "", rtl: post.language == "ar" ? true : false, onClick: () => post?.url && window.open(post?.url, "_self"), type: shouldDisplay("type") ? renderType(post) : "", lazyLoad: index > 2, showImage: shouldDisplay("image"), } satisfies CTACardProps; } else if (style === "search_results_card") { props = { image: shouldDisplay("image") ? post?.image : "", href: post?.url, tags: shouldDisplay("tag") ? tags : [], body: shouldDisplay("excerpt") ? body : "", title, category: shouldDisplay("category") ? category : "", date: shouldDisplay("date") ? date : "", rtl: post.language == "ar" ? true : false, type: shouldDisplay("type") ? renderType(post) : "", lazyLoad: index > 2, } satisfies ResultCardProps; } else if (style === "customizable_card") { props = { image: shouldDisplay("image") ? post?.image : "", href: post?.url, tags: shouldDisplay("tag") ? tags : [], body: shouldDisplay("excerpt") ? body : "", title, category: shouldDisplay("category") ? category : "", date: shouldDisplay("date") ? date : "", rtl: post.language == "ar" ? true : false, type: shouldDisplay("type") ? renderType(post) : "", lazyLoad: index > 2, } satisfies CustomizableCardProps; } return (
{style === "fancy_card" ? ( ) : style === "search_results_card" ? ( ) : style === "customizable_card" ? ( ) : null}
); }; const excludedPostsArray = frontendData?.filter?.excluded; const seenIds = new Set(); let finalFilteredPosts = filteredPosts .filter((item) => { const isDuplicate = seenIds.has(item.id); seenIds.add(item.id); return !isDuplicate; }) .filter( (post: any) => !post.excluded && !excludedPostsArray?.includes(post.id) ) .sort((a: any, b: any) => { const pinnedPostsArray = frontendData?.filter?.pinned; if ( pinnedPostsArray?.includes(a.id) && !pinnedPostsArray?.includes(b.id) ) { return -1; // a should come before b } else if ( !pinnedPostsArray?.includes(a.id) && pinnedPostsArray?.includes(b.id) ) { return 1; // b should come before a } else { return 0; // maintain existing order } }); if (parseInt(frontendData.limit) != 0) { finalFilteredPosts = finalFilteredPosts.slice(0, frontendData.limit); } else { finalFilteredPosts = finalFilteredPosts.slice( 0, frontendData?.filter?.limit ); } let gridTemplateColumn = ""; for (let i = 0; i < frontendData.columns_number; i++) { gridTemplateColumn += "1fr "; } return (
{isFetching && skeletonItems.map((item) => (
))} {!!finalFilteredPosts.length && ( )}
); } export default App;