Fix blog tables (remark-gfm), update portfolio images, fix dropdown hover issues, remove contact form

This commit is contained in:
Kunthawat Greethong
2026-02-26 21:44:02 +07:00
parent f54c020097
commit dccfce8186
14 changed files with 196 additions and 203 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 682 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 278 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 748 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 471 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 868 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 285 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 285 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 852 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 336 KiB

View File

@@ -6,9 +6,9 @@ import path from 'path';
import matter from 'gray-matter'; import matter from 'gray-matter';
import { remark } from 'remark'; import { remark } from 'remark';
import html from 'remark-html'; import html from 'remark-html';
import gfm from 'remark-gfm';
interface Props { interface Props {
params: { slug: string }; params: Promise<{ slug: string }>;
} }
async function getPost(slug: string) { async function getPost(slug: string) {
@@ -23,24 +23,32 @@ async function getPost(slug: string) {
const { data, content } = matter(fileContent); const { data, content } = matter(fileContent);
const processedContent = await remark() const processedContent = await remark()
.use(gfm)
.use(html, { sanitize: false }) .use(html, { sanitize: false })
.process(content); .process(content);
const contentHtml = processedContent.toString(); const contentHtml = processedContent.toString();
// Support both 'category' and 'categories' field names
const category = data.category || (Array.isArray(data.categories) ? data.categories[0] : 'ทั่วไป');
// Support both 'image' and 'featuredImage' field names
const image = data.image || data.featuredImage || '/images/2021/03/ppr-pipe_000C.jpg';
return { return {
slug, slug,
title: data.title || 'ไม่มีชื่อ', title: data.title || 'ไม่มีชื่อ',
excerpt: data.excerpt || '', excerpt: data.excerpt || '',
date: data.date || new Date().toISOString(), date: data.date || new Date().toISOString(),
author: data.author || 'ดีลพลัสเทค', author: data.author || 'ดีลพลัสเทค',
category: data.category || 'ทั่วไป', category,
image: data.image || '/images/2021/03/ppr-pipe_000C.jpg', image,
content: contentHtml, content: contentHtml,
}; };
} }
export async function generateMetadata({ params }: Props) { export async function generateMetadata({ params }: Props) {
const post = await getPost(params.slug); const { slug } = await params;
const decodedSlug = decodeURIComponent(slug);
const post = await getPost(decodedSlug);
if (!post) return { title: 'ไม่พบบทความ' }; if (!post) return { title: 'ไม่พบบทความ' };
return { return {
@@ -59,7 +67,9 @@ export async function generateStaticParams() {
} }
export default async function BlogPostPage({ params }: Props) { export default async function BlogPostPage({ params }: Props) {
const post = await getPost(params.slug); const { slug } = await params;
const decodedSlug = decodeURIComponent(slug);
const post = await getPost(decodedSlug);
if (!post) { if (!post) {
notFound(); notFound();

View File

@@ -18,14 +18,19 @@ function getBlogPosts() {
const fileContent = fs.readFileSync(filePath, 'utf-8'); const fileContent = fs.readFileSync(filePath, 'utf-8');
const { data } = matter(fileContent); const { data } = matter(fileContent);
// Support both 'category' and 'categories' field names
const category = data.category || (Array.isArray(data.categories) ? data.categories[0] : 'ทั่วไป');
// Support both 'image' and 'featuredImage' field names
const image = data.image || data.featuredImage || '/images/2021/03/ppr-pipe_000C.jpg';
return { return {
slug: filename.replace('.md', ''), slug: filename.replace('.md', ''),
title: data.title || 'ไม่มีชื่อ', title: data.title || 'ไม่มีชื่อ',
excerpt: data.excerpt || '', excerpt: data.excerpt || '',
date: data.date || new Date().toISOString(), date: data.date || new Date().toISOString(),
author: data.author || 'ดีลพลัสเทค', author: data.author || 'ดีลพลัสเทค',
category: data.category || 'ทั่วไป', category,
image: data.image || '/images/2021/03/ppr-pipe_000C.jpg', image,
}; };
}).sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); }).sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
} }

View File

@@ -19,9 +19,8 @@ export default function ContactPage() {
</p> </p>
</div> </div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12"> <div className="max-w-2xl mx-auto">
{/* Contact Info */} {/* Contact Info */}
<div>
<div className="bg-secondary-800 rounded-2xl p-8 mb-8"> <div className="bg-secondary-800 rounded-2xl p-8 mb-8">
<h2 className="text-2xl font-bold text-white mb-6"></h2> <h2 className="text-2xl font-bold text-white mb-6"></h2>
@@ -103,78 +102,6 @@ export default function ContactPage() {
</ul> </ul>
</div> </div>
</div> </div>
{/* Contact Form */}
<div>
<div className="bg-white rounded-2xl shadow-card p-8">
<h2 className="text-2xl font-bold text-secondary-900 mb-6"></h2>
<form className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-secondary-700 mb-2">
- *
</label>
<input
type="text"
className="w-full px-4 py-3 rounded-lg border border-secondary-200 focus:border-primary-500 focus:ring-2 focus:ring-primary-500/20 outline-none transition-colors"
placeholder="ชื่อของคุณ"
/>
</div>
<div>
<label className="block text-sm font-medium text-secondary-700 mb-2">
*
</label>
<input
type="tel"
className="w-full px-4 py-3 rounded-lg border border-secondary-200 focus:border-primary-500 focus:ring-2 focus:ring-primary-500/20 outline-none transition-colors"
placeholder="08x-xxx-xxxx"
/>
</div>
</div>
<div>
<label className="block text-sm font-medium text-secondary-700 mb-2">
</label>
<input
type="email"
className="w-full px-4 py-3 rounded-lg border border-secondary-200 focus:border-primary-500 focus:ring-2 focus:ring-primary-500/20 outline-none transition-colors"
placeholder="email@example.com"
/>
</div>
<div>
<label className="block text-sm font-medium text-secondary-700 mb-2">
*
</label>
<select className="w-full px-4 py-3 rounded-lg border border-secondary-200 focus:border-primary-500 focus:ring-2 focus:ring-primary-500/20 outline-none transition-colors">
<option value=""></option>
<option value="quote"></option>
<option value="product"></option>
<option value="service"></option>
<option value="other"></option>
</select>
</div>
<div>
<label className="block text-sm font-medium text-secondary-700 mb-2">
*
</label>
<textarea
rows={5}
className="w-full px-4 py-3 rounded-lg border border-secondary-200 focus:border-primary-500 focus:ring-2 focus:ring-primary-500/20 outline-none transition-colors resize-none"
placeholder="รายละเอียดที่ต้องการสอบถาม..."
/>
</div>
<button type="submit" className="btn-primary w-full">
</button>
</form>
</div>
</div>
</div>
</div> </div>
</div> </div>
); );

View File

@@ -7,34 +7,79 @@ export const metadata = {
const portfolioItems = [ const portfolioItems = [
{ {
title: 'โครงการอาคารสำนักงาน', title: 'Cyber World',
category: 'อาคารพาณิชย์', category: 'อาคารพาณิชย์',
image: '/images/2021/03/hdpe-pipe_000C.jpg', image: '/images/2021/02/IMG_3089.jpg',
}, },
{ {
title: 'โครงการโรงงานอุตสาหกรรม', title: 'Toyox',
category: 'อุตสาหกรรม', category: 'อุตสาหกรรม',
image: '/images/2021/03/ppr-pipe_000C.jpg', image: '/images/2021/02/IMG_2226.jpg',
}, },
{ {
title: 'โครงการบ้านพักอาศัย', title: 'PPR PIPE',
category: 'ท่อยู่อาศัย', category: 'ท่อ PPR',
image: '/images/2021/03/pvc-pipe_000C.jpg', image: '/images/2021/02/Image1.jpg',
}, },
{ {
title: 'โรงการระบบดับเพลิง', title: 'โรงงานเอธานอล',
category: 'ระบบดับเพลิง', category: 'อุตสาหกรรม',
image: '/images/2021/03/realflex_000C.jpg', image: '/images/2021/02/ลพบุรี5.jpg',
}, },
{ {
title: 'โครงการระบบปรับอากาศ', title: 'บจก.หยั่น หว่อ หยุ่น',
category: 'HVAC', category: 'อุตสาหกรรม',
image: '/images/2021/03/grilles_000C.jpg', image: '/images/2021/02/สมุทรสาคร2.jpg',
}, },
{ {
title: 'โครงการระบบประปา', title: 'ซีคอนบางแค',
category: 'ระบบประปา', category: 'อาคารพาณิชย์',
image: '/images/2021/03/upvc-pipe_000C.jpg', image: '/images/2021/02/บางแค1.jpg',
},
{
title: 'ซีพีแรม ลาดกระบัง',
category: 'อาคารพาณิชย์',
image: '/images/2021/02/ลาดกระบัง1.jpg',
},
{
title: 'ซีพีแรม (บ่อเงิน)',
category: 'อาคารพาณิชย์',
image: '/images/2021/02/บ่อเงิน3.jpg',
},
{
title: 'บริษัท บีกริม',
category: 'อุตสาหกรรม',
image: '/images/2021/02/บีกริม-จำกัด-e1503304339753.jpg',
},
{
title: 'เอฟแอนด์เอฟ ฟูด',
category: 'อาหาร',
image: '/images/2021/02/เอฟแอนดืเอฟ2-horz.jpg',
},
{
title: 'Eminent Air Factory',
category: 'อุตสาหกรรม',
image: '/images/2021/02/บางพลี1-horz.jpg',
},
{
title: 'ไทยน้ำทิพย์',
category: 'อุตสาหกรรม',
image: '/images/2021/02/ไทยน้ำทิพย์1-horz.jpg',
},
{
title: 'Essilor Factory',
category: 'อุตสาหกรรม',
image: '/images/2021/02/Essilor1-horz.jpg',
},
{
title: 'Dog Food Factory',
category: 'อาหาร',
image: '/images/2021/02/บ.เอเชี่ยน1.jpg',
},
{
title: 'โครงการประชารัฐ',
category: 'ภาครัฐ',
image: '/images/2021/02/ประชารัฐ1-e1503323597848.jpg',
}, },
]; ];
@@ -55,21 +100,24 @@ export default function PortfolioPage() {
{/* Portfolio Grid */} {/* Portfolio Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{portfolioItems.map((item, index) => ( {portfolioItems.map((item, index) => (
<div key={index} className="card group cursor-pointer"> <div key={index} className="card group cursor-pointer overflow-hidden">
<div className="relative aspect-video bg-secondary-100"> <div className="relative aspect-video bg-secondary-100 overflow-hidden">
<Image <Image
src={item.image} src={item.image}
alt={item.title} alt={item.title}
fill fill
className="object-cover group-hover:scale-105 transition-transform duration-300" className="object-cover group-hover:scale-105 transition-transform duration-300"
/> />
<div className="absolute inset-0 bg-gradient-to-t from-secondary-900/80 to-transparent opacity-0 group-hover:opacity-100 transition-opacity" /> {/* Hover Overlay */}
<div className="absolute bottom-0 left-0 right-0 p-4 translate-y-full group-hover:translate-y-0 transition-transform"> <div className="absolute inset-0 bg-gradient-to-t from-secondary-900/80 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
{/* Hover Text - hidden by default, slides up on hover */}
<div className="absolute bottom-0 left-0 right-0 p-4 translate-y-full group-hover:translate-y-0 transition-transform duration-300">
<span className="text-primary-400 text-sm">{item.category}</span> <span className="text-primary-400 text-sm">{item.category}</span>
<h3 className="text-white font-bold">{item.title}</h3> <h3 className="text-white font-bold">{item.title}</h3>
</div> </div>
</div> </div>
<div className="p-4 group-hover:hidden"> {/* Static Info - only visible when NOT hovering */}
<div className="p-4 bg-white group-hover:hidden">
<span className="text-xs text-primary-600 font-semibold">{item.category}</span> <span className="text-xs text-primary-600 font-semibold">{item.category}</span>
<h3 className="text-lg font-bold text-secondary-900 mt-1">{item.title}</h3> <h3 className="text-lg font-bold text-secondary-900 mt-1">{item.title}</h3>
</div> </div>

View File

@@ -8,7 +8,6 @@ import { cn } from '@/lib/utils';
export default function Header() { export default function Header() {
const [mobileMenuOpen, setMobileMenuOpen] = useState(false); const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const [activeDropdown, setActiveDropdown] = useState<string | null>(null);
return ( return (
<header className="fixed top-0 left-0 right-0 z-50 bg-white shadow-md"> <header className="fixed top-0 left-0 right-0 z-50 bg-white shadow-md">
@@ -60,9 +59,7 @@ export default function Header() {
{mainNavigation.map((item) => ( {mainNavigation.map((item) => (
<div <div
key={item.href} key={item.href}
className="relative" className="relative group"
onMouseEnter={() => item.children && setActiveDropdown(item.href)}
onMouseLeave={() => setActiveDropdown(null)}
> >
<Link <Link
href={item.href} href={item.href}
@@ -79,12 +76,15 @@ export default function Header() {
)} )}
</Link> </Link>
{/* Dropdown */} {/* Dropdown - CSS hover with invisible bridge */}
{item.children && activeDropdown === item.href && ( {item.children && (
<div className="absolute top-full left-1/2 -translate-x-1/2 min-w-[600px] bg-white shadow-xl rounded-lg py-4 mt-2 border border-secondary-100"> <div className="absolute top-full left-0 pt-2 opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 z-50">
{/* Invisible bridge to prevent gap */}
<div className="absolute -top-2 left-0 right-0 h-2" />
<div className="min-w-[600px] bg-white shadow-xl rounded-lg py-4 border border-secondary-100">
<div className="grid grid-cols-2 gap-1 px-4"> <div className="grid grid-cols-2 gap-1 px-4">
{item.children.map((child) => ( {item.children.map((child) => (
<div key={child.href} className="relative group"> <div key={child.href} className="relative group/sub">
<Link <Link
href={child.href} href={child.href}
className="block px-3 py-2 text-secondary-700 hover:bg-primary-50 hover:text-primary-700 transition-colors rounded font-medium" className="block px-3 py-2 text-secondary-700 hover:bg-primary-50 hover:text-primary-700 transition-colors rounded font-medium"
@@ -92,7 +92,9 @@ export default function Header() {
{child.label} {child.label}
</Link> </Link>
{child.children && ( {child.children && (
<div className="hidden group-hover:block absolute left-full top-0 ml-2 w-56 bg-white shadow-xl rounded-lg py-2 border border-secondary-100 max-h-96 overflow-y-auto"> <div className="hidden group-hover/sub:block absolute left-full top-0 w-56 !bg-white shadow-xl rounded-lg py-2 border border-secondary-100 max-h-96 overflow-y-auto z-50">
{/* Invisible bridge to prevent gap */}
<div className="absolute -top-2 -bottom-2 -left-2 w-2" />
{child.children.map((subChild) => ( {child.children.map((subChild) => (
<Link <Link
key={subChild.href} key={subChild.href}
@@ -108,6 +110,7 @@ export default function Header() {
))} ))}
</div> </div>
</div> </div>
</div>
)} )}
</div> </div>
))} ))}