Files
pasargad/src/components/layout/contentGenerator/index.tsx
T
ahasani 730e2d8ca8 feat: add certificate gallery and certifications section components
- Implemented CertificateGallery component to display certification images in a gallery format.
- Created CertificationsSection component to showcase certifications with titles and descriptions.
- Enhanced Gallery component to support animations and keyboard navigation.
- Introduced Select component for dropdown selection with images.
- Added utility functions for API responses and pagination handling.
- Implemented localization functions for text and content.
- Created a custom hook for detecting clicks outside of a component.
2026-07-19 07:59:16 +03:30

159 lines
4.0 KiB
TypeScript

import {
IContentCTA,
IContentData,
IContentDataLink,
IContentImage,
IContentQuote,
IDynamicContent,
} from '@/models/dynamicContent';
function imageContent(data: IContentImage) {
return (
<div className="flex flex-col items-center gap-3">
<img
src={data.src}
alt={data.alt}
className="mx-auto max-h-[60vh] max-w-[90vw] overflow-hidden rounded-2xl object-cover"
/>
{data.thumbTitle ? (
<h5 className="text-center text-sm text-gray-400">{data.thumbTitle}</h5>
) : (
<></>
)}
</div>
);
}
function titleContent(data: string) {
return <h2 className="text-2xl font-bold text-gray-500">{data}</h2>;
}
function subtitleContent(data: string) {
return <h3 className="text-xl font-semibold text-gray-500">{data}</h3>;
}
function fullContent(data: IContentData | string) {
const normalizedData =
typeof data === 'string'
? ({
data,
} as IContentData)
: data;
const splittedData = normalizedData.data?.split('\n') || [];
return splittedData.map((e, index) => {
return (
<p key={index} className="indent-5 text-base text-gray-400">
{generateLinkedContent(e, normalizedData.links)}
</p>
);
});
}
function generateLinkedContent(data: string, links?: IContentDataLink[]) {
const splitted = data.split(' ');
const mappedLinks = {} as Record<string, IContentDataLink>;
links?.forEach((link) => {
mappedLinks[link.key] = link;
});
return (
<p>
{splitted.map((data) => {
return mappedLinks[data] ? (
<strong>
<a
href={mappedLinks[data].link}
className="text-primary hover:underline"
>
{mappedLinks[data].label}
</a>{' '}
</strong>
) : (
<>{data} </>
);
})}
</p>
);
}
function listContent(data: string[], type: 'bullet' | 'numberList') {
return (
<ul>
{data.map((item, index) => (
<li
key={index}
className={`list-inside ${type === 'bullet' ? 'list-disc' : 'list-decimal'} text-sm text-gray-400`}
>
<h4 className="inline text-sm">{item}</h4>
</li>
))}
</ul>
);
}
function quoteContent(data: IContentQuote) {
return (
<blockquote className="rounded-xl border-l-4 border-primary bg-gray-100 px-4 py-3 text-gray-500">
<p className="text-base italic">{data.text}</p>
{data.author ? (
<cite className="mt-2 block text-sm text-gray-400"> {data.author}</cite>
) : null}
</blockquote>
);
}
function ctaContent(data: IContentCTA) {
return (
<a
href={data.link}
target={data.target || '_self'}
rel={data.target === '_blank' ? 'noopener noreferrer' : undefined}
className="bg-primary inline-flex w-fit rounded-lg px-4 py-2 text-sm text-white"
>
{data.label}
</a>
);
}
function dividerContent() {
return <hr className="my-2 border-gray-200" />;
}
export default function ContentGenerator({
data,
className,
}: {
data: IDynamicContent[];
className?: string;
}) {
return (
<div className={`flex flex-col gap-3 md:gap-6 ${className}`}>
{data.map((e, index) => {
switch (e.type) {
case 'title':
return <div key={index}>{titleContent(e.data)}</div>;
case 'subtitle':
return <div key={index}>{subtitleContent(e.data)}</div>;
case 'content':
return <div key={index}>{fullContent(e.data)}</div>;
case 'image':
return <div key={index}>{imageContent(e.data)}</div>;
case 'bullet':
case 'numberList':
return <div key={index}>{listContent(e.data, e.type)}</div>;
case 'quote':
return <div key={index}>{quoteContent(e.data)}</div>;
case 'cta':
return <div key={index}>{ctaContent(e.data)}</div>;
case 'divider':
return <div key={index}>{dividerContent()}</div>;
default:
return null;
}
})}
</div>
);
}