update
This commit is contained in:
@@ -1,13 +1,73 @@
|
||||
"use client";
|
||||
import { useState } from "react";
|
||||
import Link from "next/link";
|
||||
import PostFilterItems from "./post-filter/PostFilterItems";
|
||||
|
||||
const BlogItems = () => {
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [totalPages, setTotalPages] = useState(1);
|
||||
const postsPerPage = 6;
|
||||
|
||||
const handlePageChange = (page: number) => {
|
||||
if (page >= 1 && page <= totalPages) {
|
||||
setCurrentPage(page);
|
||||
// Scroll to top of posts section
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
}
|
||||
};
|
||||
|
||||
const handleTotalPagesChange = (total: number) => {
|
||||
setTotalPages(total);
|
||||
};
|
||||
|
||||
// Generate page numbers to display
|
||||
const getPageNumbers = () => {
|
||||
const pages: (number | string)[] = [];
|
||||
const maxPagesToShow = 5;
|
||||
|
||||
if (totalPages <= maxPagesToShow) {
|
||||
// Show all pages if total is small
|
||||
for (let i = 1; i <= totalPages; i++) {
|
||||
pages.push(i);
|
||||
}
|
||||
} else {
|
||||
// Show first page
|
||||
pages.push(1);
|
||||
|
||||
// Calculate range around current page
|
||||
let startPage = Math.max(2, currentPage - 1);
|
||||
let endPage = Math.min(totalPages - 1, currentPage + 1);
|
||||
|
||||
// Add ellipsis after first page if needed
|
||||
if (startPage > 2) {
|
||||
pages.push('...');
|
||||
}
|
||||
|
||||
// Add pages around current page
|
||||
for (let i = startPage; i <= endPage; i++) {
|
||||
pages.push(i);
|
||||
}
|
||||
|
||||
// Add ellipsis before last page if needed
|
||||
if (endPage < totalPages - 1) {
|
||||
pages.push('...');
|
||||
}
|
||||
|
||||
// Show last page
|
||||
if (totalPages > 1) {
|
||||
pages.push(totalPages);
|
||||
}
|
||||
}
|
||||
|
||||
return pages;
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="fix-top pb-120 blog-m">
|
||||
<div className="container">
|
||||
<div className="row align-items-center vertical-column-gap">
|
||||
<div className="col-12 col-lg-7">
|
||||
<h2 className="mt-8 fw-7 text-secondary title-anim">Blog</h2>
|
||||
<h2 className="mt-8 fw-7 text-secondary title-anim">Latest Company Insights</h2>
|
||||
</div>
|
||||
<div className="col-12 col-lg-5">
|
||||
<form action="#" method="post" autoComplete="off">
|
||||
@@ -30,36 +90,58 @@ const BlogItems = () => {
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<PostFilterItems />
|
||||
<div className="row mt-60">
|
||||
<div className="col-12">
|
||||
<div className="section__cta">
|
||||
<ul className="pagination">
|
||||
<li>
|
||||
<button>
|
||||
<i className="fa-solid fa-angle-left"></i>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="blog">1</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="blog" className="active">
|
||||
2
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="blog">3</Link>
|
||||
</li>
|
||||
<li>
|
||||
<button>
|
||||
<i className="fa-solid fa-angle-right"></i>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<PostFilterItems
|
||||
currentPage={currentPage}
|
||||
onPageChange={handlePageChange}
|
||||
onTotalPagesChange={handleTotalPagesChange}
|
||||
postsPerPage={postsPerPage}
|
||||
/>
|
||||
{totalPages > 1 && (
|
||||
<div className="row mt-60">
|
||||
<div className="col-12">
|
||||
<div className="section__cta">
|
||||
<ul className="pagination">
|
||||
<li>
|
||||
<button
|
||||
onClick={() => handlePageChange(currentPage - 1)}
|
||||
disabled={currentPage === 1}
|
||||
aria-label="Previous page"
|
||||
style={{ opacity: currentPage === 1 ? 0.5 : 1, cursor: currentPage === 1 ? 'not-allowed' : 'pointer' }}
|
||||
>
|
||||
<i className="fa-solid fa-angle-left"></i>
|
||||
</button>
|
||||
</li>
|
||||
{getPageNumbers().map((page, index) => (
|
||||
<li key={index}>
|
||||
{typeof page === 'number' ? (
|
||||
<button
|
||||
onClick={() => handlePageChange(page)}
|
||||
className={currentPage === page ? 'active' : ''}
|
||||
aria-label={`Go to page ${page}`}
|
||||
aria-current={currentPage === page ? 'page' : undefined}
|
||||
>
|
||||
{page}
|
||||
</button>
|
||||
) : (
|
||||
<span style={{ padding: '0 10px' }}>{page}</span>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
<li>
|
||||
<button
|
||||
onClick={() => handlePageChange(currentPage + 1)}
|
||||
disabled={currentPage === totalPages}
|
||||
aria-label="Next page"
|
||||
style={{ opacity: currentPage === totalPages ? 0.5 : 1, cursor: currentPage === totalPages ? 'not-allowed' : 'pointer' }}
|
||||
>
|
||||
<i className="fa-solid fa-angle-right"></i>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
||||
@@ -1,207 +1,289 @@
|
||||
"use client";
|
||||
import { useParams } from "next/navigation";
|
||||
import Image from "next/legacy/image";
|
||||
import Link from "next/link";
|
||||
import poster from "@/public/images/blog/blog-poster.png";
|
||||
import info from "@/public/images/blog/blog-info.png";
|
||||
import { useBlogPost } from "@/lib/hooks/useBlog";
|
||||
import { getValidImageUrl, getValidImageAlt, FALLBACK_IMAGES } from "@/lib/imageUtils";
|
||||
|
||||
const BlogSingle = () => {
|
||||
return (
|
||||
<section className="tp-post-details fix-top pb-120 fade-wrapper">
|
||||
<div className="container">
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<div className="tp-post-intro">
|
||||
<h2 className="title-anim text-xxl fw-7 text-secondary mt-8">
|
||||
Tackling data of annotation problems in healthcare
|
||||
</h2>
|
||||
<div className="mt-60 mb-24 d-flex align-items-center justify-content-between tppr">
|
||||
<div className="d-flex align-items-center tp-post-tags-container mt-8">
|
||||
<p className="text-xs">Scope:</p>
|
||||
<div className="d-flex align-items-center tp-post-tags">
|
||||
<Link href="blog">AI</Link>
|
||||
<span></span>
|
||||
<Link href="blog">Artificial Intelligence</Link>
|
||||
<span></span>
|
||||
<Link href="blog">Data Science</Link>
|
||||
<span></span>
|
||||
<Link href="blog">Machine Learning</Link>
|
||||
</div>
|
||||
const params = useParams();
|
||||
const slug = params?.slug as string;
|
||||
const { post, loading, error } = useBlogPost(slug);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<section className="blog-single-section fix-top pb-120">
|
||||
<div className="container">
|
||||
<div className="row justify-content-center">
|
||||
<div className="col-12 col-lg-10 col-xl-8">
|
||||
<div className="loading-state text-center py-5">
|
||||
<div className="spinner-border text-primary mb-3" role="status">
|
||||
<span className="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
<div className="tp-post-meta mt-8">
|
||||
<p className="author text-xs text-tertiary">Denial Lio</p>
|
||||
<span></span>
|
||||
<p className="date text-xs text-tertiary">18 Dec 2022</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="tp-post-poster fade-top">
|
||||
<div className="parallax-image-wrap">
|
||||
<div className="parallax-image-inner">
|
||||
<Image
|
||||
src={poster}
|
||||
className="w-100 mh-300 parallax-image"
|
||||
alt="Image"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="group mt-60">
|
||||
<p className="cur-lg mb-24">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec
|
||||
nec tortor id erat faucibus tempor id eget turpis. Donec
|
||||
lobortis et neque eget congue. Mauris laoreet orci ac dictum
|
||||
interdum. Sed dapibus convallis arcu, a aliquam purus sodales
|
||||
nec. Integer consequat et magna sit amet porta. Maecenas
|
||||
consectetur eros sed risus porta convallis eget et massa.
|
||||
Integer auctor convallis ligula, sit amet sollicitudin justo
|
||||
tincidunt a. Sed tellus diam.
|
||||
</p>
|
||||
<p className="cur-lg mb-24 fw-6">
|
||||
Bibendum tincidunt orci vel, sollicitudin bibendum ligula.
|
||||
Pellentesque sollicitudin nulla felis, a ornare tellus
|
||||
tristique ac. Proin ultricies a turpis sit amet lacinia. Ut
|
||||
laoreet nunc leo, ac congue enim laoreet in. Aenean suscipit
|
||||
arcu at ligula tempor porta.
|
||||
</p>
|
||||
<p className="cur-lg">
|
||||
Quisque et fringilla lacus, quis luctus elit. Curabitur eu dui
|
||||
mattis turpis commodo eleifend. Sed porta ornare nunc et
|
||||
tristique. Curabitur vel eros a ante cursus lacinia. Nam nisl
|
||||
leo, aliquet a placerat at, porttitor quis augue. Proin quis
|
||||
aliquet libero. Pellentesque habitant morbi tristique senectus
|
||||
et netus et malesuada fames ac turpis egestas. Vestibulum
|
||||
varius a ipsum ornare blandit. Integer vitae eleifend risus,
|
||||
id tincidunt elit. Integer tincidunt ipsum vitae sagittis
|
||||
porta. Aenean ut facilisis dui. Praesent at ultricies purus.
|
||||
Nam a arcu vel diam ullamcorper tincidunt. Curabitur
|
||||
vestibulum commodo erat non laoreet. Proin nibh nibh,
|
||||
scelerisque a nibh nec, scelerisque convallis leo. Nunc eget
|
||||
elit nunc.
|
||||
</p>
|
||||
</div>
|
||||
<div className="group-info mt-60">
|
||||
<div className="row align-items-center vertical-column-gap">
|
||||
<div className="col-12 col-lg-6">
|
||||
<div className="fade-top">
|
||||
<div className="parallax-image-wrap">
|
||||
<div className="parallax-image-inner">
|
||||
<Image
|
||||
src={info}
|
||||
className="w-100 mh-300 parallax-image"
|
||||
alt="Image"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 col-lg-6">
|
||||
<p className="cur-lg mb-24">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
Donec nec tortor id erat faucibus tempor id eget turpis.
|
||||
Donec lobortis et neque eget congue. Mauris laoreet orci
|
||||
ac dictum interdum. Sed dapibus convallis arcu, a aliquam
|
||||
purus sodales nec.
|
||||
</p>
|
||||
<p className="cur-lg">
|
||||
Quisque et fringilla lacus, quis luctus elit. Curabitur eu
|
||||
dui mattis turpis commodo eleifend. Sed porta ornare nunc
|
||||
et tristique. Curabitur vel eros a ante cursus lacinia.
|
||||
Nam nisl leo, aliquet a placerat at, porttitor quis augue.
|
||||
Proin quis aliquet libero. Pellentesque habitant morbi
|
||||
tristique senectus et netus et malesuada fames ac turpis
|
||||
egestas. Vestibulum varius a ipsum ornare blandit.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="group mt-60">
|
||||
<h4 className="mt-8 fw-7 text-secondary title-anim mb-24">
|
||||
The Hidden Markov Model's Limitations
|
||||
</h4>
|
||||
<p className="cur-lg mb-24">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec
|
||||
nec tortor id erat faucibus tempor id eget turpis. Donec
|
||||
lobortis et neque eget congue. Mauris laoreet orci ac dictum
|
||||
interdum. Sed dapibus convallis arcu, a aliquam purus sodales
|
||||
nec.
|
||||
</p>
|
||||
<p className="cur-lg">
|
||||
Quisque et fringilla lacus, quis luctus elit. Curabitur eu dui
|
||||
mattis turpis commodo eleifend. Sed porta ornare nunc et
|
||||
tristique. Curabitur vel eros a ante cursus lacinia. Nam nisl
|
||||
leo, aliquet a placerat at, porttitor quis augue. Proin quis
|
||||
aliquet libero. Pellentesque habitant morbi tristique senectus
|
||||
et netus et malesuada fames ac turpis egestas. Vestibulum
|
||||
varius a ipsum ornare blandit.
|
||||
</p>
|
||||
</div>
|
||||
<div className="group mt-60">
|
||||
<h4 className="mt-8 fw-7 text-secondary title-anim mb-24">
|
||||
The Effect
|
||||
</h4>
|
||||
<p className="cur-lg mb-24">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec
|
||||
nec tortor id erat faucibus tempor id eget turpis. Donec
|
||||
lobortis et neque eget congue. Mauris laoreet orci ac dictum
|
||||
interdum. Sed dapibus convallis arcu, a aliquam purus sodales
|
||||
nec.
|
||||
</p>
|
||||
<p className="cur-lg">
|
||||
Quisque et fringilla lacus, quis luctus elit. Curabitur eu dui
|
||||
mattis turpis commodo eleifend. Sed porta ornare nunc et
|
||||
tristique. Curabitur vel eros a ante cursus lacinia. Nam nisl
|
||||
leo, aliquet a placerat at, porttitor quis augue. Proin quis
|
||||
aliquet libero. Pellentesque habitant morbi tristique senectus
|
||||
et netus et malesuada fames ac turpis egestas. Vestibulum
|
||||
varius a ipsum ornare blandit.
|
||||
</p>
|
||||
<p className="text-tertiary">Loading insight...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row mt-80">
|
||||
<div className="col-12">
|
||||
<div className="bd-social">
|
||||
<p className="fw-5 text-uppercase">Share :</p>
|
||||
<ul className=" social">
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.facebook.com/"
|
||||
target="_blank"
|
||||
aria-label="share us on facebook"
|
||||
>
|
||||
<i className="fa-brands fa-facebook-f"></i>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.twitter.com/"
|
||||
target="_blank"
|
||||
aria-label="share us on twitter"
|
||||
>
|
||||
<i className="fa-brands fa-twitter"></i>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.pinterest.com/"
|
||||
target="_blank"
|
||||
aria-label="share us on pinterest"
|
||||
>
|
||||
<i className="fa-brands fa-linkedin-in"></i>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.instagram.com/"
|
||||
target="_blank"
|
||||
aria-label="share us on instagram"
|
||||
>
|
||||
<i className="fa-brands fa-instagram"></i>
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
if (error || !post) {
|
||||
return (
|
||||
<section className="blog-single-section fix-top pb-120">
|
||||
<div className="container">
|
||||
<div className="row justify-content-center">
|
||||
<div className="col-12 col-lg-10 col-xl-8">
|
||||
<div className="error-state text-center py-5">
|
||||
<div className="error-icon mb-4">
|
||||
<i className="fa-solid fa-exclamation-circle fa-4x text-tertiary"></i>
|
||||
</div>
|
||||
<h2 className="text-secondary mb-3">Insight Not Found</h2>
|
||||
<p className="text-tertiary mb-4">
|
||||
The insight you're looking for doesn't exist or has been removed.
|
||||
</p>
|
||||
<Link href="/insights" className="btn btn-primary">
|
||||
<i className="fa-solid fa-arrow-left me-2"></i>
|
||||
Back to Insights
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="blog-single-section fix-top pb-120">
|
||||
<div className="container">
|
||||
{/* Article Content */}
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<article className="blog-single-article">
|
||||
{/* Article Header */}
|
||||
<header className="article-header">
|
||||
{/* Top Meta Bar */}
|
||||
<div className="article-top-meta d-flex flex-wrap align-items-center justify-content-between mb-4">
|
||||
<div className="left-meta d-flex align-items-center gap-3">
|
||||
{/* Category Badge */}
|
||||
{post.category && (
|
||||
<Link href={`/insights?category=${post.category.slug}`} className="category-badge">
|
||||
<i className="fa-solid fa-folder-open me-2"></i>
|
||||
{post.category.title}
|
||||
</Link>
|
||||
)}
|
||||
|
||||
{/* Date */}
|
||||
<div className="meta-item d-flex align-items-center">
|
||||
<i className="fa-solid fa-calendar me-2"></i>
|
||||
<span>
|
||||
{new Date(post.published_at || post.created_at).toLocaleDateString('en-US', {
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
year: 'numeric'
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="right-meta d-flex align-items-center gap-3">
|
||||
{/* Reading Time */}
|
||||
<div className="meta-item d-flex align-items-center">
|
||||
<i className="fa-solid fa-clock me-2"></i>
|
||||
<span>{post.reading_time} min</span>
|
||||
</div>
|
||||
|
||||
{/* Views */}
|
||||
<div className="meta-item d-flex align-items-center">
|
||||
<i className="fa-solid fa-eye me-2"></i>
|
||||
<span>{post.views_count}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Title */}
|
||||
<h1 className="article-title">
|
||||
{post.title}
|
||||
</h1>
|
||||
|
||||
{/* Author and Tags Bar */}
|
||||
<div className="article-bottom-meta d-flex flex-wrap align-items-center justify-content-between mt-4 pt-4">
|
||||
{/* Author */}
|
||||
<div className="author-meta d-flex align-items-center">
|
||||
<div className="author-avatar me-3">
|
||||
{post.author?.avatar ? (
|
||||
<Image
|
||||
src={post.author.avatar}
|
||||
alt={post.author.name}
|
||||
width={48}
|
||||
height={48}
|
||||
className="rounded-circle"
|
||||
/>
|
||||
) : (
|
||||
<div className="avatar-placeholder">
|
||||
<i className="fa-solid fa-user"></i>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="author-info">
|
||||
<div className="author-label">Written by</div>
|
||||
<div className="author-name">
|
||||
{post.author?.name || post.author_name || 'Admin'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Tags */}
|
||||
{post.tags && post.tags.length > 0 && (
|
||||
<div className="article-tags d-flex flex-wrap align-items-center gap-2">
|
||||
{post.tags.map((tag) => (
|
||||
<Link
|
||||
key={tag.id}
|
||||
href={`/insights?tag=${tag.slug}`}
|
||||
className="tag-badge"
|
||||
>
|
||||
#{tag.name}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Featured Image */}
|
||||
{(post.featured_image || post.thumbnail) && (
|
||||
<div className="article-featured-image">
|
||||
<div className="image-wrapper">
|
||||
<Image
|
||||
src={getValidImageUrl(
|
||||
post.featured_image || post.thumbnail,
|
||||
FALLBACK_IMAGES.BLOG
|
||||
)}
|
||||
alt={getValidImageAlt(post.title)}
|
||||
width={1200}
|
||||
height={600}
|
||||
layout="responsive"
|
||||
className="featured-image"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Article Body */}
|
||||
<div className="article-body">
|
||||
{/* Excerpt */}
|
||||
{post.excerpt && (
|
||||
<div className="article-excerpt">
|
||||
<p className="lead">{post.excerpt}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Content */}
|
||||
{post.content && (
|
||||
<div
|
||||
className="article-content enterprise-content"
|
||||
dangerouslySetInnerHTML={{ __html: post.content }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Footer Section */}
|
||||
<footer className="article-footer">
|
||||
{/* Share Section */}
|
||||
<div className="article-share">
|
||||
<div className="share-container">
|
||||
<h6 className="share-title">
|
||||
Share this insight
|
||||
</h6>
|
||||
<div className="social-share">
|
||||
<Link
|
||||
href={`https://www.linkedin.com/shareArticle?mini=true&url=${typeof window !== 'undefined' ? encodeURIComponent(window.location.href) : ''}&title=${encodeURIComponent(post.title)}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="share-btn share-linkedin"
|
||||
aria-label="Share on LinkedIn"
|
||||
>
|
||||
<i className="fa-brands fa-linkedin-in"></i>
|
||||
<span>LinkedIn</span>
|
||||
</Link>
|
||||
<Link
|
||||
href={`https://twitter.com/intent/tweet?url=${typeof window !== 'undefined' ? encodeURIComponent(window.location.href) : ''}&text=${encodeURIComponent(post.title)}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="share-btn share-twitter"
|
||||
aria-label="Share on Twitter"
|
||||
>
|
||||
<i className="fa-brands fa-twitter"></i>
|
||||
<span>Twitter</span>
|
||||
</Link>
|
||||
<Link
|
||||
href={`https://www.facebook.com/sharer/sharer.php?u=${typeof window !== 'undefined' ? encodeURIComponent(window.location.href) : ''}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="share-btn share-facebook"
|
||||
aria-label="Share on Facebook"
|
||||
>
|
||||
<i className="fa-brands fa-facebook-f"></i>
|
||||
<span>Facebook</span>
|
||||
</Link>
|
||||
<button
|
||||
onClick={() => {
|
||||
if (typeof window !== 'undefined' && navigator.clipboard) {
|
||||
navigator.clipboard.writeText(window.location.href);
|
||||
alert('Link copied to clipboard!');
|
||||
}
|
||||
}}
|
||||
className="share-btn share-copy"
|
||||
aria-label="Copy link"
|
||||
>
|
||||
<i className="fa-solid fa-link"></i>
|
||||
<span>Copy</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Author Bio */}
|
||||
{post.author && post.author.bio && (
|
||||
<div className="author-bio-section">
|
||||
<div className="author-bio-card">
|
||||
<div className="author-avatar-container">
|
||||
{post.author.avatar ? (
|
||||
<Image
|
||||
src={post.author.avatar}
|
||||
alt={post.author.name}
|
||||
width={90}
|
||||
height={90}
|
||||
className="rounded-circle"
|
||||
/>
|
||||
) : (
|
||||
<div className="author-avatar-large">
|
||||
<i className="fa-solid fa-user"></i>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="author-bio-content">
|
||||
<div className="bio-label">About the Author</div>
|
||||
<h6 className="author-name">{post.author.name}</h6>
|
||||
<p className="author-bio-text">{post.author.bio}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Navigation */}
|
||||
<div className="article-navigation">
|
||||
<Link href="/insights" className="btn-back-insights">
|
||||
<i className="fa-solid fa-arrow-left me-2"></i>
|
||||
Back to All Insights
|
||||
</Link>
|
||||
</div>
|
||||
</footer>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
||||
@@ -4,12 +4,30 @@ import Link from "next/link";
|
||||
import { Swiper, SwiperSlide } from "swiper/react";
|
||||
import { Autoplay } from "swiper/modules";
|
||||
import "swiper/swiper-bundle.css";
|
||||
import one from "@/public/images/blog/related-one.png";
|
||||
import two from "@/public/images/blog/related-two.png";
|
||||
import three from "@/public/images/blog/related-three.png";
|
||||
import four from "@/public/images/blog/related-four.png";
|
||||
import { useLatestPosts } from "@/lib/hooks/useBlog";
|
||||
import { getValidImageUrl, getValidImageAlt, FALLBACK_IMAGES } from "@/lib/imageUtils";
|
||||
|
||||
const LatestPost = () => {
|
||||
const { posts, loading, error } = useLatestPosts(8);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<section className="tp-latest-post pt-120 pb-120 bg-quinary">
|
||||
<div className="container">
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<p className="text-center">Loading latest posts...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
if (error || posts.length === 0) {
|
||||
return null; // Don't show the section if there's an error or no posts
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="tp-latest-post pt-120 pb-120 bg-quinary">
|
||||
<div className="container">
|
||||
@@ -17,14 +35,14 @@ const LatestPost = () => {
|
||||
<div className="col-12 col-lg-7">
|
||||
<div className="tp-lp-title text-center text-lg-start">
|
||||
<h2 className="mt-8 fw-7 text-secondary title-anim">
|
||||
Related posts
|
||||
Related Insights
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 col-lg-5">
|
||||
<div className="tp-lp-cta text-center text-lg-end">
|
||||
<Link href="blog" className="btn-line text-uppercase">
|
||||
See All Posts
|
||||
<Link href="/insights" className="btn-line text-uppercase">
|
||||
View All Insights
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
@@ -39,7 +57,7 @@ const LatestPost = () => {
|
||||
slidesPerGroup={1}
|
||||
freeMode={true}
|
||||
speed={1200}
|
||||
loop={true}
|
||||
loop={posts.length > 3}
|
||||
roundLengths={true}
|
||||
modules={[Autoplay]}
|
||||
autoplay={{
|
||||
@@ -49,378 +67,43 @@ const LatestPost = () => {
|
||||
}}
|
||||
className="tp-lp-slider"
|
||||
>
|
||||
<SwiperSlide>
|
||||
<div className="tp-lp-slider__single topy-tilt">
|
||||
<div className="thumb mb-24">
|
||||
<Link href="blog-single" className="w-100 overflow-hidden">
|
||||
<Image
|
||||
src={one}
|
||||
width={400}
|
||||
height={220}
|
||||
className="w-100 mh-220"
|
||||
alt="Image"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="tp-lp-post__meta mb-24 mt-8">
|
||||
<p className="author text-xs text-tertiary">
|
||||
Denial Lio
|
||||
</p>
|
||||
<span></span>
|
||||
<p className="date text-xs text-tertiary">
|
||||
18 Dec 2022
|
||||
</p>
|
||||
</div>
|
||||
<h5 className="mt-8 fw-5 text-secondary">
|
||||
<Link href="blog-single">
|
||||
Tackling data of annotation problems in healthcare
|
||||
{posts.map((post) => (
|
||||
<SwiperSlide key={post.id}>
|
||||
<div className="tp-lp-slider__single topy-tilt">
|
||||
<div className="thumb mb-24">
|
||||
<Link href={`/insights/${post.slug}`} className="w-100 overflow-hidden">
|
||||
<Image
|
||||
src={getValidImageUrl(post.thumbnail, FALLBACK_IMAGES.BLOG)}
|
||||
width={400}
|
||||
height={220}
|
||||
className="w-100 mh-220"
|
||||
alt={getValidImageAlt(post.title)}
|
||||
/>
|
||||
</Link>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="tp-lp-slider__single topy-tilt">
|
||||
<div className="thumb mb-24">
|
||||
<Link href="blog-single" className="w-100 overflow-hidden">
|
||||
<Image
|
||||
src={two}
|
||||
width={400}
|
||||
height={220}
|
||||
className="w-100 mh-220"
|
||||
alt="Image"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="tp-lp-post__meta mb-24 mt-8">
|
||||
<p className="author text-xs text-tertiary">
|
||||
Denial Lio
|
||||
</p>
|
||||
<span></span>
|
||||
<p className="date text-xs text-tertiary">
|
||||
18 Dec 2022
|
||||
</p>
|
||||
</div>
|
||||
<h5 className="mt-8 fw-5 text-secondary">
|
||||
<Link href="blog-single">
|
||||
Tackling data of annotation problems in healthcare
|
||||
</Link>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="tp-lp-slider__single topy-tilt">
|
||||
<div className="thumb mb-24">
|
||||
<Link href="blog-single" className="w-100 overflow-hidden">
|
||||
<Image
|
||||
src={three}
|
||||
width={400}
|
||||
height={220}
|
||||
className="w-100 mh-220"
|
||||
alt="Image"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="tp-lp-post__meta mb-24 mt-8">
|
||||
<p className="author text-xs text-tertiary">
|
||||
Denial Lio
|
||||
</p>
|
||||
<span></span>
|
||||
<p className="date text-xs text-tertiary">
|
||||
18 Dec 2022
|
||||
</p>
|
||||
<div className="content">
|
||||
<div className="tp-lp-post__meta mb-24 mt-8">
|
||||
<p className="author text-xs text-tertiary">
|
||||
{post.author_name || 'Admin'}
|
||||
</p>
|
||||
<span></span>
|
||||
<p className="date text-xs text-tertiary">
|
||||
{new Date(post.published_at || post.created_at).toLocaleDateString('en-US', {
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
year: 'numeric'
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
<h5 className="mt-8 fw-5 text-secondary">
|
||||
<Link href={`/insights/${post.slug}`}>
|
||||
{post.title}
|
||||
</Link>
|
||||
</h5>
|
||||
</div>
|
||||
<h5 className="mt-8 fw-5 text-secondary">
|
||||
<Link href="blog-single">
|
||||
Tackling data of annotation problems in healthcare
|
||||
</Link>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="tp-lp-slider__single topy-tilt">
|
||||
<div className="thumb mb-24">
|
||||
<Link href="blog-single" className="w-100 overflow-hidden">
|
||||
<Image
|
||||
src={four}
|
||||
width={400}
|
||||
height={220}
|
||||
className="w-100 mh-220"
|
||||
alt="Image"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="tp-lp-post__meta mb-24 mt-8">
|
||||
<p className="author text-xs text-tertiary">
|
||||
Denial Lio
|
||||
</p>
|
||||
<span></span>
|
||||
<p className="date text-xs text-tertiary">
|
||||
18 Dec 2022
|
||||
</p>
|
||||
</div>
|
||||
<h5 className="mt-8 fw-5 text-secondary">
|
||||
<Link href="blog-single">
|
||||
Tackling data of annotation problems in healthcare
|
||||
</Link>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="tp-lp-slider__single topy-tilt">
|
||||
<div className="thumb mb-24">
|
||||
<Link href="blog-single" className="w-100 overflow-hidden">
|
||||
<Image
|
||||
src={one}
|
||||
width={400}
|
||||
height={220}
|
||||
className="w-100 mh-220"
|
||||
alt="Image"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="tp-lp-post__meta mb-24 mt-8">
|
||||
<p className="author text-xs text-tertiary">
|
||||
Denial Lio
|
||||
</p>
|
||||
<span></span>
|
||||
<p className="date text-xs text-tertiary">
|
||||
18 Dec 2022
|
||||
</p>
|
||||
</div>
|
||||
<h5 className="mt-8 fw-5 text-secondary">
|
||||
<Link href="blog-single">
|
||||
Tackling data of annotation problems in healthcare
|
||||
</Link>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="tp-lp-slider__single topy-tilt">
|
||||
<div className="thumb mb-24">
|
||||
<Link href="blog-single" className="w-100 overflow-hidden">
|
||||
<Image
|
||||
src={two}
|
||||
width={400}
|
||||
height={220}
|
||||
className="w-100 mh-220"
|
||||
alt="Image"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="tp-lp-post__meta mb-24 mt-8">
|
||||
<p className="author text-xs text-tertiary">
|
||||
Denial Lio
|
||||
</p>
|
||||
<span></span>
|
||||
<p className="date text-xs text-tertiary">
|
||||
18 Dec 2022
|
||||
</p>
|
||||
</div>
|
||||
<h5 className="mt-8 fw-5 text-secondary">
|
||||
<Link href="blog-single">
|
||||
Tackling data of annotation problems in healthcare
|
||||
</Link>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="tp-lp-slider__single topy-tilt">
|
||||
<div className="thumb mb-24">
|
||||
<Link href="blog-single" className="w-100 overflow-hidden">
|
||||
<Image
|
||||
src={three}
|
||||
width={400}
|
||||
height={220}
|
||||
className="w-100 mh-220"
|
||||
alt="Image"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="tp-lp-post__meta mb-24 mt-8">
|
||||
<p className="author text-xs text-tertiary">
|
||||
Denial Lio
|
||||
</p>
|
||||
<span></span>
|
||||
<p className="date text-xs text-tertiary">
|
||||
18 Dec 2022
|
||||
</p>
|
||||
</div>
|
||||
<h5 className="mt-8 fw-5 text-secondary">
|
||||
<Link href="blog-single">
|
||||
Tackling data of annotation problems in healthcare
|
||||
</Link>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="tp-lp-slider__single topy-tilt">
|
||||
<div className="thumb mb-24">
|
||||
<Link href="blog-single" className="w-100 overflow-hidden">
|
||||
<Image
|
||||
src={four}
|
||||
width={400}
|
||||
height={220}
|
||||
className="w-100 mh-220"
|
||||
alt="Image"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="tp-lp-post__meta mb-24 mt-8">
|
||||
<p className="author text-xs text-tertiary">
|
||||
Denial Lio
|
||||
</p>
|
||||
<span></span>
|
||||
<p className="date text-xs text-tertiary">
|
||||
18 Dec 2022
|
||||
</p>
|
||||
</div>
|
||||
<h5 className="mt-8 fw-5 text-secondary">
|
||||
<Link href="blog-single">
|
||||
Tackling data of annotation problems in healthcare
|
||||
</Link>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="tp-lp-slider__single topy-tilt">
|
||||
<div className="thumb mb-24">
|
||||
<Link href="blog-single" className="w-100 overflow-hidden">
|
||||
<Image
|
||||
src={one}
|
||||
width={400}
|
||||
height={220}
|
||||
className="w-100 mh-220"
|
||||
alt="Image"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="tp-lp-post__meta mb-24 mt-8">
|
||||
<p className="author text-xs text-tertiary">
|
||||
Denial Lio
|
||||
</p>
|
||||
<span></span>
|
||||
<p className="date text-xs text-tertiary">
|
||||
18 Dec 2022
|
||||
</p>
|
||||
</div>
|
||||
<h5 className="mt-8 fw-5 text-secondary">
|
||||
<Link href="blog-single">
|
||||
Tackling data of annotation problems in healthcare
|
||||
</Link>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="tp-lp-slider__single topy-tilt">
|
||||
<div className="thumb mb-24">
|
||||
<Link href="blog-single" className="w-100 overflow-hidden">
|
||||
<Image
|
||||
src={two}
|
||||
width={400}
|
||||
height={220}
|
||||
className="w-100 mh-220"
|
||||
alt="Image"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="tp-lp-post__meta mb-24 mt-8">
|
||||
<p className="author text-xs text-tertiary">
|
||||
Denial Lio
|
||||
</p>
|
||||
<span></span>
|
||||
<p className="date text-xs text-tertiary">
|
||||
18 Dec 2022
|
||||
</p>
|
||||
</div>
|
||||
<h5 className="mt-8 fw-5 text-secondary">
|
||||
<Link href="blog-single">
|
||||
Tackling data of annotation problems in healthcare
|
||||
</Link>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="tp-lp-slider__single topy-tilt">
|
||||
<div className="thumb mb-24">
|
||||
<Link href="blog-single" className="w-100 overflow-hidden">
|
||||
<Image
|
||||
src={three}
|
||||
width={400}
|
||||
height={220}
|
||||
className="w-100 mh-220"
|
||||
alt="Image"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="tp-lp-post__meta mb-24 mt-8">
|
||||
<p className="author text-xs text-tertiary">
|
||||
Denial Lio
|
||||
</p>
|
||||
<span></span>
|
||||
<p className="date text-xs text-tertiary">
|
||||
18 Dec 2022
|
||||
</p>
|
||||
</div>
|
||||
<h5 className="mt-8 fw-5 text-secondary">
|
||||
<Link href="blog-single">
|
||||
Tackling data of annotation problems in healthcare
|
||||
</Link>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="tp-lp-slider__single topy-tilt">
|
||||
<div className="thumb mb-24">
|
||||
<Link href="blog-single" className="w-100 overflow-hidden">
|
||||
<Image
|
||||
src={four}
|
||||
width={400}
|
||||
height={220}
|
||||
className="w-100 mh-220"
|
||||
alt="Image"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="tp-lp-post__meta mb-24 mt-8">
|
||||
<p className="author text-xs text-tertiary">
|
||||
Denial Lio
|
||||
</p>
|
||||
<span></span>
|
||||
<p className="date text-xs text-tertiary">
|
||||
18 Dec 2022
|
||||
</p>
|
||||
</div>
|
||||
<h5 className="mt-8 fw-5 text-secondary">
|
||||
<Link href="blog-single">
|
||||
Tackling data of annotation problems in healthcare
|
||||
</Link>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
</SwiperSlide>
|
||||
))}
|
||||
</Swiper>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,23 +1,54 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { BlogCategoryButtons } from "@/public/data/blog-category";
|
||||
import { useBlogCategories } from "@/lib/hooks/useBlog";
|
||||
|
||||
const PostFilterButtons = ({ handleClick, active }: any) => {
|
||||
const [categories, setCategories] = useState(BlogCategoryButtons);
|
||||
const { categories: apiCategories, loading, error } = useBlogCategories();
|
||||
const [categories, setCategories] = useState<any[]>([]);
|
||||
|
||||
// TODO: Replace with API call to get blog categories
|
||||
// useEffect(() => {
|
||||
// const fetchCategories = async () => {
|
||||
// try {
|
||||
// const response = await blogCategoryAPI.getAll();
|
||||
// if (response.success) {
|
||||
// setCategories(response.data);
|
||||
// }
|
||||
// } catch (error) {
|
||||
// console.error('Error fetching categories:', error);
|
||||
// }
|
||||
// };
|
||||
// fetchCategories();
|
||||
// }, []);
|
||||
useEffect(() => {
|
||||
if (!loading && apiCategories.length > 0) {
|
||||
// Add "All" category at the beginning
|
||||
const allCategory = {
|
||||
id: 0,
|
||||
title: "All",
|
||||
slug: "all",
|
||||
display_order: 0
|
||||
};
|
||||
setCategories([allCategory, ...apiCategories]);
|
||||
}
|
||||
}, [apiCategories, loading]);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<div className="post-filter__wrapper mt-80">
|
||||
<p>Loading categories...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
console.error('Error loading categories:', error);
|
||||
// Fallback to showing "All" button only
|
||||
return (
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<div className="post-filter__wrapper mt-80">
|
||||
<button
|
||||
aria-label="Filter Post"
|
||||
className="active"
|
||||
onClick={() => handleClick("all")}
|
||||
>
|
||||
All
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="row">
|
||||
|
||||
@@ -5,102 +5,81 @@ import Link from "next/link";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { getValidImageUrl, getValidImageAlt, FALLBACK_IMAGES } from "@/lib/imageUtils";
|
||||
import PostFilterButtons from "./PostFilterButtons";
|
||||
import { useBlogPosts } from "@/lib/hooks/useBlog";
|
||||
|
||||
const PostFilterItems = () => {
|
||||
interface PostFilterItemsProps {
|
||||
currentPage: number;
|
||||
onPageChange: (page: number) => void;
|
||||
onTotalPagesChange: (totalPages: number) => void;
|
||||
postsPerPage?: number;
|
||||
}
|
||||
|
||||
const PostFilterItems = ({ currentPage, onPageChange, onTotalPagesChange, postsPerPage = 10 }: PostFilterItemsProps) => {
|
||||
const [active, setActive] = useState("all");
|
||||
|
||||
// Static blog posts data
|
||||
const allPosts = [
|
||||
{
|
||||
id: 1,
|
||||
title: "Enterprise Software Development Best Practices",
|
||||
content: "Learn about the latest best practices in enterprise software development...",
|
||||
slug: "enterprise-software-development-best-practices",
|
||||
author_id: 1,
|
||||
category_id: 1,
|
||||
thumbnail: "/images/blog/one.png",
|
||||
published: true,
|
||||
category_title: "Development",
|
||||
category_slug: "development",
|
||||
author_name: "John Smith",
|
||||
created_at: "2024-01-15T10:00:00Z",
|
||||
updated_at: "2024-01-15T10:00:00Z"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "API Integration Strategies for Modern Enterprises",
|
||||
content: "Discover effective strategies for API integration in enterprise environments...",
|
||||
slug: "api-integration-strategies-modern-enterprises",
|
||||
author_id: 1,
|
||||
category_id: 2,
|
||||
thumbnail: "/images/blog/two.png",
|
||||
published: true,
|
||||
category_title: "Integration",
|
||||
category_slug: "integration",
|
||||
author_name: "Jane Doe",
|
||||
created_at: "2024-01-10T14:30:00Z",
|
||||
updated_at: "2024-01-10T14:30:00Z"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "Cloud Migration: A Complete Guide",
|
||||
content: "Everything you need to know about migrating your enterprise to the cloud...",
|
||||
slug: "cloud-migration-complete-guide",
|
||||
author_id: 1,
|
||||
category_id: 3,
|
||||
thumbnail: "/images/blog/three.png",
|
||||
published: true,
|
||||
category_title: "Cloud",
|
||||
category_slug: "cloud",
|
||||
author_name: "Mike Johnson",
|
||||
created_at: "2024-01-05T09:15:00Z",
|
||||
updated_at: "2024-01-05T09:15:00Z"
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: "Digital Transformation in Enterprise",
|
||||
content: "How digital transformation is reshaping enterprise operations...",
|
||||
slug: "digital-transformation-enterprise",
|
||||
author_id: 1,
|
||||
category_id: 1,
|
||||
thumbnail: "/images/blog/four.png",
|
||||
published: true,
|
||||
category_title: "Development",
|
||||
category_slug: "development",
|
||||
author_name: "Sarah Wilson",
|
||||
created_at: "2024-01-01T16:45:00Z",
|
||||
updated_at: "2024-01-01T16:45:00Z"
|
||||
const { posts: allPosts, loading, error, totalCount } = useBlogPosts({
|
||||
category: active === "all" ? undefined : active,
|
||||
page: currentPage,
|
||||
page_size: postsPerPage
|
||||
});
|
||||
const [displayData, setDisplayData] = useState<any[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading && allPosts.length > 0) {
|
||||
setDisplayData(allPosts);
|
||||
}
|
||||
];
|
||||
|
||||
const [displayData, setDisplayData] = useState(allPosts);
|
||||
}, [allPosts, loading]);
|
||||
|
||||
// Calculate and update total pages when totalCount changes
|
||||
useEffect(() => {
|
||||
if (totalCount !== undefined && totalCount !== null) {
|
||||
const calculatedTotalPages = Math.ceil(totalCount / postsPerPage);
|
||||
onTotalPagesChange(calculatedTotalPages);
|
||||
}
|
||||
}, [totalCount, postsPerPage, onTotalPagesChange]);
|
||||
|
||||
const handleCategoryClick = (category: SetStateAction<string>) => {
|
||||
if (category === active) return;
|
||||
setActive(category);
|
||||
setDisplayData([]);
|
||||
onPageChange(1); // Reset to page 1 when category changes
|
||||
|
||||
if (category === "all") {
|
||||
setDisplayData(allPosts);
|
||||
return;
|
||||
}
|
||||
|
||||
const filteredData = allPosts.filter(
|
||||
(item) => item.category_slug === category
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
setDisplayData(filteredData);
|
||||
}, 600);
|
||||
// The API call will be triggered by the change in active state
|
||||
// which will update allPosts and trigger the useEffect above
|
||||
};
|
||||
|
||||
if (loading && displayData.length === 0) {
|
||||
return (
|
||||
<>
|
||||
<PostFilterButtons active={active} handleClick={handleCategoryClick} />
|
||||
<div className="row mt-60">
|
||||
<div className="col-12">
|
||||
<p className="text-center">Loading posts...</p>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
console.error('Error loading posts:', error);
|
||||
return (
|
||||
<>
|
||||
<PostFilterButtons active={active} handleClick={handleCategoryClick} />
|
||||
<div className="row mt-60">
|
||||
<div className="col-12">
|
||||
<p className="text-center text-danger">Error loading posts. Please try again later.</p>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<PostFilterButtons active={active} handleClick={handleCategoryClick} />
|
||||
<motion.div className="row mt-60 masonry-grid" layout>
|
||||
<AnimatePresence>
|
||||
{displayData.slice(0, 8).map((item) => {
|
||||
{displayData.map((item) => {
|
||||
return (
|
||||
<motion.div
|
||||
className="col-12 col-lg-6 grid-item-main"
|
||||
@@ -114,7 +93,7 @@ const PostFilterItems = () => {
|
||||
<div className="tp-lp-slider__single topy-tilt">
|
||||
<div className="thumb mb-24">
|
||||
<Link
|
||||
href={`/blog/${item.slug}`}
|
||||
href={`/insights/${item.slug}`}
|
||||
className="w-100 overflow-hidden d-block"
|
||||
>
|
||||
<div className="parallax-image-wrap">
|
||||
@@ -137,7 +116,7 @@ const PostFilterItems = () => {
|
||||
</p>
|
||||
<span></span>
|
||||
<p className="date text-xs text-tertiary">
|
||||
{new Date(item.created_at).toLocaleDateString('en-US', {
|
||||
{new Date(item.published_at || item.created_at).toLocaleDateString('en-US', {
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
year: 'numeric'
|
||||
@@ -145,7 +124,7 @@ const PostFilterItems = () => {
|
||||
</p>
|
||||
</div>
|
||||
<h5 className="mt-8 fw-5 text-secondary">
|
||||
<Link href={`/blog/${item.slug}`}>{item.title}</Link>
|
||||
<Link href={`/insights/${item.slug}`}>{item.title}</Link>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,26 +1,37 @@
|
||||
"use client";
|
||||
import { useState } from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import Image from "next/legacy/image";
|
||||
import Link from "next/link";
|
||||
import { useCaseStudies, useClients } from "@/lib/hooks/useCaseStudy";
|
||||
import { getImageUrl } from "@/lib/imageUtils";
|
||||
import one from "@/public/images/case/one.png";
|
||||
import two from "@/public/images/case/two.png";
|
||||
import three from "@/public/images/case/three.png";
|
||||
import four from "@/public/images/case/four.png";
|
||||
import five from "@/public/images/case/five.png";
|
||||
import six from "@/public/images/case/six.png";
|
||||
import seven from "@/public/images/case/seven.png";
|
||||
import eight from "@/public/images/case/eight.png";
|
||||
import thirteen from "@/public/images/case/thirteen.png";
|
||||
import fourteen from "@/public/images/case/fourteen.png";
|
||||
import fifteen from "@/public/images/case/fifteen.png";
|
||||
import sixteen from "@/public/images/case/sixteen.png";
|
||||
|
||||
const CaseItems = () => {
|
||||
const [activeTabIndex, setActiveTabIndex] = useState(0);
|
||||
const { caseStudies, loading: casesLoading } = useCaseStudies();
|
||||
const { clients, loading: clientsLoading } = useClients();
|
||||
|
||||
const handleTabClick = (index: number) => {
|
||||
setActiveTabIndex(index);
|
||||
};
|
||||
|
||||
// Filter case studies by category
|
||||
const caseStudiesData = caseStudies.filter((cs) => !cs.client);
|
||||
const clientCaseStudies = caseStudies.filter((cs) => cs.client);
|
||||
|
||||
if (casesLoading || clientsLoading) {
|
||||
return (
|
||||
<section className="fix-top pb-120 c-study">
|
||||
<div className="container">
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<p className="text-center">Loading case studies...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="fix-top pb-120 c-study">
|
||||
@@ -79,194 +90,38 @@ const CaseItems = () => {
|
||||
}`}
|
||||
>
|
||||
<div className="row vertical-column-gap-lg">
|
||||
<div className="col-12 col-lg-6">
|
||||
<div className="c-study-single">
|
||||
<div className="thumb mb-24">
|
||||
<Link href="case-study-single" className="w-100">
|
||||
<Image
|
||||
src={two}
|
||||
className="w-100 mh-300 "
|
||||
alt="Image"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<Link href="case-study" className="mb-30 fw-6">
|
||||
3D Render
|
||||
</Link>
|
||||
<h4 className="fw-6 mt-8 text-secondary">
|
||||
<Link href="case-study-single">
|
||||
3D computer graphics, or “3D graphics.
|
||||
{caseStudiesData.map((caseStudy) => (
|
||||
<div key={caseStudy.id} className="col-12 col-lg-6">
|
||||
<div className="c-study-single">
|
||||
<div className="thumb mb-24">
|
||||
<Link href={`/case-study/${caseStudy.slug}`} className="w-100">
|
||||
<Image
|
||||
src={caseStudy.thumbnail ? getImageUrl(caseStudy.thumbnail) : one}
|
||||
className="w-100 mh-300"
|
||||
alt={caseStudy.title}
|
||||
width={600}
|
||||
height={400}
|
||||
/>
|
||||
</Link>
|
||||
</h4>
|
||||
</div>
|
||||
<div className="content">
|
||||
<Link href={`/case-study/${caseStudy.slug}`} className="mb-30 fw-6">
|
||||
{caseStudy.category_name || 'Case Study'}
|
||||
</Link>
|
||||
<h4 className="fw-6 mt-8 text-secondary">
|
||||
<Link href={`/case-study/${caseStudy.slug}`}>
|
||||
{caseStudy.title}
|
||||
</Link>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 col-lg-6">
|
||||
<div className="c-study-single">
|
||||
<div className="thumb mb-24">
|
||||
<Link href="case-study-single" className="w-100">
|
||||
<Image
|
||||
src={one}
|
||||
className="w-100 mh-300 "
|
||||
alt="Image"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<Link href="case-study" className="mb-30 fw-6">
|
||||
3D Render
|
||||
</Link>
|
||||
<h4 className="fw-6 mt-8 text-secondary">
|
||||
<Link href="case-study-single">
|
||||
Artificial intelligence is the simulation of human
|
||||
processes.
|
||||
</Link>
|
||||
</h4>
|
||||
</div>
|
||||
))}
|
||||
{caseStudiesData.length === 0 && (
|
||||
<div className="col-12">
|
||||
<p className="text-center">No case studies found.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 col-lg-6">
|
||||
<div className="c-study-single">
|
||||
<div className="thumb mb-24">
|
||||
<Link href="case-study-single" className="w-100">
|
||||
<Image
|
||||
src={three}
|
||||
className="w-100 mh-300 "
|
||||
alt="Image"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<Link href="case-study" className="mb-30 fw-6">
|
||||
UI / UX
|
||||
</Link>
|
||||
<h4 className="fw-6 mt-8 text-secondary">
|
||||
<Link href="case-study-single">
|
||||
User experience (UX) design is the process design
|
||||
teams.
|
||||
</Link>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 col-lg-6">
|
||||
<div className="c-study-single">
|
||||
<div className="thumb mb-24">
|
||||
<Link href="case-study-single" className="w-100">
|
||||
<Image
|
||||
src={four}
|
||||
className="w-100 mh-300 "
|
||||
alt="Image"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<Link href="case-study" className="mb-30 fw-6">
|
||||
Photography
|
||||
</Link>
|
||||
<h4 className="fw-6 mt-8 text-secondary">
|
||||
<Link href="case-study-single">
|
||||
Photography is the art, application, and practice.
|
||||
</Link>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 col-lg-6">
|
||||
<div className="c-study-single">
|
||||
<div className="thumb mb-24">
|
||||
<Link href="case-study-single" className="w-100">
|
||||
<Image
|
||||
src={five}
|
||||
className="w-100 mh-300 "
|
||||
alt="Image"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<Link href="case-study" className="mb-30 fw-6">
|
||||
UI / UX
|
||||
</Link>
|
||||
<h4 className="fw-6 mt-8 text-secondary">
|
||||
<Link href="case-study-single">
|
||||
UX case study for a medical app- medical product
|
||||
design.
|
||||
</Link>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 col-lg-6">
|
||||
<div className="c-study-single">
|
||||
<div className="thumb mb-24">
|
||||
<Link href="case-study-single" className="w-100">
|
||||
<Image
|
||||
src={six}
|
||||
className="w-100 mh-300 "
|
||||
alt="Image"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<Link href="case-study" className="mb-30 fw-6">
|
||||
Icon Set
|
||||
</Link>
|
||||
<h4 className="fw-6 mt-8 text-secondary">
|
||||
<Link href="case-study-single">
|
||||
Make icon set for the educational project.
|
||||
</Link>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 col-lg-6">
|
||||
<div className="c-study-single">
|
||||
<div className="thumb mb-24">
|
||||
<Link href="case-study-single" className="w-100">
|
||||
<Image
|
||||
src={seven}
|
||||
className="w-100 mh-300 "
|
||||
alt="Image"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<Link href="case-study" className="mb-30 fw-6">
|
||||
AI
|
||||
</Link>
|
||||
<h4 className="fw-6 mt-8 text-secondary">
|
||||
<Link href="case-study-single">
|
||||
User experience (UX) design is the process design
|
||||
teams.
|
||||
</Link>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 col-lg-6">
|
||||
<div className="c-study-single">
|
||||
<div className="thumb mb-24">
|
||||
<Link href="case-study-single" className="w-100">
|
||||
<Image
|
||||
src={eight}
|
||||
className="w-100 mh-300 "
|
||||
alt="Image"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<Link href="case-study" className="mb-30 fw-6">
|
||||
Road Map
|
||||
</Link>
|
||||
<h4 className="fw-6 mt-8 text-secondary">
|
||||
<Link href="case-study-single">
|
||||
UX site rode map app product design system
|
||||
</Link>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@@ -275,154 +130,48 @@ const CaseItems = () => {
|
||||
}`}
|
||||
>
|
||||
<div className="row vertical-column-gap-lg">
|
||||
<div className="col-12">
|
||||
<div className="row vertical-column-gap-md align-items-center">
|
||||
<div className="col-12 col-lg-6">
|
||||
<div className="c-tab__client">
|
||||
<h2 className="mt-8 fw-7 title-anim text-secondary mb-24">
|
||||
Tarapio
|
||||
</h2>
|
||||
<p className="cur-lg">
|
||||
Lorem ipsum dolor sit amet consectetur. Pharetra sit
|
||||
amet est tellus nibh sit lacus in duis. Condimentum
|
||||
tellus sit pharetra consectetur magna massa. In odio
|
||||
leo pellentesque aenean egestas est risus etiam.
|
||||
Quam in nunc consectetur blandit id.
|
||||
</p>
|
||||
<div className="mt-40">
|
||||
<Link
|
||||
href="case-study-single"
|
||||
className="btn-anim btn-anim-light"
|
||||
>
|
||||
Read More
|
||||
<i className="fa-solid fa-arrow-trend-up"></i>
|
||||
<span></span>
|
||||
</Link>
|
||||
{clientCaseStudies.map((caseStudy, index) => (
|
||||
<div key={caseStudy.id} className="col-12">
|
||||
<div className={`row vertical-column-gap-md align-items-center ${index % 2 === 1 ? 'flex-row-reverse' : ''}`}>
|
||||
<div className="col-12 col-lg-6">
|
||||
<div className="c-tab__client">
|
||||
<h2 className="mt-8 fw-7 title-anim text-secondary mb-24">
|
||||
{caseStudy.client?.name || caseStudy.title}
|
||||
</h2>
|
||||
<p className="cur-lg">
|
||||
{caseStudy.excerpt || caseStudy.client?.description}
|
||||
</p>
|
||||
<div className="mt-40">
|
||||
<Link
|
||||
href={`/case-study/${caseStudy.slug}`}
|
||||
className="btn-anim btn-anim-light"
|
||||
>
|
||||
Read More
|
||||
<i className="fa-solid fa-arrow-trend-up"></i>
|
||||
<span></span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 col-lg-6 col-xxl-5 offset-xxl-1">
|
||||
<div className="c-tab__thumb">
|
||||
<Image
|
||||
src={thirteen}
|
||||
className="w-100 mh-300"
|
||||
alt="Image"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12">
|
||||
<div className="row vertical-column-gap-md align-items-center">
|
||||
<div className="col-12 col-lg-6 col-xxl-5">
|
||||
<div className="c-tab__thumb">
|
||||
<Image
|
||||
src={fourteen}
|
||||
className="w-100 mh-300"
|
||||
alt="Image"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 col-lg-6 offset-xxl-1">
|
||||
<div className="c-tab__client">
|
||||
<h2 className="mt-8 fw-7 title-anim text-secondary mb-24">
|
||||
Melenpo
|
||||
</h2>
|
||||
<p className="cur-lg">
|
||||
Lorem ipsum dolor sit amet consectetur. Pharetra sit
|
||||
amet est tellus nibh sit lacus in duis. Condimentum
|
||||
tellus sit pharetra consectetur magna massa. In odio
|
||||
leo pellentesque aenean egestas est risus etiam.
|
||||
Quam in nunc consectetur blandit id.
|
||||
</p>
|
||||
<div className="mt-40">
|
||||
<Link
|
||||
href="case-study-single"
|
||||
className="btn-anim btn-anim-light"
|
||||
>
|
||||
Read More
|
||||
<i className="fa-solid fa-arrow-trend-up"></i>
|
||||
<span></span>
|
||||
</Link>
|
||||
<div className={`col-12 col-lg-6 col-xxl-5 ${index % 2 === 0 ? 'offset-xxl-1' : ''}`}>
|
||||
<div className="c-tab__thumb">
|
||||
<Image
|
||||
src={caseStudy.thumbnail ? getImageUrl(caseStudy.thumbnail) : one}
|
||||
className="w-100 mh-300"
|
||||
alt={caseStudy.client?.name || caseStudy.title}
|
||||
width={600}
|
||||
height={400}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12">
|
||||
<div className="row vertical-column-gap-md align-items-center">
|
||||
<div className="col-12 col-lg-6">
|
||||
<div className="c-tab__client">
|
||||
<h2 className="mt-8 fw-7 title-anim text-secondary mb-24">
|
||||
Polax
|
||||
</h2>
|
||||
<p className="cur-lg">
|
||||
Lorem ipsum dolor sit amet consectetur. Pharetra sit
|
||||
amet est tellus nibh sit lacus in duis. Condimentum
|
||||
tellus sit pharetra consectetur magna massa. In odio
|
||||
leo pellentesque aenean egestas est risus etiam.
|
||||
Quam in nunc consectetur blandit id.
|
||||
</p>
|
||||
<div className="mt-40">
|
||||
<Link
|
||||
href="case-study-single"
|
||||
className="btn-anim btn-anim-light"
|
||||
>
|
||||
Read More
|
||||
<i className="fa-solid fa-arrow-trend-up"></i>
|
||||
<span></span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 col-lg-6 col-xxl-5 offset-xxl-1">
|
||||
<div className="c-tab__thumb">
|
||||
<Image
|
||||
src={fifteen}
|
||||
className="w-100 mh-300"
|
||||
alt="Image"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{clientCaseStudies.length === 0 && (
|
||||
<div className="col-12">
|
||||
<p className="text-center">No client case studies found.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12">
|
||||
<div className="row vertical-column-gap-md align-items-center">
|
||||
<div className="col-12 col-lg-6 col-xxl-5">
|
||||
<div className="c-tab__thumb">
|
||||
<Image
|
||||
src={sixteen}
|
||||
className="w-100 mh-300"
|
||||
alt="Image"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 col-lg-6 offset-xxl-1">
|
||||
<div className="c-tab__client">
|
||||
<h2 className="mt-8 fw-7 title-anim text-secondary mb-24">
|
||||
AINA
|
||||
</h2>
|
||||
<p className="cur-lg">
|
||||
Lorem ipsum dolor sit amet consectetur. Pharetra sit
|
||||
amet est tellus nibh sit lacus in duis. Condimentum
|
||||
tellus sit pharetra consectetur magna massa. In odio
|
||||
leo pellentesque aenean egestas est risus etiam.
|
||||
Quam in nunc consectetur blandit id.
|
||||
</p>
|
||||
<div className="mt-40">
|
||||
<Link
|
||||
href="case-study-single"
|
||||
className="btn-anim btn-anim-light"
|
||||
>
|
||||
Read More
|
||||
<i className="fa-solid fa-arrow-trend-up"></i>
|
||||
<span></span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,12 +1,47 @@
|
||||
"use client";
|
||||
import { use } from 'react';
|
||||
import Image from "next/legacy/image";
|
||||
import { useCaseStudy } from "@/lib/hooks/useCaseStudy";
|
||||
import { getImageUrl } from "@/lib/imageUtils";
|
||||
import poster from "@/public/images/case/poster.png";
|
||||
import project from "@/public/images/case/project.png";
|
||||
import nine from "@/public/images/case/nine.png";
|
||||
import ten from "@/public/images/case/ten.png";
|
||||
import eleven from "@/public/images/case/eleven.png";
|
||||
import twelve from "@/public/images/case/twelve.png";
|
||||
|
||||
const CaseSingle = () => {
|
||||
interface CaseSingleProps {
|
||||
slug: string;
|
||||
}
|
||||
|
||||
const CaseSingle = ({ slug }: CaseSingleProps) => {
|
||||
const { caseStudy, loading, error } = useCaseStudy(slug);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<section className="c-details fix-top pb-120">
|
||||
<div className="container">
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<p className="text-center">Loading case study...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
if (error || !caseStudy) {
|
||||
return (
|
||||
<section className="c-details fix-top pb-120">
|
||||
<div className="container">
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<p className="text-center text-danger">Case study not found.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="c-details fix-top pb-120">
|
||||
<div className="container">
|
||||
@@ -14,16 +49,20 @@ const CaseSingle = () => {
|
||||
<div className="col-12">
|
||||
<div className="c-details-intro">
|
||||
<h2 className="mt-8 text-secondary title-anim fw-7">
|
||||
Artificial intelligence is the simulation of human intelligence
|
||||
processes.
|
||||
{caseStudy.title}
|
||||
</h2>
|
||||
{caseStudy.subtitle && (
|
||||
<h4 className="mt-4 text-secondary">{caseStudy.subtitle}</h4>
|
||||
)}
|
||||
<div className="poster mt-60 fade-top">
|
||||
<div className="parallax-image-wrap">
|
||||
<div className="parallax-image-inner">
|
||||
<Image
|
||||
src={poster}
|
||||
src={caseStudy.poster_image ? getImageUrl(caseStudy.poster_image) : poster}
|
||||
className="w-100 parallax-image mh-300"
|
||||
alt="Image"
|
||||
alt={caseStudy.title}
|
||||
width={1200}
|
||||
height={600}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -34,17 +73,14 @@ const CaseSingle = () => {
|
||||
<h2 className="mt-8 fw-7 text-secondary title-anim mb-24">
|
||||
Project
|
||||
</h2>
|
||||
<p className="cur-lg">
|
||||
Lorem ipsum dolor sit amet consectetur. Vestibulum
|
||||
malesuada amet sagittis urna. Mattis eget ultricies est
|
||||
morbi velit ultrices viverra elit facilisi. Amet est cras
|
||||
euismod accumsan ornare sagittis ut integer. Sagittis sed
|
||||
neque massa amet. Lorem vulputate nunc pulvinar maecenas
|
||||
convallis augue. Magna massa viverra tincidunt vitae lacus
|
||||
donec arcu consequat in. Maecenas dui nunc in convallis
|
||||
vulputate vitae lectus eu lacus donec arcu consequat in.
|
||||
Maecenas dui nunc in convallis vulputate vitae lectus eu.
|
||||
</p>
|
||||
{caseStudy.project_overview ? (
|
||||
<p className="cur-lg">{caseStudy.project_overview}</p>
|
||||
) : (
|
||||
<div
|
||||
className="cur-lg"
|
||||
dangerouslySetInnerHTML={{ __html: caseStudy.description || '' }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 col-lg-6 col-xxl-5 offset-xxl-1 fade-wrapper">
|
||||
@@ -52,88 +88,50 @@ const CaseSingle = () => {
|
||||
<div className="parallax-image-wrap">
|
||||
<div className="parallax-image-inner">
|
||||
<Image
|
||||
src={project}
|
||||
src={caseStudy.project_image ? getImageUrl(caseStudy.project_image) : project}
|
||||
className="w-100 parallax-image mh-260"
|
||||
alt="Image"
|
||||
alt={`${caseStudy.title} - Project`}
|
||||
width={600}
|
||||
height={500}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<div className="road-map__content">
|
||||
<h2 className="mt-8 fw-7 text-secondary title-anim mb-24">
|
||||
Site Map
|
||||
</h2>
|
||||
<p className="cur-lg">
|
||||
Lorem ipsum dolor sit amet consectetur. Vestibulum
|
||||
malesuada amet sagittis urna. Mattis eget ultricies est
|
||||
morbi velit ultrices viverra elit facilisi. Amet est cras
|
||||
euismod accumsan ornare sagittis ut integer. Sagittis sed
|
||||
neque massa amet. Lorem vulputate nunc pulvinar maecenas
|
||||
convallis augue. Magna massa viverra tincidunt vitae lacus
|
||||
donec arcu consequat in. Maecenas dui nunc in convallis
|
||||
vulputate vitae lectus eu.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row vertical-column-gap mt-60 fade-wrapper">
|
||||
<div className="col-12 col-sm-6 col-xl-3">
|
||||
<div className="c-details-thumb fade-top">
|
||||
<div className="parallax-image-wrap">
|
||||
<div className="parallax-image-inner">
|
||||
<Image
|
||||
src={nine}
|
||||
className="w-100 mh-300 parallax-image"
|
||||
alt="Image"
|
||||
/>
|
||||
</div>
|
||||
{caseStudy.site_map_content && (
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<div className="road-map__content">
|
||||
<h2 className="mt-8 fw-7 text-secondary title-anim mb-24">
|
||||
Site Map
|
||||
</h2>
|
||||
<p className="cur-lg">{caseStudy.site_map_content}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 col-sm-6 col-xl-3">
|
||||
<div className="c-details-thumb fade-top">
|
||||
<div className="parallax-image-wrap">
|
||||
<div className="parallax-image-inner">
|
||||
<Image
|
||||
src={ten}
|
||||
className="w-100 mh-300 parallax-image"
|
||||
alt="Image"
|
||||
/>
|
||||
)}
|
||||
{caseStudy.gallery_images && caseStudy.gallery_images.length > 0 && (
|
||||
<div className="row vertical-column-gap mt-60 fade-wrapper">
|
||||
{caseStudy.gallery_images.map((image) => (
|
||||
<div key={image.id} className="col-12 col-sm-6 col-xl-3">
|
||||
<div className="c-details-thumb fade-top">
|
||||
<div className="parallax-image-wrap">
|
||||
<div className="parallax-image-inner">
|
||||
<Image
|
||||
src={getImageUrl(image.image) || nine}
|
||||
className="w-100 mh-300 parallax-image"
|
||||
alt={image.caption || caseStudy.title}
|
||||
width={300}
|
||||
height={300}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="col-12 col-sm-6 col-xl-3">
|
||||
<div className="c-details-thumb fade-top">
|
||||
<div className="parallax-image-wrap">
|
||||
<div className="parallax-image-inner">
|
||||
<Image
|
||||
src={eleven}
|
||||
className="w-100 mh-300 parallax-image"
|
||||
alt="Image"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 col-sm-6 col-xl-3">
|
||||
<div className="c-details-thumb fade-top">
|
||||
<div className="parallax-image-wrap">
|
||||
<div className="parallax-image-inner">
|
||||
<Image
|
||||
src={twelve}
|
||||
className="w-100 mh-300 parallax-image"
|
||||
alt="Image"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,17 @@
|
||||
const Process = () => {
|
||||
"use client";
|
||||
import { useCaseStudy } from "@/lib/hooks/useCaseStudy";
|
||||
|
||||
interface ProcessProps {
|
||||
slug: string;
|
||||
}
|
||||
|
||||
const Process = ({ slug }: ProcessProps) => {
|
||||
const { caseStudy, loading } = useCaseStudy(slug);
|
||||
|
||||
if (loading || !caseStudy || !caseStudy.process_steps || caseStudy.process_steps.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="pt-120 pb-120 tp-process bg-black sticky-wrapper">
|
||||
<div className="container">
|
||||
@@ -6,64 +19,28 @@ const Process = () => {
|
||||
<div className="col-12 col-lg-6">
|
||||
<div className="process__content sticky-item">
|
||||
<h2 className="mt-8 title-anim text-white fw-7 mb-24">
|
||||
Artificial intelligence Process
|
||||
{caseStudy.title} Process
|
||||
</h2>
|
||||
<p className="cur-lg text-quinary">
|
||||
Quisque varius malesuada dui, ut posuere purus gravida in.
|
||||
Phasellus ultricies ullamcorper mollis. Pellentesque varius
|
||||
lectus in massa placerat cursus. Donec in dictum nisl. In
|
||||
maximus posuere leo nec porttitor.
|
||||
{caseStudy.excerpt}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 col-lg-6 col-xxl-5 offset-xxl-1">
|
||||
<div className="process__thumb sticky-item">
|
||||
<div className="process__single">
|
||||
<span className="op-text text-white mb-40 cur-lg">01</span>
|
||||
<h5 className="mt-8 text-white mb-24 title-anim">
|
||||
Computer Vision
|
||||
</h5>
|
||||
<p className="cur-lg text-quinary">
|
||||
Quisque varius malesuada dui, ut posuere purus gravida in.
|
||||
Phasellus ultricies ullamcorper mollis.
|
||||
</p>
|
||||
</div>
|
||||
<div className="process__single">
|
||||
<span className="op-text text-white mb-40 cur-lg">02</span>
|
||||
<h5 className="mt-8 text-white mb-24 title-anim">
|
||||
Computer Vision
|
||||
</h5>
|
||||
<p className="cur-lg text-quinary">
|
||||
Quisque varius malesuada dui, ut posuere purus gravida in.
|
||||
Phasellus ultricies ullamcorper mollis.
|
||||
</p>
|
||||
</div>
|
||||
<div className="process__single">
|
||||
<span className="op-text text-white mb-40 cur-lg">03</span>
|
||||
<h5 className="mt-8 text-white mb-24 title-anim">3D Vision</h5>
|
||||
<p className="cur-lg text-quinary">
|
||||
Quisque varius malesuada dui, ut posuere purus gravida in.
|
||||
Phasellus ultricies ullamcorper mollis.
|
||||
</p>
|
||||
</div>
|
||||
<div className="process__single">
|
||||
<span className="op-text text-white mb-40 cur-lg">04</span>
|
||||
<h5 className="mt-8 text-white mb-24 title-anim">
|
||||
Computer Vision
|
||||
</h5>
|
||||
<p className="cur-lg text-quinary">
|
||||
Quisque varius malesuada dui, ut posuere purus gravida in.
|
||||
Phasellus ultricies ullamcorper mollis.
|
||||
</p>
|
||||
</div>
|
||||
<div className="process__single">
|
||||
<span className="op-text text-white mb-40 cur-lg">05</span>
|
||||
<h5 className="mt-8 text-white mb-24 title-anim">3D Vision</h5>
|
||||
<p className="cur-lg text-quinary">
|
||||
Quisque varius malesuada dui, ut posuere purus gravida in.
|
||||
Phasellus ultricies ullamcorper mollis.
|
||||
</p>
|
||||
</div>
|
||||
{caseStudy.process_steps.map((step) => (
|
||||
<div key={step.id} className="process__single">
|
||||
<span className="op-text text-white mb-40 cur-lg">
|
||||
{String(step.step_number).padStart(2, '0')}
|
||||
</span>
|
||||
<h5 className="mt-8 text-white mb-24 title-anim">
|
||||
{step.title}
|
||||
</h5>
|
||||
<p className="cur-lg text-quinary">
|
||||
{step.description}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,21 @@
|
||||
"use client";
|
||||
import Image from "next/legacy/image";
|
||||
import Link from "next/link";
|
||||
import { useCaseStudy } from "@/lib/hooks/useCaseStudy";
|
||||
import { getImageUrl } from "@/lib/imageUtils";
|
||||
import one from "@/public/images/case/one.png";
|
||||
import two from "@/public/images/case/two.png";
|
||||
|
||||
const RelatedCase = () => {
|
||||
interface RelatedCaseProps {
|
||||
slug: string;
|
||||
}
|
||||
|
||||
const RelatedCase = ({ slug }: RelatedCaseProps) => {
|
||||
const { caseStudy, loading } = useCaseStudy(slug);
|
||||
|
||||
if (loading || !caseStudy || !caseStudy.related_case_studies || caseStudy.related_case_studies.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="pt-120 pb-120 c-study fade-wrapper">
|
||||
<div className="container">
|
||||
@@ -15,61 +27,37 @@ const RelatedCase = () => {
|
||||
</div>
|
||||
</div>
|
||||
<div className="row vertical-column-gap-lg">
|
||||
<div className="col-12 col-lg-6">
|
||||
<div className="c-study-single fade-top">
|
||||
<div className="thumb mb-24">
|
||||
<Link href="case-study-single" className="w-100">
|
||||
<div className="parallax-image-wrap">
|
||||
<div className="parallax-image-inner">
|
||||
<Image
|
||||
src={two}
|
||||
className="w-100 mh-300 parallax-image"
|
||||
alt="Image"
|
||||
/>
|
||||
{caseStudy.related_case_studies.slice(0, 2).map((relatedCase) => (
|
||||
<div key={relatedCase.id} className="col-12 col-lg-6">
|
||||
<div className="c-study-single fade-top">
|
||||
<div className="thumb mb-24">
|
||||
<Link href={`/case-study/${relatedCase.slug}`} className="w-100">
|
||||
<div className="parallax-image-wrap">
|
||||
<div className="parallax-image-inner">
|
||||
<Image
|
||||
src={relatedCase.thumbnail ? getImageUrl(relatedCase.thumbnail) : one}
|
||||
className="w-100 mh-300 parallax-image"
|
||||
alt={relatedCase.title}
|
||||
width={600}
|
||||
height={400}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<Link href="case-study" className="mb-30 fw-6">
|
||||
3D Render
|
||||
</Link>
|
||||
<h4 className="fw-6 mt-8 text-secondary">
|
||||
<Link href="case-study-single">
|
||||
3D computer graphics, or “3D graphics.
|
||||
</Link>
|
||||
</h4>
|
||||
</div>
|
||||
<div className="content">
|
||||
<Link href={`/case-study/${relatedCase.slug}`} className="mb-30 fw-6">
|
||||
{relatedCase.category_name || 'Case Study'}
|
||||
</Link>
|
||||
<h4 className="fw-6 mt-8 text-secondary">
|
||||
<Link href={`/case-study/${relatedCase.slug}`}>
|
||||
{relatedCase.title}
|
||||
</Link>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 col-lg-6">
|
||||
<div className="c-study-single fade-top">
|
||||
<div className="thumb mb-24">
|
||||
<Link href="case-study-single" className="w-100">
|
||||
<div className="parallax-image-wrap">
|
||||
<div className="parallax-image-inner">
|
||||
<Image
|
||||
src={one}
|
||||
className="w-100 mh-300 parallax-image"
|
||||
alt="Image"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<Link href="case-study" className="mb-30 fw-6">
|
||||
3D Render
|
||||
</Link>
|
||||
<h4 className="fw-6 mt-8 text-secondary">
|
||||
<Link href="case-study-single">
|
||||
Artificial intelligence is the simulation of human
|
||||
processes.
|
||||
</Link>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -4,12 +4,12 @@ import Link from "next/link";
|
||||
import { Swiper, SwiperSlide } from "swiper/react";
|
||||
import { Autoplay } from "swiper/modules";
|
||||
import "swiper/swiper-bundle.css";
|
||||
import one from "@/public/images/blog/related-one.png";
|
||||
import two from "@/public/images/blog/related-two.png";
|
||||
import three from "@/public/images/blog/related-three.png";
|
||||
import four from "@/public/images/blog/related-four.png";
|
||||
import { useLatestPosts } from "@/lib/hooks/useBlog";
|
||||
import { getValidImageUrl, getValidImageAlt, FALLBACK_IMAGES } from "@/lib/imageUtils";
|
||||
|
||||
const HomeLatestPost = () => {
|
||||
const { posts, loading, error } = useLatestPosts(12); // Get 12 latest posts
|
||||
|
||||
return (
|
||||
<section className="tp-latest-post pt-120 pb-120 bg-quinary">
|
||||
<div className="container">
|
||||
@@ -17,14 +17,14 @@ const HomeLatestPost = () => {
|
||||
<div className="col-12 col-lg-7">
|
||||
<div className="tp-lp-title text-center text-lg-start">
|
||||
<h2 className="mt-8 fw-7 text-secondary title-anim">
|
||||
Related posts
|
||||
Latest Insights
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 col-lg-5">
|
||||
<div className="tp-lp-cta text-center text-lg-end">
|
||||
<Link href="blog" className="btn-line text-uppercase">
|
||||
See All Posts
|
||||
<Link href="/insights" className="btn-line text-uppercase">
|
||||
View All Insights
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
@@ -32,397 +32,86 @@ const HomeLatestPost = () => {
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<div className="tp-lp-slider-wrapper mt-60">
|
||||
<div className="tp-lp-slider-wrapper">
|
||||
<Swiper
|
||||
slidesPerView={"auto"}
|
||||
spaceBetween={24}
|
||||
slidesPerGroup={1}
|
||||
freeMode={true}
|
||||
speed={1200}
|
||||
loop={true}
|
||||
roundLengths={true}
|
||||
modules={[Autoplay]}
|
||||
autoplay={{
|
||||
delay: 5000,
|
||||
disableOnInteraction: false,
|
||||
pauseOnMouseEnter: true,
|
||||
}}
|
||||
className="tp-lp-slider"
|
||||
>
|
||||
<SwiperSlide>
|
||||
<div className="tp-lp-slider__single topy-tilt">
|
||||
<div className="thumb mb-24">
|
||||
<Link href="blog-single" className="w-100 overflow-hidden">
|
||||
<Image
|
||||
src={one}
|
||||
width={400}
|
||||
height={220}
|
||||
className="w-100 mh-220"
|
||||
alt="Image"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="tp-lp-post__meta mb-24 mt-8">
|
||||
<p className="author text-xs text-tertiary">
|
||||
Denial Lio
|
||||
</p>
|
||||
<span></span>
|
||||
<p className="date text-xs text-tertiary">
|
||||
18 Dec 2022
|
||||
</p>
|
||||
{loading ? (
|
||||
<div className="text-center py-5">
|
||||
<div className="spinner-border text-primary" role="status">
|
||||
<span className="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
<p className="text-tertiary mt-3">Loading insights...</p>
|
||||
</div>
|
||||
) : error ? (
|
||||
<div className="text-center py-5">
|
||||
<p className="text-danger">Error loading insights. Please try again later.</p>
|
||||
</div>
|
||||
) : posts.length === 0 ? (
|
||||
<div className="text-center py-5">
|
||||
<p className="text-tertiary">No insights available yet.</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="tp-lp-slider-wrapper">
|
||||
<Swiper
|
||||
slidesPerView={"auto"}
|
||||
spaceBetween={24}
|
||||
slidesPerGroup={1}
|
||||
freeMode={true}
|
||||
speed={1200}
|
||||
loop={posts.length > 3}
|
||||
roundLengths={true}
|
||||
modules={[Autoplay]}
|
||||
autoplay={{
|
||||
delay: 5000,
|
||||
disableOnInteraction: false,
|
||||
pauseOnMouseEnter: true,
|
||||
}}
|
||||
className="tp-lp-slider"
|
||||
>
|
||||
{posts.map((post) => (
|
||||
<SwiperSlide key={post.id}>
|
||||
<div className="tp-lp-slider__single topy-tilt">
|
||||
<div className="thumb mb-24">
|
||||
<Link
|
||||
href={`/insights/${post.slug}`}
|
||||
className="w-100 overflow-hidden d-block"
|
||||
>
|
||||
<div className="parallax-image-wrap">
|
||||
<div className="parallax-image-inner">
|
||||
<Image
|
||||
src={getValidImageUrl(post.thumbnail, FALLBACK_IMAGES.BLOG)}
|
||||
width={400}
|
||||
height={220}
|
||||
className="w-100 mh-220 parallax-image"
|
||||
alt={getValidImageAlt(post.title)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="tp-lp-post__meta mb-24 mt-8">
|
||||
<p className="author text-xs text-tertiary">
|
||||
{post.author_name || 'Admin'}
|
||||
</p>
|
||||
<span></span>
|
||||
<p className="date text-xs text-tertiary">
|
||||
{new Date(post.published_at || post.created_at).toLocaleDateString('en-US', {
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
year: 'numeric'
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
<h5 className="mt-8 fw-5 text-secondary">
|
||||
<Link href={`/insights/${post.slug}`}>
|
||||
{post.title}
|
||||
</Link>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
<h5 className="mt-8 fw-5 text-secondary">
|
||||
<Link href="blog-single">
|
||||
Tackling data of annotation problems in healthcare
|
||||
</Link>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="tp-lp-slider__single topy-tilt">
|
||||
<div className="thumb mb-24">
|
||||
<Link href="blog-single" className="w-100 overflow-hidden">
|
||||
<Image
|
||||
src={two}
|
||||
width={400}
|
||||
height={220}
|
||||
className="w-100 mh-220"
|
||||
alt="Image"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="tp-lp-post__meta mb-24 mt-8">
|
||||
<p className="author text-xs text-tertiary">
|
||||
Denial Lio
|
||||
</p>
|
||||
<span></span>
|
||||
<p className="date text-xs text-tertiary">
|
||||
18 Dec 2022
|
||||
</p>
|
||||
</div>
|
||||
<h5 className="mt-8 fw-5 text-secondary">
|
||||
<Link href="blog-single">
|
||||
Tackling data of annotation problems in healthcare
|
||||
</Link>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="tp-lp-slider__single topy-tilt">
|
||||
<div className="thumb mb-24">
|
||||
<Link href="blog-single" className="w-100 overflow-hidden">
|
||||
<Image
|
||||
src={three}
|
||||
width={400}
|
||||
height={220}
|
||||
className="w-100 mh-220"
|
||||
alt="Image"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="tp-lp-post__meta mb-24 mt-8">
|
||||
<p className="author text-xs text-tertiary">
|
||||
Denial Lio
|
||||
</p>
|
||||
<span></span>
|
||||
<p className="date text-xs text-tertiary">
|
||||
18 Dec 2022
|
||||
</p>
|
||||
</div>
|
||||
<h5 className="mt-8 fw-5 text-secondary">
|
||||
<Link href="blog-single">
|
||||
Tackling data of annotation problems in healthcare
|
||||
</Link>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="tp-lp-slider__single topy-tilt">
|
||||
<div className="thumb mb-24">
|
||||
<Link href="blog-single" className="w-100 overflow-hidden">
|
||||
<Image
|
||||
src={four}
|
||||
width={400}
|
||||
height={220}
|
||||
className="w-100 mh-220"
|
||||
alt="Image"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="tp-lp-post__meta mb-24 mt-8">
|
||||
<p className="author text-xs text-tertiary">
|
||||
Denial Lio
|
||||
</p>
|
||||
<span></span>
|
||||
<p className="date text-xs text-tertiary">
|
||||
18 Dec 2022
|
||||
</p>
|
||||
</div>
|
||||
<h5 className="mt-8 fw-5 text-secondary">
|
||||
<Link href="blog-single">
|
||||
Tackling data of annotation problems in healthcare
|
||||
</Link>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="tp-lp-slider__single topy-tilt">
|
||||
<div className="thumb mb-24">
|
||||
<Link href="blog-single" className="w-100 overflow-hidden">
|
||||
<Image
|
||||
src={one}
|
||||
width={400}
|
||||
height={220}
|
||||
className="w-100 mh-220"
|
||||
alt="Image"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="tp-lp-post__meta mb-24 mt-8">
|
||||
<p className="author text-xs text-tertiary">
|
||||
Denial Lio
|
||||
</p>
|
||||
<span></span>
|
||||
<p className="date text-xs text-tertiary">
|
||||
18 Dec 2022
|
||||
</p>
|
||||
</div>
|
||||
<h5 className="mt-8 fw-5 text-secondary">
|
||||
<Link href="blog-single">
|
||||
Tackling data of annotation problems in healthcare
|
||||
</Link>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="tp-lp-slider__single topy-tilt">
|
||||
<div className="thumb mb-24">
|
||||
<Link href="blog-single" className="w-100 overflow-hidden">
|
||||
<Image
|
||||
src={two}
|
||||
width={400}
|
||||
height={220}
|
||||
className="w-100 mh-220"
|
||||
alt="Image"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="tp-lp-post__meta mb-24 mt-8">
|
||||
<p className="author text-xs text-tertiary">
|
||||
Denial Lio
|
||||
</p>
|
||||
<span></span>
|
||||
<p className="date text-xs text-tertiary">
|
||||
18 Dec 2022
|
||||
</p>
|
||||
</div>
|
||||
<h5 className="mt-8 fw-5 text-secondary">
|
||||
<Link href="blog-single">
|
||||
Tackling data of annotation problems in healthcare
|
||||
</Link>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="tp-lp-slider__single topy-tilt">
|
||||
<div className="thumb mb-24">
|
||||
<Link href="blog-single" className="w-100 overflow-hidden">
|
||||
<Image
|
||||
src={three}
|
||||
width={400}
|
||||
height={220}
|
||||
className="w-100 mh-220"
|
||||
alt="Image"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="tp-lp-post__meta mb-24 mt-8">
|
||||
<p className="author text-xs text-tertiary">
|
||||
Denial Lio
|
||||
</p>
|
||||
<span></span>
|
||||
<p className="date text-xs text-tertiary">
|
||||
18 Dec 2022
|
||||
</p>
|
||||
</div>
|
||||
<h5 className="mt-8 fw-5 text-secondary">
|
||||
<Link href="blog-single">
|
||||
Tackling data of annotation problems in healthcare
|
||||
</Link>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="tp-lp-slider__single topy-tilt">
|
||||
<div className="thumb mb-24">
|
||||
<Link href="blog-single" className="w-100 overflow-hidden">
|
||||
<Image
|
||||
src={four}
|
||||
width={400}
|
||||
height={220}
|
||||
className="w-100 mh-220"
|
||||
alt="Image"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="tp-lp-post__meta mb-24 mt-8">
|
||||
<p className="author text-xs text-tertiary">
|
||||
Denial Lio
|
||||
</p>
|
||||
<span></span>
|
||||
<p className="date text-xs text-tertiary">
|
||||
18 Dec 2022
|
||||
</p>
|
||||
</div>
|
||||
<h5 className="mt-8 fw-5 text-secondary">
|
||||
<Link href="blog-single">
|
||||
Tackling data of annotation problems in healthcare
|
||||
</Link>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="tp-lp-slider__single topy-tilt">
|
||||
<div className="thumb mb-24">
|
||||
<Link href="blog-single" className="w-100 overflow-hidden">
|
||||
<Image
|
||||
src={one}
|
||||
width={400}
|
||||
height={220}
|
||||
className="w-100 mh-220"
|
||||
alt="Image"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="tp-lp-post__meta mb-24 mt-8">
|
||||
<p className="author text-xs text-tertiary">
|
||||
Denial Lio
|
||||
</p>
|
||||
<span></span>
|
||||
<p className="date text-xs text-tertiary">
|
||||
18 Dec 2022
|
||||
</p>
|
||||
</div>
|
||||
<h5 className="mt-8 fw-5 text-secondary">
|
||||
<Link href="blog-single">
|
||||
Tackling data of annotation problems in healthcare
|
||||
</Link>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="tp-lp-slider__single topy-tilt">
|
||||
<div className="thumb mb-24">
|
||||
<Link href="blog-single" className="w-100 overflow-hidden">
|
||||
<Image
|
||||
src={two}
|
||||
width={400}
|
||||
height={220}
|
||||
className="w-100 mh-220"
|
||||
alt="Image"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="tp-lp-post__meta mb-24 mt-8">
|
||||
<p className="author text-xs text-tertiary">
|
||||
Denial Lio
|
||||
</p>
|
||||
<span></span>
|
||||
<p className="date text-xs text-tertiary">
|
||||
18 Dec 2022
|
||||
</p>
|
||||
</div>
|
||||
<h5 className="mt-8 fw-5 text-secondary">
|
||||
<Link href="blog-single">
|
||||
Tackling data of annotation problems in healthcare
|
||||
</Link>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="tp-lp-slider__single topy-tilt">
|
||||
<div className="thumb mb-24">
|
||||
<Link href="blog-single" className="w-100 overflow-hidden">
|
||||
<Image
|
||||
src={three}
|
||||
width={400}
|
||||
height={220}
|
||||
className="w-100 mh-220"
|
||||
alt="Image"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="tp-lp-post__meta mb-24 mt-8">
|
||||
<p className="author text-xs text-tertiary">
|
||||
Denial Lio
|
||||
</p>
|
||||
<span></span>
|
||||
<p className="date text-xs text-tertiary">
|
||||
18 Dec 2022
|
||||
</p>
|
||||
</div>
|
||||
<h5 className="mt-8 fw-5 text-secondary">
|
||||
<Link href="blog-single">
|
||||
Tackling data of annotation problems in healthcare
|
||||
</Link>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<div className="tp-lp-slider__single topy-tilt">
|
||||
<div className="thumb mb-24">
|
||||
<Link href="blog-single" className="w-100 overflow-hidden">
|
||||
<Image
|
||||
src={four}
|
||||
width={400}
|
||||
height={220}
|
||||
className="w-100 mh-220"
|
||||
alt="Image"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="tp-lp-post__meta mb-24 mt-8">
|
||||
<p className="author text-xs text-tertiary">
|
||||
Denial Lio
|
||||
</p>
|
||||
<span></span>
|
||||
<p className="date text-xs text-tertiary">
|
||||
18 Dec 2022
|
||||
</p>
|
||||
</div>
|
||||
<h5 className="mt-8 fw-5 text-secondary">
|
||||
<Link href="blog-single">
|
||||
Tackling data of annotation problems in healthcare
|
||||
</Link>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
</Swiper>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
))}
|
||||
</Swiper>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,72 +1,92 @@
|
||||
"use client";
|
||||
import { useState, useEffect } from "react";
|
||||
import { useState, useEffect, useMemo } from "react";
|
||||
import Image from "next/legacy/image";
|
||||
import Link from "next/link";
|
||||
import { getValidImageUrl, FALLBACK_IMAGES } from "@/lib/imageUtils";
|
||||
import { useCaseStudies } from "@/lib/hooks/useCaseStudy";
|
||||
import { API_CONFIG } from "@/lib/config/api";
|
||||
|
||||
const Story = () => {
|
||||
const [activeIndex, setActiveIndex] = useState(0);
|
||||
const [activeImageIndex, setActiveImageIndex] = useState(0);
|
||||
|
||||
// Static case studies data
|
||||
const storyData = [
|
||||
// Fetch case studies from API with ordering and limit
|
||||
const params = useMemo(() => ({
|
||||
ordering: 'display_order',
|
||||
page_size: 5
|
||||
}), []);
|
||||
|
||||
const { caseStudies, loading, error } = useCaseStudies(params);
|
||||
|
||||
// Fallback to static data if API fails or is loading
|
||||
const staticStoryData = [
|
||||
{
|
||||
id: 1,
|
||||
destination: "Financial Services",
|
||||
category_name: "Financial Services",
|
||||
title: "Banking System Modernization",
|
||||
subtitle: "Complete digital transformation of legacy banking systems with enhanced security and real-time processing capabilities.",
|
||||
image: "/images/case/one.png",
|
||||
path: "/case-study/banking-system-modernization",
|
||||
excerpt: "Complete digital transformation of legacy banking systems with enhanced security and real-time processing capabilities.",
|
||||
thumbnail: "/images/case/one.png",
|
||||
slug: "banking-system-modernization",
|
||||
display_order: 1,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
destination: "Healthcare",
|
||||
category_name: "Healthcare",
|
||||
title: "Patient Management System",
|
||||
subtitle: "Enterprise-grade patient management system with HIPAA compliance and seamless integration across multiple healthcare facilities.",
|
||||
image: "/images/case/two.png",
|
||||
path: "/case-study/patient-management-system",
|
||||
excerpt: "Enterprise-grade patient management system with HIPAA compliance and seamless integration across multiple healthcare facilities.",
|
||||
thumbnail: "/images/case/two.png",
|
||||
slug: "patient-management-system",
|
||||
display_order: 2,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
destination: "Manufacturing",
|
||||
category_name: "Manufacturing",
|
||||
title: "Supply Chain Optimization",
|
||||
subtitle: "Advanced supply chain management system with real-time tracking, predictive analytics, and automated inventory management.",
|
||||
image: "/images/case/three.png",
|
||||
path: "/case-study/supply-chain-optimization",
|
||||
excerpt: "Advanced supply chain management system with real-time tracking, predictive analytics, and automated inventory management.",
|
||||
thumbnail: "/images/case/three.png",
|
||||
slug: "supply-chain-optimization",
|
||||
display_order: 3,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
destination: "E-commerce",
|
||||
category_name: "E-commerce",
|
||||
title: "Multi-Platform Integration",
|
||||
subtitle: "Seamless integration of multiple e-commerce platforms with unified inventory management and real-time synchronization.",
|
||||
image: "/images/case/four.png",
|
||||
path: "/case-study/multi-platform-integration",
|
||||
excerpt: "Seamless integration of multiple e-commerce platforms with unified inventory management and real-time synchronization.",
|
||||
thumbnail: "/images/case/four.png",
|
||||
slug: "multi-platform-integration",
|
||||
display_order: 4,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
destination: "Education",
|
||||
category_name: "Education",
|
||||
title: "Learning Management System",
|
||||
subtitle: "Comprehensive LMS with advanced analytics, automated grading, and seamless integration with existing educational tools.",
|
||||
image: "/images/case/five.png",
|
||||
path: "/case-study/learning-management-system",
|
||||
excerpt: "Comprehensive LMS with advanced analytics, automated grading, and seamless integration with existing educational tools.",
|
||||
thumbnail: "/images/case/five.png",
|
||||
slug: "learning-management-system",
|
||||
display_order: 5,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
}
|
||||
];
|
||||
|
||||
// Use API data if available, otherwise use static data
|
||||
const storyData = caseStudies.length > 0 ? caseStudies : staticStoryData;
|
||||
|
||||
// Log when API data is loaded
|
||||
useEffect(() => {
|
||||
if (caseStudies.length > 0) {
|
||||
console.log('Case studies loaded from API:', caseStudies.length);
|
||||
}
|
||||
}, [caseStudies]);
|
||||
|
||||
const handleMouseEnter = (index: number) => {
|
||||
setActiveIndex(index);
|
||||
setActiveImageIndex(index);
|
||||
@@ -93,12 +113,14 @@ const Story = () => {
|
||||
onMouseEnter={() => handleMouseEnter(index)}
|
||||
>
|
||||
<p className="fw-6 mt-8">
|
||||
<Link href={`${item.path}`}>{item.destination}</Link>
|
||||
<Link href={`/case-study/${item.slug}`}>
|
||||
{item.category_name || "Case Study"}
|
||||
</Link>
|
||||
</p>
|
||||
<h5 className="fw-4 mt-12 mb-12 text-white">
|
||||
{item.title}
|
||||
</h5>
|
||||
<p className="text-xs">{item.subtitle}</p>
|
||||
<p className="text-xs">{item.excerpt}</p>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
@@ -108,6 +130,26 @@ const Story = () => {
|
||||
<div className="col-12 col-lg-7 col-xxl-6 offset-xxl-1 d-none d-lg-block">
|
||||
<div className="tp-story__thumbs sticky-item">
|
||||
{storyData.map((item, index) => {
|
||||
// Get the image URL - handle different scenarios
|
||||
let imageUrl;
|
||||
if (item.thumbnail) {
|
||||
if (item.thumbnail.startsWith('http')) {
|
||||
// Full URL (external)
|
||||
imageUrl = item.thumbnail;
|
||||
} else if (item.thumbnail.startsWith('/media')) {
|
||||
// Relative path starting with /media
|
||||
imageUrl = `${API_CONFIG.BASE_URL}${item.thumbnail}`;
|
||||
} else {
|
||||
// Just filename or relative path
|
||||
imageUrl = `${API_CONFIG.MEDIA_URL}/${item.thumbnail}`;
|
||||
}
|
||||
console.log('Case study image URL:', item.title, '→', imageUrl);
|
||||
} else {
|
||||
// Fallback to static image
|
||||
imageUrl = getValidImageUrl('/images/case/one.png', FALLBACK_IMAGES.CASE_STUDY);
|
||||
console.log('Using fallback image for:', item.title);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
@@ -116,11 +158,11 @@ const Story = () => {
|
||||
}`}
|
||||
>
|
||||
<Image
|
||||
src={getValidImageUrl(item.image, FALLBACK_IMAGES.CASE_STUDY)}
|
||||
src={imageUrl}
|
||||
width={600}
|
||||
height={300}
|
||||
className="w-100 mh-300"
|
||||
alt="Image"
|
||||
alt={item.title || "Case Study"}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user