update
This commit is contained in:
@@ -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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user