This commit is contained in:
Iliyan Angelov
2025-11-24 08:18:18 +02:00
parent 366f28677a
commit 136f75a859
133 changed files with 14977 additions and 3350 deletions

View File

@@ -1,26 +1,57 @@
"use client";
import { use } from 'react';
import Image from "next/legacy/image";
import { useEffect } from "react";
import Image from "next/image";
import Link from "next/link";
import { useRouter } from "next/navigation";
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";
interface CaseSingleProps {
slug: string;
}
const CaseSingle = ({ slug }: CaseSingleProps) => {
const router = useRouter();
const { caseStudy, loading, error } = useCaseStudy(slug);
const handleGoBack = () => {
router.back();
};
// Debug logging
useEffect(() => {
if (error) {
console.error('Error loading case study:', error);
}
if (caseStudy) {
console.log('Case Study Data:', {
title: caseStudy.title,
hasCategory: !!caseStudy.category,
hasClient: !!caseStudy.client,
processStepsCount: caseStudy.process_steps?.length || 0,
relatedCaseStudiesCount: caseStudy.related_case_studies?.length || 0,
hasPosterImage: !!caseStudy.poster_image,
hasProjectImage: !!caseStudy.project_image,
hasFeaturedImage: !!caseStudy.featured_image,
hasProjectOverview: !!caseStudy.project_overview,
hasSiteMapContent: !!caseStudy.site_map_content,
fullData: caseStudy
});
}
}, [caseStudy, error]);
if (loading) {
return (
<section className="c-details fix-top pb-120">
<section className="case-study-details luxury-case-study fix-top">
<div className="container">
<div className="row">
<div className="col-12">
<p className="text-center">Loading case study...</p>
<div className="loading-state">
<div className="loading-spinner"></div>
<p className="text-center">Loading case study...</p>
</div>
</div>
</div>
</div>
@@ -30,11 +61,17 @@ const CaseSingle = ({ slug }: CaseSingleProps) => {
if (error || !caseStudy) {
return (
<section className="c-details fix-top pb-120">
<section className="case-study-details luxury-case-study fix-top">
<div className="container">
<div className="row">
<div className="col-12">
<p className="text-center text-danger">Case study not found.</p>
<div className="error-state">
<h2>Case Study Not Found</h2>
<p>The case study you're looking for doesn't exist or has been removed.</p>
<Link href="/case-study" className="btn btn-primary">
View All Case Studies
</Link>
</div>
</div>
</div>
</div>
@@ -43,100 +80,323 @@ const CaseSingle = ({ slug }: CaseSingleProps) => {
}
return (
<section className="c-details fix-top pb-120">
<div className="container">
<div className="row">
<div className="col-12">
<div className="c-details-intro">
<h2 className="mt-8 text-secondary title-anim fw-7">
{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={caseStudy.poster_image ? getImageUrl(caseStudy.poster_image) : poster}
className="w-100 parallax-image mh-300"
alt={caseStudy.title}
width={1200}
height={600}
/>
<>
{/* Hero Banner Section */}
<section className="case-study-hero luxury-case-hero fix-top">
<div className="hero-background-overlay"></div>
<div className="container">
<div className="row">
<div className="col-12">
<div className="case-study-hero-content">
{/* Category Badge */}
{caseStudy.category && (
<div className="hero-badge">
<i className="fa-solid fa-folder-open"></i>
<span>{caseStudy.category.name}</span>
</div>
)}
{/* Title */}
<h1 className="hero-title">
{caseStudy.title}
</h1>
{/* Subtitle */}
{caseStudy.subtitle && (
<h2 className="hero-subtitle">
{caseStudy.subtitle}
</h2>
)}
{/* Meta Information */}
<div className="hero-meta">
{caseStudy.client && (
<div className="meta-item">
<i className="fa-solid fa-building"></i>
<span>{caseStudy.client.name}</span>
</div>
)}
{caseStudy.published_at && (
<div className="meta-item">
<i className="fa-solid fa-calendar"></i>
<span>{new Date(caseStudy.published_at).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })}</span>
</div>
)}
{caseStudy.views_count !== undefined && (
<div className="meta-item">
<i className="fa-solid fa-eye"></i>
<span>{caseStudy.views_count} views</span>
</div>
)}
</div>
</div>
<div className="row vertical-column-gap align-items-center pt-120 pb-120">
<div className="col-12 col-lg-6">
<div className="c-details-content">
<h2 className="mt-8 fw-7 text-secondary title-anim mb-24">
Project
</h2>
{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">
<div className="c-details-thumb fade-top">
<div className="parallax-image-wrap">
<div className="parallax-image-inner">
<Image
src={caseStudy.project_image ? getImageUrl(caseStudy.project_image) : project}
className="w-100 parallax-image mh-260"
alt={`${caseStudy.title} - Project`}
width={600}
height={500}
/>
</div>
</div>
</div>
</div>
</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>
)}
{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>
</div>
</div>
</section>
{/* Hero Image */}
{caseStudy.featured_image && (
<div className="hero-image-container">
<div className="hero-image-wrapper">
<Image
src={getImageUrl(caseStudy.featured_image)}
alt={caseStudy.title}
fill
className="hero-image"
priority
sizes="100vw"
style={{ objectFit: 'cover' }}
/>
</div>
<div className="hero-image-overlay"></div>
</div>
)}
</section>
{/* Main Content Section */}
<section className="case-study-details luxury-case-study">
<div className="container">
{/* Back Button - Top */}
<div className="row pt-60">
<div className="col-12">
<button
onClick={handleGoBack}
className="btn btn-outline mb-40"
style={{
display: 'inline-flex',
alignItems: 'center',
gap: '10px',
padding: '14px 28px',
fontSize: '16px',
fontWeight: 600,
cursor: 'pointer',
border: '2px solid #007bff',
backgroundColor: '#007bff',
color: '#ffffff',
borderRadius: '6px',
transition: 'all 0.3s ease',
boxShadow: '0 2px 8px rgba(0, 123, 255, 0.3)'
}}
onMouseEnter={(e) => {
e.currentTarget.style.backgroundColor = '#0056b3';
e.currentTarget.style.borderColor = '#0056b3';
e.currentTarget.style.boxShadow = '0 4px 12px rgba(0, 123, 255, 0.4)';
e.currentTarget.style.transform = 'translateY(-2px)';
}}
onMouseLeave={(e) => {
e.currentTarget.style.backgroundColor = '#007bff';
e.currentTarget.style.borderColor = '#007bff';
e.currentTarget.style.boxShadow = '0 2px 8px rgba(0, 123, 255, 0.3)';
e.currentTarget.style.transform = 'translateY(0)';
}}
>
<i className="fa-solid fa-arrow-left"></i>
<span>Back to Case Studies</span>
</button>
</div>
</div>
{/* Project Overview Section */}
<div className="row vertical-column-gap-lg align-items-start">
<div className="col-12 col-lg-7">
<div className="case-study-content">
<h2 className="section-title">Project Overview</h2>
{caseStudy.project_overview ? (
<div
className="content-html"
dangerouslySetInnerHTML={{ __html: caseStudy.project_overview }}
/>
) : (
<div
className="content-html"
dangerouslySetInnerHTML={{ __html: caseStudy.description || '' }}
/>
)}
{/* Full Description Content */}
{caseStudy.description && (
<div
className="content-html full-description mt-40"
dangerouslySetInnerHTML={{ __html: caseStudy.description }}
/>
)}
</div>
</div>
<div className="col-12 col-lg-5">
<div className="case-study-sidebar">
{/* Project Image */}
{caseStudy.project_image && (
<div className="sidebar-image mb-40">
<div className="image-wrapper">
<Image
src={getImageUrl(caseStudy.project_image)}
alt={`${caseStudy.title} - Project`}
width={600}
height={500}
className="project-image"
/>
</div>
</div>
)}
{/* Project Info Card */}
<div className="info-card">
<h3 className="info-card-title">Project Details</h3>
<div className="info-list">
{caseStudy.category && (
<div className="info-item">
<span className="info-label">
<i className="fa-solid fa-tag"></i>
Category
</span>
<span className="info-value">{caseStudy.category.name}</span>
</div>
)}
{caseStudy.client && (
<div className="info-item">
<span className="info-label">
<i className="fa-solid fa-building"></i>
Client
</span>
<span className="info-value">{caseStudy.client.name}</span>
</div>
)}
{caseStudy.published_at && (
<div className="info-item">
<span className="info-label">
<i className="fa-solid fa-calendar"></i>
Published
</span>
<span className="info-value">
{new Date(caseStudy.published_at).toLocaleDateString('en-US', { year: 'numeric', month: 'short' })}
</span>
</div>
)}
{caseStudy.featured && (
<div className="info-item">
<span className="info-label">
<i className="fa-solid fa-star"></i>
Status
</span>
<span className="info-value featured-badge">Featured</span>
</div>
)}
</div>
{caseStudy.client?.website && (
<div className="info-card-footer">
<a
href={caseStudy.client.website}
target="_blank"
rel="noopener noreferrer"
className="btn btn-outline"
>
<i className="fa-solid fa-external-link"></i>
Visit Client Website
</a>
</div>
)}
</div>
</div>
</div>
</div>
{/* Statistics/Metrics Section */}
{caseStudy.description && caseStudy.description.includes('Key Results') && (
<div className="row pt-60">
<div className="col-12">
<div className="case-study-stats">
<h2 className="section-title text-center mb-50">Key Achievements</h2>
<div className="stats-grid">
{(() => {
// Extract metrics from description
const resultsMatch = caseStudy.description.match(/<h3>Key Results<\/h3>[\s\S]*?<ul>([\s\S]*?)<\/ul>/i);
if (resultsMatch) {
const items = resultsMatch[1].match(/<li>([^<]+)<\/li>/g);
if (items && items.length > 0) {
return items.slice(0, 4).map((item, index) => {
const text = item.replace(/<[^>]+>/g, '');
const numberMatch = text.match(/(\d+[%$]?)/);
const number = numberMatch ? numberMatch[1] : '';
const description = text.replace(/\d+[%$]?\s*/, '').trim();
return (
<div key={index} className="stat-card">
<div className="stat-number">{number}</div>
<div className="stat-label">{description}</div>
</div>
);
});
}
}
return null;
})()}
</div>
</div>
</div>
</div>
)}
{/* Site Map / Process Section */}
{caseStudy.site_map_content && (
<div className="row pt-60">
<div className="col-12">
<div className="case-study-section">
<h2 className="section-title">Site Map & Process</h2>
<div
className="content-html"
dangerouslySetInnerHTML={{ __html: caseStudy.site_map_content }}
/>
</div>
</div>
</div>
)}
{/* Back Button - Bottom */}
<div className="row pt-60 pb-60">
<div className="col-12">
<div style={{ textAlign: 'center' }}>
<button
onClick={handleGoBack}
className="btn btn-outline"
style={{
display: 'inline-flex',
alignItems: 'center',
gap: '10px',
padding: '14px 28px',
fontSize: '16px',
fontWeight: 600,
cursor: 'pointer',
border: '2px solid #007bff',
backgroundColor: '#007bff',
color: '#ffffff',
borderRadius: '6px',
transition: 'all 0.3s ease',
boxShadow: '0 2px 8px rgba(0, 123, 255, 0.3)'
}}
onMouseEnter={(e) => {
e.currentTarget.style.backgroundColor = '#0056b3';
e.currentTarget.style.borderColor = '#0056b3';
e.currentTarget.style.boxShadow = '0 4px 12px rgba(0, 123, 255, 0.4)';
e.currentTarget.style.transform = 'translateY(-2px)';
}}
onMouseLeave={(e) => {
e.currentTarget.style.backgroundColor = '#007bff';
e.currentTarget.style.borderColor = '#007bff';
e.currentTarget.style.boxShadow = '0 2px 8px rgba(0, 123, 255, 0.3)';
e.currentTarget.style.transform = 'translateY(0)';
}}
>
<i className="fa-solid fa-arrow-left"></i>
<span>Back to Case Studies</span>
</button>
</div>
</div>
</div>
</div>
</section>
</>
);
};