Files
365devnet/src/components/widgets/CompactCertifications.astro
2025-02-26 01:53:49 +01:00

105 lines
3.7 KiB
Plaintext

---
import Headline from '~/components/ui/Headline.astro';
import WidgetWrapper from '~/components/ui/WidgetWrapper.astro';
import Button from '~/components/ui/Button.astro';
import ImageModal from '~/components/ui/ImageModal.astro';
import type { Testimonials as Props } from '~/types';
import DefaultImage from '~/assets/images/default.png';
// Function to get the correct image path for a testimonial
const getImagePath = (image: unknown) => {
if (typeof image === 'object' && image !== null && 'src' in image && typeof (image as { src: unknown }).src === 'string') {
// If the image has a src property, use it
return String((image as { src: string }).src);
}
// Otherwise, return the default image path
return DefaultImage.src;
};
// Function to get the alt text for an image
const getImageAlt = (image: unknown, fallback: string = "Certification badge") => {
if (typeof image === 'object' && image !== null && 'alt' in image && typeof (image as { alt: unknown }).alt === 'string') {
return String((image as { alt: string }).alt);
}
return fallback;
};
const {
title = '',
subtitle = '',
tagline = '',
testimonials = [],
callToAction,
id,
isDark = false,
classes = {},
bg = await Astro.slots.render('bg'),
} = Astro.props;
---
<WidgetWrapper id={id} isDark={isDark} containerClass={`max-w-7xl mx-auto ${classes?.container ?? ''}`} bg={bg}>
<Headline title={title} subtitle={subtitle} tagline={tagline} classes={{
container: 'max-w-3xl',
title: 'text-3xl lg:text-4xl',
}} />
<div class="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-4 gap-4 mt-6">
{
testimonials &&
testimonials.map(({ linkUrl, name, issueDate, description, image }) => (
<div class="flex flex-col p-3 rounded-md shadow-md dark:shadow-none dark:border dark:border-slate-600 hover:shadow-lg transition-all duration-300 ease-in-out hover:bg-gray-50 dark:hover:bg-gray-800">
<div class="flex items-center mb-3">
<div
class="h-12 w-12 mr-3 flex-shrink-0 bg-gray-100 dark:bg-gray-800 rounded-md flex items-center justify-center overflow-hidden cursor-pointer"
onclick={`window.openImageModal('${getImagePath(image)}', ${JSON.stringify(name || "Certification badge")})`}
>
<img
src={getImagePath(image)}
alt={getImageAlt(image, name || "Certification badge")}
class="h-10 w-10 object-contain transition-transform duration-300 hover:scale-110"
/>
</div>
<a href={linkUrl} target="_blank" rel="noopener noreferrer" class="flex-1">
<div>
{name && <p class="text-sm font-semibold line-clamp-2">{name}</p>}
{issueDate && <p class="text-xs text-muted">{issueDate}</p>}
</div>
</a>
</div>
<a href={linkUrl} target="_blank" rel="noopener noreferrer" class="block">
<div class="text-xs text-muted overflow-hidden">
<div class="max-h-[8rem] hover:max-h-[300px] transition-all duration-500 ease-cert">
{description}
</div>
</div>
</a>
</div>
))
}
</div>
{/* Include the image modal component */}
<ImageModal />
{
callToAction && (
<div class="flex justify-center mx-auto w-fit mt-8 font-medium">
<Button {...callToAction} />
</div>
)
}
</WidgetWrapper>
<style>
/* Custom easing function for certification description expansion */
.ease-cert {
transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
/* Fade effect for the gradient overlay */
.hover\:opacity-0:hover {
opacity: 0;
}
</style>