All files / components/block-renderer/layout post-block.tsx

91.66% Statements 11/12
90% Branches 9/10
100% Functions 5/5
100% Lines 11/11

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 1812x                                           2x 10x               2x 10x                 10x   10x     10x 14x     10x                                                                                             14x                                                                                                                                                                      
"use client"; // Required for Swiper
 
import { Container } from "@/components/forms/container";
import { Heading } from "@/components/elements/heading";
import { Subheading } from "@/components/elements/subheading";
import { FeatureIconContainer } from "@/components/block-renderer/layout/features/feature-icon-container";
import { IconArticle } from "@tabler/icons-react";
import type { PostBlockProps } from "@/types";
import Image from "next/image";
import Link from "next/link";
import { ChevronLeft, ChevronRight } from "lucide-react";
import { Swiper, SwiperSlide } from "swiper/react";
import { Navigation, Pagination, Autoplay } from "swiper/modules";
import "swiper/css";
import "swiper/css/navigation";
import "swiper/css/pagination";
 
// Import Framer Motion for animations
import { motion } from "framer-motion";
 
// Custom navigation buttons
 
const CustomPrevButton = () => (
  <button
    aria-label="Previous Slide"
    className="swiper-button-prev-custom absolute left-[-35px] top-1/2 transform -translate-y-1/2 bg-primary text-white dark:text-gray-900 rounded-full w-12 h-12 flex items-center justify-center shadow-xl transition-all duration-300 ease-in-out hover:scale-105 hover:bg-opacity-90 focus:outline-none focus:ring-2 focus:ring-primary"
  >
    <ChevronLeft size={24} strokeWidth={2} />
  </button>
);
 
const CustomNextButton = () => (
  <button
    aria-label="Next Slide"
    className="swiper-button-next-custom absolute right-[-35px] top-1/2 transform -translate-y-1/2 bg-primary text-white dark:text-gray-900 rounded-full w-12 h-12 flex items-center justify-center shadow-xl transition-all duration-300 ease-in-out hover:scale-105 hover:bg-opacity-90 focus:outline-none focus:ring-2 focus:ring-primary"
  >
    <ChevronRight size={24} strokeWidth={2} />
  </button>
);
 
export function PostBlock(data: Readonly<PostBlockProps>) {
  Iif (!data) return null; // Handle cases where data might be undefined
 
  const { heading, sub_heading, posts } = data;
 
  // Function to truncate text
  const truncateText = (text: string, maxLength: number) => {
    return text.length > maxLength ? text.slice(0, maxLength) + "..." : text;
  };
 
  return (
    <Container className="flex flex-col items-center justify-between pb-20">
      {/* Header Section */}
      <motion.div
        initial={{ opacity: 0, y: -30 }}
        animate={{ opacity: 1, y: 0 }}
        transition={{ duration: 0.8, ease: "easeOut" }}
        className="relative z-20 py-10 md:pt-40 text-center mb-10"
      >
        <FeatureIconContainer className="flex justify-center items-center overflow-hidden">
          <IconArticle className="h-12 w-12 text-primary text-slate-200 dark:text-white" />
        </FeatureIconContainer>
        <Heading as="h1" className="mt-4 font-heading text-primary dark:text-white">
          {heading}
        </Heading>
        <Subheading as="h2" className="mt-2 font-sans text-muted-foreground">
          {sub_heading}
        </Subheading>
      </motion.div>
 
      {/* Swiper Slider Section */}
      <motion.div
        initial={{ opacity: 0, scale: 0.95 }}
        animate={{ opacity: 1, scale: 1 }}
        transition={{ duration: 1, ease: "easeOut" }}
        className="w-full max-w-6xl mt-10 relative"
      >
      <Swiper
        modules={[Navigation, Pagination, Autoplay]}
        spaceBetween={20}
        slidesPerView={1}
        breakpoints={{
          640: { slidesPerView: 1 },
          768: { slidesPerView: 2 },
          1024: { slidesPerView: 3 },
        }}
        navigation={{
          prevEl: ".swiper-button-prev-custom",
          nextEl: ".swiper-button-next-custom",
        }}
        pagination={{ clickable: true }}
        autoplay={{ delay: 3000 }}
        loop
        className="w-full"
      >
        {posts && posts.length > 0 ? (
          posts.map((post) => (
          <SwiperSlide key={post.id} className="h-full flex flex-col">
              {/* Clickable Card */}
              <Link href={`/blog/${post.slug}`} passHref>
                <motion.div
                  whileHover={{ scale: 1.03 }}
                  transition={{ duration: 0.3 }}
                  className="h-full flex flex-col bg-white dark:bg-gray-900 bg-opacity-90 dark:bg-opacity-90 backdrop-blur-lg border border-gray-200 dark:border-gray-800 rounded-lg overflow-hidden shadow-lg hover:shadow-xl transition-shadow duration-300 min-h-[450px]] md:min-h-[450px]] lg:min-h-[450px]"
                >
                  {/* Post Image */}
                  {post.image && (
                    <div className="w-full h-48 relative">
                      <Image
                        src={`${process.env.NEXT_PUBLIC_STRAPI_BASE_URL}${post.image.url}`}
                        alt={post.title}
                        fill
                        style={{ objectFit: "cover" }}
                        className="rounded-t-lg"
                      />
                    </div>
                  )}
 
                  {/* Post Content - Flex column for equal height */}
                  <div className="p-6 flex flex-col flex-grow">
                  <motion.h3
                    initial={{ opacity: 0, y: 10 }}
                    animate={{ opacity: 1, y: 0 }}
                    transition={{ delay: 0.2, duration: 0.5 }}
                    className="text-xl font-heading font-bold text-gray-900 dark:text-white line-clamp-2"
                  >
                    {post.title}
                  </motion.h3>
                    <motion.p
                      initial={{ opacity: 0, y: 10 }}
                      animate={{ opacity: 1, y: 0 }}
                      transition={{ delay: 0.4, duration: 0.5 }}
                      className="text-gray-600 font-sans dark:text-gray-400 mt-2 flex-grow"
                    >
                      {truncateText(post.description, 120)}
                    </motion.p>
 
                    {/* Read More Button - Stays at Bottom */}
                    <motion.div
                      initial={{ opacity: 0, y: 10 }}
                      animate={{ opacity: 1, y: 0 }}
                      transition={{ delay: 0.6, duration: 0.5 }}
                      className="mt-auto text-primary dark:text-primary-light font-semibold hover:underline"
                    >
                      Read More →
                    </motion.div>
                  </div>
                </motion.div>
              </Link>
            </SwiperSlide>
          ))
        ) : (
          <p className="text-gray-600 dark:text-gray-400 text-center">
            No posts available.
          </p>
        )}
      </Swiper>
 
 
        {/* Custom Navigation Buttons */}
        <CustomPrevButton />
        <CustomNextButton />
      </motion.div>
 
      {/* View All Blogs Button */}
      <motion.div
        initial={{ opacity: 0, y: 20 }}
        animate={{ opacity: 1, y: 0 }}
        transition={{ delay: 1, duration: 0.6 }}
        className="mt-10"
      >
        <Link href="/blog">
          <button className="font-heading font-bold px-6 py-3 text-white dark:text-slate-800 bg-gradient-to-r from-blue-600 to-indigo-500 dark:from-blue-400 dark:to-indigo-400 rounded-lg shadow-md hover:bg-opacity-90 transition transform hover:scale-105">
            View All Blogs
          </button>
        </Link>
      </motion.div>
    </Container>
  );
}