402 lines
15 KiB
TypeScript
402 lines
15 KiB
TypeScript
"use client";
|
|
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";
|
|
|
|
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="case-study-details luxury-case-study fix-top">
|
|
<div className="container">
|
|
<div className="row">
|
|
<div className="col-12">
|
|
<div className="loading-state">
|
|
<div className="loading-spinner"></div>
|
|
<p className="text-center">Loading case study...</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
if (error || !caseStudy) {
|
|
return (
|
|
<section className="case-study-details luxury-case-study fix-top">
|
|
<div className="container">
|
|
<div className="row">
|
|
<div className="col-12">
|
|
<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>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<>
|
|
{/* 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>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 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>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default CaseSingle;
|