init commit
This commit is contained in:
473
.history/views/portfolio-detail_20251019163806.ejs
Normal file
473
.history/views/portfolio-detail_20251019163806.ejs
Normal file
@@ -0,0 +1,473 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><%= portfolio.title %> - SmartSolTech 포트폴리오</title>
|
||||
|
||||
<!-- SEO Meta Tags -->
|
||||
<meta name="description" content="<%= portfolio.seo?.metaDescription || portfolio.shortDescription || portfolio.description %>">
|
||||
<meta name="keywords" content="<%= portfolio.seo?.keywords?.join(', ') || portfolio.technologies?.join(', ') %>">
|
||||
|
||||
<!-- Open Graph -->
|
||||
<meta property="og:title" content="<%= portfolio.title %> - SmartSolTech">
|
||||
<meta property="og:description" content="<%= portfolio.shortDescription || portfolio.description %>">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="<%= `${req.protocol}://${req.get('host')}/portfolio/${portfolio._id}` %>">
|
||||
<% if (portfolio.images && portfolio.images.length > 0) { %>
|
||||
<meta property="og:image" content="<%= `${req.protocol}://${req.get('host')}${portfolio.images.find(img => img.isPrimary)?.url || portfolio.images[0].url}` %>">
|
||||
<% } %>
|
||||
|
||||
<!-- PWA -->
|
||||
<meta name="theme-color" content="#3B82F6">
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<link rel="apple-touch-icon" href="/images/icons/icon-192x192.png">
|
||||
|
||||
<!-- Styles -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
|
||||
<link href="https://unpkg.com/aos@2.3.1/dist/aos.css" rel="stylesheet">
|
||||
<link href="https://unpkg.com/swiper@8/swiper-bundle.min.css" rel="stylesheet">
|
||||
<link href="/css/main.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<%- include('partials/navigation') %>
|
||||
|
||||
<!-- Portfolio Header -->
|
||||
<section class="pt-20 pb-12 bg-gradient-to-br from-gray-900 via-blue-900 to-purple-900">
|
||||
<div class="container mx-auto px-4">
|
||||
<!-- Breadcrumb -->
|
||||
<nav class="flex text-white text-sm mb-8" data-aos="fade-up">
|
||||
<a href="/" class="hover:text-blue-300 transition-colors">홈</a>
|
||||
<span class="mx-2">/</span>
|
||||
<a href="/portfolio" class="hover:text-blue-300 transition-colors">포트폴리오</a>
|
||||
<span class="mx-2">/</span>
|
||||
<span class="text-blue-300"><%= portfolio.title %></span>
|
||||
</nav>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
|
||||
<!-- Project Info -->
|
||||
<div class="text-white" data-aos="fade-right">
|
||||
<div class="flex items-center gap-4 mb-4">
|
||||
<span class="bg-blue-600 text-white px-4 py-2 rounded-full text-sm font-semibold">
|
||||
<%= getCategoryName(portfolio.category) %>
|
||||
</span>
|
||||
<% if (portfolio.featured) { %>
|
||||
<span class="bg-yellow-500 text-white px-3 py-1 rounded-full text-xs font-bold">
|
||||
<i class="fas fa-star"></i> FEATURED
|
||||
</span>
|
||||
<% } %>
|
||||
<% if (portfolio.status === 'completed') { %>
|
||||
<span class="bg-green-500 text-white px-3 py-1 rounded-full text-xs font-semibold">
|
||||
<i class="fas fa-check"></i> 완료
|
||||
</span>
|
||||
<% } else if (portfolio.status === 'in-progress') { %>
|
||||
<span class="bg-orange-500 text-white px-3 py-1 rounded-full text-xs font-semibold">
|
||||
<i class="fas fa-cog fa-spin"></i> 진행 중
|
||||
</span>
|
||||
<% } %>
|
||||
</div>
|
||||
|
||||
<h1 class="text-4xl md:text-5xl font-bold mb-6">
|
||||
<%= portfolio.title %>
|
||||
</h1>
|
||||
|
||||
<p class="text-xl text-gray-300 mb-8 leading-relaxed">
|
||||
<%= portfolio.description %>
|
||||
</p>
|
||||
|
||||
<!-- Project Stats -->
|
||||
<div class="grid grid-cols-3 gap-6 mb-8">
|
||||
<div class="text-center">
|
||||
<div class="text-2xl font-bold text-blue-300"><%= portfolio.viewCount || 0 %></div>
|
||||
<div class="text-sm text-gray-400">조회수</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="text-2xl font-bold text-red-300"><%= portfolio.likes || 0 %></div>
|
||||
<div class="text-sm text-gray-400">좋아요</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="text-2xl font-bold text-yellow-300">
|
||||
<%= portfolio.completedAt ? new Date(portfolio.completedAt).getFullYear() : new Date().getFullYear() %>
|
||||
</div>
|
||||
<div class="text-sm text-gray-400">완료년도</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<div class="flex flex-col sm:flex-row gap-4">
|
||||
<% if (portfolio.projectUrl) { %>
|
||||
<a href="<%= portfolio.projectUrl %>"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="bg-white text-blue-900 px-6 py-3 rounded-full font-semibold hover:bg-gray-100 transition-colors flex items-center justify-center">
|
||||
<i class="fas fa-external-link-alt mr-2"></i>
|
||||
프로젝트 보기
|
||||
</a>
|
||||
<% } %>
|
||||
<button id="like-btn"
|
||||
class="border-2 border-white text-white px-6 py-3 rounded-full font-semibold hover:bg-white hover:text-blue-900 transition-colors flex items-center justify-center">
|
||||
<i class="fas fa-heart mr-2"></i>
|
||||
좋아요 (<span id="like-count"><%= portfolio.likes || 0 %></span>)
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Project Preview Image -->
|
||||
<div class="relative" data-aos="fade-left">
|
||||
<% if (portfolio.images && portfolio.images.length > 0) { %>
|
||||
<div class="relative rounded-2xl overflow-hidden shadow-2xl">
|
||||
<img src="<%= portfolio.images.find(img => img.isPrimary)?.url || portfolio.images[0].url %>"
|
||||
alt="<%= portfolio.title %>"
|
||||
class="w-full h-auto max-h-96 object-cover">
|
||||
<div class="absolute inset-0 bg-gradient-to-t from-black/20 to-transparent"></div>
|
||||
</div>
|
||||
<% } else { %>
|
||||
<div class="w-full h-96 bg-gradient-to-br from-blue-400 to-purple-500 rounded-2xl flex items-center justify-center">
|
||||
<i class="fas fa-code text-white text-6xl opacity-50"></i>
|
||||
</div>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Project Details -->
|
||||
<section class="section-padding">
|
||||
<div class="container mx-auto px-4">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-12">
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="lg:col-span-2">
|
||||
|
||||
<!-- Project Images Gallery -->
|
||||
<% if (portfolio.images && portfolio.images.length > 1) { %>
|
||||
<div class="mb-12" data-aos="fade-up">
|
||||
<h2 class="text-3xl font-bold text-gray-900 mb-6">프로젝트 갤러리</h2>
|
||||
|
||||
<!-- Swiper -->
|
||||
<div class="swiper portfolio-swiper">
|
||||
<div class="swiper-wrapper">
|
||||
<% portfolio.images.forEach(image => { %>
|
||||
<div class="swiper-slide">
|
||||
<div class="relative rounded-xl overflow-hidden shadow-lg">
|
||||
<img src="<%= image.url %>"
|
||||
alt="<%= image.alt || portfolio.title %>"
|
||||
class="w-full h-64 md:h-80 object-cover">
|
||||
<div class="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/70 to-transparent p-4">
|
||||
<p class="text-white text-sm"><%= image.alt || portfolio.title %></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% }) %>
|
||||
</div>
|
||||
<div class="swiper-pagination"></div>
|
||||
<div class="swiper-button-next"></div>
|
||||
<div class="swiper-button-prev"></div>
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<!-- Project Description -->
|
||||
<div class="mb-12" data-aos="fade-up">
|
||||
<h2 class="text-3xl font-bold text-gray-900 mb-6">프로젝트 개요</h2>
|
||||
<div class="prose max-w-none text-gray-600 leading-relaxed">
|
||||
<%= portfolio.description %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Technologies Used -->
|
||||
<% if (portfolio.technologies && portfolio.technologies.length > 0) { %>
|
||||
<div class="mb-12" data-aos="fade-up">
|
||||
<h2 class="text-3xl font-bold text-gray-900 mb-6">사용된 기술</h2>
|
||||
<div class="grid grid-cols-2 md:grid-cols-3 gap-4">
|
||||
<% portfolio.technologies.forEach(tech => { %>
|
||||
<div class="bg-gradient-to-r from-blue-50 to-purple-50 border border-blue-200 rounded-lg p-4 text-center hover:shadow-md transition-shadow">
|
||||
<div class="font-semibold text-gray-800"><%= tech %></div>
|
||||
</div>
|
||||
<% }) %>
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<!-- Share Section -->
|
||||
<div class="border-t pt-8" data-aos="fade-up">
|
||||
<h3 class="text-xl font-bold text-gray-900 mb-4">이 프로젝트 공유하기</h3>
|
||||
<div class="flex gap-4">
|
||||
<button onclick="shareOnSocial('facebook')"
|
||||
class="bg-blue-600 text-white p-3 rounded-full hover:bg-blue-700 transition-colors">
|
||||
<i class="fab fa-facebook text-lg"></i>
|
||||
</button>
|
||||
<button onclick="shareOnSocial('twitter')"
|
||||
class="bg-blue-400 text-white p-3 rounded-full hover:bg-blue-500 transition-colors">
|
||||
<i class="fab fa-twitter text-lg"></i>
|
||||
</button>
|
||||
<button onclick="shareOnSocial('linkedin')"
|
||||
class="bg-blue-700 text-white p-3 rounded-full hover:bg-blue-800 transition-colors">
|
||||
<i class="fab fa-linkedin text-lg"></i>
|
||||
</button>
|
||||
<button onclick="copyToClipboard()"
|
||||
class="bg-gray-600 text-white p-3 rounded-full hover:bg-gray-700 transition-colors">
|
||||
<i class="fas fa-link text-lg"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sidebar -->
|
||||
<div class="lg:col-span-1">
|
||||
|
||||
<!-- Project Info -->
|
||||
<div class="bg-white rounded-2xl shadow-lg p-6 mb-8" data-aos="fade-up">
|
||||
<h3 class="text-xl font-bold text-gray-900 mb-6">프로젝트 정보</h3>
|
||||
|
||||
<div class="space-y-4">
|
||||
<% if (portfolio.clientName) { %>
|
||||
<div>
|
||||
<div class="text-sm text-gray-500 font-medium">클라이언트</div>
|
||||
<div class="text-gray-900"><%= portfolio.clientName %></div>
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<div>
|
||||
<div class="text-sm text-gray-500 font-medium">카테고리</div>
|
||||
<div class="text-gray-900"><%= getCategoryName(portfolio.category) %></div>
|
||||
</div>
|
||||
|
||||
<% if (portfolio.completedAt) { %>
|
||||
<div>
|
||||
<div class="text-sm text-gray-500 font-medium">완료일</div>
|
||||
<div class="text-gray-900">
|
||||
<%= new Date(portfolio.completedAt).toLocaleDateString('ko-KR') %>
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<% if (portfolio.projectUrl) { %>
|
||||
<div>
|
||||
<div class="text-sm text-gray-500 font-medium">프로젝트 URL</div>
|
||||
<a href="<%= portfolio.projectUrl %>"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="text-blue-600 hover:underline break-all">
|
||||
<%= portfolio.projectUrl %>
|
||||
</a>
|
||||
</div>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Contact CTA -->
|
||||
<div class="bg-gradient-to-br from-blue-600 to-purple-600 rounded-2xl shadow-lg p-6 text-white" data-aos="fade-up">
|
||||
<h3 class="text-xl font-bold mb-4">비슷한 프로젝트가 필요하신가요?</h3>
|
||||
<p class="text-blue-100 mb-6">
|
||||
이런 프로젝트에 관심이 있으시다면 언제든 연락주세요.
|
||||
무료 상담을 통해 최적의 솔루션을 제안해드리겠습니다.
|
||||
</p>
|
||||
<div class="space-y-3">
|
||||
<a href="/contact"
|
||||
class="block w-full bg-white text-blue-600 text-center py-3 rounded-lg font-semibold hover:bg-gray-100 transition-colors">
|
||||
프로젝트 문의하기
|
||||
</a>
|
||||
<a href="/calculator"
|
||||
class="block w-full border-2 border-white text-white text-center py-3 rounded-lg font-semibold hover:bg-white hover:text-blue-600 transition-colors">
|
||||
비용 계산하기
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Related Projects -->
|
||||
<section class="section-padding bg-gray-50">
|
||||
<div class="container mx-auto px-4">
|
||||
<div class="text-center mb-12" data-aos="fade-up">
|
||||
<h2 class="text-3xl md:text-4xl font-bold text-gray-900 mb-4">
|
||||
관련 프로젝트
|
||||
</h2>
|
||||
<p class="text-xl text-gray-600">
|
||||
비슷한 카테고리의 다른 프로젝트들도 확인해보세요
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
<% if (relatedProjects && relatedProjects.length > 0) { %>
|
||||
<% relatedProjects.forEach((project, index) => { %>
|
||||
<div class="bg-white rounded-xl shadow-lg overflow-hidden hover:shadow-xl transition-all duration-300 transform hover:-translate-y-2"
|
||||
data-aos="fade-up"
|
||||
data-aos-delay="<%= index * 100 %>">
|
||||
|
||||
<div class="relative h-48 overflow-hidden">
|
||||
<% if (project.images && project.images.length > 0) { %>
|
||||
<img src="<%= project.images.find(img => img.isPrimary)?.url || project.images[0].url %>"
|
||||
alt="<%= project.title %>"
|
||||
class="w-full h-full object-cover transition-transform duration-300 hover:scale-110">
|
||||
<% } else { %>
|
||||
<div class="w-full h-full bg-gradient-to-br from-blue-400 to-purple-500 flex items-center justify-center">
|
||||
<i class="fas fa-code text-white text-3xl opacity-50"></i>
|
||||
</div>
|
||||
<% } %>
|
||||
</div>
|
||||
|
||||
<div class="p-6">
|
||||
<h3 class="text-xl font-bold text-gray-900 mb-2 line-clamp-1">
|
||||
<%= project.title %>
|
||||
</h3>
|
||||
<p class="text-gray-600 mb-4 line-clamp-2">
|
||||
<%= project.shortDescription || project.description %>
|
||||
</p>
|
||||
<a href="/portfolio/<%= project._id %>"
|
||||
class="text-blue-600 font-semibold hover:underline">
|
||||
자세히 보기 →
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<% }) %>
|
||||
<% } else { %>
|
||||
<div class="col-span-full text-center py-8">
|
||||
<p class="text-gray-500">관련 프로젝트가 없습니다.</p>
|
||||
</div>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<%- include('partials/footer') %>
|
||||
|
||||
<!-- Scripts -->
|
||||
<script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script>
|
||||
<script src="https://unpkg.com/swiper@8/swiper-bundle.min.js"></script>
|
||||
<script src="/js/main.js"></script>
|
||||
|
||||
<script>
|
||||
// Initialize Swiper
|
||||
if (document.querySelector('.portfolio-swiper')) {
|
||||
const swiper = new Swiper('.portfolio-swiper', {
|
||||
loop: true,
|
||||
pagination: {
|
||||
el: '.swiper-pagination',
|
||||
clickable: true,
|
||||
},
|
||||
navigation: {
|
||||
nextEl: '.swiper-button-next',
|
||||
prevEl: '.swiper-button-prev',
|
||||
},
|
||||
autoplay: {
|
||||
delay: 5000,
|
||||
},
|
||||
breakpoints: {
|
||||
640: {
|
||||
slidesPerView: 1,
|
||||
},
|
||||
768: {
|
||||
slidesPerView: 1,
|
||||
},
|
||||
1024: {
|
||||
slidesPerView: 1,
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Like functionality
|
||||
document.getElementById('like-btn').addEventListener('click', async function() {
|
||||
try {
|
||||
const response = await fetch(`/api/portfolio/<%= portfolio._id %>/like`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
document.getElementById('like-count').textContent = result.likes;
|
||||
this.classList.add('animate-pulse');
|
||||
setTimeout(() => {
|
||||
this.classList.remove('animate-pulse');
|
||||
}, 1000);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error liking project:', error);
|
||||
}
|
||||
});
|
||||
|
||||
// Social sharing
|
||||
function shareOnSocial(platform) {
|
||||
const url = encodeURIComponent(window.location.href);
|
||||
const title = encodeURIComponent('<%= portfolio.title %>');
|
||||
const description = encodeURIComponent('<%= portfolio.shortDescription || portfolio.description %>');
|
||||
|
||||
let shareUrl = '';
|
||||
|
||||
switch(platform) {
|
||||
case 'facebook':
|
||||
shareUrl = `https://www.facebook.com/sharer/sharer.php?u=${url}`;
|
||||
break;
|
||||
case 'twitter':
|
||||
shareUrl = `https://twitter.com/intent/tweet?url=${url}&text=${title}`;
|
||||
break;
|
||||
case 'linkedin':
|
||||
shareUrl = `https://www.linkedin.com/sharing/share-offsite/?url=${url}`;
|
||||
break;
|
||||
}
|
||||
|
||||
if (shareUrl) {
|
||||
window.open(shareUrl, '_blank', 'width=600,height=400');
|
||||
}
|
||||
}
|
||||
|
||||
// Copy link to clipboard
|
||||
function copyToClipboard() {
|
||||
navigator.clipboard.writeText(window.location.href).then(function() {
|
||||
// Show toast message
|
||||
const toast = document.createElement('div');
|
||||
toast.className = 'fixed bottom-4 right-4 bg-green-500 text-white px-4 py-2 rounded-lg shadow-lg z-50';
|
||||
toast.textContent = '링크가 복사되었습니다!';
|
||||
document.body.appendChild(toast);
|
||||
|
||||
setTimeout(() => {
|
||||
toast.remove();
|
||||
}, 3000);
|
||||
}).catch(function(err) {
|
||||
console.error('Error copying to clipboard:', err);
|
||||
});
|
||||
}
|
||||
|
||||
// Update view count
|
||||
fetch(`/api/portfolio/<%= portfolio._id %>/view`, {
|
||||
method: 'POST'
|
||||
}).catch(err => console.error('Error updating view count:', err));
|
||||
|
||||
// Category name helper
|
||||
function getCategoryName(category) {
|
||||
const categoryNames = {
|
||||
'web-development': '웹 개발',
|
||||
'mobile-app': '모바일 앱',
|
||||
'ui-ux-design': 'UI/UX 디자인',
|
||||
'branding': '브랜딩',
|
||||
'marketing': '디지털 마케팅'
|
||||
};
|
||||
return categoryNames[category] || category;
|
||||
}
|
||||
</script>
|
||||
|
||||
<%
|
||||
// Helper function for category names
|
||||
function getCategoryName(category) {
|
||||
const categoryNames = {
|
||||
'web-development': '웹 개발',
|
||||
'mobile-app': '모바일 앱',
|
||||
'ui-ux-design': 'UI/UX 디자인',
|
||||
'branding': '브랜딩',
|
||||
'marketing': '디지털 마케팅'
|
||||
};
|
||||
return categoryNames[category] || category;
|
||||
}
|
||||
%>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user