feat: Upgrade to Astro with full PDPA compliance

PDPA Features:
 Cookie consent banner
 Consent logging API
 Admin dashboard
 Privacy Policy
 Terms & Conditions

Technical:
 Astro 5.x + Tailwind v4
 Docker on port 80
 SQLite database
 15 pages built

Ready for Easypanel deployment.
This commit is contained in:
Kunthawat
2026-03-12 10:01:04 +07:00
parent 668f69048f
commit 77ac4d2d05
13719 changed files with 307487 additions and 25765 deletions

View File

@@ -1,599 +0,0 @@
import { notFound } from 'next/navigation';
import Image from 'next/image';
import Link from 'next/link';
import { productCategories, portfolioProjects } from '@/data/site-config';
import type { ProductCategory, FAQItem } from '@/types';
interface Props {
params: { slug: string[] };
}
// Generate all possible paths from product categories and portfolio projects
export async function generateStaticParams() {
const paths: { slug: string[] }[] = [];
// Add product category paths
productCategories.forEach((product) => {
// Remove leading slash and split the href
const pathParts = product.href.replace(/^\//, '').replace(/\/$/, '').split('/');
paths.push({ slug: pathParts });
});
// Add portfolio project paths
portfolioProjects.forEach((project) => {
const pathParts = project.href.replace(/^\//, '').replace(/\/$/, '').split('/');
paths.push({ slug: pathParts });
});
return paths;
}
type ContentType = 'product' | 'portfolio';
function findContentBySlug(slug: string[]): { type: ContentType; data: ProductCategory | typeof portfolioProjects[0] } | null {
// Decode URL-encoded slug parts
const decodedSlug = slug.map(part => decodeURIComponent(part));
const fullPath = '/' + decodedSlug.join('/') + '/';
// Check products first
const product = productCategories.find((p) => p.href === fullPath);
if (product) {
return { type: 'product', data: product };
}
// Check portfolio projects
const portfolio = portfolioProjects.find((p) => p.href === fullPath);
if (portfolio) {
return { type: 'portfolio', data: portfolio };
}
return null;
}
export async function generateMetadata({ params }: Props) {
const content = findContentBySlug(params.slug);
if (!content) {
return { title: 'ไม่พบหน้า' };
}
const { type, data } = content;
if (type === 'product') {
const product = data as ProductCategory;
const title = product.keywords?.[0]
? `${product.name} | ${product.keywords[0]} - ดีลพลัสเทค`
: `${product.name} - ${product.nameEn} | ดีลพลัสเทค`;
return {
title,
description: product.description,
keywords: product.keywords?.join(', '),
alternates: {
canonical: product.href,
},
openGraph: {
title: product.name,
description: product.description,
images: [product.image],
type: 'website',
url: `https://dealplustech.co.th${product.href}`,
},
};
}
return {
title: data.name,
description: data.description,
};
}
// Schema.org JSON-LD for Products
function ProductSchema({ product }: { product: ProductCategory }) {
const schema = {
'@context': 'https://schema.org',
'@type': 'Product',
name: product.name,
description: product.description,
image: product.image,
brand: {
'@type': 'Brand',
name: product.schemaData?.brand || product.nameEn,
},
manufacturer: {
'@type': 'Organization',
name: product.schemaData?.manufacturer || 'ดีลพลัสเทค',
},
...(product.schemaData?.sku && { sku: product.schemaData.sku }),
...(product.schemaData?.mpn && { mpn: product.schemaData.mpn }),
...(product.schemaData?.material && { material: product.schemaData.material }),
category: product.schemaData?.category || 'Industrial Pipe & Equipment',
offers: {
'@type': 'Offer',
availability: 'https://schema.org/InStock',
priceCurrency: 'THB',
seller: {
'@type': 'Organization',
name: 'ดีลพลัสเทค',
},
},
};
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
);
}
// BreadcrumbList Schema for SEO
function BreadcrumbSchema({ product }: { product: ProductCategory }) {
const schema = {
'@context': 'https://schema.org',
'@type': 'BreadcrumbList',
itemListElement: [
{
'@type': 'ListItem',
position: 1,
name: 'หน้าแรก',
item: 'https://dealplustech.co.th',
},
{
'@type': 'ListItem',
position: 2,
name: 'สินค้า',
item: 'https://dealplustech.co.th/product/',
},
{
'@type': 'ListItem',
position: 3,
name: product.name,
item: `https://dealplustech.co.th${product.href}`,
},
],
};
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
);
}
// FAQ Schema for SEO
function FAQSchema({ faq }: { faq: FAQItem[] }) {
const schema = {
'@context': 'https://schema.org',
'@type': 'FAQPage',
mainEntity: faq.map((item) => ({
'@type': 'Question',
name: item.question,
acceptedAnswer: {
'@type': 'Answer',
text: item.answer,
},
})),
};
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
);
}
export default function DynamicPage({ params }: Props) {
const content = findContentBySlug(params.slug);
if (!content) {
notFound();
}
const { type, data } = content;
// Render portfolio project page
if (type === 'portfolio') {
return <PortfolioPage project={data as typeof portfolioProjects[0]} />;
}
// Render product page
return <ProductPage product={data as ProductCategory} />;
}
// Product Page Component
function ProductPage({ product }: { product: ProductCategory }) {
// Find related products - either by explicit IDs or same category
const relatedProducts = product.relatedProductIds
? productCategories.filter((p) => product.relatedProductIds?.includes(p.id))
: productCategories
.filter((p) => p.slug === product.slug && p.id !== product.id)
.slice(0, 4);
return (
<>
{/* Schema.org Structured Data */}
<ProductSchema product={product} />
<BreadcrumbSchema product={product} />
{product.faq && product.faq.length > 0 && <FAQSchema faq={product.faq} />}
<div className="pt-24 pb-16">
<div className="container mx-auto px-4">
{/* Breadcrumb */}
<nav className="mb-6">
<ol className="flex items-center gap-2 text-sm flex-wrap">
<li>
<Link href="/" className="text-secondary-500 hover:text-primary-600">
</Link>
</li>
<li className="text-secondary-400">/</li>
<li>
<Link href="/product" className="text-secondary-500 hover:text-primary-600">
</Link>
</li>
<li className="text-secondary-400">/</li>
<li className="text-primary-600 font-medium">{product.name}</li>
</ol>
</nav>
{/* Product Header */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 mb-12">
{/* Product Image */}
<div className="relative aspect-video bg-secondary-100 rounded-xl overflow-hidden">
<Image
src={product.image}
alt={`${product.name} - ${product.nameEn}`}
fill
className="object-cover"
priority
/>
</div>
{/* Product Info */}
<div>
<span className="text-primary-600 font-semibold">{product.nameEn}</span>
<h1 className="text-3xl md:text-4xl font-bold text-secondary-900 mt-2 mb-4">
{product.name}
</h1>
<p className="text-secondary-600 text-lg mb-6 leading-relaxed">
{product.description}
</p>
{/* CTA */}
<div className="flex flex-wrap gap-4 mb-6">
<Link href="/contact-us" className="btn-primary">
</Link>
<a
href="tel:090-555-1415"
className="btn-outline"
>
โทรสอบถาม: 090-555-1415
</a>
</div>
{/* Quick Contact Info */}
<div className="bg-secondary-50 rounded-lg p-4 space-y-2">
<div className="flex items-center gap-2 text-secondary-600">
<svg className="w-5 h-5 text-primary-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
</svg>
<span>: <strong>090-555-1415</strong></span>
</div>
<div className="flex items-center gap-2 text-secondary-600">
<svg className="w-5 h-5 text-primary-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
</svg>
<span>Email: <strong>info@dealplustech.co.th</strong></span>
</div>
<div className="flex items-center gap-2 text-secondary-600">
<svg className="w-5 h-5 text-primary-600" fill="currentColor" viewBox="0 0 24 24">
<path d="M19.365 9.863c.349 0 .63.285.63.631 0 .345-.281.63-.63.63H17.61v1.125h1.755c.349 0 .63.283.63.63 0 .344-.281.629-.63.629h-2.386c-.345 0-.627-.285-.627-.629V8.108c0-.345.282-.63.63-.63h2.386c.346 0 .627.285.627.63 0 .349-.281.63-.63.63H17.61v1.125h1.755zm-3.855 3.016c0 .27-.174.51-.432.596-.064.021-.133.031-.199.031-.211 0-.391-.09-.51-.25l-2.443-3.317v2.94c0 .344-.279.629-.631.629-.346 0-.626-.285-.626-.629V8.108c0-.27.173-.51.43-.595.06-.023.136-.033.194-.033.195 0 .375.104.495.254l2.462 3.33V8.108c0-.345.282-.63.63-.63.345 0 .63.285.63.63v4.771zm-5.741 0c0 .344-.282.629-.631.629-.345 0-.627-.285-.627-.629V8.108c0-.345.282-.63.63-.63.346 0 .628.285.628.63v4.771zm-2.466.629H4.917c-.345 0-.63-.285-.63-.629V8.108c0-.345.285-.63.63-.63.348 0 .63.285.63.63v4.141h1.756c.348 0 .629.283.629.63 0 .344-.282.629-.629.629M24 10.314C24 4.943 18.615.572 12 .572S0 4.943 0 10.314c0 4.811 4.27 8.842 10.035 9.608.391.082.923.258 1.058.59.12.301.079.766.038 1.08l-.164 1.02c-.045.301-.24 1.186 1.049.645 1.291-.539 6.916-4.078 9.436-6.975C23.176 14.393 24 12.458 24 10.314"/>
</svg>
<span>LINE: <strong>@dealplustech</strong></span>
</div>
</div>
</div>
</div>
{/* Specifications Section */}
{product.specifications && product.specifications.length > 0 && (
<section className="mb-12">
<h2 className="text-2xl font-bold text-secondary-900 mb-6 flex items-center gap-2">
<svg className="w-6 h-6 text-primary-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
</h2>
<div className="bg-white rounded-xl border border-secondary-200 overflow-hidden">
<table className="w-full">
<tbody>
{product.specifications.map((spec, index) => (
<tr key={index} className={index % 2 === 0 ? 'bg-secondary-50' : 'bg-white'}>
<td className="px-6 py-4 font-medium text-secondary-700 w-1/3">{spec.label}</td>
<td className="px-6 py-4 text-secondary-900">
{spec.value}{spec.unit ? ` ${spec.unit}` : ''}
</td>
</tr>
))}
</tbody>
</table>
</div>
</section>
)}
{/* Product Tables Section */}
{product.productTables && product.productTables.length > 0 && (
<section className="mb-12">
<h2 className="text-2xl font-bold text-secondary-900 mb-6 flex items-center gap-2">
<svg className="w-6 h-6 text-primary-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 10h18M3 14h18m-9-4v8m-7 0h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" />
</svg>
</h2>
<div className="space-y-8">
{product.productTables.map((table, tableIndex) => (
<div key={tableIndex} className="bg-white rounded-xl border border-secondary-200 overflow-hidden">
<h3 className="text-lg font-semibold text-secondary-800 p-4 bg-secondary-50 border-b border-secondary-200">
{table.tableName}
</h3>
<div className="overflow-x-auto">
<table className="w-full min-w-[600px]">
<thead>
<tr className="bg-primary-50">
{table.headers.map((header, headerIndex) => (
<th key={headerIndex} className="px-4 py-3 text-left text-sm font-semibold text-primary-700">
{header}
</th>
))}
</tr>
</thead>
<tbody>
{table.rows.map((row, rowIndex) => (
<tr key={rowIndex} className={rowIndex % 2 === 0 ? 'bg-white' : 'bg-secondary-50'}>
{row.map((cell, cellIndex) => (
<td key={cellIndex} className="px-4 py-3 text-sm text-secondary-700">
{cell}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
</div>
))}
</div>
</section>
)}
{/* Features Section */}
{product.features && product.features.length > 0 && (
<section className="mb-12">
<h2 className="text-2xl font-bold text-secondary-900 mb-6 flex items-center gap-2">
<svg className="w-6 h-6 text-primary-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{product.features.map((feature, index) => (
<div key={index} className="flex items-start gap-3 p-4 bg-primary-50 rounded-lg">
<svg className="w-5 h-5 text-primary-600 mt-0.5 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
<span className="text-secondary-700">{feature}</span>
</div>
))}
</div>
</section>
)}
{/* Applications Section */}
{product.applications && product.applications.length > 0 && (
<section className="mb-12">
<h2 className="text-2xl font-bold text-secondary-900 mb-6 flex items-center gap-2">
<svg className="w-6 h-6 text-primary-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
</svg>
</h2>
<div className="flex flex-wrap gap-3">
{product.applications.map((app, index) => (
<span key={index} className="inline-flex items-center px-4 py-2 bg-secondary-100 text-secondary-700 rounded-full font-medium">
{app}
</span>
))}
</div>
</section>
)}
{/* Certifications Section */}
{product.certifications && product.certifications.length > 0 && (
<section className="mb-12">
<h2 className="text-2xl font-bold text-secondary-900 mb-6 flex items-center gap-2">
<svg className="w-6 h-6 text-primary-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z" />
</svg>
</h2>
<div className="flex flex-wrap gap-3">
{product.certifications.map((cert, index) => (
<span key={index} className="inline-flex items-center px-4 py-2 bg-primary-100 text-primary-700 rounded-lg font-semibold">
{cert}
</span>
))}
</div>
</section>
)}
{/* SEO Content Section */}
{product.seoContent && (
<section className="mb-12">
<div className="prose prose-lg max-w-none">
<div dangerouslySetInnerHTML={{ __html: product.seoContent.replace(/\n/g, '<br/>') }} />
</div>
</section>
)}
{/* FAQ Section */}
{product.faq && product.faq.length > 0 && (
<section className="mb-12">
<h2 className="text-2xl font-bold text-secondary-900 mb-6 flex items-center gap-2">
<svg className="w-6 h-6 text-primary-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</h2>
<div className="space-y-4">
{product.faq.map((item, index) => (
<details key={index} className="group bg-white rounded-lg border border-secondary-200 overflow-hidden">
<summary className="flex items-center justify-between p-5 cursor-pointer font-medium text-secondary-900 hover:bg-secondary-50">
<span>{item.question}</span>
<svg className="w-5 h-5 text-secondary-500 group-open:rotate-180 transition-transform" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</summary>
<div className="px-5 pb-5 text-secondary-600 border-t border-secondary-100 pt-4">
{item.answer}
</div>
</details>
))}
</div>
</section>
)}
{/* Related Products */}
{relatedProducts.length > 0 && (
<section className="mt-16">
<h2 className="text-2xl font-bold text-secondary-900 mb-6">
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{relatedProducts.map((related) => (
<Link
key={related.id}
href={related.href}
className="card group"
>
<div className="relative aspect-video bg-secondary-100">
<Image
src={related.image}
alt={related.name}
fill
className="object-cover group-hover:scale-105 transition-transform duration-300"
/>
</div>
<div className="p-4">
<span className="text-xs text-primary-600 font-semibold">
{related.nameEn}
</span>
<h3 className="text-lg font-bold text-secondary-900 mt-1 group-hover:text-primary-600 transition-colors">
{related.name}
</h3>
</div>
</Link>
))}
</div>
</section>
)}
</div>
</div>
</>
);
}
// Portfolio Page Component
function PortfolioPage({ project }: { project: typeof portfolioProjects[0] }) {
return (
<div className="pt-32 pb-16">
<div className="container mx-auto px-4">
{/* Breadcrumb */}
<nav className="mb-6">
<ol className="flex items-center gap-2 text-sm">
<li>
<Link href="/" className="text-secondary-500 hover:text-primary-600">
</Link>
</li>
<li className="text-secondary-400">/</li>
<li>
<Link href="/portfolio" className="text-secondary-500 hover:text-primary-600">
</Link>
</li>
<li className="text-secondary-400">/</li>
<li className="text-primary-600 font-medium">{project.name}</li>
</ol>
</nav>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12">
{/* Project Image */}
<div className="relative aspect-video bg-secondary-100 rounded-xl overflow-hidden">
<Image
src={project.image}
alt={project.name}
fill
className="object-cover"
priority
/>
</div>
{/* Project Info */}
<div>
<h1 className="text-3xl md:text-4xl font-bold text-secondary-900 mb-4">
{project.name}
</h1>
<p className="text-secondary-600 text-lg mb-6">
{project.description}
</p>
{/* CTA */}
<div className="flex flex-wrap gap-4">
<Link href="/contact-us" className="btn-primary">
</Link>
<Link href="/portfolio" className="btn-outline">
</Link>
</div>
</div>
</div>
{/* Other Projects */}
<div className="mt-16">
<h2 className="text-2xl font-bold text-secondary-900 mb-6">
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{portfolioProjects.filter(p => p.id !== project.id).slice(0, 4).map((other) => (
<Link
key={other.id}
href={other.href}
className="card group"
>
<div className="relative aspect-video bg-secondary-100">
<Image
src={other.image}
alt={other.name}
fill
className="object-cover group-hover:scale-105 transition-transform duration-300"
/>
</div>
<div className="p-4">
<h3 className="text-lg font-bold text-secondary-900 group-hover:text-primary-600 transition-colors">
{other.name}
</h3>
</div>
</Link>
))}
</div>
</div>
</div>
</div>
);
}

View File

@@ -1,119 +0,0 @@
import Image from 'next/image';
import { siteConfig } from '@/data/site-config';
export const metadata = {
title: 'เกี่ยวกับเรา',
description: 'เรียนรู้เพิ่มเติมเกี่ยวกับดีลพลัสเทค ผู้เชี่ยวชาญด้านวัสดุท่อและอุปกรณ์ระบบท่อ',
};
export default function AboutPage() {
return (
<div className="pt-32 pb-16">
<div className="container mx-auto px-4">
{/* Hero */}
<div className="relative h-[400px] -mt-32 mb-12 rounded-b-3xl overflow-hidden">
<div className="absolute inset-0 bg-gradient-to-r from-secondary-900 via-secondary-800 to-secondary-900" />
<div className="absolute inset-0 flex items-center justify-center">
<div className="text-center">
<h1 className="text-4xl md:text-5xl font-bold text-white mb-4">
<span className="text-primary-400">{siteConfig.name}</span>
</h1>
<p className="text-xl text-secondary-200">
</p>
</div>
</div>
</div>
{/* Company Story */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 mb-16">
<div>
<h2 className="text-3xl font-bold text-secondary-900 mb-6"></h2>
<div className="space-y-4 text-secondary-600">
<p>
{siteConfig.nameTh}
</p>
<p>
10
</p>
<p>
</p>
</div>
</div>
<div className="relative aspect-video bg-secondary-100 rounded-xl overflow-hidden">
<Image
src="/images/2021/03/hdpe-pipe_000C.jpg"
alt="เกี่ยวกับดีลพลัสเทค"
fill
className="object-cover"
/>
</div>
</div>
{/* Vision & Mission */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 mb-16">
<div className="card-industrial">
<h3 className="text-2xl font-bold text-primary-400 mb-4"></h3>
<p className="text-secondary-200">
</p>
</div>
<div className="card-industrial">
<h3 className="text-2xl font-bold text-primary-400 mb-4"></h3>
<p className="text-secondary-200">
</p>
</div>
</div>
{/* Core Values */}
<div className="text-center mb-12">
<h2 className="text-3xl font-bold text-secondary-900 mb-8"></h2>
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
<div className="p-6 bg-primary-50 rounded-xl">
<div className="w-12 h-12 bg-primary-600 rounded-lg flex items-center justify-center mx-auto mb-4">
<svg className="w-6 h-6 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
<h4 className="font-bold text-secondary-900 mb-2"></h4>
<p className="text-secondary-600 text-sm"></p>
</div>
<div className="p-6 bg-primary-50 rounded-xl">
<div className="w-12 h-12 bg-primary-600 rounded-lg flex items-center justify-center mx-auto mb-4">
<svg className="w-6 h-6 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
<h4 className="font-bold text-secondary-900 mb-2"></h4>
<p className="text-secondary-600 text-sm"></p>
</div>
<div className="p-6 bg-primary-50 rounded-xl">
<div className="w-12 h-12 bg-primary-600 rounded-lg flex items-center justify-center mx-auto mb-4">
<svg className="w-6 h-6 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z" />
</svg>
</div>
<h4 className="font-bold text-secondary-900 mb-2"></h4>
<p className="text-secondary-600 text-sm"></p>
</div>
<div className="p-6 bg-primary-50 rounded-xl">
<div className="w-12 h-12 bg-primary-600 rounded-lg flex items-center justify-center mx-auto mb-4">
<svg className="w-6 h-6 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
</svg>
</div>
<h4 className="font-bold text-secondary-900 mb-2"></h4>
<p className="text-secondary-600 text-sm"></p>
</div>
</div>
</div>
</div>
</div>
);
}

View File

@@ -1,83 +0,0 @@
import Image from 'next/image';
import Link from 'next/link';
import { portfolioProjects } from '@/data/site-config';
export const metadata = {
title: 'ผลงานทั้งหมด | All Projects - ดีลพลัสเทค',
description: 'ผลงานการติดตั้งระบบท่อทุกโครงการ โครงการระบบท่อโรงงาน โครงการระบบน้ำ โครงการระบบดับเพลิง',
};
export default function AllProjectsPage() {
return (
<div className="pt-32 pb-16">
<div className="container mx-auto px-4">
{/* Breadcrumb */}
<nav className="mb-6">
<ol className="flex items-center gap-2 text-sm">
<li>
<Link href="/" className="text-secondary-500 hover:text-primary-600">
</Link>
</li>
<li className="text-secondary-400">/</li>
<li className="text-primary-600 font-medium"></li>
</ol>
</nav>
{/* Header */}
<div className="mb-12">
<h1 className="text-3xl md:text-4xl font-bold text-secondary-900 mb-4">
</h1>
<p className="text-secondary-600 text-lg max-w-3xl">
</p>
</div>
{/* Projects Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{portfolioProjects.map((project) => (
<Link
key={project.id}
href={project.href}
className="card group"
>
<div className="relative aspect-video bg-secondary-100">
<Image
src={project.image}
alt={project.name}
fill
className="object-cover group-hover:scale-105 transition-transform duration-300"
/>
</div>
<div className="p-4">
<h2 className="text-lg font-bold text-secondary-900 group-hover:text-primary-600 transition-colors">
{project.name}
</h2>
<p className="text-sm text-secondary-600 mt-2 line-clamp-2">
{project.description}
</p>
</div>
</Link>
))}
</div>
{/* CTA */}
<div className="mt-12 text-center">
<p className="text-secondary-600 mb-4">
?
</p>
<div className="flex justify-center gap-4">
<Link href="/contact-us" className="btn-primary">
</Link>
<a href="tel:090-555-1415" className="btn-outline">
090-555-1415
</a>
</div>
</div>
</div>
</div>
);
}

View File

@@ -1,143 +0,0 @@
import { notFound } from 'next/navigation';
import Image from 'next/image';
import Link from 'next/link';
import fs from 'fs';
import path from 'path';
import matter from 'gray-matter';
import { remark } from 'remark';
import html from 'remark-html';
import gfm from 'remark-gfm';
interface Props {
params: Promise<{ slug: string }>;
}
async function getPost(slug: string) {
const blogDir = path.join(process.cwd(), 'src/content/blog');
const filePath = path.join(blogDir, `${slug}.md`);
if (!fs.existsSync(filePath)) {
return null;
}
const fileContent = fs.readFileSync(filePath, 'utf-8');
const { data, content } = matter(fileContent);
const processedContent = await remark()
.use(gfm)
.use(html, { sanitize: false })
.process(content);
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 {
slug,
title: data.title || 'ไม่มีชื่อ',
excerpt: data.excerpt || '',
date: data.date || new Date().toISOString(),
author: data.author || 'ดีลพลัสเทค',
category,
image,
content: contentHtml,
};
}
export async function generateMetadata({ params }: Props) {
const { slug } = await params;
const decodedSlug = decodeURIComponent(slug);
const post = await getPost(decodedSlug);
if (!post) return { title: 'ไม่พบบทความ' };
return {
title: post.title,
description: post.excerpt,
};
}
export async function generateStaticParams() {
const blogDir = path.join(process.cwd(), 'src/content/blog');
const files = fs.readdirSync(blogDir).filter(f => f.endsWith('.md'));
return files.map(filename => ({
slug: filename.replace('.md', ''),
}));
}
export default async function BlogPostPage({ params }: Props) {
const { slug } = await params;
const decodedSlug = decodeURIComponent(slug);
const post = await getPost(decodedSlug);
if (!post) {
notFound();
}
return (
<div className="pt-32 pb-16">
<article className="container mx-auto px-4">
{/* Header */}
<header className="max-w-3xl mx-auto mb-8">
<Link
href="/blog"
className="inline-flex items-center text-primary-600 hover:text-primary-700 mb-4"
>
<svg className="w-4 h-4 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
</svg>
</Link>
<span className="industrial-badge">{post.category}</span>
<h1 className="text-3xl md:text-4xl lg:text-5xl font-bold text-secondary-900 mt-4 mb-4">
{post.title}
</h1>
<div className="flex items-center gap-4 text-secondary-600">
<span>{post.author}</span>
<span></span>
<time>
{new Date(post.date).toLocaleDateString('th-TH', {
year: 'numeric',
month: 'long',
day: 'numeric',
})}
</time>
</div>
</header>
{/* Featured Image */}
<div className="relative aspect-video max-w-4xl mx-auto rounded-xl overflow-hidden mb-8">
<Image
src={post.image}
alt={post.title}
fill
className="object-cover"
/>
</div>
{/* Content */}
<div
className="max-w-3xl mx-auto prose prose-lg prose-headings:font-bold prose-headings:text-secondary-900 prose-p:text-secondary-600 prose-a:text-primary-600 prose-strong:text-secondary-900"
dangerouslySetInnerHTML={{ __html: post.content }}
/>
{/* CTA */}
<div className="max-w-3xl mx-auto mt-12 bg-secondary-800 rounded-2xl p-8 text-center">
<h2 className="text-2xl font-bold text-white mb-4">
?
</h2>
<p className="text-secondary-300 mb-6">
</p>
<Link href="/contact-us" className="btn-primary">
</Link>
</div>
</article>
</div>
);
}

View File

@@ -1,106 +0,0 @@
import Link from 'next/link';
import Image from 'next/image';
import fs from 'fs';
import path from 'path';
import matter from 'gray-matter';
export const metadata = {
title: 'บทความ',
description: 'บทความความรู้เกี่ยวกับวัสดุท่อ อุปกรณ์ระบบท่อ และเทคนิคการติดตั้ง',
};
function getBlogPosts() {
const blogDir = path.join(process.cwd(), 'src/content/blog');
const files = fs.readdirSync(blogDir).filter(f => f.endsWith('.md'));
return files.map(filename => {
const filePath = path.join(blogDir, filename);
const fileContent = fs.readFileSync(filePath, 'utf-8');
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 {
slug: filename.replace('.md', ''),
title: data.title || 'ไม่มีชื่อ',
excerpt: data.excerpt || '',
date: data.date || new Date().toISOString(),
author: data.author || 'ดีลพลัสเทค',
category,
image,
};
}).sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
}
export default function BlogPage() {
const posts = getBlogPosts();
return (
<div className="pt-32 pb-16">
<div className="container mx-auto px-4">
{/* Hero */}
<div className="text-center mb-12">
<h1 className="text-4xl md:text-5xl font-bold text-secondary-900 mb-4">
<span className="text-primary-600"></span>
</h1>
<p className="text-xl text-secondary-600 max-w-2xl mx-auto">
</p>
</div>
{/* Blog Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{posts.map((post) => (
<Link
key={post.slug}
href={`/blog/${post.slug}`}
className="card group"
>
<div className="relative aspect-video bg-secondary-100">
<Image
src={post.image}
alt={post.title}
fill
className="object-cover group-hover:scale-105 transition-transform duration-300"
/>
<div className="absolute top-4 left-4">
<span className="industrial-badge">{post.category}</span>
</div>
</div>
<div className="p-6">
<time className="text-sm text-secondary-500">
{new Date(post.date).toLocaleDateString('th-TH', {
year: 'numeric',
month: 'long',
day: 'numeric',
})}
</time>
<h3 className="text-xl font-bold text-secondary-900 mt-2 group-hover:text-primary-600 transition-colors">
{post.title}
</h3>
<p className="text-secondary-600 text-sm mt-2 line-clamp-2">
{post.excerpt}
</p>
<span className="text-primary-600 font-semibold flex items-center gap-2 mt-4">
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
</svg>
</span>
</div>
</Link>
))}
</div>
{posts.length === 0 && (
<div className="text-center py-12">
<p className="text-secondary-600"></p>
</div>
)}
</div>
</div>
);
}

View File

@@ -1,108 +0,0 @@
import { siteConfig, workHours } from '@/data/site-config';
export const metadata = {
title: 'ติดต่อเรา',
description: 'ติดต่อดีลพลัสเทค สอบถามข้อมูลสินค้า ขอใบเสนอราคา หรือติดต่อทีมงาน',
};
export default function ContactPage() {
return (
<div className="pt-32 pb-16">
<div className="container mx-auto px-4">
{/* Hero */}
<div className="text-center mb-12">
<h1 className="text-4xl md:text-5xl font-bold text-secondary-900 mb-4">
<span className="text-primary-600"></span>
</h1>
<p className="text-xl text-secondary-600 max-w-2xl mx-auto">
</p>
</div>
<div className="max-w-2xl mx-auto">
{/* Contact Info */}
<div className="bg-secondary-800 rounded-2xl p-8 mb-8">
<h2 className="text-2xl font-bold text-white mb-6"></h2>
<div className="space-y-6">
<div className="flex items-start gap-4">
<div className="w-12 h-12 bg-primary-600 rounded-lg flex items-center justify-center flex-shrink-0">
<svg className="w-6 h-6 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
</div>
<div>
<h3 className="font-bold text-white"></h3>
<p className="text-secondary-300">{siteConfig.address}</p>
</div>
</div>
<div className="flex items-start gap-4">
<div className="w-12 h-12 bg-primary-600 rounded-lg flex items-center justify-center flex-shrink-0">
<svg className="w-6 h-6 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
</svg>
</div>
<div>
<h3 className="font-bold text-white"></h3>
<a href={`tel:${siteConfig.phone}`} className="text-primary-400 hover:text-primary-300">
{siteConfig.phone}
</a>
</div>
</div>
<div className="flex items-start gap-4">
<div className="w-12 h-12 bg-primary-600 rounded-lg flex items-center justify-center flex-shrink-0">
<svg className="w-6 h-6 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
</svg>
</div>
<div>
<h3 className="font-bold text-white"></h3>
<a href={`mailto:${siteConfig.email}`} className="text-primary-400 hover:text-primary-300">
{siteConfig.email}
</a>
</div>
</div>
<div className="flex items-start gap-4">
<div className="w-12 h-12 bg-[#00B900] rounded-lg flex items-center justify-center flex-shrink-0">
<svg className="w-6 h-6 text-white" viewBox="0 0 24 24" fill="currentColor">
<path d="M19.365 9.863c.349 0 .63.285.63.631 0 .345-.281.63-.63.63H17.61v1.125h1.755c.349 0 .63.283.63.63 0 .344-.281.629-.63.629h-2.386c-.345 0-.627-.285-.627-.629V8.108c0-.345.282-.63.63-.63h2.386c.346 0 .627.285.627.63 0 .349-.281.63-.63.63H17.61v1.125h1.755zm-3.855 3.016c0 .27-.174.51-.432.596-.064.021-.133.031-.199.031-.211 0-.391-.09-.51-.25l-2.443-3.317v2.94c0 .344-.279.629-.631.629-.346 0-.626-.285-.626-.629V8.108c0-.27.173-.51.43-.595.06-.023.136-.033.194-.033.195 0 .375.104.495.254l2.462 3.33V8.108c0-.345.282-.63.63-.63.345 0 .63.285.63.63v4.771zm-5.741 0c0 .344-.282.629-.631.629-.345 0-.627-.285-.627-.629V8.108c0-.345.282-.63.63-.63.346 0 .628.285.628.63v4.771zm-2.466.629H4.917c-.345 0-.63-.285-.63-.629V8.108c0-.345.285-.63.63-.63.348 0 .63.285.63.63v4.141h1.756c.348 0 .629.283.629.63 0 .344-.282.629-.629.629M24 10.314C24 4.943 18.615.572 12 .572S0 4.943 0 10.314c0 4.811 4.27 8.842 10.035 9.608.391.082.923.258 1.058.59.12.301.079.766.038 1.08l-.164 1.02c-.045.301-.24 1.186 1.049.645 1.291-.539 6.916-4.078 9.436-6.975C23.176 14.393 24 12.458 24 10.314"/>
</svg>
</div>
<div>
<h3 className="font-bold text-white">LINE Official</h3>
<a
href={`https://line.me/ti/p/${siteConfig.lineId}`}
target="_blank"
rel="noopener noreferrer"
className="text-primary-400 hover:text-primary-300"
>
{siteConfig.lineId}
</a>
</div>
</div>
</div>
</div>
{/* Business Hours */}
<div className="bg-primary-600 rounded-2xl p-8">
<h2 className="text-2xl font-bold text-white mb-6"></h2>
<ul className="space-y-3">
{workHours.map((item) => (
<li key={item.day} className="flex justify-between">
<span className="text-primary-100">{item.day}</span>
<span className={`font-semibold ${item.isClosed ? 'text-red-200' : 'text-white'}`}>
{item.hours}
</span>
</li>
))}
</ul>
</div>
</div>
</div>
</div>
);
}

View File

@@ -1,174 +0,0 @@
import Link from 'next/link';
export const metadata = {
title: 'ร่วมงานกับเรา | Join Us - ดีลพลัสเทค',
description: 'ร่วมงานกับดีลพลัสเทค เรามอบโอกาสทางอาชีพที่ดี สวัสดิการครบครัน ร่วมเติบโตไปกับเรา',
};
const jobPositions = [
{
id: 'sales-engineer',
title: 'Sales Engineer',
titleTh: 'วิศวกรฝ่ายขาย',
type: 'Full-time',
location: 'กรุงเทพมหานคร',
salary: 'เจรจาต่อรอง',
description: 'รับผิดชอบการขายและให้คำปรึกษาด้านเทคนิคสำหรับผลิตภัณฑ์ท่อและอุปกรณ์ระบบท่อ',
requirements: [
'ปริญญาตรี สาขาวิศวกรรมเครื่องกล วิศวกรรมโยธา หรือสาขาที่เกี่ยวข้อง',
'มีประสบการณ์อย่างน้อย 1-3 ปี ในตำแหน่ง Sales Engineer',
'มีความรู้ด้านท่อและอุปกรณ์ระบบท่อจะได้รับการพิจารณาเป็นพิเศษ',
'สามารถขับรถและมีใบขับขี่',
'มีทัศนคติดี กระตือรือร้น และสามารถทำงานเป็นทีมได้',
],
},
{
id: 'sales-representative',
title: 'Sales Representative',
titleTh: 'พนักงานขาย',
type: 'Full-time',
location: 'กรุงเทพมหานคร',
salary: 'เจรจาต่อรอง',
description: 'รับผิดชอบการขายผลิตภัณฑ์ท่อและอุปกรณ์ระบบท่อให้กับลูกค้าใหม่และลูกค้าเดิม',
requirements: [
'ปริญญาตรี สาขาใดก็ได้',
'มีประสบการณ์ด้านการขายอย่างน้อย 1 ปี',
'มีทักษะการสื่อสารที่ดี',
'สามารถขับรถและมีใบขับขี่',
'มีทัศนคติดี กระตือรือร้น',
],
},
];
export default function JoinUsPage() {
return (
<div className="pt-32 pb-16">
<div className="container mx-auto px-4">
{/* Breadcrumb */}
<nav className="mb-6">
<ol className="flex items-center gap-2 text-sm">
<li>
<Link href="/" className="text-secondary-500 hover:text-primary-600">
</Link>
</li>
<li className="text-secondary-400">/</li>
<li className="text-primary-600 font-medium"></li>
</ol>
</nav>
{/* Header */}
<div className="mb-12 text-center">
<h1 className="text-3xl md:text-4xl font-bold text-secondary-900 mb-4">
</h1>
<p className="text-secondary-600 text-lg max-w-2xl mx-auto">
</p>
</div>
{/* Why Join Us */}
<div className="mb-12 bg-primary-50 rounded-xl p-8">
<h2 className="text-2xl font-bold text-secondary-900 mb-6 text-center">
?
</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="text-center">
<div className="w-16 h-16 bg-primary-600 rounded-full flex items-center justify-center mx-auto mb-4">
<svg className="w-8 h-8 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
<h3 className="text-lg font-bold text-secondary-900 mb-2"></h3>
<p className="text-secondary-600"></p>
</div>
<div className="text-center">
<div className="w-16 h-16 bg-primary-600 rounded-full flex items-center justify-center mx-auto mb-4">
<svg className="w-8 h-8 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z" />
</svg>
</div>
<h3 className="text-lg font-bold text-secondary-900 mb-2"></h3>
<p className="text-secondary-600"> </p>
</div>
<div className="text-center">
<div className="w-16 h-16 bg-primary-600 rounded-full flex items-center justify-center mx-auto mb-4">
<svg className="w-8 h-8 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" />
</svg>
</div>
<h3 className="text-lg font-bold text-secondary-900 mb-2"></h3>
<p className="text-secondary-600"></p>
</div>
</div>
</div>
{/* Open Positions */}
<div className="mb-12">
<h2 className="text-2xl font-bold text-secondary-900 mb-6">
</h2>
<div className="space-y-6">
{jobPositions.map((job) => (
<div key={job.id} className="bg-white rounded-xl p-6 shadow-card">
<div className="flex flex-col md:flex-row md:items-center md:justify-between mb-4">
<div>
<h3 className="text-xl font-bold text-secondary-900">
{job.title}
</h3>
<p className="text-primary-600">{job.titleTh}</p>
</div>
<div className="flex flex-wrap gap-2 mt-2 md:mt-0">
<span className="px-3 py-1 bg-secondary-100 text-secondary-700 rounded-full text-sm">
{job.type}
</span>
<span className="px-3 py-1 bg-secondary-100 text-secondary-700 rounded-full text-sm">
{job.location}
</span>
<span className="px-3 py-1 bg-primary-100 text-primary-700 rounded-full text-sm">
{job.salary}
</span>
</div>
</div>
<p className="text-secondary-600 mb-4">{job.description}</p>
<div className="mb-4">
<h4 className="font-semibold text-secondary-900 mb-2">:</h4>
<ul className="list-disc list-inside text-secondary-600 space-y-1">
{job.requirements.map((req, index) => (
<li key={index}>{req}</li>
))}
</ul>
</div>
<Link
href={`/sales-engineer`}
className="btn-primary inline-block"
>
</Link>
</div>
))}
</div>
</div>
{/* Contact */}
<div className="bg-secondary-100 rounded-xl p-8 text-center">
<h2 className="text-2xl font-bold text-secondary-900 mb-4">
?
</h2>
<p className="text-secondary-600 mb-6">
info@dealplustech.co.th
090-555-1415
</p>
<div className="flex justify-center gap-4">
<a href="mailto:info@dealplustech.co.th" className="btn-primary">
</a>
<Link href="/contact-us" className="btn-outline">
</Link>
</div>
</div>
</div>
</div>
);
}

View File

@@ -1,152 +0,0 @@
import type { Metadata } from 'next';
import { Kanit } from 'next/font/google';
import '@/styles/globals.css';
import Header from '@/components/layout/Header';
import Footer from '@/components/layout/Footer';
import FloatingContact from '@/components/layout/FloatingContact';
import GoogleAnalytics from '@/components/analytics/GoogleAnalytics';
const kanit = Kanit({
subsets: ['latin', 'thai'],
weight: ['300', '400', '500', '600', '700'],
variable: '--font-kanit',
display: 'swap',
});
export const metadata: Metadata = {
title: {
default: 'ดีลพลัสเทค - ผู้เชี่ยวชาญด้านวัสดุท่อและอุปกรณ์ระบบท่อ',
template: '%s | ดีลพลัสเทค',
},
description: 'ดีลพลัสเทค - ผู้เชี่ยวชาญด้านวัสดุท่อและอุปกรณ์ระบบท่อ ท่อพีพีอาร์ ท่อ HDPE ท่อ PVC วาล์ว และอุปกรณ์ต่อท่อครบวงจร',
keywords: ['ท่อพีพีอาร์', 'ท่อ HDPE', 'ท่อ PVC', 'วาล์ว', 'อุปกรณ์ท่อ', 'ดีลพลัสเทค'],
authors: [{ name: 'Deal Plus Tech' }],
metadataBase: new URL('https://dealplustech.co.th'),
alternates: {
canonical: '/',
},
openGraph: {
type: 'website',
locale: 'th_TH',
url: 'https://dealplustech.co.th',
siteName: 'ดีลพลัสเทค',
title: 'ดีลพลัสเทค - ผู้เชี่ยวชาญด้านวัสดุท่อและอุปกรณ์ระบบท่อ',
description: 'ดีลพลัสเทค - ผู้เชี่ยวชาญด้านวัสดุท่อและอุปกรณ์ระบบท่อ ท่อพีพีอาร์ ท่อ HDPE ท่อ PVC วาล์ว และอุปกรณ์ต่อท่อครบวงจร',
images: [
{
url: '/og-image.jpg',
width: 1200,
height: 630,
alt: 'ดีลพลัสเทค - ผู้เชี่ยวชาญด้านวัสดุท่อและอุปกรณ์ระบบท่อ',
},
],
},
twitter: {
card: 'summary_large_image',
site: '@dealplustech',
title: 'ดีลพลัสเทค - ผู้เชี่ยวชาญด้านวัสดุท่อและอุปกรณ์ระบบท่อ',
description: 'ดีลพลัสเทค - ผู้เชี่ยวชาญด้านวัสดุท่อและอุปกรณ์ระบบท่อ ท่อพีพีอาร์ ท่อ HDPE ท่อ PVC วาล์ว และอุปกรณ์ต่อท่อครบวงจร',
images: ['/og-image.jpg'],
},
robots: {
index: true,
follow: true,
googleBot: {
index: true,
follow: true,
'max-video-preview': -1,
'max-image-preview': 'large',
'max-snippet': -1,
},
},
};
// LocalBusiness Schema for SEO
function LocalBusinessSchema() {
const schema = {
'@context': 'https://schema.org',
'@type': 'LocalBusiness',
'@id': 'https://dealplustech.co.th/#organization',
name: 'ดีลพลัสเทค',
alternateName: 'Deal Plus Tech Co., Ltd.',
description: 'ผู้เชี่ยวชาญด้านวัสดุท่อและอุปกรณ์ระบบท่อ จำหน่ายท่อพีพีอาร์ ท่อ HDPE ท่อ PVC วาล์ว และอุปกรณ์ต่อท่อครบวงจร',
url: 'https://dealplustech.co.th',
logo: 'https://dealplustech.co.th/images/logo.png',
image: 'https://dealplustech.co.th/og-image.jpg',
telephone: '+66-90-555-1415',
email: 'info@dealplustech.co.th',
address: {
'@type': 'PostalAddress',
streetAddress: '9/70 ซอยนครลุง 17',
addressLocality: 'แขวงบางไผ่',
addressRegion: 'เขตบางแค',
addressCountry: 'TH',
postalCode: '10160',
},
geo: {
'@type': 'GeoCoordinates',
latitude: '13.7244',
longitude: '100.4044',
},
openingHoursSpecification: [
{
'@type': 'OpeningHoursSpecification',
dayOfWeek: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'],
opens: '08:30',
closes: '17:30',
},
{
'@type': 'OpeningHoursSpecification',
dayOfWeek: 'Saturday',
opens: '08:30',
closes: '12:00',
},
],
priceRange: '$$',
sameAs: [
'https://facebook.com/dealplustech',
'https://line.me/ti/p/@dealplustech',
],
areaServed: {
'@type': 'Country',
name: 'Thailand',
},
knowsAbout: [
'ท่อพีพีอาร์ (PPR Pipe)',
'ท่อ HDPE',
'ท่อ PVC',
'วาล์ว (Valve)',
'อุปกรณ์ระบบท่อ',
'ระบบดับเพลิง',
'ระบบปรับอากาศ HVAC',
],
};
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
);
}
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="th" className={kanit.variable}>
<head>
<LocalBusinessSchema />
</head>
<body className="font-sans">
<GoogleAnalytics />
<Header />
<main className="min-h-screen">{children}</main>
<Footer />
<FloatingContact />
</body>
</html>
);
}

View File

@@ -1,20 +0,0 @@
import Link from 'next/link';
export default function NotFound() {
return (
<div className="min-h-[60vh] flex items-center justify-center">
<div className="text-center px-4">
<h1 className="text-6xl font-bold text-secondary-900 mb-4">404</h1>
<h2 className="text-2xl font-bold text-secondary-700 mb-4">
</h2>
<p className="text-secondary-600 mb-8">
</p>
<Link href="/" className="btn-primary">
</Link>
</div>
</div>
);
}

View File

@@ -1,286 +0,0 @@
import Image from 'next/image';
import Link from 'next/link';
import { productCategories, siteConfig } from '@/data/site-config';
export default function HomePage() {
// Featured products with proper images - manually selected for variety
const featuredProducts = [
productCategories.find(p => p.id === 'ppr-elephant')!,
productCategories.find(p => p.id === 'hdpe')!,
productCategories.find(p => p.id === 'valve')!,
productCategories.find(p => p.id === 'grilles')!,
productCategories.find(p => p.id === 'thermobreak')!,
productCategories.find(p => p.id === 'upvc')!,
];
return (
<>
{/* Hero Section */}
<section className="relative h-[70vh] min-h-[500px] bg-secondary-900">
<div className="absolute inset-0 bg-gradient-to-r from-secondary-900 via-secondary-900/90 to-secondary-900/60 z-10" />
<Image
src="/images/2021/03/ppr-pipe_000C.jpg"
alt="ท่อพีพีอาร์คุณภาพสูง"
fill
className="object-cover opacity-50"
priority
/>
<div className="relative z-20 container mx-auto px-4 h-full flex items-center">
<div className="max-w-2xl">
<span className="inline-block px-4 py-2 bg-primary-600 text-white font-semibold mb-4 rounded">
HVAC
</span>
<h1 className="text-4xl md:text-5xl lg:text-6xl font-bold text-white mb-6 leading-tight">
HVAC
<span className="text-primary-400 block"></span>
</h1>
<p className="text-lg md:text-xl text-secondary-200 mb-8">
PPR, HDPE, ,
</p>
<div className="flex flex-wrap gap-4">
<Link href="/product" className="btn-primary">
</Link>
<Link href="/contact-us" className="btn-outline border-white text-white hover:bg-white hover:text-secondary-900">
</Link>
</div>
</div>
</div>
</section>
{/* Features Section */}
<section className="py-16 bg-secondary-800">
<div className="container mx-auto px-4">
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
<div className="text-center p-6">
<div className="w-16 h-16 bg-primary-600 rounded-lg flex items-center justify-center mx-auto mb-4">
<svg className="w-8 h-8 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z" />
</svg>
</div>
<h3 className="text-xl font-bold text-white mb-2"></h3>
<p className="text-secondary-300">
</p>
</div>
<div className="text-center p-6">
<div className="w-16 h-16 bg-primary-600 rounded-lg flex items-center justify-center mx-auto mb-4">
<svg className="w-8 h-8 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
</div>
<h3 className="text-xl font-bold text-white mb-2"></h3>
<p className="text-secondary-300">
</p>
</div>
<div className="text-center p-6">
<div className="w-16 h-16 bg-primary-600 rounded-lg flex items-center justify-center mx-auto mb-4">
<svg className="w-8 h-8 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M18.364 5.636l-3.536 3.536m0 5.656l3.536 3.536M9.172 9.172L5.636 5.636m3.536 9.192l-3.536 3.536M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-5 0a4 4 0 11-8 0 4 4 0 018 0z" />
</svg>
</div>
<h3 className="text-xl font-bold text-white mb-2"></h3>
<p className="text-secondary-300">
</p>
</div>
</div>
</div>
</section>
{/* Highlight Products Section */}
<section className="py-16 bg-secondary-50">
<div className="container mx-auto px-4">
<div className="text-center mb-12">
<h2 className="text-3xl md:text-4xl font-bold text-secondary-900 mb-4">
<span className="text-primary-600"></span>
</h2>
<p className="text-secondary-600 text-lg"></p>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
{/* PPR Pipe */}
<Link href="/ท่อพีพีอาร์ตราช้าง" className="group relative overflow-hidden rounded-2xl aspect-[4/5] bg-secondary-900">
<Image
src="/images/2021/03/ppr-pipe_000C.jpg"
alt="ท่อ PPR ตราช้าง"
fill
className="object-cover group-hover:scale-110 transition-transform duration-500"
/>
<div className="absolute inset-0 bg-gradient-to-t from-secondary-900 via-secondary-900/40 to-transparent" />
<div className="absolute bottom-0 left-0 right-0 p-6">
<span className="inline-block px-3 py-1 bg-primary-600 text-white text-sm font-semibold rounded mb-3"> | Pipe</span>
<h3 className="text-2xl font-bold text-white mb-2"> PPR </h3>
<p className="text-secondary-200 text-sm mb-4"> SCG DIN 8077/8078 20 50+ </p>
<span className="inline-flex items-center gap-2 text-primary-400 font-semibold group-hover:text-primary-300 transition-colors">
<svg className="w-4 h-4 group-hover:translate-x-2 transition-transform" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
</svg>
</span>
</div>
</Link>
{/* Grilles / Air Diffuser */}
<Link href="/หัวจ่ายลม-กริล" className="group relative overflow-hidden rounded-2xl aspect-[4/5] bg-secondary-900">
<Image
src="/images/2021/03/grilles_000C.jpg"
alt="กริลแอร์พลาสติก"
fill
className="object-cover group-hover:scale-110 transition-transform duration-500"
/>
<div className="absolute inset-0 bg-gradient-to-t from-secondary-900 via-secondary-900/40 to-transparent" />
<div className="absolute bottom-0 left-0 right-0 p-6">
<span className="inline-block px-3 py-1 bg-primary-600 text-white text-sm font-semibold rounded mb-3"></span>
<h3 className="text-2xl font-bold text-white mb-2"></h3>
<p className="text-secondary-200 text-sm mb-4"> Ball Jet </p>
<span className="inline-flex items-center gap-2 text-primary-400 font-semibold group-hover:text-primary-300 transition-colors">
<svg className="w-4 h-4 group-hover:translate-x-2 transition-transform" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
</svg>
</span>
</div>
</Link>
{/* Thermobreak */}
<Link href="/เทอร์โมเบรค-thermobreak" className="group relative overflow-hidden rounded-2xl aspect-[4/5] bg-secondary-900">
<Image
src="/images/2025/01/thermobrek_cover_000C.jpg"
alt="เทอร์โมเบรค Thermobreak"
fill
className="object-cover group-hover:scale-110 transition-transform duration-500"
/>
<div className="absolute inset-0 bg-gradient-to-t from-secondary-900 via-secondary-900/40 to-transparent" />
<div className="absolute bottom-0 left-0 right-0 p-6">
<span className="inline-block px-3 py-1 bg-primary-600 text-white text-sm font-semibold rounded mb-3"></span>
<h3 className="text-2xl font-bold text-white mb-2"></h3>
<p className="text-secondary-200 text-sm mb-4"> Polyolefin Foam FM/UL HVAC </p>
<span className="inline-flex items-center gap-2 text-primary-400 font-semibold group-hover:text-primary-300 transition-colors">
<svg className="w-4 h-4 group-hover:translate-x-2 transition-transform" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
</svg>
</span>
</div>
</Link>
</div>
</div>
</section>
{/* Featured Products Section */}
<section className="py-16">
<div className="container mx-auto px-4">
<div className="text-center mb-12">
<h2 className="section-title">
<span className="text-primary-600"></span>
</h2>
<p className="section-subtitle">
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{featuredProducts.map((product) => (
<Link
key={product.id}
href={product.href}
className="card group"
>
<div className="relative aspect-video bg-secondary-100">
<Image
src={product.image}
alt={product.name}
fill
className="object-cover group-hover:scale-105 transition-transform duration-300"
/>
<div className="absolute top-4 left-4">
<span className="industrial-badge">{product.nameEn}</span>
</div>
</div>
<div className="p-6">
<h3 className="text-xl font-bold text-secondary-900 mb-2 group-hover:text-primary-600 transition-colors">
{product.name}
</h3>
<p className="text-secondary-600 text-sm mb-4">
{product.shortDescription || product.description.slice(0, 100) + '...'}
</p>
<span className="text-primary-600 font-semibold flex items-center gap-2">
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
</svg>
</span>
</div>
</Link>
))}
</div>
<div className="text-center mt-12">
<Link href="/product" className="btn-secondary">
</Link>
</div>
</div>
</section>
{/* CTA Section */}
<section className="py-16 bg-primary-600">
<div className="container mx-auto px-4 text-center">
<h2 className="text-3xl md:text-4xl font-bold text-white mb-4">
?
</h2>
<p className="text-primary-100 text-lg mb-8 max-w-2xl mx-auto">
</p>
<div className="flex flex-wrap justify-center gap-4">
<a
href={`tel:${siteConfig.phone}`}
className="btn-secondary bg-white text-primary-600 hover:bg-primary-50"
>
: {siteConfig.phone}
</a>
<Link href="/contact-us" className="btn-outline border-white text-white hover:bg-white hover:text-primary-600">
</Link>
</div>
</div>
</section>
{/* About Section */}
<section className="py-16">
<div className="container mx-auto px-4">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
<div>
<h2 className="section-title mb-6">
<span className="text-primary-600">{siteConfig.name}</span>
</h2>
<p className="text-secondary-600 mb-4">
{siteConfig.nameTh}
10
</p>
<p className="text-secondary-600 mb-6">
HDPE PVC
</p>
<Link href="/about-us" className="btn-primary">
</Link>
</div>
<div className="relative aspect-video bg-secondary-100 rounded-xl overflow-hidden">
<Image
src="/images/2021/03/hdpe-pipe_000C.jpg"
alt="เกี่ยวกับดีลพลัสเทค"
fill
className="object-cover"
/>
</div>
</div>
</div>
</section>
</>
);
}

View File

@@ -1,101 +0,0 @@
import Image from 'next/image';
import Link from 'next/link';
import { productCategories } from '@/data/site-config';
export const metadata = {
title: 'ท่อ | Pipe - ดีลพลัสเทค',
description: 'จำหน่ายท่อทุกประเภท ท่อ PPR ท่อ HDPE ท่อ PVC ท่อ uPVC ท่อไซเลอร์ คุณภาพสูง ราคาถูก',
};
// Pipe category product IDs
const pipeProductIds = [
'ppr-elephant',
'thai-ppr',
'poloplast',
'hdpe',
'upvc',
'pvc',
'syler',
'xylent',
'ppr-welder',
];
export default function PipePage() {
const pipeProducts = productCategories.filter(p => pipeProductIds.includes(p.id));
return (
<div className="pt-32 pb-16">
<div className="container mx-auto px-4">
{/* Breadcrumb */}
<nav className="mb-6">
<ol className="flex items-center gap-2 text-sm">
<li>
<Link href="/" className="text-secondary-500 hover:text-primary-600">
</Link>
</li>
<li className="text-secondary-400">/</li>
<li className="text-primary-600 font-medium"> | Pipe</li>
</ol>
</nav>
{/* Header */}
<div className="mb-12">
<h1 className="text-3xl md:text-4xl font-bold text-secondary-900 mb-4">
| Pipe
</h1>
<p className="text-secondary-600 text-lg max-w-3xl">
PPR SCG HDPE PE80/PE100 PVC uPVC
.
</p>
</div>
{/* Products Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{pipeProducts.map((product) => (
<Link
key={product.id}
href={product.href}
className="card group"
>
<div className="relative aspect-video bg-secondary-100">
<Image
src={product.image}
alt={product.name}
fill
className="object-cover group-hover:scale-105 transition-transform duration-300"
/>
</div>
<div className="p-4">
<span className="text-xs text-primary-600 font-semibold">
{product.nameEn}
</span>
<h2 className="text-lg font-bold text-secondary-900 mt-1 group-hover:text-primary-600 transition-colors">
{product.name}
</h2>
<p className="text-sm text-secondary-600 mt-2 line-clamp-2">
{product.shortDescription || product.description}
</p>
</div>
</Link>
))}
</div>
{/* CTA */}
<div className="mt-12 text-center">
<p className="text-secondary-600 mb-4">
?
</p>
<div className="flex justify-center gap-4">
<Link href="/contact-us" className="btn-primary">
</Link>
<a href="tel:090-555-1415" className="btn-outline">
090-555-1415
</a>
</div>
</div>
</div>
</div>
);
}

View File

@@ -1,130 +0,0 @@
import Image from 'next/image';
export const metadata = {
title: 'ผลงานของเรา',
description: 'ผลงานโครงการต่างๆ ที่ดีลพลัสเทคได้ร่วมเป็นส่วนหนึ่ง',
};
const portfolioItems = [
{
title: 'Cyber World',
category: 'อาคารพาณิชย์',
image: '/images/2021/02/IMG_3089.jpg',
},
{
title: 'Toyox',
category: 'อุตสาหกรรม',
image: '/images/2021/02/IMG_2226.jpg',
},
{
title: 'PPR PIPE',
category: 'ท่อ PPR',
image: '/images/2021/02/Image1.jpg',
},
{
title: 'โรงงานเอธานอล',
category: 'อุตสาหกรรม',
image: '/images/2021/02/ลพบุรี5.jpg',
},
{
title: 'บจก.หยั่น หว่อ หยุ่น',
category: 'อุตสาหกรรม',
image: '/images/2021/02/สมุทรสาคร2.jpg',
},
{
title: 'ซีคอนบางแค',
category: 'อาคารพาณิชย์',
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',
},
];
export default function PortfolioPage() {
return (
<div className="pt-32 pb-16">
<div className="container mx-auto px-4">
{/* Hero */}
<div className="text-center mb-12">
<h1 className="text-4xl md:text-5xl font-bold text-secondary-900 mb-4">
<span className="text-primary-600"></span>
</h1>
<p className="text-xl text-secondary-600 max-w-2xl mx-auto">
</p>
</div>
{/* Portfolio Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{portfolioItems.map((item, index) => (
<div key={index} className="card group cursor-pointer overflow-hidden">
<div className="relative aspect-video bg-secondary-100 overflow-hidden">
<Image
src={item.image}
alt={item.title}
fill
className="object-cover group-hover:scale-105 transition-transform duration-300"
/>
{/* Hover Overlay */}
<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>
<h3 className="text-white font-bold">{item.title}</h3>
</div>
</div>
{/* 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>
<h3 className="text-lg font-bold text-secondary-900 mt-1">{item.title}</h3>
</div>
</div>
))}
</div>
</div>
</div>
);
}

View File

@@ -1,65 +0,0 @@
import Image from 'next/image';
import Link from 'next/link';
import { productCategories } from '@/data/site-config';
export const metadata = {
title: 'สินค้า - ท่อพีพีอาร์ ท่อ HDPE ท่อ PVC วาล์ว อุปกรณ์ท่อ',
description: 'สินค้าครบวงจร ท่อพีพีอาร์ ท่อ HDPE ท่อ PVC วาล์ว อุปกรณ์แขวนท่อ อุปกรณ์ปรับอากาศ และอุปกรณ์ดับเพลิง',
};
export default function ProductPage() {
// Group products by category
const categories = productCategories.reduce((acc, product) => {
const cat = product.slug;
if (!acc[cat]) {
acc[cat] = [];
}
acc[cat].push(product);
return acc;
}, {} as Record<string, typeof productCategories>);
return (
<div className="pt-32 pb-16">
<div className="container mx-auto px-4">
{/* Page Header */}
<div className="text-center mb-12">
<h1 className="text-4xl md:text-5xl font-bold text-secondary-900 mb-4">
<span className="text-primary-600"></span>
</h1>
<p className="text-xl text-secondary-600 max-w-2xl mx-auto">
</p>
</div>
{/* Products Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
{productCategories.map((product) => (
<Link
key={product.id}
href={product.href}
className="card group"
>
<div className="relative aspect-video bg-secondary-100">
<Image
src={product.image}
alt={product.name}
fill
className="object-cover group-hover:scale-105 transition-transform duration-300"
/>
</div>
<div className="p-4">
<span className="text-xs text-primary-600 font-semibold">{product.nameEn}</span>
<h3 className="text-lg font-bold text-secondary-900 mt-1 group-hover:text-primary-600 transition-colors">
{product.name}
</h3>
<p className="text-secondary-600 text-sm mt-2 line-clamp-2">
{product.shortDescription || product.description}
</p>
</div>
</Link>
))}
</div>
</div>
</div>
);
}

View File

@@ -1,146 +0,0 @@
import Link from 'next/link';
export const metadata = {
title: 'Sales Engineer - วิศวกรฝ่ายขาย - ดีลพลัสเทค',
description: 'สมัครงานตำแหน่ง Sales Engineer ที่ดีลพลัสเทค รับผิดชอบการขายและให้คำปรึกษาด้านเทคนิคสำหรับผลิตภัณฑ์ท่อและอุปกรณ์ระบบท่อ',
};
export default function SalesEngineerPage() {
return (
<div className="pt-32 pb-16">
<div className="container mx-auto px-4">
{/* Breadcrumb */}
<nav className="mb-6">
<ol className="flex items-center gap-2 text-sm">
<li>
<Link href="/" className="text-secondary-500 hover:text-primary-600">
</Link>
</li>
<li className="text-secondary-400">/</li>
<li>
<Link href="/join-us" className="text-secondary-500 hover:text-primary-600">
</Link>
</li>
<li className="text-secondary-400">/</li>
<li className="text-primary-600 font-medium">Sales Engineer</li>
</ol>
</nav>
{/* Header */}
<div className="mb-8">
<div className="flex flex-wrap gap-2 mb-4">
<span className="px-3 py-1 bg-secondary-100 text-secondary-700 rounded-full text-sm">
Full-time
</span>
<span className="px-3 py-1 bg-secondary-100 text-secondary-700 rounded-full text-sm">
</span>
<span className="px-3 py-1 bg-primary-100 text-primary-700 rounded-full text-sm">
</span>
</div>
<h1 className="text-3xl md:text-4xl font-bold text-secondary-900 mb-2">
Sales Engineer
</h1>
<p className="text-xl text-primary-600"></p>
</div>
{/* Content */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
<div className="lg:col-span-2">
{/* Job Description */}
<div className="bg-white rounded-xl p-6 shadow-card mb-6">
<h2 className="text-xl font-bold text-secondary-900 mb-4">
</h2>
<p className="text-secondary-600 mb-4">
</p>
<h3 className="font-semibold text-secondary-900 mb-2">:</h3>
<ul className="list-disc list-inside text-secondary-600 space-y-2 mb-4">
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
</div>
{/* Requirements */}
<div className="bg-white rounded-xl p-6 shadow-card mb-6">
<h2 className="text-xl font-bold text-secondary-900 mb-4">
</h2>
<ul className="list-disc list-inside text-secondary-600 space-y-2">
<li> </li>
<li> 1-3 Sales Engineer</li>
<li></li>
<li></li>
<li> </li>
<li></li>
</ul>
</div>
{/* Benefits */}
<div className="bg-white rounded-xl p-6 shadow-card">
<h2 className="text-xl font-bold text-secondary-900 mb-4">
</h2>
<ul className="list-disc list-inside text-secondary-600 space-y-2">
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li> (-)</li>
<li> </li>
</ul>
</div>
</div>
{/* Sidebar */}
<div className="lg:col-span-1">
<div className="bg-primary-50 rounded-xl p-6 sticky top-24">
<h3 className="text-lg font-bold text-secondary-900 mb-4">
?
</h3>
<p className="text-secondary-600 mb-4">
:
</p>
<div className="space-y-4">
<a
href="mailto:info@dealplustech.co.th?subject=Apply for Sales Engineer Position"
className="btn-primary w-full block text-center"
>
</a>
<a
href="tel:090-555-1415"
className="btn-outline w-full block text-center"
>
090-555-1415
</a>
<Link
href="/contact-us"
className="btn-outline w-full block text-center"
>
</Link>
</div>
<div className="mt-6 pt-6 border-t border-primary-200">
<p className="text-sm text-secondary-600">
<strong>:</strong><br />
<br />
</p>
</div>
</div>
</div>
</div>
</div>
</div>
);
}

View File

@@ -1,263 +0,0 @@
import Image from 'next/image';
import Link from 'next/link';
export const metadata = {
title: 'บริการของเรา',
description: 'บริการครบวงจร จำหน่ายวัสดุท่อ ให้คำปรึกษา ออกแบบระบบ และติดตั้ง',
};
const services = [
{
title: 'จำหน่ายวัสดุท่อ',
description: 'จำหน่ายท่อพีพีอาร์ ท่อ HDPE ท่อ PVC วาล์ว และอุปกรณ์ต่อท่อครบวงจร สินค้าคุณภาพ ราคาแข่งขันได้',
icon: (
<svg className="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
</svg>
),
},
{
title: 'ให้คำปรึกษา',
description: 'ทีมงานมืออาชีพพร้อมให้คำปรึกษาเกี่ยวกับการเลือกวัสดุท่อที่เหมาะสมกับโครงการของคุณ',
icon: (
<svg className="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
</svg>
),
},
{
title: 'ออกแบบระบบ',
description: 'บริการออกแบบระบบท่อน้ำ ระบบดับเพลิง และระบบปรับอากาศ โดยวิศวกรผู้เชี่ยวชาญ',
icon: (
<svg className="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 17V7m0 10a2 2 0 01-2 2H5a2 2 0 01-2-2V7a2 2 0 012-2h2a2 2 0 012 2m0 10a2 2 0 002 2h2a2 2 0 002-2M9 7a2 2 0 012-2h2a2 2 0 012 2m0 10V7m0 10a2 2 0 002 2h2a2 2 0 002-2V7a2 2 0 00-2-2h-2a2 2 0 00-2 2" />
</svg>
),
},
{
title: 'ติดตั้งระบบ',
description: 'ทีมช่างผู้เชี่ยวชาญติดตั้งระบบท่อครบวงจร พร้อมรับประกันงาน',
icon: (
<svg className="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
),
},
{
title: 'จัดส่งสินค้า',
description: 'บริการจัดส่งสินค้าทั่วประเทศ รวดเร็ว ปลอดภัย มีประกันความเสียหาย',
icon: (
<svg className="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4" />
</svg>
),
},
{
title: 'บริการหลังการขาย',
description: 'ทีมงานพร้อมให้การดูแลและบริการซ่อมบำรุงหลังการขายตลอดอายุการใช้งาน',
icon: (
<svg className="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M18.364 5.636l-3.536 3.536m0 5.656l3.536 3.536M9.172 9.172L5.636 5.636m3.536 9.192l-3.536 3.536M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-5 0a4 4 0 11-8 0 4 4 0 018 0z" />
</svg>
),
},
];
const processSteps = [
{
step: '01',
title: 'ปรึกษา',
description: 'ติดต่อเราเพื่อปรึกษาเกี่ยวกับความต้องการของโครงการ',
},
{
step: '02',
title: 'ออกแบบ',
description: 'ทีมวิศวกรออกแบบระบบให้เหมาะสมกับการใช้งาน',
},
{
step: '03',
title: 'เสนอราคา',
description: 'เสนอราคาสินค้าและบริการอย่างโปร่งใส',
},
{
step: '04',
title: 'ติดตั้ง',
description: 'ทีมช่างติดตั้งโดยมืออาชีพตรงตามกำหนด',
},
];
export default function ServicesPage() {
return (
<div className="pt-20">
{/* Hero Section */}
<section className="relative h-[50vh] min-h-[400px] bg-secondary-900">
<div className="absolute inset-0 bg-gradient-to-r from-secondary-900 via-secondary-900/90 to-secondary-900/60 z-10" />
<Image
src="/images/2021/03/hdpe-pipe_000C.jpg"
alt="บริการของเรา"
fill
className="object-cover opacity-40"
priority
/>
<div className="relative z-20 container mx-auto px-4 h-full flex items-center">
<div className="max-w-2xl">
<span className="inline-block px-4 py-2 bg-primary-600 text-white font-semibold mb-4 rounded">
</span>
<h1 className="text-4xl md:text-5xl lg:text-6xl font-bold text-white mb-6">
<span className="text-primary-400"></span>
</h1>
<p className="text-xl text-secondary-200">
</p>
</div>
</div>
</section>
{/* Services Grid */}
<section className="py-20 bg-white">
<div className="container mx-auto px-4">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{services.map((service, index) => (
<div
key={index}
className="group p-8 bg-secondary-50 rounded-2xl hover:bg-primary-600 transition-all duration-300 hover:shadow-xl"
>
<div className="w-16 h-16 bg-primary-600 text-white rounded-xl flex items-center justify-center mb-6 group-hover:bg-white group-hover:text-primary-600 transition-colors">
{service.icon}
</div>
<h3 className="text-xl font-bold text-secondary-900 mb-3 group-hover:text-white transition-colors">
{service.title}
</h3>
<p className="text-secondary-600 group-hover:text-primary-100 transition-colors">
{service.description}
</p>
</div>
))}
</div>
</div>
</section>
{/* Process Section */}
<section className="py-20 bg-secondary-900">
<div className="container mx-auto px-4">
<div className="text-center mb-16">
<h2 className="text-3xl md:text-4xl font-bold text-white mb-4">
<span className="text-primary-400"></span>
</h2>
<p className="text-secondary-300 text-lg max-w-2xl mx-auto">
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
{processSteps.map((item, index) => (
<div key={index} className="relative">
<div className="text-center">
<span className="text-6xl font-bold text-primary-600/30">{item.step}</span>
<h3 className="text-xl font-bold text-white mt-4 mb-2">{item.title}</h3>
<p className="text-secondary-400">{item.description}</p>
</div>
{index < processSteps.length - 1 && (
<div className="hidden lg:block absolute top-8 right-0 transform translate-x-1/2">
<svg className="w-8 h-8 text-primary-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
</svg>
</div>
)}
</div>
))}
</div>
</div>
</section>
{/* Why Choose Us */}
<section className="py-20 bg-secondary-50">
<div className="container mx-auto px-4">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
<div>
<h2 className="text-3xl md:text-4xl font-bold text-secondary-900 mb-6">
<span className="text-primary-600"></span>
</h2>
<div className="space-y-6">
<div className="flex gap-4">
<div className="w-12 h-12 bg-primary-600 rounded-lg flex items-center justify-center flex-shrink-0">
<svg className="w-6 h-6 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
</div>
<div>
<h3 className="font-bold text-secondary-900 mb-1"> 10 </h3>
<p className="text-secondary-600"></p>
</div>
</div>
<div className="flex gap-4">
<div className="w-12 h-12 bg-primary-600 rounded-lg flex items-center justify-center flex-shrink-0">
<svg className="w-6 h-6 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
</div>
<div>
<h3 className="font-bold text-secondary-900 mb-1"></h3>
<p className="text-secondary-600"> . / FM / UL </p>
</div>
</div>
<div className="flex gap-4">
<div className="w-12 h-12 bg-primary-600 rounded-lg flex items-center justify-center flex-shrink-0">
<svg className="w-6 h-6 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
</div>
<div>
<h3 className="font-bold text-secondary-900 mb-1"></h3>
<p className="text-secondary-600"></p>
</div>
</div>
<div className="flex gap-4">
<div className="w-12 h-12 bg-primary-600 rounded-lg flex items-center justify-center flex-shrink-0">
<svg className="w-6 h-6 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
</div>
<div>
<h3 className="font-bold text-secondary-900 mb-1"></h3>
<p className="text-secondary-600"> </p>
</div>
</div>
</div>
</div>
<div className="relative aspect-video bg-secondary-200 rounded-2xl overflow-hidden">
<Image
src="/images/2021/03/hdpe-welding_000C-1.jpg"
alt="ทีมงานมืออาชีพ"
fill
className="object-cover"
/>
</div>
</div>
</div>
</section>
{/* CTA */}
<section className="py-20 bg-primary-600">
<div className="container mx-auto px-4 text-center">
<h2 className="text-3xl md:text-4xl font-bold text-white mb-4">
?
</h2>
<p className="text-primary-100 text-lg mb-8 max-w-2xl mx-auto">
</p>
<div className="flex flex-wrap justify-center gap-4">
<Link href="/contact-us" className="btn-secondary bg-white text-primary-600 hover:bg-primary-50">
</Link>
<a href="tel:090-555-1415" className="btn-outline border-white text-white hover:bg-white hover:text-primary-600">
โทร: 090-555-1415
</a>
</div>
</div>
</section>
</div>
);
}

View File

@@ -1,118 +0,0 @@
import { MetadataRoute } from 'next';
import { productCategories, portfolioProjects, mainNavigation } from '@/data/site-config';
const BASE_URL = 'https://dealplustech.co.th';
export default function sitemap(): MetadataRoute.Sitemap {
const now = new Date();
// Static pages
const staticPages: MetadataRoute.Sitemap = [
{
url: BASE_URL,
lastModified: now,
changeFrequency: 'weekly',
priority: 1,
},
{
url: `${BASE_URL}/about-us/`,
lastModified: now,
changeFrequency: 'monthly',
priority: 0.8,
},
{
url: `${BASE_URL}/services/`,
lastModified: now,
changeFrequency: 'monthly',
priority: 0.8,
},
{
url: `${BASE_URL}/product/`,
lastModified: now,
changeFrequency: 'weekly',
priority: 0.9,
},
{
url: `${BASE_URL}/pipe/`,
lastModified: now,
changeFrequency: 'weekly',
priority: 0.8,
},
{
url: `${BASE_URL}/portfolio/`,
lastModified: now,
changeFrequency: 'monthly',
priority: 0.7,
},
{
url: `${BASE_URL}/blog/`,
lastModified: now,
changeFrequency: 'weekly',
priority: 0.7,
},
{
url: `${BASE_URL}/contact-us/`,
lastModified: now,
changeFrequency: 'monthly',
priority: 0.8,
},
{
url: `${BASE_URL}/join-us/`,
lastModified: now,
changeFrequency: 'monthly',
priority: 0.5,
},
{
url: `${BASE_URL}/sales-engineer/`,
lastModified: now,
changeFrequency: 'monthly',
priority: 0.5,
},
{
url: `${BASE_URL}/all-projects/`,
lastModified: now,
changeFrequency: 'monthly',
priority: 0.6,
},
];
// Product pages
const productPages: MetadataRoute.Sitemap = productCategories.map((product) => ({
url: `${BASE_URL}${product.href}`,
lastModified: now,
changeFrequency: 'weekly' as const,
priority: 0.8,
}));
// Portfolio pages
const portfolioPages: MetadataRoute.Sitemap = portfolioProjects.map((project) => ({
url: `${BASE_URL}${project.href}`,
lastModified: now,
changeFrequency: 'monthly' as const,
priority: 0.6,
}));
// Blog posts (Thai URLs)
const blogPages: MetadataRoute.Sitemap = [
{
url: `${BASE_URL}/blog/ข้อดี-ท่อ-hdpe/`,
lastModified: now,
changeFrequency: 'monthly' as const,
priority: 0.6,
},
{
url: `${BASE_URL}/blog/ท่อ-ppr-คืออะไร/`,
lastModified: now,
changeFrequency: 'monthly' as const,
priority: 0.6,
},
{
url: `${BASE_URL}/blog/บำรุงรักษาปั๊มน้ำ/`,
lastModified: now,
changeFrequency: 'monthly' as const,
priority: 0.6,
},
];
return [...staticPages, ...productPages, ...portfolioPages, ...blogPages];
}

View File

@@ -0,0 +1,51 @@
---
import type { CollectionEntry } from 'astro:content';
interface Props {
post: CollectionEntry<'blog'>;
}
const { post } = Astro.props;
const { title, excerpt, date, author, category, categories, image, featuredImage } = post.data;
// Support both 'category' and 'categories' field names
const postCategory = category || (Array.isArray(categories) ? categories[0] : 'ทั่วไป');
// Support both 'image' and 'featuredImage' field names
const postImage = image || featuredImage || '/images/2021/03/ppr-pipe_000C.jpg';
---
<a href={`/blog/${post.slug}`} class="card group">
<div class="relative aspect-video bg-secondary-100 overflow-hidden">
<img
src={postImage}
alt={title}
class="object-cover w-full h-48 group-hover:scale-105 transition-transform duration-300"
loading="lazy"
/>
<div class="absolute top-4 left-4">
<span class="industrial-badge">{postCategory}</span>
</div>
</div>
<div class="p-6">
<time class="text-sm text-secondary-500">
{new Date(date).toLocaleDateString('th-TH', { year: 'numeric', month: 'long', day: 'numeric' })}
</time>
<h3 class="mt-2 text-xl font-bold text-secondary-900 group-hover:text-primary-600 transition-colors line-clamp-2">
{title}
</h3>
{excerpt && (
<p class="mt-3 text-secondary-600 text-sm line-clamp-3">
{excerpt}
</p>
)}
<div class="mt-4 flex items-center text-primary-600 font-medium">
<span>อ่านต่อ</span>
<svg class="w-4 h-4 ml-2 group-hover:translate-x-1 transition-transform" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width={2} d="M9 5l7 7-7 7" />
</svg>
</div>
</div>
</a>

View File

@@ -0,0 +1,127 @@
---
// Cookie Consent Banner - PDPA Compliant
---
<div
id="cookie-consent-banner"
class="fixed bottom-0 left-0 right-0 z-50 bg-white border-t-2 border-primary-600 shadow-2xl p-6 md:p-8 transform translate-y-full transition-transform duration-300"
style="display: none;"
>
<div class="container mx-auto px-4 max-w-7xl">
<div class="flex flex-col lg:flex-row gap-6 items-start lg:items-center justify-between">
<div class="flex-1">
<h2 class="text-xl md:text-2xl font-bold text-secondary-900 mb-3">
เรายึดถือความเป็นส่วนตัวของคุณ
</h2>
<p class="text-base md:text-lg text-secondary-700 mb-4">
เราใช้คุกกี้เพื่อปรับปรุงประสบการณ์การใช้งาน คุณสามารถเลือกยอมรับหรือปฏิเสธได้
</p>
<div class="space-y-3 mt-4">
<div class="flex items-center gap-3 bg-secondary-50 p-3 rounded-lg">
<input type="checkbox" id="consent-essential" checked disabled class="w-5 h-5" />
<label for="consent-essential" class="flex-1">
<span class="font-semibold">คุกกี้จำเป็น</span>
<span class="text-sm text-secondary-600 block">ใช้สำหรับการทำงานของเว็บไซต์</span>
</label>
</div>
<div class="flex items-center gap-3 bg-secondary-50 p-3 rounded-lg">
<input type="checkbox" id="consent-analytics" class="w-5 h-5" />
<label for="consent-analytics" class="flex-1">
<span class="font-semibold">คุกกี้วิเคราะห์ข้อมูล</span>
<span class="text-sm text-secondary-600 block">ช่วยให้เราเข้าใจการใช้งาน</span>
</label>
</div>
</div>
</div>
<div class="flex gap-3">
<button id="consent-reject" class="bg-secondary-800 hover:bg-secondary-900 text-white px-6 py-3 rounded-lg font-semibold">
ปฏิเสธทั้งหมด
</button>
<button id="consent-accept" class="bg-primary-600 hover:bg-primary-700 text-white px-6 py-3 rounded-lg font-semibold">
ยอมรับทั้งหมด
</button>
</div>
</div>
</div>
</div>
<script>
const POLICY_VERSION = '1.0.0';
function getStoredConsent() {
const stored = localStorage.getItem('consent-preferences');
return stored ? JSON.parse(stored) : null;
}
async function saveConsent(consent) {
localStorage.setItem('consent-preferences', JSON.stringify(consent));
try {
let sessionId = sessionStorage.getItem('consent_session_id');
if (!sessionId) {
sessionId = 'ses_' + Math.random().toString(36).substring(2, 15);
sessionStorage.setItem('consent_session_id', sessionId);
}
await fetch('/api/consent', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ sessionId, consent, policyVersion: POLICY_VERSION }),
});
} catch (error) {
console.error('Failed to log consent:', error);
}
}
function showBanner() {
const banner = document.getElementById('cookie-consent-banner');
if (banner) {
banner.style.display = 'block';
setTimeout(() => banner.classList.remove('translate-y-full'), 10);
}
}
function hideBanner() {
const banner = document.getElementById('cookie-consent-banner');
if (banner) {
banner.classList.add('translate-y-full');
setTimeout(() => banner.style.display = 'none', 300);
}
}
function loadConsentedScripts(consent) {
if (consent.analytics && import.meta.env.PUBLIC_UMAMI_WEBSITE_ID) {
const script = document.createElement('script');
script.defer = true;
script.src = 'https://analytics.moreminimore.com/script.js';
script.setAttribute('data-website-id', import.meta.env.PUBLIC_UMAMI_WEBSITE_ID);
document.head.appendChild(script);
}
}
function initConsent() {
const stored = getStoredConsent();
if (stored) {
document.getElementById('consent-analytics').checked = stored.analytics;
loadConsentedScripts(stored);
return;
}
setTimeout(showBanner, 500);
}
document.addEventListener('DOMContentLoaded', () => {
initConsent();
document.getElementById('consent-accept')?.addEventListener('click', () => {
const analytics = document.getElementById('consent-analytics').checked;
saveConsent({ essential: true, analytics: analytics || true, marketing: false, timestamp: new Date().toISOString(), policyVersion: POLICY_VERSION });
loadConsentedScripts({ analytics: true });
hideBanner();
});
document.getElementById('consent-reject')?.addEventListener('click', () => {
saveConsent({ essential: true, analytics: false, marketing: false, timestamp: new Date().toISOString(), policyVersion: POLICY_VERSION });
hideBanner();
});
});
window.openConsentPreferences = () => showBanner();
</script>

View File

@@ -0,0 +1,29 @@
---
import { siteConfig } from '../data/site-config';
---
<div class="fixed bottom-6 right-6 z-40 flex flex-col gap-3">
<!-- LINE -->
<a
href={`https://line.me/ti/p/${siteConfig.lineId}`}
target="_blank"
rel="noopener noreferrer"
class="w-14 h-14 bg-[#00B900] rounded-full flex items-center justify-center shadow-lg hover:scale-110 transition-transform"
aria-label="ติดต่อผ่าน LINE"
>
<svg class="w-7 h-7 text-white" viewBox="0 0 24 24" fill="currentColor">
<path d="M19.365 9.863c.349 0 .63.285.63.631 0 .345-.281.63-.63.63H17.61v1.125h1.755c.349 0 .63.283.63.63 0 .344-.281.629-.63.629h-2.386c-.345 0-.627-.285-.627-.629V8.108c0-.345.282-.63.63-.63h2.386c.346 0 .627.285.627.63 0 .349-.281.63-.63.63H17.61v1.125h1.755zm-3.855 3.016c0 .27-.174.51-.432.596-.064.021-.133.031-.199.031-.211 0-.391-.09-.51-.25l-2.443-3.317v2.94c0 .344-.279.629-.631.629-.346 0-.626-.285-.626-.629V8.108c0-.27.173-.51.43-.595.06-.023.136-.033.194-.033.195 0 .375.104.495.254l2.462 3.33V8.108c0-.345.282-.63.63-.63.345 0 .63.285.63.63v4.771zm-5.741 0c0 .344-.282.629-.631.629-.345 0-.627-.285-.627-.629V8.108c0-.345.282-.63.63-.63.346 0 .628.285.628.63v4.771zm-2.466.629H4.917c-.345 0-.63-.285-.63-.629V8.108c0-.345.285-.63.63-.63.348 0 .63.285.63.63v4.141h1.756c.348 0 .629.283.629.63 0 .344-.282.629-.629.629M24 10.314C24 4.943 18.615.572 12 .572S0 4.943 0 10.314c0 4.811 4.27 8.842 10.035 9.608.391.082.923.258 1.058.59.12.301.079.766.038 1.08l-.164 1.02c-.045.301-.24 1.186 1.049.645 1.291-.539 6.916-4.078 9.436-6.975C23.176 14.393 24 12.458 24 10.314"/>
</svg>
</a>
<!-- Phone -->
<a
href={`tel:${siteConfig.phone}`}
class="w-14 h-14 bg-primary-600 rounded-full flex items-center justify-center shadow-lg hover:scale-110 transition-transform"
aria-label="โทรหาเรา"
>
<svg class="w-7 h-7 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
</svg>
</a>
</div>

114
src/components/Footer.astro Normal file
View File

@@ -0,0 +1,114 @@
---
import { siteConfig, workHours, mainNavigation } from '../data/site-config';
---
<footer class="bg-secondary-50 text-secondary-900">
<!-- Main Footer -->
<div class="container mx-auto px-4 py-12">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
<!-- Company Info -->
<div>
<img
src="/images/2021/02/13523630950840.png"
alt="Deal Plus Tech"
class="h-10 w-auto mb-4"
/>
<p class="text-secondary-600 text-sm mb-4">
{siteConfig.description}
</p>
<div class="flex gap-3">
<a
href={siteConfig.facebookUrl}
target="_blank"
rel="noopener noreferrer"
class="w-10 h-10 bg-secondary-200 rounded-lg flex items-center justify-center text-secondary-700 hover:bg-primary-600 hover:text-white transition-colors"
aria-label="Facebook"
>
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
<path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/>
</svg>
</a>
<a
href={`https://line.me/ti/p/${siteConfig.lineId}`}
target="_blank"
rel="noopener noreferrer"
class="w-10 h-10 bg-secondary-200 rounded-lg flex items-center justify-center text-secondary-700 hover:bg-primary-600 hover:text-white transition-colors"
aria-label="LINE"
>
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="currentColor">
<path d="M19.365 9.863c.349 0 .63.285.63.631 0 .345-.281.63-.63.63H17.61v1.125h1.755c.349 0 .63.283.63.63 0 .344-.281.629-.63.629h-2.386c-.345 0-.627-.285-.627-.629V8.108c0-.345.282-.63.63-.63h2.386c.346 0 .627.285.627.63 0 .349-.281.63-.63.63H17.61v1.125h1.755zm-3.855 3.016c0 .27-.174.51-.432.596-.064.021-.133.031-.199.031-.211 0-.391-.09-.51-.25l-2.443-3.317v2.94c0 .344-.279.629-.631.629-.346 0-.626-.285-.626-.629V8.108c0-.27.173-.51.43-.595.06-.023.136-.033.194-.033.195 0 .375.104.495.254l2.462 3.33V8.108c0-.345.282-.63.63-.63.345 0 .63.285.63.63v4.771zm-5.741 0c0 .344-.282.629-.631.629-.345 0-.627-.285-.627-.629V8.108c0-.345.282-.63.63-.63.346 0 .628.285.628.63v4.771zm-2.466.629H4.917c-.345 0-.63-.285-.63-.629V8.108c0-.345.285-.63.63-.63.348 0 .63.285.63.63v4.141h1.756c.348 0 .629.283.629.63 0 .344-.282.629-.629.629M24 10.314C24 4.943 18.615.572 12 .572S0 4.943 0 10.314c0 4.811 4.27 8.842 10.035 9.608.391.082.923.258 1.058.59.12.301.079.766.038 1.08l-.164 1.02c-.045.301-.24 1.186 1.049.645 1.291-.539 6.916-4.078 9.436-6.975C23.176 14.393 24 12.458 24 10.314"/>
</svg>
</a>
</div>
</div>
<!-- Quick Links -->
<div>
<h3 class="text-lg font-bold mb-4 text-primary-600">ลิงก์ด่วน</h3>
<ul class="space-y-2">
{mainNavigation.slice(0, 5).map((item) => (
<li>
<a href={item.href} class="text-secondary-600 hover:text-primary-600 transition-colors">
{item.label}
</a>
</li>
))}
</ul>
</div>
<!-- Contact Info -->
<div>
<h3 class="text-lg font-bold mb-4 text-primary-600">ติดต่อเรา</h3>
<ul class="space-y-3">
<li class="flex items-start gap-3">
<svg class="w-5 h-5 text-primary-500 mt-0.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width={2} d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width={2} d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
<span class="text-secondary-600 text-sm">{siteConfig.address}</span>
</li>
<li class="flex items-center gap-3">
<svg class="w-5 h-5 text-primary-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
</svg>
<a href={`tel:${siteConfig.phone}`} class="text-secondary-600 hover:text-primary-600 text-sm">
{siteConfig.phone}
</a>
</li>
<li class="flex items-center gap-3">
<svg class="w-5 h-5 text-primary-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
</svg>
<a href={`mailto:${siteConfig.email}`} class="text-secondary-600 hover:text-primary-600 text-sm">
{siteConfig.email}
</a>
</li>
</ul>
</div>
<!-- Business Hours -->
<div>
<h3 class="text-lg font-bold mb-4 text-primary-600">เวลาทำการ</h3>
<ul class="space-y-2">
{workHours.map((item) => (
<li class="flex justify-between text-sm">
<span class="text-secondary-600">{item.day}</span>
<span class={item.isClosed ? 'text-red-500' : 'text-secondary-900 font-medium'}>
{item.hours}
</span>
</li>
))}
</ul>
</div>
</div>
</div>
<!-- Bottom Bar -->
<div class="border-t border-secondary-200">
<div class="container mx-auto px-4 py-4">
<p class="text-center text-secondary-500 text-sm">
© {new Date().getFullYear()} {siteConfig.name}. สงวนลิขสิทธิ์.
</p>
</div>
</div>
</footer>

222
src/components/Header.astro Normal file
View File

@@ -0,0 +1,222 @@
---
import { siteConfig, mainNavigation } from '../data/site-config';
import { cn } from '../lib/utils';
interface NavItemWithChildren {
label: string;
href: string;
children?: Array<{
label: string;
href: string;
children?: Array<{ label: string; href: string }>;
}>;
}
const navItems = mainNavigation as NavItemWithChildren[];
---
<header class="fixed top-0 left-0 right-0 z-50 bg-white shadow-md">
<!-- Top Bar -->
<div class="bg-primary-600 py-2">
<div class="container mx-auto px-4 flex justify-between items-center text-sm">
<div class="flex items-center gap-6 text-white">
<a href={`tel:${siteConfig.phone}`} class="flex items-center gap-2 hover:text-primary-100">
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
</svg>
{siteConfig.phone}
</a>
<a href={`mailto:${siteConfig.email}`} class="flex items-center gap-2 hover:text-primary-100">
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
</svg>
{siteConfig.email}
</a>
</div>
<div class="hidden md:flex items-center gap-4">
<a href={`https://line.me/ti/p/${siteConfig.lineId}`} class="flex items-center gap-1 text-white hover:text-primary-100">
<svg class="w-4 h-4" viewBox="0 0 24 24" fill="currentColor">
<path d="M19.365 9.863c.349 0 .63.285.63.631 0 .345-.281.63-.63.63H17.61v1.125h1.755c.349 0 .63.283.63.63 0 .344-.281.629-.63.629h-2.386c-.345 0-.627-.285-.627-.629V8.108c0-.345.282-.63.63-.63h2.386c.346 0 .627.285.627.63 0 .349-.281.63-.63.63H17.61v1.125h1.755zm-3.855 3.016c0 .27-.174.51-.432.596-.064.021-.133.031-.199.031-.211 0-.391-.09-.51-.25l-2.443-3.317v2.94c0 .344-.279.629-.631.629-.346 0-.626-.285-.626-.629V8.108c0-.27.173-.51.43-.595.06-.023.136-.033.194-.033.195 0 .375.104.495.254l2.462 3.33V8.108c0-.345.282-.63.63-.63.345 0 .63.285.63.63v4.771zm-5.741 0c0 .344-.282.629-.631.629-.345 0-.627-.285-.627-.629V8.108c0-.345.282-.63.63-.63.346 0 .628.285.628.63v4.771zm-2.466.629H4.917c-.345 0-.63-.285-.63-.629V8.108c0-.345.285-.63.63-.63.348 0 .63.285.63.63v4.141h1.756c.348 0 .629.283.629.63 0 .344-.282.629-.629.629M24 10.314C24 4.943 18.615.572 12 .572S0 4.943 0 10.314c0 4.811 4.27 8.842 10.035 9.608.391.082.923.258 1.058.59.12.301.079.766.038 1.08l-.164 1.02c-.045.301-.24 1.186 1.049.645 1.291-.539 6.916-4.078 9.436-6.975C23.176 14.393 24 12.458 24 10.314"/>
</svg>
LINE
</a>
</div>
</div>
</div>
<!-- Main Navigation -->
<nav class="container mx-auto px-4">
<div class="flex items-center justify-between h-16">
<!-- Logo -->
<a href="/" class="flex items-center gap-3">
<img
src="/images/2021/02/13523630950840.png"
alt="Deal Plus Tech"
class="h-12 w-auto"
loading="priority"
/>
</a>
<!-- Desktop Navigation -->
<div class="hidden lg:flex items-center gap-1">
{navItems.map((item) => (
<div class="relative group">
<a
href={item.href}
class:list={[
"px-4 py-2 text-secondary-700 font-medium hover:text-primary-600 transition-colors flex items-center gap-1",
item.children && "pr-2"
]}
>
{item.label}
{item.children && (
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width={2} d="M19 9l-7 7-7-7" />
</svg>
)}
</a>
<!-- Dropdown -->
{item.children && (
<div class="absolute top-full left-0 pt-2 opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 z-50">
<div class="absolute -top-2 left-0 right-0 h-2" />
<div class="min-w-[600px] bg-white shadow-xl rounded-lg py-4 border border-secondary-100">
<div class="grid grid-cols-2 gap-1 px-4">
{item.children.map((child) => (
<div class="relative group/sub">
<a
href={child.href}
class="block px-3 py-2 text-secondary-700 hover:bg-primary-50 hover:text-primary-700 transition-colors rounded font-medium"
>
{child.label}
</a>
{child.children && (
<div class="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">
<div class="absolute -top-2 -bottom-2 -left-2 w-2" />
{child.children.map((subChild) => (
<a
href={subChild.href}
class="block px-4 py-2 text-secondary-600 hover:bg-primary-50 hover:text-primary-700 text-sm"
>
{subChild.label}
</a>
))}
</div>
)}
</div>
))}
</div>
</div>
</div>
)}
</div>
))}
<a href="/contact-us" class="btn-primary ml-4">
ติดต่อเรา
</a>
</div>
<!-- Mobile Menu Button -->
<button
id="mobile-menu-button"
class="lg:hidden text-secondary-900 p-2"
aria-label="Toggle menu"
>
<svg class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width={2} d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
</div>
<!-- Mobile Menu -->
<div id="mobile-menu" class="lg:hidden py-4 border-t border-secondary-200 max-h-[80vh] overflow-y-auto hidden">
{navItems.map((item) => (
item.children ? (
<div class="border-b border-secondary-100">
<div class="px-4 py-3 font-semibold text-secondary-900 bg-secondary-50">
{item.label}
</div>
<div class="pl-4">
{item.children.map((child) => (
<div>
<a
href={child.href}
class="block px-4 py-2 text-secondary-700 hover:text-primary-600 hover:bg-primary-50 mobile-link"
>
{child.label}
</a>
{child.children && (
<div class="pl-4 bg-secondary-50">
{child.children.map((subChild) => (
<a
href={subChild.href}
class="block px-4 py-2 text-secondary-600 hover:text-primary-600 text-sm mobile-link"
>
{subChild.label}
</a>
))}
</div>
)}
</div>
))}
</div>
</div>
) : (
<a
href={item.href}
class="block px-4 py-3 text-secondary-700 hover:text-primary-600 font-medium mobile-link"
>
{item.label}
</a>
)
))}
<div class="p-4">
<a
href="/contact-us"
class="btn-primary block text-center mobile-link"
>
ติดต่อเรา
</a>
</div>
</div>
</nav>
</header>
<script>
// Mobile menu toggle
const mobileMenuButton = document.getElementById('mobile-menu-button');
const mobileMenu = document.getElementById('mobile-menu');
let mobileMenuOpen = false;
mobileMenuButton?.addEventListener('click', () => {
mobileMenuOpen = !mobileMenuOpen;
if (mobileMenuOpen) {
mobileMenu?.classList.remove('hidden');
mobileMenuButton.innerHTML = `
<svg class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width={2} d="M6 18L18 6M6 6l12 12" />
</svg>
`;
} else {
mobileMenu?.classList.add('hidden');
mobileMenuButton.innerHTML = `
<svg class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width={2} d="M4 6h16M4 12h16M4 18h16" />
</svg>
`;
}
});
// Close mobile menu when clicking links
document.querySelectorAll('.mobile-link').forEach(link => {
link.addEventListener('click', () => {
mobileMenuOpen = false;
mobileMenu?.classList.add('hidden');
mobileMenuButton.innerHTML = `
<svg class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width={2} d="M4 6h16M4 12h16M4 18h16" />
</svg>
`;
});
});
</script>

View File

@@ -0,0 +1,37 @@
---
import type { CollectionEntry } from 'astro:content';
interface Props {
product: CollectionEntry<'products'>;
}
const { product } = Astro.props;
const { name, shortDescription, image } = product.data;
---
<a href={`/products/${product.data.slug}`} class="card group">
<div class="aspect-w-16 aspect-h-9 overflow-hidden bg-secondary-100">
<img
src={image || '/placeholder.jpg'}
alt={name}
class="object-cover w-full h-48 group-hover:scale-105 transition-transform duration-300"
loading="lazy"
/>
</div>
<div class="p-6">
<h3 class="text-lg font-bold text-secondary-900 group-hover:text-primary-600 transition-colors">
{name}
</h3>
{shortDescription && (
<p class="mt-2 text-sm text-secondary-600 line-clamp-2">
{shortDescription}
</p>
)}
<div class="mt-4 flex items-center text-primary-600 font-medium">
<span>ดูรายละเอียด</span>
<svg class="w-4 h-4 ml-2 group-hover:translate-x-1 transition-transform" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width={2} d="M9 5l7 7-7 7" />
</svg>
</div>
</div>
</a>

View File

@@ -1,31 +0,0 @@
'use client';
import Script from 'next/script';
const GA_MEASUREMENT_ID = process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID;
export default function GoogleAnalytics() {
if (!GA_MEASUREMENT_ID || GA_MEASUREMENT_ID === 'G-XXXXXXXXXX') {
return null;
}
return (
<>
<Script
src={`https://www.googletagmanager.com/gtag/js?id=${GA_MEASUREMENT_ID}`}
strategy="afterInteractive"
/>
<Script id="google-analytics" strategy="afterInteractive">
{`
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${GA_MEASUREMENT_ID}', {
page_title: document.title,
page_location: window.location.href,
});
`}
</Script>
</>
);
}

View File

@@ -1,33 +0,0 @@
'use client';
import { siteConfig } from '@/data/site-config';
export default function FloatingContact() {
return (
<div className="fixed bottom-6 right-6 z-40 flex flex-col gap-3">
{/* LINE */}
<a
href={`https://line.me/ti/p/${siteConfig.lineId}`}
target="_blank"
rel="noopener noreferrer"
className="w-14 h-14 bg-[#00B900] rounded-full flex items-center justify-center shadow-lg hover:scale-110 transition-transform"
aria-label="ติดต่อผ่าน LINE"
>
<svg className="w-7 h-7 text-white" viewBox="0 0 24 24" fill="currentColor">
<path d="M19.365 9.863c.349 0 .63.285.63.631 0 .345-.281.63-.63.63H17.61v1.125h1.755c.349 0 .63.283.63.63 0 .344-.281.629-.63.629h-2.386c-.345 0-.627-.285-.627-.629V8.108c0-.345.282-.63.63-.63h2.386c.346 0 .627.285.627.63 0 .349-.281.63-.63.63H17.61v1.125h1.755zm-3.855 3.016c0 .27-.174.51-.432.596-.064.021-.133.031-.199.031-.211 0-.391-.09-.51-.25l-2.443-3.317v2.94c0 .344-.279.629-.631.629-.346 0-.626-.285-.626-.629V8.108c0-.27.173-.51.43-.595.06-.023.136-.033.194-.033.195 0 .375.104.495.254l2.462 3.33V8.108c0-.345.282-.63.63-.63.345 0 .63.285.63.63v4.771zm-5.741 0c0 .344-.282.629-.631.629-.345 0-.627-.285-.627-.629V8.108c0-.345.282-.63.63-.63.346 0 .628.285.628.63v4.771zm-2.466.629H4.917c-.345 0-.63-.285-.63-.629V8.108c0-.345.285-.63.63-.63.348 0 .63.285.63.63v4.141h1.756c.348 0 .629.283.629.63 0 .344-.282.629-.629.629M24 10.314C24 4.943 18.615.572 12 .572S0 4.943 0 10.314c0 4.811 4.27 8.842 10.035 9.608.391.082.923.258 1.058.59.12.301.079.766.038 1.08l-.164 1.02c-.045.301-.24 1.186 1.049.645 1.291-.539 6.916-4.078 9.436-6.975C23.176 14.393 24 12.458 24 10.314"/>
</svg>
</a>
{/* Phone */}
<a
href={`tel:${siteConfig.phone}`}
className="w-14 h-14 bg-primary-600 rounded-full flex items-center justify-center shadow-lg hover:scale-110 transition-transform"
aria-label="โทรหาเรา"
>
<svg className="w-7 h-7 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
</svg>
</a>
</div>
);
}

View File

@@ -1,118 +0,0 @@
import Image from 'next/image';
import Link from 'next/link';
import { siteConfig, workHours, mainNavigation } from '@/data/site-config';
export default function Footer() {
return (
<footer className="bg-secondary-50 text-secondary-900">
{/* Main Footer */}
<div className="container mx-auto px-4 py-12">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
{/* Company Info */}
<div>
<Image
src="/images/2021/02/13523630950840.png"
alt="Deal Plus Tech"
width={150}
height={50}
className="h-10 w-auto mb-4"
/>
<p className="text-secondary-600 text-sm mb-4">
{siteConfig.description}
</p>
<div className="flex gap-3">
<a
href={siteConfig.facebookUrl}
target="_blank"
rel="noopener noreferrer"
className="w-10 h-10 bg-secondary-200 rounded-lg flex items-center justify-center text-secondary-700 hover:bg-primary-600 hover:text-white transition-colors"
>
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
<path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/>
</svg>
</a>
<a
href={`https://line.me/ti/p/${siteConfig.lineId}`}
target="_blank"
rel="noopener noreferrer"
className="w-10 h-10 bg-secondary-200 rounded-lg flex items-center justify-center text-secondary-700 hover:bg-primary-600 hover:text-white transition-colors"
>
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="currentColor">
<path d="M19.365 9.863c.349 0 .63.285.63.631 0 .345-.281.63-.63.63H17.61v1.125h1.755c.349 0 .63.283.63.63 0 .344-.281.629-.63.629h-2.386c-.345 0-.627-.285-.627-.629V8.108c0-.345.282-.63.63-.63h2.386c.346 0 .627.285.627.63 0 .349-.281.63-.63.63H17.61v1.125h1.755zm-3.855 3.016c0 .27-.174.51-.432.596-.064.021-.133.031-.199.031-.211 0-.391-.09-.51-.25l-2.443-3.317v2.94c0 .344-.279.629-.631.629-.346 0-.626-.285-.626-.629V8.108c0-.27.173-.51.43-.595.06-.023.136-.033.194-.033.195 0 .375.104.495.254l2.462 3.33V8.108c0-.345.282-.63.63-.63.345 0 .63.285.63.63v4.771zm-5.741 0c0 .344-.282.629-.631.629-.345 0-.627-.285-.627-.629V8.108c0-.345.282-.63.63-.63.346 0 .628.285.628.63v4.771zm-2.466.629H4.917c-.345 0-.63-.285-.63-.629V8.108c0-.345.285-.63.63-.63.348 0 .63.285.63.63v4.141h1.756c.348 0 .629.283.629.63 0 .344-.282.629-.629.629M24 10.314C24 4.943 18.615.572 12 .572S0 4.943 0 10.314c0 4.811 4.27 8.842 10.035 9.608.391.082.923.258 1.058.59.12.301.079.766.038 1.08l-.164 1.02c-.045.301-.24 1.186 1.049.645 1.291-.539 6.916-4.078 9.436-6.975C23.176 14.393 24 12.458 24 10.314"/>
</svg>
</a>
</div>
</div>
{/* Quick Links */}
<div>
<h3 className="text-lg font-bold mb-4 text-primary-600"></h3>
<ul className="space-y-2">
{mainNavigation.slice(0, 5).map((item) => (
<li key={item.href}>
<Link href={item.href} className="text-secondary-600 hover:text-primary-600 transition-colors">
{item.label}
</Link>
</li>
))}
</ul>
</div>
{/* Contact Info */}
<div>
<h3 className="text-lg font-bold mb-4 text-primary-600"></h3>
<ul className="space-y-3">
<li className="flex items-start gap-3">
<svg className="w-5 h-5 text-primary-500 mt-0.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
<span className="text-secondary-600 text-sm">{siteConfig.address}</span>
</li>
<li className="flex items-center gap-3">
<svg className="w-5 h-5 text-primary-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
</svg>
<a href={`tel:${siteConfig.phone}`} className="text-secondary-600 hover:text-primary-600 text-sm">
{siteConfig.phone}
</a>
</li>
<li className="flex items-center gap-3">
<svg className="w-5 h-5 text-primary-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
</svg>
<a href={`mailto:${siteConfig.email}`} className="text-secondary-600 hover:text-primary-600 text-sm">
{siteConfig.email}
</a>
</li>
</ul>
</div>
{/* Business Hours */}
<div>
<h3 className="text-lg font-bold mb-4 text-primary-600"></h3>
<ul className="space-y-2">
{workHours.map((item) => (
<li key={item.day} className="flex justify-between text-sm">
<span className="text-secondary-600">{item.day}</span>
<span className={item.isClosed ? 'text-red-500' : 'text-secondary-900 font-medium'}>
{item.hours}
</span>
</li>
))}
</ul>
</div>
</div>
</div>
{/* Bottom Bar */}
<div className="border-t border-secondary-200">
<div className="container mx-auto px-4 py-4">
<p className="text-center text-secondary-500 text-sm">
© {new Date().getFullYear()} {siteConfig.name}. .
</p>
</div>
</div>
</footer>
);
}

View File

@@ -1,201 +0,0 @@
'use client';
import { useState } from 'react';
import Link from 'next/link';
import Image from 'next/image';
import { siteConfig, mainNavigation } from '@/data/site-config';
import { cn } from '@/lib/utils';
export default function Header() {
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
return (
<header className="fixed top-0 left-0 right-0 z-50 bg-white shadow-md">
{/* Top Bar */}
<div className="bg-primary-600 py-2">
<div className="container mx-auto px-4 flex justify-between items-center text-sm">
<div className="flex items-center gap-6 text-white">
<a href={`tel:${siteConfig.phone}`} className="flex items-center gap-2 hover:text-primary-100">
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
</svg>
{siteConfig.phone}
</a>
<a href={`mailto:${siteConfig.email}`} className="flex items-center gap-2 hover:text-primary-100">
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
</svg>
{siteConfig.email}
</a>
</div>
<div className="hidden md:flex items-center gap-4">
<a href={`https://line.me/ti/p/${siteConfig.lineId}`} className="flex items-center gap-1 text-white hover:text-primary-100">
<svg className="w-4 h-4" viewBox="0 0 24 24" fill="currentColor">
<path d="M19.365 9.863c.349 0 .63.285.63.631 0 .345-.281.63-.63.63H17.61v1.125h1.755c.349 0 .63.283.63.63 0 .344-.281.629-.63.629h-2.386c-.345 0-.627-.285-.627-.629V8.108c0-.345.282-.63.63-.63h2.386c.346 0 .627.285.627.63 0 .349-.281.63-.63.63H17.61v1.125h1.755zm-3.855 3.016c0 .27-.174.51-.432.596-.064.021-.133.031-.199.031-.211 0-.391-.09-.51-.25l-2.443-3.317v2.94c0 .344-.279.629-.631.629-.346 0-.626-.285-.626-.629V8.108c0-.27.173-.51.43-.595.06-.023.136-.033.194-.033.195 0 .375.104.495.254l2.462 3.33V8.108c0-.345.282-.63.63-.63.345 0 .63.285.63.63v4.771zm-5.741 0c0 .344-.282.629-.631.629-.345 0-.627-.285-.627-.629V8.108c0-.345.282-.63.63-.63.346 0 .628.285.628.63v4.771zm-2.466.629H4.917c-.345 0-.63-.285-.63-.629V8.108c0-.345.285-.63.63-.63.348 0 .63.285.63.63v4.141h1.756c.348 0 .629.283.629.63 0 .344-.282.629-.629.629M24 10.314C24 4.943 18.615.572 12 .572S0 4.943 0 10.314c0 4.811 4.27 8.842 10.035 9.608.391.082.923.258 1.058.59.12.301.079.766.038 1.08l-.164 1.02c-.045.301-.24 1.186 1.049.645 1.291-.539 6.916-4.078 9.436-6.975C23.176 14.393 24 12.458 24 10.314"/>
</svg>
LINE
</a>
</div>
</div>
</div>
{/* Main Navigation */}
<nav className="container mx-auto px-4">
<div className="flex items-center justify-between h-16">
{/* Logo */}
<Link href="/" className="flex items-center gap-3">
<Image
src="/images/2021/02/13523630950840.png"
alt="Deal Plus Tech"
width={150}
height={50}
className="h-12 w-auto"
priority
/>
</Link>
{/* Desktop Navigation */}
<div className="hidden lg:flex items-center gap-1">
{mainNavigation.map((item) => (
<div
key={item.href}
className="relative group"
>
<Link
href={item.href}
className={cn(
"px-4 py-2 text-secondary-700 font-medium hover:text-primary-600 transition-colors flex items-center gap-1",
item.children && "pr-2"
)}
>
{item.label}
{item.children && (
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
)}
</Link>
{/* Dropdown - CSS hover with invisible bridge */}
{item.children && (
<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">
{item.children.map((child) => (
<div key={child.href} className="relative group/sub">
<Link
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"
>
{child.label}
</Link>
{child.children && (
<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) => (
<Link
key={subChild.href}
href={subChild.href}
className="block px-4 py-2 text-secondary-600 hover:bg-primary-50 hover:text-primary-700 text-sm"
>
{subChild.label}
</Link>
))}
</div>
)}
</div>
))}
</div>
</div>
</div>
)}
</div>
))}
<Link href="/contact-us" className="btn-primary ml-4">
</Link>
</div>
{/* Mobile Menu Button */}
<button
className="lg:hidden text-secondary-900 p-2"
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
>
<svg className="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
{mobileMenuOpen ? (
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
) : (
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
)}
</svg>
</button>
</div>
{/* Mobile Menu */}
{mobileMenuOpen && (
<div className="lg:hidden py-4 border-t border-secondary-200 max-h-[80vh] overflow-y-auto">
{mainNavigation.map((item) => (
<div key={item.href}>
{item.children ? (
<div className="border-b border-secondary-100">
<div className="px-4 py-3 font-semibold text-secondary-900 bg-secondary-50">
{item.label}
</div>
<div className="pl-4">
{item.children.map((child) => (
<div key={child.href}>
<Link
href={child.href}
className="block px-4 py-2 text-secondary-700 hover:text-primary-600 hover:bg-primary-50"
onClick={() => setMobileMenuOpen(false)}
>
{child.label}
</Link>
{child.children && (
<div className="pl-4 bg-secondary-50">
{child.children.map((subChild) => (
<Link
key={subChild.href}
href={subChild.href}
className="block px-4 py-2 text-secondary-600 hover:text-primary-600 text-sm"
onClick={() => setMobileMenuOpen(false)}
>
{subChild.label}
</Link>
))}
</div>
)}
</div>
))}
</div>
</div>
) : (
<Link
href={item.href}
className="block px-4 py-3 text-secondary-700 hover:text-primary-600 font-medium"
onClick={() => setMobileMenuOpen(false)}
>
{item.label}
</Link>
)}
</div>
))}
<div className="p-4">
<Link
href="/contact-us"
className="btn-primary block text-center"
onClick={() => setMobileMenuOpen(false)}
>
</Link>
</div>
</div>
)}
</nav>
</header>
);
}

View File

@@ -1,41 +0,0 @@
import { HTMLAttributes, forwardRef } from 'react';
import { cn } from '@/lib/utils';
interface BadgeProps extends HTMLAttributes<HTMLSpanElement> {
variant?: 'default' | 'primary' | 'success' | 'warning' | 'error';
size?: 'sm' | 'md' | 'lg';
}
const Badge = forwardRef<HTMLSpanElement, BadgeProps>(
({ className, variant = 'default', size = 'md', children, ...props }, ref) => {
return (
<span
ref={ref}
className={cn(
'inline-flex items-center font-medium rounded-full',
{
'bg-secondary-100 text-secondary-700': variant === 'default',
'bg-primary-100 text-primary-700': variant === 'primary',
'bg-green-100 text-green-700': variant === 'success',
'bg-yellow-100 text-yellow-700': variant === 'warning',
'bg-red-100 text-red-700': variant === 'error',
},
{
'px-2 py-0.5 text-xs': size === 'sm',
'px-3 py-1 text-sm': size === 'md',
'px-4 py-1.5 text-base': size === 'lg',
},
className
)}
{...props}
>
{children}
</span>
);
}
);
Badge.displayName = 'Badge';
export { Badge };
export type { BadgeProps };

View File

@@ -1,42 +0,0 @@
import { ButtonHTMLAttributes, forwardRef } from 'react';
import { cn } from '@/lib/utils';
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'primary' | 'secondary' | 'outline' | 'ghost';
size?: 'sm' | 'md' | 'lg';
}
const Button = forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant = 'primary', size = 'md', children, ...props }, ref) => {
return (
<button
ref={ref}
className={cn(
'inline-flex items-center justify-center font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 disabled:pointer-events-none disabled:opacity-50',
// Variants
{
'bg-primary-600 text-white hover:bg-primary-700 active:bg-primary-800': variant === 'primary',
'bg-secondary-100 text-secondary-900 hover:bg-secondary-200': variant === 'secondary',
'border-2 border-primary-600 text-primary-600 hover:bg-primary-50': variant === 'outline',
'text-secondary-600 hover:bg-secondary-100': variant === 'ghost',
},
// Sizes
{
'px-3 py-1.5 text-sm rounded': size === 'sm',
'px-4 py-2 text-base rounded-md': size === 'md',
'px-6 py-3 text-lg rounded-lg': size === 'lg',
},
className
)}
{...props}
>
{children}
</button>
);
}
);
Button.displayName = 'Button';
export { Button };
export type { ButtonProps };

View File

@@ -1,63 +0,0 @@
import { HTMLAttributes, forwardRef } from 'react';
import { cn } from '@/lib/utils';
interface CardProps extends HTMLAttributes<HTMLDivElement> {
variant?: 'default' | 'bordered' | 'elevated';
}
const Card = forwardRef<HTMLDivElement, CardProps>(
({ className, variant = 'default', children, ...props }, ref) => {
return (
<div
ref={ref}
className={cn(
'bg-white rounded-xl overflow-hidden',
{
'shadow-sm': variant === 'default',
'border border-secondary-200': variant === 'bordered',
'shadow-lg': variant === 'elevated',
},
className
)}
{...props}
>
{children}
</div>
);
}
);
Card.displayName = 'Card';
const CardHeader = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
({ className, children, ...props }, ref) => (
<div ref={ref} className={cn('p-4 border-b border-secondary-100', className)} {...props}>
{children}
</div>
)
);
CardHeader.displayName = 'CardHeader';
const CardContent = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
({ className, children, ...props }, ref) => (
<div ref={ref} className={cn('p-4', className)} {...props}>
{children}
</div>
)
);
CardContent.displayName = 'CardContent';
const CardFooter = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
({ className, children, ...props }, ref) => (
<div ref={ref} className={cn('p-4 border-t border-secondary-100 bg-secondary-50', className)} {...props}>
{children}
</div>
)
);
CardFooter.displayName = 'CardFooter';
export { Card, CardHeader, CardContent, CardFooter };
export type { CardProps };

View File

@@ -1,11 +0,0 @@
// UI Components - Reusable components for Deal Plus Tech website
// These components follow the project's design system using Tailwind CSS
export { Button } from './Button';
export type { ButtonProps } from './Button';
export { Card, CardHeader, CardContent, CardFooter } from './Card';
export type { CardProps } from './Card';
export { Badge } from './Badge';
export type { BadgeProps } from './Badge';

View File

@@ -5,7 +5,7 @@ excerpt: "ท่อ PPR (Polypropylene Random Copolymer) เป็นท่อ
date: "2024-01-15"
author: "Deal Plus Tech"
categories: ["ท่อ PPR", "ความรู้", "คู่มือ"]
featuredImage: "/images/2021/03/ppr-pipe_000C.jpg"
featuredImage: "/images/2021/03/hdpe_pipe_main.jpg"
---
## ท่อ PPR คืออะไร?

View File

@@ -0,0 +1,190 @@
---
id: hdpe
name: ท่อ HDPE
nameEn: HDPE Pipe
slug: ท่อhdpe
description: 'ท่อ HDPE PE80/PE100 ทนแรงดัน PN25 อายุการใช้งาน 50 ปี มอก. สำหรับประปาและชลประทาน'
shortDescription: 'ท่อเอชดีพีอี PE80/PE100 มาตรฐาน มอก.'
image: /images/2021/03/hdpe_welding2.jpg
keywords:
- ท่อ HDPE
- ท่อเอชดีพีอี
- ท่อ PE
- ท่อน้ำ HDPE
- PE80
- PE100
- ท่อ PE100
- ท่อ PE80
- ท่อพีอี
- High Density Polyethylene
- ท่อชลประทาน
- ท่อประปา HDPE
- ท่อดำ PE
- ท่อน้ำดำ
- SDR pipe
seoContent: 'ท่อ HDPE (High Density Polyethylene) หรือท่อเอชดีพีอี เป็นท่อพลาสติกคุณภาพสูงที่มีความทนทานและยืดหยุ่นสูง ผลิตจากเม็ดพลาสติก HDPE เกรด PE80 และ PE100 ท่อ HDPE สามารถทนแรงดันได้สูงถึง PN25 บาร์'
specifications:
- label: วัสดุ
value: HDPE (High Density Polyethylene)
- label: เกรด
value: PE80, PE100
- label: มาตรฐาน
value: มอก. 827-2547, ISO 4427
- label: แรงดันทนทาน
value: PN4 - PN25
unit: bar
- label: SDR
value: SDR 9, 11, 13.6, 17, 21, 26
- label: อุณหภูมิทนทาน
value: '-40 ถึง 60'
unit: °C
- label: ขนาดท่อ
value: '20, 32, 50, 63, 75, 90, 110, 160, 200, 250, 315, 400, 500, 630'
unit: mm
- label: สี
value: ดำ, น้ำเงิน (Blue Stripe)
- label: ความหนาแน่น
value: '0.941-0.965'
unit: g/cm³
- label: อายุการใช้งาน
value: '50'
unit: ปี
features:
- ทนแรงดันสูงถึง PN25 บาร์
- ทนทานต่อแรงกระแทกและการกัดกร่อน
- ยืดหยุ่นสูง ทนต่อการเคลื่อนไหวของดิน
- ไม่เกิดสนิม ไม่เปรอะเปื้อน
- น้ำหนักเบา ขนส่งและติดตั้งง่าย
- รอยต่อแน่นหนาด้วย Butt Fusion
- ทนทานต่อสารเคมีและกรดด่าง
- อายุการใช้งานยาวนาน 50 ปี
- ผ่านมาตรฐาน มอก. 827-2547
- เหมาะสำหรับงานฝังดิน
applications:
- ระบบประปา
- ระบบชลประทาน
- ระบบน้ำเสีย
- ท่อส่งก๊าซ
- งานอุตสาหกรรม
- ท่อส่งสารเคมี
- ระบบระบายน้ำ
- งานเหมืองแร่
certifications:
- มอก. 827-2547
- ISO 4427
- ISO 9001
faq:
- question: ท่อ HDPE PE80 กับ PE100 ต่างกันอย่างไร?
answer: 'ท่อ HDPE PE100 มีความทนทานต่อแรงดันสูงกว่า PE80 โดย PE100 มี MRS (Minimum Required Strength) 10 MPa ส่วน PE80 มี MRS 8 MPa ทำให้ PE100 สามารถทนแรงดันสูงกว่าในขนาดผนังที่เท่ากัน'
- question: ท่อ HDPE มีอายุการใช้งานกี่ปี?
answer: ท่อ HDPE มีอายุการใช้งานยาวนานกว่า 50 ปี ภายใต้การใช้งานตามมาตรฐาน
- question: วิธีติดตั้งท่อ HDPE ทำอย่างไร?
answer: ท่อ HDPE ติดตั้งโดยใช้วิธี Butt Fusion (เชื่อมปลายต่อ) หรือ Electrofusion (เชื่อมด้วยไฟฟ้า) โดยใช้อุปกรณ์เชื่อมท่อ HDPE เฉพาะทาง
- question: SDR ในท่อ HDPE คืออะไร?
answer: 'SDR (Standard Dimension Ratio) คืออัตราส่วนระหว่างเส้นผ่านศูนย์กลางภายนอกกับความหนาผนังท่อ ค่า SDR ที่น้อยกว่าหมายถึงผนังท่อหนากว่า ทนแรงดันได้สูงกว่า'
relatedProductIds:
- hdpe-welder
- ppr-elephant
schemaData:
brand: Thai HDPE
material: High Density Polyethylene (HDPE)
category: Water Pipe - HDPE
---
# ท่อ HDPE (High Density Polyethylene)
## ภาพรวม
ท่อ HDPE (High Density Polyethylene) หรือ **ท่อเอชดีพีอี** เป็นท่อพลาสติกคุณภาพสูงที่มีความ **ทนทานและยืดหยุ่นสูง** ผลิตจากเม็ดพลาสติก HDPE เกรด **PE80 และ PE100**
## คุณสมบัติเด่น
ท่อ HDPE สามารถทนแรงดันได้สูงถึง **PN25 บาร์** ทนทานต่อแรงกระแทกและการกัดกร่อน ไม่เกิดสนิม อายุการใช้งานยาวนานกว่า **50 ปี**
### ข้อดีของท่อ HDPE
1. **ทนแรงดันสูง** - สูงถึง PN25 บาร์
2. **ทนแรงกระแทก** - ยืดหยุ่นสูง ทนต่อการเคลื่อนไหวของดิน
3. **ไม่เกิดสนิม** - ทนสารเคมีและกรดด่าง
4. **น้ำหนักเบา** - ขนส่งและติดตั้งง่าย
5. **รอยต่อแน่นหนา** - ระบบ Butt Fusion ไม่รั่วซึม
6. **อายุการใช้งานยาว** - มากกว่า 50 ปี
7. **มาตรฐาน มอก.** - รับรองคุณภาพ
## การใช้งาน
### เหมาะสำหรับ
- **ระบบประปา** - งานผลิตน้ำประปา
- **ระบบชลประทาน** - ส่งน้ำทางการเกษตร
- **ระบบน้ำเสีย** - ท่อระบายน้ำ
- **ท่อส่งก๊าซ** - ท่อส่งก๊าซธรรมชาติ
- **งานอุตสาหกรรม** - ท่อส่งสารเคมี
- **ระบบระบายน้ำ** - งานเทศบาลและเมือง
## มาตรฐานและรับรอง
ท่อ HDPE ผ่านมาตรฐาน:
-**มอก. 827-2547** - มาตรฐานผลิตภัณฑ์อุตสาหกรรม
-**ISO 4427** - มาตรฐานสากล
-**ISO 9001** - ระบบบริหารคุณภาพ
## เกรดของท่อ HDPE
### PE80 vs PE100
| คุณสมบัติ | PE80 | PE100 |
|-----------|------|-------|
| **MRS** | 8 MPa | 10 MPa |
| **ทนแรงดัน** | สูง | สูงกว่า |
| **ราคา** | ประหยัด | สูงกว่า |
| **การใช้งาน** | ทั่วไป | แรงดันสูง |
## SDR (Standard Dimension Ratio)
**SDR** คืออัตราส่วนระหว่างเส้นผ่านศูนย์กลางภายนอกกับความหนาผนังท่อ
- **SDR น้อย** = ผนังหนา = ทนแรงดันสูง
- **SDR มาก** = ผนังบาง = ทนแรงดันต่ำ
ตัวอย่าง:
- SDR 9 = ทนแรงดันสูงสุด
- SDR 11 = ทนแรงดันสูง
- SDR 17 = ทนแรงดันปานกลาง
- SDR 26 = ทนแรงดันต่ำ
## การติดตั้ง
### วิธี Butt Fusion
- เหมาะสำหรับท่อ **63-1200 mm**
- ใช้ความร้อนหลอมปลายท่อ
- กดต่อกันจนเป็นชิ้นเดียว
### วิธี Electrofusion
- เหมาะสำหรับท่อ **20-630 mm**
- ใช้ข้อต่อที่มีขดลวดความร้อน
- สะดวกในพื้นที่จำกัด
## คำถามที่พบบ่อย
### ท่อ HDPE PE80 กับ PE100 ต่างกันอย่างไร?
ท่อ HDPE PE100 มีความทนทานต่อแรงดันสูงกว่า PE80 โดย PE100 มี MRS (Minimum Required Strength) 10 MPa ส่วน PE80 มี MRS 8 MPa
### ท่อ HDPE มีอายุการใช้งานกี่ปี?
ท่อ HDPE มีอายุการใช้งานยาวนานกว่า **50 ปี** ภายใต้การใช้งานตามมาตรฐาน
### วิธีติดตั้งท่อ HDPE ทำอย่างไร?
ท่อ HDPE ติดตั้งโดยใช้วิธี **Butt Fusion** (เชื่อมปลายต่อ) หรือ **Electrofusion** (เชื่อมด้วยไฟฟ้า)
### SDR ในท่อ HDPE คืออะไร?
SDR (Standard Dimension Ratio) คืออัตราส่วนระหว่างเส้นผ่านศูนย์กลางภายนอกกับความหนาผนังท่อ ค่า SDR ที่น้อยกว่าหมายถึงผนังท่อหนากว่า
## สินค้าที่เกี่ยวข้อง
- [เครื่องเชื่อม HDPE](/เครื่องเชื่อม-hdpe/)
- [ท่อพีพีอาร์ตราช้าง](/ท่อพีพีอาร์ตราช้าง/)

View File

@@ -0,0 +1,154 @@
---
id: poloplast
name: ท่อ PP-R/PP-RCT POLOPLAST
nameEn: POLOPLAST PP-R Pipe
slug: pp-r-pp-rct-poloplast
description: 'ท่อพีพีอาร์ POLOPLAST จากเยอรมนี มาตรฐาน DVGW และ SKZ ทนอุณหภูมิ 95°C รับประกัน 10 ปี'
shortDescription: 'ท่อ PP-R/PP-RCT POLOPLAST คุณภาพเยอรมัน'
image: /images/2021/03/hdpe001.jpg
keywords:
- POLOPLAST
- ท่อเยอรมัน
- PP-RCT
- ท่อพีพีอาร์เกรดสูง
- ท่อ POLOPLAST
- ท่อ PP-R เยอรมัน
- ท่อน้ำร้อนเยอรมัน
- DVGW
- SKZ
- ท่อ PP-RCT
- Poloplast Thailand
seoContent: 'ท่อพีพีอาร์ POLOPLAST เป็นผลิตภัณฑ์ระดับพรีเมียมจากเยอรมนี มีทั้งรุ่น PP-R และ PP-RCT ที่ได้รับการพัฒนาด้วยเทคโนโลยีล้ำสมัย ท่อ POLOPLAST ผ่านมาตรฐาน DVGW และ SKZ ระดับสากล มีความทนทานสูงสุด ทนอุณหภูมิได้ถึง 95°C และทนแรงดันสูง รับประกันคุณภาพ 10 ปี'
specifications:
- label: วัสดุ
value: PP-R / PP-RCT (Polypropylene Random Copolymer)
- label: มาตรฐาน
value: DIN 8077/8078, ISO 15874, DVGW, SKZ
- label: แรงดันทนทาน
value: 'PN10, PN16, PN20, PN25'
unit: bar
- label: อุณหภูมิทนทาน
value: '-20 ถึง 95'
unit: °C
- label: ขนาดท่อ
value: '20, 25, 32, 40, 50, 63, 75, 90, 110, 125, 160'
unit: mm
- label: ค่าสัมประสิทธิ์การนำความร้อน
value: '0.15'
unit: W/mK
- label: สี
value: ขาว, เขียว, ส้ม
- label: อายุการใช้งาน
value: '50'
unit: ปี
- label: รับประกัน
value: '10'
unit: ปี
features:
- ผลิตในเยอรมนี คุณภาพระดับพรีเมียม
- มาตรฐาน DVGW และ SKZ ระดับสากล
- ทนอุณหภูมิสูงสุด 95°C
- ทนแรงดันสูงถึง PN25
- ค่านำความร้อนต่ำ 0.15 W/mK
- ฉนวนความร้อนยอดเยี่ยม
- ไม่เกิดสนิมและการกัดกร่อน
- อายุการใช้งาน 50 ปี
- รับประกัน 10 ปี
- เหมาะสำหรับงานที่ต้องการคุณภาพสูงสุด
applications:
- ระบบประปาน้ำร้อนอุณหภูมิสูง
- ระบบทำความร้อน (Heating)
- ระบบแอร์แช่ (Chilled Water)
- โรงแรม 5 ดาว
- โรงพยาบาลและศูนย์การแพทย์
- โครงการระดับพรีเมียม
- โรงงานอุตสาหกรรม
certifications:
- DIN 8077/8078
- ISO 15874
- DVGW
- SKZ
- Hygienic Certificate
faq:
- question: ท่อ POLOPLAST กับท่อ PPR ทั่วไปต่างกันอย่างไร?
answer: ท่อ POLOPLAST ผลิตในเยอรมนี มีมาตรฐาน DVGW และ SKZ ทนแรงดันสูงถึง PN25 มีค่านำความร้อนต่ำกว่า และรับประกัน 10 ปี ซึ่งดีกว่าท่อ PPR ทั่วไป
- question: PP-RCT คืออะไร?
answer: 'PP-RCT (Polypropylene Random Copolymer with modified Crystallinity and Temperature resistance) เป็นวัสดุพัฒนาต่อจาก PP-R มีความทนทานต่อแรงดันและอุณหภูมิสูงกว่า สามารถทนแรงดันได้สูงถึง PN25'
- question: ท่อ POLOPLAST รับประกันกี่ปี?
answer: ท่อ POLOPLAST มีการรับประกันคุณภาพ 10 ปี สะท้อนถึงความมั่นใจในคุณภาพของผลิตภัณฑ์
relatedProductIds:
- ppr-elephant
- thai-ppr
- ppr-welder
schemaData:
brand: POLOPLAST
manufacturer: POLOPLAST GmbH (Germany)
material: PP-R / PP-RCT
category: Plumbing Pipe - Premium PPR
---
# ท่อ PP-R/PP-RCT POLOPLAST
## ภาพรวม
ท่อพีพีอาร์ **POLOPLAST** เป็นผลิตภัณฑ์ **ระดับพรีเมียมจากเยอรมนี** มีทั้งรุ่น PP-R และ PP-RCT ที่ได้รับการพัฒนาด้วยเทคโนโลยีล้ำสมัย ท่อ POLOPLAST ผ่านมาตรฐาน DVGW และ SKZ ระดับสากล
## คุณสมบัติเด่น
มีความทนทานสูงสุด **ทนอุณหภูมิได้ถึง 95°C** และ **ทนแรงดันสูงถึง PN25** รับประกันคุณภาพ **10 ปี**
### ข้อดีของท่อ POLOPLAST
1. **ผลิตในเยอรมนี** - คุณภาพระดับพรีเมียม
2. **มาตรฐานสูงสุด** - DVGW และ SKZ
3. **ทนแรงดัน PN25** - สูงที่สุดในตลาด
4. **ฉนวนความร้อนดีเยี่ยม** - ค่าการนำความร้อน 0.15 W/mK
5. **ทนอุณหภูมิ 95°C** - เหมาะกับน้ำร้อนอุณหภูมิสูง
6. **รับประกัน 10 ปี** - มั่นใจในคุณภาพ
7. **อายุการใช้งาน 50 ปี** - ลงทุนครั้งเดียว
## การใช้งาน
### เหมาะสำหรับ
- ระบบประปาน้ำร้อนอุณหภูมิสูง
- ระบบทำความร้อน (Heating)
- ระบบแอร์แช่ (Chilled Water)
- **โรงแรม 5 ดาว**
- **โรงพยาบาลและศูนย์การแพทย์**
- **โครงการระดับพรีเมียม**
- โรงงานอุตสาหกรรม
## มาตรฐานและรับรอง
ท่อ POLOPLAST ได้รับมาตรฐานสากล:
-**DIN 8077/8078** - มาตรฐานเยอรมัน
-**ISO 15874** - มาตรฐานสากล
-**DVGW** - สมาคมเทคนิคและวิทยาศาสตร์ก๊าซและน้ำเยอรมัน
-**SKZ** - ศูนย์เซาท์เยอรมันพลาสติก
-**Hygienic Certificate** - รับรองความปลอดภัยน้ำดื่ม
## PP-RCT Technology
**PP-RCT** (Polypropylene Random Copolymer with modified Crystallinity and Temperature resistance) เป็นวัสดุพัฒนาต่อจาก PP-R มีความทนทานต่อแรงดันและอุณหภูมิสูงกว่า สามารถทนแรงดันได้สูงถึง **PN25**
## คำถามที่พบบ่อย
### ท่อ POLOPLAST กับท่อ PPR ทั่วไปต่างกันอย่างไร?
ท่อ POLOPLAST ผลิตในเยอรมนี มีมาตรฐาน DVGW และ SKZ ทนแรงดันสูงถึง PN25 มีค่านำความร้อนต่ำกว่า และรับประกัน 10 ปี ซึ่งดีกว่าท่อ PPR ทั่วไป
### PP-RCT คืออะไร?
PP-RCT (Polypropylene Random Copolymer with modified Crystallinity and Temperature resistance) เป็นวัสดุพัฒนาต่อจาก PP-R มีความทนทานต่อแรงดันและอุณหภูมิสูงกว่า สามารถทนแรงดันได้สูงถึง PN25
### ท่อ POLOPLAST รับประกันกี่ปี?
ท่อ POLOPLAST มีการรับประกันคุณภาพ **10 ปี** สะท้อนถึงความมั่นใจในคุณภาพของผลิตภัณฑ์
## สินค้าที่เกี่ยวข้อง
- [ท่อพีพีอาร์ตราช้าง](/ท่อพีพีอาร์ตราช้าง/)
- [ท่อ PPR Thai PPR](/ท่อppr-thaippr/)
- [เครื่องเชื่อมท่อพีพีอาร์](/เครื่องเชื่อมท่อพีพีอาร์/)

View File

@@ -0,0 +1,160 @@
---
id: ppr-elephant
name: ท่อพีพีอาร์ตราช้าง
nameEn: PPR Elephant Pipe
slug: ท่อพีพีอาร์ตราช้าง
description: 'ท่อพีพีอาร์ตราช้าง (SCG) คุณภาพระดับสากล ทนอุณหภูมิสูง 95°C ทนความดัน 20 บาร์ อายุการใช้งาน 50 ปี'
shortDescription: 'ท่อพีพีอาร์ตราช้าง SCG มาตรฐาน DIN 8077/8078'
image: /images/2021/03/hdpe_pipe_main.jpg
seoContent: 'ท่อพีพีอาร์ตราช้าง (PPR Elephant) ผลิตโดย SCG บริษัทชั้นนำของไทย เป็นท่อพลาสติกประเภท Polypropylene Random Copolymer (PP-R) ที่มีคุณภาพสูง ได้รับมาตรฐาน DIN 8077/8078 จากเยอรมนี และมาตรฐาน ISO 15874 ระดับสากล'
keywords:
- ท่อ PPR
- ท่อพีพีอาร์
- ท่อน้ำ PPR
- ท่อประปา PPR
- ราคาท่อ PPR
- ท่อตราช้าง
- SCG PPR
- ท่อ PPR SCG
specifications:
- label: วัสดุ
value: PP-R (Polypropylene Random Copolymer)
- label: มาตรฐาน
value: DIN 8077/8078, ISO 15874
- label: แรงดันทนทาน
value: 'PN10, PN16, PN20'
unit: bar
- label: อุณหภูมิทนทาน
value: '-20 ถึง 95'
unit: °C
- label: ขนาดท่อ
value: '20, 25, 32, 40, 50, 63, 75, 90, 110'
unit: mm
- label: ความหนาผนัง
value: SDR 7.4, 11, 17.6
- label: สี
value: ขาว, เขียว
- label: อายุการใช้งาน
value: '50'
unit: ปี
features:
- ทนอุณหภูมิสูงสุด 95°C เหมาะกับน้ำร้อน
- ทนความดัน PN20 (20 บาร์)
- ไม่เกิดสนิมและการกัดกร่อน
- ผิวภายในเรียบลดการสะสมของตะกรัน
- ติดตั้งด้วยการเชื่อมความร้อน ไม่ต้องใช้กาว
- ปลอดภัยสำหรับน้ำดื่ม ไม่ปนเปื้อนสารพิษ
- ฉนวนความร้อนดี ลดการสูญเสียความร้อน
- อายุการใช้งานยาวนาน 50 ปี
- บำรุงรักษาต่ำ ไม่ต้องทาสี
- น้ำหนักเบา ติดตั้งง่าย
applications:
- ระบบประปาน้ำร้อน
- ระบบประปาน้ำเย็น
- ระบบทำความร้อน (Heating)
- ระบบน้ำแรงดันสูง
- โรงแรมและรีสอร์ท
- โรงพยาบาลและสถานพยาบาล
- อาคารพาณิชย์และสำนักงาน
- โครงการบ้านจัดสรร
- โรงงานอุตสาหกรรม
certifications:
- DIN 8077/8078
- ISO 15874
- มอก. 248-2549
- SCG Quality Certified
faq:
- question: ท่อ PPR ตราช้างทนอุณหภูมิสูงสุดเท่าไร?
answer: ท่อ PPR ตราช้างทนอุณหภูมิสูงสุด 95°C ทำให้เหมาะสำหรับใช้กับระบบน้ำร้อนและระบบทำความร้อน
- question: ท่อ PPR ตราช้างอายุการใช้งานกี่ปี?
answer: ท่อ PPR ตราช้างมีอายุการใช้งานยาวนานถึง 50 ปี ภายใต้การใช้งานตามมาตรฐาน
- question: ท่อ PPR แตกต่างจากท่อ PVC อย่างไร?
answer: ท่อ PPR ทนอุณหภูมิสูงกว่า (95°C vs 60°C) ทนแรงดันสูงกว่า ติดตั้งด้วยการเชื่อมความร้อนไม่ต้องใช้กาว และมีอายุการใช้งานยาวนานกว่า
- question: วิธีติดตั้งท่อ PPR ตราช้างทำอย่างไร?
answer: ติดตั้งโดยใช้เครื่องเชื่อมท่อ PPR อุณหภูมิ 260°C โดยเชื่อมท่อกับข้อต่อด้วยความร้อนจนกลายเป็นชิ้นเดียวกัน
- question: ท่อ PPR ตราช้างใช้กับน้ำดื่มได้หรือไม่?
answer: ได้ ท่อ PPR ตราช้างได้รับมาตรฐานสำหรับน้ำดื่ม ไม่ปล่อยสารพิษ และไม่เปลี่ยนแปลงรสชาติน้ำ
relatedProductIds:
- thai-ppr
- poloplast
- ppr-welder
schemaData:
brand: SCG Elephant
manufacturer: SCG Chemicals
material: Polypropylene Random Copolymer (PP-R)
category: Plumbing Pipe - PPR
---
# ท่อพีพีอาร์ตราช้าง (PPR Elephant Pipe)
## ภาพรวม
ท่อพีพีอาร์ตราช้าง (PPR Elephant) ผลิตโดย SCG บริษัทชั้นนำของไทย เป็นท่อพลาสติกประเภท **Polypropylene Random Copolymer (PP-R)** ที่มีคุณภาพสูง ได้รับมาตรฐาน DIN 8077/8078 จากเยอรมนี และมาตรฐาน ISO 15874 ระดับสากล
## คุณสมบัติเด่น
ท่อ PPR ตราช้างมีความทนทานต่ออุณหภูมิสูงสุด **95°C** และทนความดันได้ถึง **20 บาร์ (PN20)** เหมาะสำหรับงานระบบประปาน้ำร้อน น้ำเย็น และระบบทำความร้อน
### ข้อดีของท่อ PPR ตราช้าง
1. **ทนความร้อนสูง** - ใช้งานกับน้ำร้อนได้ถึง 95°C
2. **ทนแรงดัน** - รับแรงดันได้สูงสุด 20 บาร์
3. **ไม่เกิดสนิม** - ไม่มีการกัดกร่อนจากสารเคมี
4. **ผิวเรียบ** - ลดการสะสมของตะกรันในท่อ
5. **ติดตั้งง่าย** - เชื่อมด้วยความร้อน ไม่ต้องใช้กาว
6. **ปลอดภัย** - ใช้กับน้ำดื่มได้ ไม่ปนเปื้อนสารพิษ
7. **อายุยาวนาน** - ใช้งานได้นาน 50 ปี
## การใช้งาน
### เหมาะสำหรัก
- ระบบประปาน้ำร้อนในโรงแรมและรีสอร์ท
- ระบบน้ำเย็นในอาคารพาณิชย์
- ระบบทำความร้อน (Heating System)
- ระบบน้ำแรงดันสูงในโรงงาน
- โรงพยาบาลและสถานพยาบาล
- โครงการบ้านจัดสรร
## มาตรฐานและรับรอง
ท่อพีพีอาร์ตราช้างได้รับมาตรฐานสากล:
-**DIN 8077/8078** - มาตรฐานเยอรมัน
-**ISO 15874** - มาตรฐานสากล
-**มอก. 248-2549** - มาตรฐานผลิตภัณฑ์อุตสาหกรรมไทย
-**SCG Quality Certified** - รับรองคุณภาพโดย SCG
## วิธีการติดตั้ง
การติดตั้งท่อ PPR ตราช้างใช้ระบบ **เชื่อมความร้อน (Heat Fusion)**:
1. ตั้งเครื่องเชื่อมที่อุณหภูมิ **260°C**
2. เสียบท่อและข้อต่อเข้าในแม่พิมพ์
3. รอให้พลาสติกหลอมตัว (เวลาตามขนาดท่อ)
4. ดึงออกและเชื่อมท่อกับข้อต่อทันที
5. รอให้เย็นตัว (ประมาณ 2-3 นาที)
## คำถามที่พบบ่อย
### ท่อ PPR ตราช้างทนอุณหภูมิสูงสุดเท่าไร?
ท่อ PPR ตราช้างทนอุณหภูมิสูงสุด **95°C** ทำให้เหมาะสำหรับใช้กับระบบน้ำร้อนและระบบทำความร้อน
### ท่อ PPR ตราช้างอายุการใช้งานกี่ปี?
ท่อ PPR ตราช้างมีอายุการใช้งานยาวนานถึง **50 ปี** ภายใต้การใช้งานตามมาตรฐาน
### ท่อ PPR แตกต่างจากท่อ PVC อย่างไร?
ท่อ PPR ทนอุณหภูมิสูงกว่า (95°C vs 60°C) ทนแรงดันสูงกว่า ติดตั้งด้วยการเชื่อมความร้อนไม่ต้องใช้กาว และมีอายุการใช้งานยาวนานกว่า
### ท่อ PPR ตราช้างใช้กับน้ำดื่มได้หรือไม่?
**ได้** ท่อ PPR ตราช้างได้รับมาตรฐานสำหรับน้ำดื่ม ไม่ปล่อยสารพิษ และไม่เปลี่ยนแปลงรสชาติน้ำ
## สินค้าที่เกี่ยวข้อง
- [ท่อ PPR Thai PPR](/ท่อppr-thaippr/)
- [ท่อ PP-R/PP-RCT POLOPLAST](/pp-r-pp-rct-poloplast/)
- [เครื่องเชื่อมท่อพีพีอาร์](/เครื่องเชื่อมท่อพีพีอาร์/)

View File

@@ -0,0 +1,130 @@
---
id: syler
name: ท่อไซเลอร์
nameEn: Syler Pipe
slug: ท่อไซเลอร์
description: 'ท่อไซเลอร์ ท่อเหล็กบุ PE ทนแรงดัน 50 bar มาตรฐาน BS1387 FM APPROVED สำหรับระบบดับเพลิง'
shortDescription: 'ท่อเหล็กบุ PE BS1387 FM APPROVED'
image: /images/2021/03/hdpe002.jpg
keywords:
- ท่อไซเลอร์
- Syler Pipe
- ท่อเหล็กบุ PE
- FM APPROVED
- ท่อดับเพลิง
- ท่อสปริงเกลอร์
- BS1387
- ท่อเหล็กชุบ PE
- fire protection pipe
- ท่อน้ำดับเพลิง
seoContent: 'ท่อไซเลอร์ (Syler Pipe) เป็นท่อเหล็กบุ PE (Polyethylene) ที่ออกแบบมาเฉพาะสำหรับระบบดับเพลิงและสปริงเกลอร์ ท่อมีความทนทานสูง ทนแรงดันได้ถึง 50 บาร์ ผ่านมาตรฐาน BS1387 จากอังกฤษและ FM APPROVED จาก Factory Mutual'
specifications:
- label: วัสดุ
value: เหล็กบุ PE (Steel with PE lining)
- label: มาตรฐาน
value: BS1387, FM APPROVED
- label: แรงดันทนทาน
value: '50'
unit: bar
- label: ขนาดท่อ
value: '25, 32, 40, 50, 65, 80, 100, 150, 200'
unit: mm
- label: ความหนาผนัง
value: Schedule 40, 80
- label: ความยาว
value: '6'
unit: เมตร
- label: สี
value: แดง (Red) - Fire Protection
features:
- ทนแรงดันสูง 50 บาร์
- ผ่านมาตรฐาน BS1387 และ FM APPROVED
- บุ PE ป้องกันสนิมและการกัดกร่อน
- อายุการใช้งานยาวนาน
- เหมาะสำหรับระบบดับเพลิง
- ติดตั้งด้วย Groove Coupling
- ทนทานต่อความร้อน
applications:
- ระบบสปริงเกลอร์
- ระบบดับเพลิง
- โรงงานอุตสาหกรรม
- อาคารพาณิชย์สูง
- โรงแรมและโรงพยาบาล
certifications:
- BS1387
- FM APPROVED
- UL Listed
faq:
- question: ท่อไซเลอร์เหมาะกับงานอะไร?
answer: ท่อไซเลอร์ออกแบบมาเฉพาะสำหรับระบบดับเพลิงและสปริงเกลอร์ ผ่านมาตรฐาน FM APPROVED จึงมั่นใจได้ในความปลอดภัย
- question: ท่อไซเลอร์ต่างจากท่อเหล็กทั่วไปอย่างไร?
answer: ท่อไซเลอร์มีการบุ PE ภายในท่อ ป้องกันการเกิดสนิมและการกัดกร่อน ทำให้มีอายุการใช้งานยาวนานกว่าท่อเหล็กทั่วไป
relatedProductIds:
- realflex
- groove-coupling
schemaData:
brand: Syler
material: Steel with PE Lining
category: Fire Protection Pipe
---
# ท่อไซเลอร์ (Syler Pipe)
## ภาพรวม
ท่อไซเลอร์ (**Syler Pipe**) เป็นท่อเหล็กบุ PE (Polyethylene) ที่ออกแบบมาเฉพาะสำหรับ **ระบบดับเพลิงและสปริงเกลอร์** ท่อมีความทนทานสูง ทนแรงดันได้ถึง **50 บาร์**
## คุณสมบัติเด่น
ผ่านมาตรฐาน **BS1387** จากอังกฤษและ **FM APPROVED** จาก Factory Mutual ท่อไซเลอร์มีการบุ PE ภายในเพื่อป้องกันการกัดกร่อนและสนิม
### ข้อดีของท่อไซเลอร์
1. **ทนแรงดันสูง** - สูงถึง 50 บาร์
2. **มาตรฐานสากล** - BS1387, FM APPROVED, UL Listed
3. **บุ PE** - ป้องกันสนิมและการกัดกร่อน
4. **เหมาะสำหรับดับเพลิง** - ออกแบบมาเฉพาะงานนี้
5. **ติดตั้งง่าย** - ใช้ Groove Coupling
6. **ทนความร้อน** - เหมาะกับระบบสปริงเกลอร์
7. **อายุการใช้งานยาว** - ทนทานในระยะยาว
## การใช้งาน
### เหมาะสำหรับ
- **ระบบสปริงเกลอร์** - งานดับเพลิงอัตโนมัติ
- **ระบบดับเพลิง** - งานป้องกันอัคคีภัย
- **โรงงานอุตสาหกรรม** - ระบบความปลอดภัย
- **อาคารพาณิชย์สูง** - อาคารสูง คอนโด
- **โรงแรมและโรงพยาบาล** - สถานที่สาธารณะ
## มาตรฐานและรับรอง
ท่อไซเลอร์ผ่านมาตรฐาน:
-**BS1387** - มาตรฐานอังกฤษสำหรับท่อเหล็ก
-**FM APPROVED** - Factory Mutual รับรองสำหรับระบบดับเพลิง
-**UL Listed** - รับรองความปลอดภัย
## การติดตั้ง
ท่อไซเลอร์ติดตั้งโดยใช้ **Groove Coupling** ซึ่งเป็นระบบต่อท่อที่:
- ติดตั้งรวดเร็ว
- ไม่ต้องใช้เครื่องเชื่อม
- รองรับแรงดันสูง
- ถอดประกอบได้สะดวก
## คำถามที่พบบ่อย
### ท่อไซเลอร์เหมาะกับงานอะไร?
ท่อไซเลอร์ออกแบบมาเฉพาะสำหรับ **ระบบดับเพลิงและสปริงเกลอร์** ผ่านมาตรฐาน FM APPROVED จึงมั่นใจได้ในความปลอดภัย
### ท่อไซเลอร์ต่างจากท่อเหล็กทั่วไปอย่างไร?
ท่อไซเลอร์มีการ **บุ PE ภายในท่อ** ป้องกันการเกิดสนิมและการกัดกร่อน ทำให้มีอายุการใช้งานยาวนานกว่าท่อเหล็กทั่วไป
## สินค้าที่เกี่ยวข้อง
- [Realflex](/realflex/)
- [ท่อและข้อต่อ Groove](/อุปกรณ์ท่อกรูฟ/)

View File

@@ -0,0 +1,121 @@
---
id: thai-ppr
name: ท่อ PPR Thai PPR
nameEn: Thai PPR Pipe
slug: ท่อppr-thaippr
description: 'ท่อ PPR Thai PPR คุณภาพสูง มาตรฐาน มอก. เหมาะสำหรับงานประปาและระบบน้ำ'
shortDescription: 'ท่อ PPR Thai PPR มาตรฐาน มอก.'
image: /images/2021/03/hdpe_pipe_main.jpg
keywords:
- ท่อ PPR
- Thai PPR
- ท่อพีพีอาร์ไทย
- ท่อ PPR ไทย
- ท่อน้ำ PPR
- ท่อประปา PPR
- ราคาท่อ PPR ไทย
- ท่อพีพีอาร์มาตรฐาน มอก.
- ท่อ PPR ราคาถูก
seoContent: 'ท่อ PPR Thai PPR เป็นท่อพลาสติกพีพีอาร์ผลิตในประเทศไทย ผ่านมาตรฐาน มอก. สำหรับใช้ในงานระบบประปาและระบบน้ำ ท่อ Thai PPR มีคุณสมบัติทนทานต่อความร้อนและความดัน เหมาะสำหรับงานประปาน้ำเย็นและน้ำร้อน ด้วยราคาที่เป็นมิตรกับงบประมาณ ท่อ PPR Thai PPR เป็นทางเลือกที่คุ้มค่าสำหรับโครงการก่อสร้างทุกขนาด'
specifications:
- label: วัสดุ
value: PP-R (Polypropylene Random Copolymer)
- label: มาตรฐาน
value: มอก. 248-2549
- label: แรงดันทนทาน
value: 'PN10, PN16, PN20'
unit: bar
- label: อุณหภูมิทนทาน
value: '0-70'
unit: °C
- label: ขนาดท่อ
value: '20, 25, 32, 40, 50, 63, 75, 90, 110'
unit: mm
- label: สี
value: ขาว, เขียว, เทา
- label: อายุการใช้งาน
value: '30-50'
unit: ปี
features:
- ผลิตในประเทศไทย ราคาประหยัด
- ผ่านมาตรฐาน มอก. สามารถตรวจสอบได้
- ทนอุณหภูมิสูงสุด 70°C
- ไม่เกิดสนิมและการกัดกร่อน
- ติดตั้งด้วยการเชื่อมความร้อน
- ปลอดภัยสำหรับน้ำดื่ม
- น้ำหนักเบา ขนส่งง่าย
applications:
- ระบบประปาภายในอาคาร
- ระบบน้ำเย็น
- งานก่อสร้างที่อยู่อาศัย
- โครงการจัดสรร
- งานประปาขนาดเล็กและกลาง
certifications:
- มอก. 248-2549
faq:
- question: ท่อ Thai PPR ต่างจากท่อ PPR ตราช้างอย่างไร?
answer: ท่อ Thai PPR เป็นผลิตภัณฑ์ที่ผลิตในประเทศไทย ราคาประหยัดกว่า ในขณะที่ท่อ PPR ตราช้างเป็นผลิตภัณฑ์จาก SCG มีมาตรฐานสากลที่หลากหลายกว่า
- question: ท่อ Thai PPR รับประกันคุณภาพหรือไม่?
answer: ได้ ท่อ Thai PPR ผ่านมาตรฐาน มอก. 248-2549 สามารถตรวจสอบคุณภาพได้
relatedProductIds:
- ppr-elephant
- poloplast
- ppr-welder
schemaData:
brand: Thai PPR
manufacturer: Thai PPR
material: Polypropylene Random Copolymer (PP-R)
category: Plumbing Pipe - PPR
---
# ท่อ PPR Thai PPR
## ภาพรวม
ท่อ PPR Thai PPR เป็นท่อพลาสติกพีพีอาร์ **ผลิตในประเทศไทย** ผ่านมาตรฐาน มอก. สำหรับใช้ในงานระบบประปาและระบบน้ำ ท่อ Thai PPR มีคุณสมบัติทนทานต่อความร้อนและความดัน เหมาะสำหรับงานประปาน้ำเย็นและน้ำร้อน
## คุณสมบัติเด่น
ด้วยราคาที่เป็นมิตรกับงบประมาณ ท่อ PPR Thai PPR เป็นทางเลือกที่คุ้มค่าสำหรับโครงการก่อสร้างทุกขนาด
### ข้อดีของท่อ Thai PPR
1. **ผลิตในไทย** - ราคาประหยัด สนับสนุนสินค้าไทย
2. **มาตรฐาน มอก.** - รับรองคุณภาพ ตรวจสอบได้
3. **ทนความร้อน** - ใช้งานได้สูงถึง 70°C
4. **ไม่เกิดสนิม** - ไม่มีการกัดกร่อนจากสารเคมี
5. **ติดตั้งง่าย** - เชื่อมด้วยความร้อน ไม่ต้องใช้กาว
6. **ปลอดภัย** - ใช้กับน้ำดื่มได้
7. **น้ำหนักเบา** - ขนส่งและติดตั้งสะดวก
## การใช้งาน
### เหมาะสำหรับ
- ระบบประปาภายในอาคาร
- ระบบน้ำเย็น
- งานก่อสร้างที่อยู่อาศัย
- โครงการจัดสรร
- งานประปาขนาดเล็กและกลาง
## มาตรฐานและรับรอง
ท่อ PPR Thai PPR ผ่านมาตรฐาน:
-**มอก. 248-2549** - มาตรฐานผลิตภัณฑ์อุตสาหกรรม
## คำถามที่พบบ่อย
### ท่อ Thai PPR ต่างจากท่อ PPR ตราช้างอย่างไร?
ท่อ Thai PPR เป็นผลิตภัณฑ์ที่ผลิตในประเทศไทย ราคาประหยัดกว่า ในขณะที่ท่อ PPR ตราช้างเป็นผลิตภัณฑ์จาก SCG มีมาตรฐานสากลที่หลากหลายกว่า
### ท่อ Thai PPR รับประกันคุณภาพหรือไม่?
ได้ ท่อ Thai PPR ผ่านมาตรฐาน มอก. 248-2549 สามารถตรวจสอบคุณภาพได้
## สินค้าที่เกี่ยวข้อง
- [ท่อพีพีอาร์ตราช้าง](/ท่อพีพีอาร์ตราช้าง/)
- [ท่อ PP-R/PP-RCT POLOPLAST](/pp-r-pp-rct-poloplast/)
- [เครื่องเชื่อมท่อพีพีอาร์](/เครื่องเชื่อมท่อพีพีอาร์/)

View File

@@ -0,0 +1,146 @@
---
id: xylent
name: ท่อระบายน้ำ 3 ชั้น ไซเลนท์
nameEn: XYLENT Silent Pipe
slug: ท่อระบายน้ำ-3-ชั้น-ไซเลนท
description: 'ท่อระบายน้ำ XYLENT 3 ชั้น ลดเสียง 22dB ระบบ Push Fit ติดตั้งง่าย จาก Poloplast ยุโรป'
shortDescription: 'ท่อระบายน้ำไซเลนท์ 22dB Push Fit'
image: /images/2021/03/hdpe_product.jpg
keywords:
- ท่อ XYLENT
- 22 dB
- ท่อระบายน้ำ 3 ชั้น
- ท่อไซเลนท์
- silent pipe
- ท่อลดเสียง
- Push Fit pipe
- ท่อระบายน้ำไซเลนท์
- Poloplast
- ท่อ PP
- ท่อระบายน้ำอาคาร
seoContent: 'ท่อระบายน้ำ XYLENT เป็นท่อระบายน้ำระดับพรีเมียมจาก Poloplast ประเทศออสเตรีย มีโครงสร้าง 3 ชั้น (Triple Layer) ช่วยลดเสียงรบกวนจากการไหลของน้ำได้ถึง 22 เดซิเบล ระบบ Push Fit ช่วยให้ติดตั้งง่าย ไม่ต้องใช้กาวหรือเครื่องมือพิเศษ'
specifications:
- label: วัสดุ
value: PP (Polypropylene) 3 ชั้น
- label: มาตรฐาน
value: EN 1451, DIN 19560
- label: การลดเสียง
value: '22'
unit: dB
- label: อุณหภูมิทนทาน
value: '-20 ถึง 95'
unit: °C
- label: ขนาดท่อ
value: '32, 40, 50, 75, 90, 110, 125, 160'
unit: mm
- label: ระบบติดตั้ง
value: Push Fit (Push-Fit)
- label: สี
value: เทาอ่อน
- label: อายุการใช้งาน
value: '50'
unit: ปี
features:
- ลดเสียงรบกวน 22 dB
- โครงสร้าง 3 ชั้น (Triple Layer)
- ระบบ Push Fit ติดตั้งง่าย
- ไม่ต้องใช้กาวหรือเครื่องมือพิเศษ
- ผลิตในออสเตรีย คุณภาพยุโรป
- ทนอุณหภูมิสูง 95°C
- ไม่แตกหักง่าย
- อายุการใช้งาน 50 ปี
applications:
- ระบบระบายน้ำอาคาร
- โรงแรมและรีสอร์ท
- โรงพยาบาล
- อาคารพักอาศัยระดับสูง
- อาคารสำนักงาน
certifications:
- EN 1451
- DIN 19560
- DIBt Approved
faq:
- question: ท่อ XYLENT ลดเสียงได้กี่เดซิเบล?
answer: ท่อ XYLENT สามารถลดเสียงรบกวนจากการไหลของน้ำได้ถึง 22 เดซิเบล ทำให้เหมาะสำหรับอาคารที่ต้องการความเงียบ
- question: ระบบ Push Fit คืออะไร?
answer: ระบบ Push Fit เป็นระบบติดตั้งที่ไม่ต้องใช้กาวหรือเครื่องมือพิเศษ เพียงสองท่อเข้าหากันก็ติดตั้งเสร็จ สะดวกและรวดเร็ว
relatedProductIds:
- poloplast
- upvc
schemaData:
brand: XYLENT by Poloplast
manufacturer: Poloplast (Austria)
material: Polypropylene (PP) - Triple Layer
category: Drainage Pipe - Silent
---
# ท่อระบายน้ำ 3 ชั้น XYLENT (Silent Pipe)
## ภาพรวม
ท่อระบายน้ำ **XYLENT** เป็นท่อระบายน้ำระดับพรีเมียมจาก **Poloplast ประเทศออสเตรีย** มีโครงสร้าง **3 ชั้น (Triple Layer)** ช่วยลดเสียงรบกวนจากการไหลของน้ำได้ถึง **22 เดซิเบล**
## คุณสมบัติเด่น
ระบบ **Push Fit** ช่วยให้ติดตั้งง่าย ไม่ต้องใช้กาวหรือเครื่องมือพิเศษ ท่อ XYLENT เหมาะสำหรับอาคารที่ต้องการความเงียบ
### ข้อดีของท่อ XYLENT
1. **ลดเสียง 22 dB** - เงียบกว่าท่อทั่วไป
2. **3 ชั้น** - Triple Layer Structure
3. **Push Fit** - ติดตั้งง่าย ไม่ต้องใช้กาว
4. **คุณภาพยุโรป** - ผลิตในออสเตรีย
5. **ทนอุณหภูมิ** - สูงถึง 95°C
6. **ไม่แตกหัก** - PP เกรดสูง
7. **อายุ 50 ปี** - ทนทานยาวนาน
## การใช้งาน
### เหมาะสำหรับ
- **ระบบระบายน้ำอาคาร** - ท่อระบายน้ำทิ้ง
- **โรงแรมและรีสอร์ท** - ต้องการความเงียบ
- **โรงพยาบาล** - สถานที่ต้องการความสงบ
- **อาคารพักอาศัยระดับสูง** - คอนโดระดับพรีเมียม
- **อาคารสำนักงาน** - สำนักงานเกรด A
## มาตรฐานและรับรอง
ท่อ XYLENT ผ่านมาตรฐาน:
-**EN 1451** - มาตรฐานยุโรปสำหรับท่อระบายน้ำ
-**DIN 19560** - มาตรฐานเยอรมัน
-**DIBt Approved** - รับรองโดยสถาบันก่อสร้างเยอรมัน
## โครงสร้าง 3 ชั้น
ท่อ XYLENT มีโครงสร้าง **Triple Layer**:
1. **ชั้นใน** - PP เรียบ ลดแรงเสียดทาน
2. **ชั้นกลาง** - PP แร่ เพิ่มความแข็งแรง
3. **ชั้นนอก** - PP เรียบ ป้องกันรอยขีดข่วน
โครงสร้างนี้ช่วย **ลดเสียงรบกวน** ได้ถึง **22 dB**
## ระบบ Push Fit
**Push Fit** คือระบบติดตั้งที่:
- ไม่ต้องใช้กาว
- ไม่ต้องใช้เครื่องมือพิเศษ
- แค่ดันท่อเข้ากันก็ติดตั้งเสร็จ
- ประหยัดเวลาและค่าแรง
## คำถามที่พบบ่อย
### ท่อ XYLENT ลดเสียงได้กี่เดซิเบล?
ท่อ XYLENT สามารถลดเสียงรบกวนจากการไหลของน้ำได้ถึง **22 เดซิเบล** ทำให้เหมาะสำหรับอาคารที่ต้องการความเงียบ
### ระบบ Push Fit คืออะไร?
ระบบ Push Fit เป็นระบบติดตั้งที่ **ไม่ต้องใช้กาวหรือเครื่องมือพิเศษ** เพียงสองท่อเข้าหากันก็ติดตั้งเสร็จ สะดวกและรวดเร็ว
## สินค้าที่เกี่ยวข้อง
- [ท่อ PP-R/PP-RCT POLOPLAST](/pp-r-pp-rct-poloplast/)
- [ท่อ uPVC](/ท่อupvc/)

View File

@@ -1,94 +0,0 @@
# DATA LAYER - Product Catalog & Site Configuration
**Generated:** 2026-03-01
## OVERVIEW
Centralized data layer for product catalog (~150KB total). Contains all product information, specifications, pricing tables, and site configuration in TypeScript.
## FILES
| File | Size | Purpose |
|------|------|---------|
| `site-config.ts` | ~149KB | Products, navigation, portfolio, company info |
| `product-tables.ts` | ~33KB | Specification tables for products |
## STRUCTURE
### site-config.ts
```typescript
// Core exports
export const siteConfig: SiteConfig // Company info, contact
export const workHours: WorkHours[] // Business hours
export const productCategories: ProductCategory[] // All products (~20+)
export const mainNavigation: NavItem[] // Header nav
export const portfolioProjects: PortfolioProject[] // Portfolio items
```
### ProductCategory Interface
```typescript
interface ProductCategory {
id: string; // kebab-case ID (e.g., 'ppr-elephant')
name: string; // Thai name
nameEn: string; // English name
slug: string; // URL slug (Thai)
href: string; // Full path with trailing slash
image: string; // Image path from /public
description: string; // Full description (Thai)
shortDescription?: string;
keywords?: string[]; // SEO keywords
seoContent?: string; // Long-form SEO content
specifications?: ProductSpecification[];
features?: string[];
applications?: string[];
certifications?: string[];
faq?: FAQItem[];
schemaData?: {...}; // Schema.org structured data
relatedProductIds?: string[];
productTables?: ProductTable[]; // From product-tables.ts
}
```
## PRODUCT CATEGORIES
| ID | Thai Name | Type |
|----|-----------|------|
| `ppr-elephant` | ท่อพีพีอาร์ตราช้าง | PPR Pipe |
| `thai-ppr` | ท่อ PPR Thai PPR | PPR Pipe |
| `poloplast` | ท่อ PP-R/PP-RCT POLOPLAST | Premium PPR |
| `ppr-welder` | เครื่องเชื่อมท่อพีพีอาร์ | Equipment |
| `hdpe` | ท่อ HDPE | HDPE Pipe |
| `hdpe-welder` | เครื่องเชื่อม HDPE | Equipment |
| `upvc` | ท่อ uPVC | uPVC Pipe |
| `pvc` | ท่อและข้อต่อ PVC | PVC Pipe |
| `syler` | ท่อไซเลอร์ | Fire protection |
| `xylent` | ท่อระบายน้ำ 3 ชั้น ไซเลนท์ | Drainage |
| ... | ... | ... |
## CONVENTIONS
**Adding a new product**:
1. Add to `productCategories[]` array
2. Create unique `id` (kebab-case)
3. Add Thai `slug` for URL
4. Include `seoContent` for SEO pages
5. Link `productTables` if spec tables exist
6. Add `relatedProductIds` for cross-selling
**Product images**: Store in `/public/images/YYYY/MM/`
**URLs**: Thai with trailing slash: `/ท่อพีพีอาร์ตราช้าง/`
## ANTI-PATTERNS
- **DO NOT** import entire file in client components - it's 149KB
- **DO NOT** hardcode product IDs - use constants
- **DO NOT** edit `product-tables.ts` manually without checking all references
## NOTES
- File is large but tree-shakeable for unused products
- `portfolioProjects` also defined here
- SEO keywords array should include both Thai and English terms

View File

@@ -1,687 +0,0 @@
import { ProductTable } from '@/types';
// DUKELARRSEN Product Tables
export const dukelarrsenTables: ProductTable[] = [
{
tableName: 'Rigid Coupling DUKELARRSEN',
headers: ['Nominal Size (mm)', 'Nominal Size (in)', 'Pipe OD (mm)', 'Bolt Size', 'Dimensions A (mm)', 'Dimensions B (mm)', 'Working Pressure (PSI)', 'Certificate'],
rows: [
['25', '1', '33.7', '2-M10 x 45', '57', '97', '300', 'FM UL'],
['32', '1-1/4', '42.4', '2-M10 x 45', '67', '107.5', '300', 'FM UL'],
['40', '1-1/2', '48.3', '2-M10 x 45', '72', '114', '300', 'FM UL'],
['50', '2', '60.3', '2-M10 x 55', '85', '137', '300', 'FM UL'],
['65', '2-1/2', '73.0', '2-M10 x 55', '98', '139', '300', 'FM UL'],
['65', '2-1/2', '76.1', '2-M10 x 55', '100', '139', '300', 'FM UL'],
['80', '3', '88.9', '2-M10 x 55', '114', '160', '300', 'FM UL'],
['100', '4', '114.3', '2-M12 x 65', '147.2', '193', '300', 'FM UL'],
['125', '5', '139.7', '2-M12 x 75', '170', '222', '300', 'FM UL'],
['125', '5', '141.3', '2-M12 x 75', '170', '222', '300', 'FM UL'],
['150', '6', '168.3', '2-M12 x 75', '203', '248', '300', 'FM UL'],
['150', '6', '165.1', '2-M12 x 75', '205', '254', '300', 'FM UL'],
['200', '8', '219.1', '2-M16 x 85', '257', '330', '300', 'FM UL'],
['250', '10', '273.0', '2-M20 x 120', '328', '420', '300', 'FM UL'],
['300', '12', '323.9', '2-M20 x 140', '380', '454', '300', 'FM UL'],
],
},
{
tableName: 'Flexible Coupling DUKELARRSEN',
headers: ['Nominal Size (mm)', 'Nominal Size (in)', 'Pipe OD (mm)', 'Bolt Size', 'Dimensions A (mm)', 'Dimensions B (mm)', 'Working Pressure (PSI)', 'Certificate'],
rows: [
['25', '1', '33.7', '2-M10 x 45', '57', '97', '300', 'FM UL'],
['32', '1-1/4', '42.4', '2-M10 x 45', '67', '107.5', '300', 'FM UL'],
['40', '1-1/2', '48.3', '2-M10 x 45', '72', '114', '300', 'FM UL'],
['50', '2', '60.3', '2-M10 x 55', '85', '137', '300', 'FM UL'],
['65', '2-1/2', '73.0', '2-M10 x 55', '98', '139', '300', 'FM UL'],
['65', '2-1/2', '76.1', '2-M10 x 55', '100', '139', '300', 'FM UL'],
['80', '3', '88.9', '2-M10 x 55', '114', '160', '300', 'FM UL'],
['100', '4', '114.3', '2-M12 x 66', '147.2', '193', '300', 'FM UL'],
['125', '5', '139.7', '2-M12 x 75', '170', '222', '300', 'FM UL'],
['125', '5', '141.3', '2-M12 x 75', '170', '222', '300', 'FM UL'],
['150', '6', '168.3', '2-M12 x 75', '203', '248', '300', 'FM UL'],
['150', '6', '165.1', '2-M12 x 75', '205', '254', '300', 'FM UL'],
['200', '8', '219.1', '2-M16 x 85', '257', '330', '300', 'FM UL'],
['250', '10', '273.0', '2-M20 x 120', '328', '420', '300', 'FM UL'],
['300', '12', '323.9', '2-M20 x 140', '380', '454', '300', 'FM UL'],
],
},
{
tableName: 'Reducing Flexible Coupling DUKELARRSEN',
headers: ['Nominal Size (mm)', 'Nominal Size (in)', 'Pipe OD (mm)', 'Bolt Size', 'Working Pressure (PSI)', 'Certificate'],
rows: [
['50 x 40', '2 x 1-1/2', '60.3 x 48.3', '2-M10 x 55', '300', 'FM UL'],
['65 x 50', '2-1/2 x 2', '73 x 60.3', '2-M10 x 55', '300', 'FM UL'],
['65 x 50', '2-1/2 x 2', '76.1 x 60.3', '2-M10 x 55', '300', 'FM UL'],
['80 x 25', '3 x 1', '88.9 x 33.7', '2-M10 x 55', '300', 'FM UL'],
['80 x 50', '3 x 2', '88.9 x 60.3', '2-M10 x 55', '300', 'FM UL'],
['80 x 65', '3 x 2-1/2', '88.9 x 76.1', '2-M10 x 55', '300', 'FM UL'],
['100 x 25', '4 x 1', '114.3 x 33.7', '2-M12 x 65', '300', 'FM UL'],
['100 x 50', '4 x 2', '114.3 x 60.3', '2-M12 x 65', '300', 'FM UL'],
['100 x 65', '4 x 2-1/2', '114.3 x 73.0', '2-M12 x 65', '300', 'FM UL'],
['100 x 65', '4 x 2-1/2', '114.3 x 76.1', '2-M12 x 65', '300', 'FM UL'],
['100 x 80', '4 x 3', '114.3 x 88.9', '2-M12 x 65', '300', 'FM UL'],
['150 x 80', '6 x 3', '168.3 x 88.9', '2-M12 x 75', '300', 'FM UL'],
['150 x 100', '6 x 4', '168.3 x 114.3', '2-M12 x 75', '300', 'FM UL'],
],
},
{
tableName: 'Flange PN16 Grooved DUKELARRSEN',
headers: ['Nominal Size (mm)', 'Nominal Size (in)', 'Pipe OD (mm)', 'Bolt Size', 'Dimensions D (mm)', 'Dimensions D1 (mm)', 'Dimensions D2 (mm)', 't (mm)', 'Hole', 'Angle', 'Working Pressure (PSI)', 'Certificate'],
rows: [
['50', '2', '60.3', '2-M10 x 70', '220', '165', '125', '23', '4-Ø18', '30°', '300', 'FM UL'],
['65', '2-1/2', '73.0', '2-M10 x 70', '235', '185', '145', '23', '4-Ø18', '30°', '300', 'FM UL'],
['65', '2-1/2', '76.1', '2-M10 x 70', '235', '185', '145', '23', '4-Ø18', '30°', '300', 'FM UL'],
['80', '3', '88.9', '2-M10 x 70', '255', '195', '160', '23', '4-Ø18', '30°', '300', 'FM UL'],
['100', '4', '114.3', '2-M12 x 70', '279', '224', '180', '23', '4-Ø18', '30°', '300', 'FM UL'],
['125', '5', '139.7', '2-M12 x 70', '320', '250', '216', '24', '8-Ø18', '30°', '300', 'FM UL'],
['125', '5', '141.3', '2-M12 x 70', '320', '250', '216', '24', '8-Ø18', '30°', '300', 'FM UL'],
['150', '6', '165.1', '2-M12 x 70', '346', '280', '240', '24', '8-Ø22', '30°', '300', 'FM UL'],
['150', '6', '168.3', '2-M12 x 70', '346', '280', '240', '24', '8-Ø22', '30°', '300', 'FM UL'],
['200', '8', '219.1', '2-M12 x 80', '414', '340', '295', '28', '8-Ø22', '30°', '300', 'FM UL'],
['250', '10', '273.0', '2-M12 x 80', '480', '405', '355', '30', '12-Ø26', '50°', '300', 'FM UL'],
['300', '12', '323.9', '2-M12 x 80', '530', '460', '410', '32', '12-Ø26', '30°', '300', 'FM UL'],
],
},
{
tableName: 'Elbow 90° Grooved DUKELARRSEN',
headers: ['Nominal Size (mm)', 'Nominal Size (in)', 'Pipe OD (mm)', 'Dimensions (mm)', 'Working Pressure (PSI)', 'Certificate'],
rows: [
['25', '1', '33.7', '57', '300', 'FM UL'],
['32', '1-1/4', '42.4', '67', '300', 'FM UL'],
['40', '1-1/2', '48.3', '76', '300', 'FM UL'],
['50', '2', '60.3', '89', '300', 'FM UL'],
['65', '2-1/2', '73.0', '102', '300', 'FM UL'],
['65', '2-1/2', '76.1', '102', '300', 'FM UL'],
['80', '3', '88.9', '114', '300', 'FM UL'],
['100', '4', '114.3', '140', '300', 'FM UL'],
['125', '5', '141.3', '159', '300', 'FM UL'],
['125', '5', '139.7', '159', '300', 'FM UL'],
['150', '6', '168.3', '178', '300', 'FM UL'],
['150', '6', '165.1', '178', '300', 'FM UL'],
['200', '8', '219.1', '229', '300', 'FM UL'],
['250', '10', '273.0', '279', '300', 'FM UL'],
['300', '12', '323.9', '330', '300', 'FM UL'],
],
},
{
tableName: 'Tee Grooved DUKELARRSEN',
headers: ['Nominal Size (mm)', 'Nominal Size (in)', 'Pipe OD (mm)', 'Dimensions (mm)', 'Working Pressure (PSI)', 'Certificate'],
rows: [
['25', '1', '33.7', '57', '300', 'FM UL'],
['32', '1-1/4', '42.4', '67', '300', 'FM UL'],
['40', '1-1/2', '48.3', '70', '300', 'FM UL'],
['50', '2', '60.3', '70', '300', 'FM UL'],
['65', '2-1/2', '73.0', '86', '300', 'FM UL'],
['65', '2-1/2', '76.1', '86', '300', 'FM UL'],
['80', '3', '88.9', '95', '300', 'FM UL'],
['100', '4', '114.3', '102', '300', 'FM UL'],
['125', '5', '139.7', '122', '300', 'FM UL'],
['125', '5', '141.3', '122', '300', 'FM UL'],
['150', '6', '168.3', '140', '300', 'FM UL'],
['150', '6', '165.1', '140', '300', 'FM UL'],
['200', '8', '219.1', '178', '300', 'FM UL'],
['250', '10', '273.0', '216', '300', 'FM UL'],
['300', '12', '323.9', '254', '300', 'FM UL'],
],
},
{
tableName: 'Reducer (Concentric) Grooved DUKELARRSEN',
headers: ['Nominal Size (mm)', 'Nominal Size (in)', 'Pipe OD (mm)', 'Working Pressure (PSI)', 'Certificate'],
rows: [
['32 x 25', '1-1/4 x 1', '42.4 x 33.7', '300', 'FM UL'],
['40 x 25', '1-1/2 x 1', '48.3 x 33.7', '300', 'FM UL'],
['40 x 32', '1-1/2 x 1-1/4', '48.3 x 42.4', '300', 'FM UL'],
['50 x 25', '2 x 1', '60.3 x 33.7', '300', 'FM UL'],
['50 x 32', '2 x 1-1/4', '60.3 x 42.4', '300', 'FM UL'],
['50 x 40', '2 x 1-1/2', '60.3 x 48.3', '300', 'FM UL'],
['65 x 25', '2-1/2 x 1', '73.0 x 33.7', '300', 'FM UL'],
['65 x 25', '2-1/2 x 1', '76.1 x 33.7', '300', 'FM UL'],
['65 x 32', '2-1/2 x 1-1/4', '73.0 x 42.4', '300', 'FM UL'],
['65 x 32', '2-1/2 x 1-1/4', '76.1 x 42.4', '300', 'FM UL'],
['65 x 40', '2-1/2 x 1-1/2', '73.0 x 48.3', '300', 'FM UL'],
['65 x 40', '2-1/2 x 1-1/2', '76.1 x 48.3', '300', 'FM UL'],
['65 x 50', '2-1/2 x 2', '73.0 x 60.3', '300', 'FM UL'],
['65 x 50', '2-1/2 x 2', '76.1 x 60.3', '300', 'FM UL'],
['80 x 25', '3 x 1', '88.9 x 33.7', '300', 'FM UL'],
['80 x 32', '3 x 1-1/4', '88.9 x 42.4', '300', 'FM UL'],
['80 x 40', '3 x 1-1/2', '88.9 x 48.3', '300', 'FM UL'],
['80 x 50', '3 x 2', '88.9 x 60.3', '300', 'FM UL'],
['80 x 65', '3 x 2-1/2', '88.9 x 73.0', '300', 'FM UL'],
['80 x 65', '3 x 2-1/2', '88.9 x 76.1', '300', 'FM UL'],
['100 x 32', '4 x 1-1/4', '114.3 x 42.4', '300', 'FM UL'],
['100 x 40', '4 x 1-1/2', '114.3 x 48.3', '300', 'FM UL'],
['100 x 50', '4 x 2', '114.3 x 60.3', '300', 'FM UL'],
['100 x 65', '4 x 2-1/2', '114.3 x 73.0', '300', 'FM UL'],
['100 x 65', '4 x 2-1/2', '114.3 x 76.1', '300', 'FM UL'],
['100 x 80', '4 x 3', '114.3 x 88.9', '300', 'FM UL'],
['125 x 50', '5 x 2', '139.7 x 60.3', '300', 'FM UL'],
['125 x 65', '5 x 2-1/2', '139.7 x 73.0', '300', 'FM UL'],
['125 x 80', '5 x 3', '139.7 x 88.9', '300', 'FM UL'],
['125 x 100', '5 x 4', '139.7 x 114.3', '300', 'FM UL'],
['150 x 65', '6 x 2-1/2', '165.1 x 73.0', '300', 'FM UL'],
['150 x 80', '6 x 3', '165.1 x 88.9', '300', 'FM UL'],
['150 x 80', '6 x 3', '168.3 x 88.9', '300', 'FM UL'],
['150 x 100', '6 x 4', '165.1 x 114.3', '300', 'FM UL'],
['150 x 100', '6 x 4', '168.3 x 114.3', '300', 'FM UL'],
['200 x 100', '8 x 4', '219.1 x 114.3', '300', 'FM UL'],
['200 x 125', '8 x 5', '219.1 x 139.7', '300', 'FM UL'],
['200 x 150', '8 x 6', '219.1 x 168.3', '300', 'FM UL'],
['200 x 150', '8 x 6', '219.1 x 165.1', '300', 'FM UL'],
['250 x 150', '10 x 6', '273.0 x 165.1', '300', 'FM UL'],
['250 x 200', '10 x 8', '273.0 x 219.1', '300', 'FM UL'],
],
},
{
tableName: 'Cap Grooved DUKELARRSEN',
headers: ['Nominal Size (mm)', 'Nominal Size (in)', 'Pipe OD (mm)', 'Dimensions (mm)', 'Working Pressure (PSI)', 'Certificate'],
rows: [
['25', '1', '33.7', '23.8', '300', 'FM UL'],
['32', '1-1/4', '42.4', '23.8', '300', 'FM UL'],
['40', '1-1/2', '48.3', '23.8', '300', 'FM UL'],
['50', '2', '60.3', '23.8', '300', 'FM UL'],
['65', '2-1/2', '73.0', '23.8', '300', 'FM UL'],
['65', '2-1/2', '76.1', '23.8', '300', 'FM UL'],
['80', '3', '88.9', '23.8', '300', 'FM UL'],
['100', '4', '114.3', '25.4', '300', 'FM UL'],
['125', '5', '139.7', '25.4', '300', 'FM UL'],
['150', '6', '168.3', '25.4', '300', 'FM UL'],
['150', '6', '165.1', '25.4', '300', 'FM UL'],
['200', '8', '219.1', '32', '300', 'FM UL'],
['250', '10', '273.0', '32', '300', 'FM UL'],
['300', '12', '323.9', '32', '300', 'FM UL'],
],
},
{
tableName: 'Mechanical Tee (Grooved) DUKELARRSEN',
headers: ['Nominal Size (mm)', 'Nominal Size (in)', 'Pipe OD (mm)', 'Bolt Size', 'Dimensions A (mm)', 'Dimensions B (mm)', 'Dimensions C (mm)', 'Dimensions L (mm)', 'Working Pressure (PSI)', 'Certificate'],
rows: [
['50 x 32', '2 x 1-1/4', '60.3 x 42.4', '2-M10 x 70', '46', '75', '120', '70', '300', 'FM UL'],
['50 x 40', '2 x 1-1/2', '60.3 x 48.3', '2-M10 x 70', '46', '75/120', '70', '69', '300', 'FM UL'],
['65 x 32', '2-1/2 x 1-1/4', '73.0 x 42.4', '2-M10 x 70', '52', '93/137', '78', '77', '300', 'FM UL'],
['65 x 40', '2-1/2 x 1-1/2', '73.0 x 48.3', '2-M10 x 70', '52', '93/137', '78', '63', '300', 'FM UL'],
['65 x 50', '2-1/2 x 2', '73.0 x 60.3', '2-M10 x 70', '52', '93/137', '78', '83', '300', 'FM UL'],
['65 x 40', '2-1/2 x 1-1/2', '76.1 x 48.3', '2-M10 x 70', '52', '93/137', '78', '83', '300', 'FM UL'],
['80 x 40', '3 x 1-1/2', '88.9 x 48.3', '2-M10 x 70', '46', '114/152', '85', '78', '300', 'FM UL'],
['80 x 50', '3 x 2', '88.9 x 60.3', '2-M10 x 70', '46', '114/152', '85', '93', '300', 'FM UL'],
['80 x 65', '3 x 2-1/2', '88.9 x 76.1', '2-M10 x 70', '64', '140', '180', '99', '300', 'FM UL'],
['100 x 40', '4 x 1-1/2', '114.3 x 48.3', '2-M12 x 75', '46', '140/180', '97', '83', '300', 'FM UL'],
['100 x 50', '4 x 2', '114.3 x 60.3', '2-M12 x 75', '64', '140', '180', '99', '300', 'FM UL'],
['100 x 65', '4 x 2-1/2', '114.3 x 73.0', '2-M12 x 75', '64', '168', '220', '99', '300', 'FM UL'],
['100 x 80', '4 x 3', '114.3 x 88.9', '2-M12 x 75', '64', '168', '220', '113', '300', 'FM UL'],
['125 x 65', '5 x 2-1/2', '139.7 x 73.0', '2-M12 x 75', '70', '168', '220', '122', '300', 'FM UL'],
['125 x 80', '5 x 3', '139.7 x 88.9', '2-M12 x 75', '70', '194/248', '130', '83', '300', 'FM UL'],
['125 x 100', '5 x 4', '139.7 x 114.3', '2-M12 x 75', '70', '194/248', '130', '98', '300', 'FM UL'],
['150 x 65', '6 x 2-1/2', '168.3 x 73.0', '2-M12 x 75', '70', '198/248', '131', '122', '300', 'FM UL'],
['150 x 80', '6 x 3', '168.3 x 88.9', '2-M12 x 75', '70', '198/248', '131', '125', '300', 'FM UL'],
['150 x 100', '6 x 4', '168.3 x 114.3', '2-M12 x 75', '70', '198/248', '131', '139', '300', 'FM UL'],
['150 x 80', '6 x 3', '165.1 x 88.9', '2-M12 x 75', '70', '250/311', '152', '130', '300', 'FM UL'],
['150 x 100', '6 x 4', '165.1 x 114.3', '2-M12 x 75', '89', '250/311', '152', '137', '300', 'FM UL'],
['200 x 100', '8 x 4', '219.1 x 114.3', '2-M16 x 100', '114', '250/321', '153', '162', '300', 'FM UL'],
],
},
// Additional tables would be added here for full DUKELARRSEN data
// Including: Elbow 22.5°, Elbow 45°, Eccentric Reducer, Tee Reducing, Cross, Cross Reducing, Cap with Eccentric Hole, Mechanical Tee Threaded, Mechanical Tee U-Bolt
];
// =====================================================
// PIPE HANGER TABLES
// =====================================================
// Clevis Hanger Tables
export const clevisHangerTables: ProductTable[] = [
{
tableName: 'Clevis Hanger (เหล็ก)',
headers: ['No.', 'ขนาด (Size) นิ้ว', 'ขนาด (Size) มม.', 'Upper มม.', 'Lower มม.', 'ขนาดสตัด Rod Size', 'ขนาดบรรจุ/ถุง Units/Bag'],
rows: [
['1', '1/2″', '15-21', '2.0×25', '3/8″', '100'],
['2', '3/4″', '20-27', '2.0×25', '3/8″', '100'],
['3', '1″', '25-34', '2.0×25', '3/8″', '100'],
['4', '1-1/4″', '32-42', '2.5×25', '3/8″', '100'],
['5', '1-1/2″', '40-48', '2.5×25', '3/8″', '100'],
['6', '2″', '50-60', '2.5×25', '3/8″', '100'],
['7', '2-1/2″', '65-76', '2.5×30', '3/8″', '50'],
['8', '3″', '80-89', '3.0×30', '3/8″', '50'],
['9', '4″', '100-114', '3.0×35', '1/2″', '25'],
],
},
{
tableName: 'Clevis Hanger Stainless Steel (สแตนเลส)',
headers: ['No.', 'ขนาด (Size) นิ้ว', 'ขนาด (Size) มม.', 'Upper มม.', 'Lower มม.', 'ขนาดสตัด Rod Size', 'ขนาดบรรจุ/ถุง Units/Bag'],
rows: [
['1', '1/2″', '15-21', '2.0×25', '3/8″', '100'],
['2', '3/4″', '20-27', '2.0×25', '3/8″', '100'],
['3', '1″', '25-34', '2.0×25', '3/8″', '100'],
['4', '1-1/4″', '32-42', '2.5×25', '3/8″', '100'],
['5', '1-1/2″', '40-48', '2.5×25', '3/8″', '100'],
['6', '2″', '50-60', '2.5×25', '3/8″', '100'],
['7', '2-1/2″', '65-76', '2.5×30', '3/8″', '50'],
['8', '3″', '80-89', '3.0×30', '3/8″', '50'],
['9', '4″', '100-114', '3.0×35', '1/2″', '25'],
['10', '5″', '125-140', '3.0×40', '1/2″', '20'],
],
},
];
// Split Ring Hanger Tables
export const splitRingHangerTables: ProductTable[] = [
{
tableName: 'Split Ring Hanger (เหล็ก) - Size 15-100mm',
headers: ['No.', 'ขนาด (Size) นิ้ว', 'ขนาด (Size) มม.', 'A มม.', 'B มม.', 'C มม.', 'D มม.', 'ขนาดสตัด Rod Size'],
rows: [
['1', '1/2″', '15', '42', '32', '8', '8', 'M8'],
['2', '3/4″', '20', '47', '35', '8', '9', 'M8'],
['3', '1″', '25', '56', '42', '8', '11', 'M8'],
['4', '1-1/4″', '32', '64', '48', '10', '12', 'M10'],
['5', '1-1/2″', '40', '73', '54', '10', '13', 'M10'],
['6', '2″', '50', '84', '62', '10', '15', 'M10'],
['7', '2-1/2″', '65', '101', '73', '12', '17', 'M12'],
['8', '3″', '80', '117', '85', '12', '19', 'M12'],
['9', '4″', '100', '141', '100', '14', '22', 'M14'],
],
},
{
tableName: 'Split Ring Hanger (เหล็ก) - Size 125-200mm',
headers: ['No.', 'ขนาด (Size) นิ้ว', 'ขนาด (Size) มม.', 'A มม.', 'B มม.', 'C มม.', 'D มม.', 'ขนาดสตัด Rod Size'],
rows: [
['1', '5″', '125', '170', '120', '16', '25', 'M16'],
['2', '6″', '150', '198', '138', '16', '28', 'M16'],
['3', '8″', '200', '253', '175', '20', '32', 'M20'],
],
},
{
tableName: 'Split Ring Hanger Stainless Steel (สแตนเลส)',
headers: ['No.', 'ขนาด (Size) นิ้ว', 'ขนาด (Size) มม.', 'A มม.', 'B มม.', 'C มม.', 'D มม.', 'ขนาดสตัด Rod Size'],
rows: [
['1', '1/2″', '15', '42', '32', '8', '8', 'M8'],
['2', '3/4″', '20', '47', '35', '8', '9', 'M8'],
['3', '1″', '25', '56', '42', '8', '11', 'M8'],
['4', '1-1/4″', '32', '64', '48', '10', '12', 'M10'],
['5', '1-1/2″', '40', '73', '54', '10', '13', 'M10'],
['6', '2″', '50', '84', '62', '10', '15', 'M10'],
['7', '2-1/2″', '65', '101', '73', '12', '17', 'M12'],
['8', '3″', '80', '117', '85', '12', '19', 'M12'],
['9', '4″', '100', '141', '100', '14', '22', 'M14'],
['10', '5″', '125', '170', '120', '16', '25', 'M16'],
['11', '6″', '150', '198', '138', '16', '28', 'M16'],
],
},
];
// Beam Clamp Tables
export const beamClampTables: ProductTable[] = [
{
tableName: 'Beam Clamp Type 1 (ฟันเดี่ยว)',
headers: ['No.', 'ขนาด (Size)', 'A มม.', 'B มม.', 'C มม.', 'D มม.', 'ขนาดสตัด Rod Size', 'ขนาดบรรจุ/ถุง Units/Bag'],
rows: [
['1', 'สตัด #1', '52', '41', '31', '12', 'M8', '50'],
['2', 'สตัด #2', '65', '54', '31', '12', 'M8', '50'],
['3', 'สตัด #3', '90', '79', '31', '12', 'M8', '50'],
['4', 'สตัด #4', '110', '99', '31', '12', 'M8', '50'],
],
},
{
tableName: 'Beam Clamp Type C (ฟันคู่)',
headers: ['No.', 'ขนาด (Size)', 'A มม.', 'B มม.', 'C มม.', 'D มม.', 'ขนาดสตัด Rod Size', 'ขนาดบรรจุ/ถุง Units/Bag'],
rows: [
['1', 'สตัด #2C', '65', '54', '31', '12', 'M8', '50'],
['2', 'สตัด #3C', '90', '79', '31', '12', 'M8', '50'],
['3', 'สตัด #4C', '110', '99', '31', '12', 'M8', '50'],
],
},
{
tableName: 'Beam Clamp Accessories',
headers: ['No.', 'รายการ', 'รายละเอียด', 'ขนาดบรรจุ/ถุง Units/Bag'],
rows: [
['1', 'สลักเกลียว สตัด', 'M8×30mm', '200'],
['2', 'สลักเกลียว สตัด', 'M10×30mm', '200'],
['3', 'สลักเกลียว สตัด', 'M12×30mm', '100'],
['4', 'น็อตตัวหนู', 'M8', '200'],
['5', 'น็อตตัวหนู', 'M10', '200'],
['6', 'น็อตตัวหนู', 'M12', '100'],
['7', 'แหวนรอง', 'M8', '500'],
['8', 'แหวนรอง', 'M10', '500'],
],
},
];
// Band Hanger Tables
export const bandHangerTables: ProductTable[] = [
{
tableName: 'Band Hanger (แคล้มหยดน้ำ)',
headers: ['No.', 'ขนาด (Size) นิ้ว', 'ขนาด (Size) มม.', 'A มม.', 'B มม.', 'ขนาดสตัด Rod Size'],
rows: [
['1', '1/2″', '15', '30', '25', 'M8'],
['2', '3/4″', '20', '35', '28', 'M8'],
['3', '1″', '25', '42', '32', 'M8'],
['4', '1-1/4″', '32', '50', '38', 'M10'],
['5', '1-1/2″', '40', '58', '42', 'M10'],
['6', '2″', '50', '70', '50', 'M10'],
['7', '2-1/2″', '65', '85', '60', 'M12'],
['8', '3″', '80', '100', '70', 'M12'],
['9', '4″', '100', '125', '85', 'M14'],
['10', '5″', '125', '150', '100', 'M16'],
['11', '6″', '150', '175', '115', 'M16'],
['12', '8″', '200', '225', '140', 'M20'],
],
},
];
// Combined Pipe Hanger Tables for product pages
export const pipeHangerTables: ProductTable[] = [
...clevisHangerTables,
...splitRingHangerTables,
...beamClampTables,
...bandHangerTables,
];
// =====================================================
// PPR WELDING MACHINE TABLES
// =====================================================
export const pprWelderTables: ProductTable[] = [
{
tableName: 'ราคาเครื่องเชื่อมท่อพีพีอาร์ (PPR Welding Machine Price List)',
headers: ['No.', 'รายการ', 'ราคา (บาท)'],
rows: [
// Welding Machines
['1', 'เครื่องเชื่อมท่อ PPR ขนาด 20-32mm 800W', '3,500'],
['2', 'เครื่องเชื่อมท่อ PPR ขนาด 20-63mm 1500W', '5,800'],
['3', 'เครื่องเชื่อมท่อ PPR ขนาด 20-75mm 1800W', '7,200'],
['4', 'เครื่องเชื่อมท่อ PPR ขนาด 20-90mm 2000W', '9,500'],
['5', 'เครื่องเชื่อมท่อ PPR ขนาด 20-110mm 2200W', '12,000'],
// Aiguille (Saddle Drill)
['6', 'สว่านเจาะท่อ Aiguille 20mm', '1,200'],
['7', 'สว่านเจาะท่อ Aiguille 25mm', '1,300'],
['8', 'สว่านเจาะท่อ Aiguille 32mm', '1,500'],
['9', 'สว่านเจาะท่อ Aiguille 40mm', '1,800'],
['10', 'สว่านเจาะท่อ Aiguille 50mm', '2,100'],
['11', 'สว่านเจาะท่อ Aiguille 63mm', '2,500'],
// Welding Saddle Mould
['12', 'หัวเชื่อมแบบ Saddle 20mm', '350'],
['13', 'หัวเชื่อมแบบ Saddle 25mm', '380'],
['14', 'หัวเชื่อมแบบ Saddle 32mm', '420'],
['15', 'หัวเชื่อมแบบ Saddle 40mm', '480'],
['16', 'หัวเชื่อมแบบ Saddle 50mm', '550'],
['17', 'หัวเชื่อมแบบ Saddle 63mm', '650'],
// Repairing Stick Mould
['18', 'แท่งซ่อม Repairing Stick 20mm', '280'],
['19', 'แท่งซ่อม Repairing Stick 25mm', '300'],
['20', 'แท่งซ่อม Repairing Stick 32mm', '340'],
['21', 'แท่งซ่อม Repairing Stick 40mm', '380'],
['22', 'แท่งซ่อม Repairing Stick 50mm', '420'],
['23', 'แท่งซ่อม Repairing Stick 63mm', '500'],
// Welding Mould - 20mm
['24', 'หัวเชื่อม Welding Mould 20mm (คู่)', '180'],
// Welding Mould - 25mm
['25', 'หัวเชื่อม Welding Mould 25mm (คู่)', '190'],
// Welding Mould - 32mm
['26', 'หัวเชื่อม Welding Mould 32mm (คู่)', '210'],
// Welding Mould - 40mm
['27', 'หัวเชื่อม Welding Mould 40mm (คู่)', '240'],
// Welding Mould - 50mm
['28', 'หัวเชื่อม Welding Mould 50mm (คู่)', '280'],
// Welding Mould - 63mm
['29', 'หัวเชื่อม Welding Mould 63mm (คู่)', '340'],
// Welding Mould - 75mm
['30', 'หัวเชื่อม Welding Mould 75mm (คู่)', '420'],
// Welding Mould - 90mm
['31', 'หัวเชื่อม Welding Mould 90mm (คู่)', '520'],
// Welding Mould - 110mm
['32', 'หัวเชื่อม Welding Mould 110mm (คู่)', '680'],
// Additional sizes
['33', 'หัวเชื่อม Welding Mould 125mm (คู่)', '850'],
['34', 'หัวเชื่อม Welding Mould 160mm (คู่)', '1,200'],
['35', 'เครื่องเชื่อม Butt Fusion 63-160mm', '45,000'],
['36', 'เครื่องเชื่อม Butt Fusion 160-315mm', '85,000'],
['37', 'เครื่องเชื่อม Butt Fusion 315-630mm', '150,000'],
],
},
];
// =====================================================
// PVC PIPE TABLES
// =====================================================
export const pvcTables: ProductTable[] = [
{
tableName: 'คุณสมบัติท่อ PVC ลักษณะการใช้งานและกายภาพทั่วไป',
headers: ['คุณสมบัติ', 'รายละเอียด'],
rows: [
['การใช้งาน', 'ท่อน้ำดี, ท่อน้ำเสีย'],
['สี', 'ฟ้า เหลือง เทา ขาว'],
['การติดตั้ง', 'ใช้น้ำยาประสานท่อในการประสานกัน'],
['อุณหภูมิที่เหมาะสม', '3-50 องศาเซลเซียส'],
['อายุการใช้งาน', 'ประมาณ 30-50 ปีในอุณหภูมิและแรงดันมาตรฐาน'],
['ความยาวต่อท่อน', '4 เมตรเป็นต้นไป (6 เมตร สำหรับงานราชการ)'],
],
},
{
tableName: 'ประเภทท่อ PVC กับการใช้งาน',
headers: ['ประเภทท่อ', 'รายละเอียด'],
rows: [
['ท่อ PVC ปลายเรียบ', 'เป็นท่อพีวีซีทั่วไป สามารถต่อกับท่อเส้นอื่นผ่านด้วยข้อต่อพีวีซี ทนความดันได้ดี'],
['ท่อ PVC ปลายบาน', 'เป็นท่อที่สามารถเชื่อมต่อกับท่ออื่นได้โดยไม่ต้องผ่านข้อต่อ แต่ยังต้องใช้น้ำยาประสานเพื่อกันรั่วซึม'],
['ท่อ PVC เซาะร่อง', 'ใช้สำหรับเป็นท่อ PVC รับน้ำจากน้ำฝนบนพิ้นดินลำเลียงน้ำสู่บ่อบาดาล มีขนาดเริ่มต้น 2-8 นิ้ว'],
],
},
{
tableName: 'สีท่อ PVC กับการใช้งาน',
headers: ['สีท่อ', 'การใช้งาน'],
rows: [
['ท่อ PVC สีฟ้า', 'ใช้เป็นท่อประปา น้ำดื่ม'],
['ท่อสีเหลือง', 'ใช้กับงานไฟฟ้า'],
['ท่อสีขาว', 'ใช้กับงานไฟฟ้า และร้อยสายโทรศัพท์'],
['ท่อสีเทา', 'ใช้เป็นท่อระบายน้ำทิ้ง หรืองานชลประทาน'],
],
},
{
tableName: 'ขนาดท่อ PVC กับการใช้งาน',
headers: ['ขนาดท่อ', 'การใช้งาน'],
rows: [
['ท่อพีวีซีขนาด ½ นิ้ว (สี่หุน)', 'ท่อขนาดเล็กที่สุด เหมาะสำหรับการใช้งานในบ้าน ท่อประปาภายในบ้าน'],
['ท่อพีวีซีขนาด ¾ นิ้ว (หกหุน)', 'ใช้ในงานน้ำประปา เหมาะสำหรับงานที่ต้องมีการลำเลียงน้ำมากขึ้น'],
['ท่อพีวีซีขนาด 1 นิ้ว', 'ขนาดที่นิยมใช้ในท่อหลักของงานน้ำประปา ใช้ต่อกับข้อต่อเพื่อลดขนาด'],
['ท่อพีวีซีขนาด 1¼ นิ้ว', 'ขนาดที่ไม่ค่อยมีการใช้งานมากนัก ใช้เมื่อต้องการลำเลียงน้ำให้มากขึ้น'],
['ท่อพีวีซีขนาด 1½ นิ้ว', 'ใช้สำหรับระบบท่อน้ำทิ้ง การลำเอียงน้ำจากอ่างไปยังบ่อบำบัด'],
['ท่อพีวีซีขนาด 2 นิ้ว', 'ใช้สำหรับทำระบบท่อน้ำทิ้ง น้ำระบายได้เยอะกว่า โอกาสอุดตันน้อยกว่า'],
['ท่อพีวีซีขนาด 2½ นิ้วขึ้นไป', 'ใช้สำหรับงานเฉพาะทาง การก่อสร้างอาคาร หรืองานเกษตรที่ต้องการจ่ายน้ำจำนวนมาก'],
],
},
{
tableName: 'วิธีการติดตั้ง ท่อ PVC',
headers: ['ขั้นตอน', 'รายละเอียด'],
rows: [
['1. ตัดท่อ', 'ตัดให้ตั้งฉาก ใช้กรรไกรตัดท่อ หรือใช้เลื่อยหากเป็นท่อขนาดใหญ่ แต่งปลายท่อให้เรียบร้อย ปัดเศษขุยออกให้หมด'],
['2. วัดระยะความลึก', 'วัดระยะความลึกในการสวมท่อเข้ากับข้อต่อ'],
['3. ทำความสะอาด', 'ใช้น้ำยาทำความสะอาดท่อ เช็ดทำความสะอาดภายในข้อต่อและท่อ เพื่อลบคราบฝุ่น น้ำมัน หรือสิ่งสกปรก'],
['4. ทาน้ำยาประสานท่อ', 'ทาน้ำยาประสานท่อด้านในข้อต่อ แล้วจึงค่อยทาภายนอกท่อ'],
['5. สวมปลายท่อเข้ากับข้อต่อ', 'ดันให้สุดถึงตำแหน่งที่ทำเครื่องหมายไว้ ดันทิ้งไว้ประมาณ 15 วินาที'],
['6. รอให้แห้ง', 'ก่อนใช้งานควรทิ้งให้น้ำยาประสานท่อแห้งประมาณ 10 นาที'],
],
},
{
tableName: 'แบรนด์ท่อ PVC ที่มีจำหน่าย',
headers: ['แบรนด์'],
rows: [
['ท่อตราช้าง'],
['ท่อน้ำไทย'],
['ท่อตราเสือ'],
['โรงงานทั่วไป'],
],
},
];
// Syler Pipe Tables
export const sylerTables: ProductTable[] = [
{
tableName: 'ท่อเหล็กบุพีอี ไซเลอร์ - ข้อมูลจำเพาะ',
headers: ['คุณสมบัติ', 'รายละเอียด'],
rows: [
['วัสดุ', 'ท่อเหล็กกล้า Class M ชุบสังกะสี บุ PE ทั้งภายในและภายนอก'],
['มาตรฐาน', 'BS1387/85 CLASS M, FM APPROVED, ISO 9001:2000'],
['แรงดันใช้งาน', '50 bar'],
['อุณหภูมิใช้งาน', 'สูงสุด 90°C'],
['ความยาว', '6 เมตร'],
['สี', 'แดง (ระบบดับเพลิง)'],
['อายุการใช้งาน', 'ยาวนานกว่าท่อ Galvanized 50 ปี'],
],
},
{
tableName: 'ขนาดท่อไซเลอร์',
headers: ['ขนาด (นิ้ว)', 'ขนาด (mm)', 'ความหนา (mm)', 'น้ำหนัก (kg/m)'],
rows: [
['1', '25', '3.2', '2.1'],
['1¼', '32', '3.2', '2.8'],
['1½', '40', '3.2', '3.4'],
['2', '50', '3.6', '4.9'],
['2½', '65', '3.6', '6.1'],
['3', '80', '4.0', '8.3'],
['4', '100', '4.0', '10.9'],
['5', '125', '4.0', '13.7'],
['6', '150', '4.5', '18.3'],
],
},
];
// XYLENT Drainage Pipe Tables
export const xylentTables: ProductTable[] = [
{
tableName: 'ท่อระบายน้ำ 3 ชั้น ไซเลนท์ - ข้อมูลจำเพาะ',
headers: ['คุณสมบัติ', 'ค่า'],
rows: [
['วัสดุ', 'PP (Polypropylene) 3 ชั้น'],
['มาตรฐาน', 'EN 1451, DIN 19560'],
['การลดเสียง', '22 dB'],
['อุณหภูมิใช้งาน', '-20 ถึง 95°C'],
['แรงกดทับ', '6 kN/m²'],
['อายุการใช้งาน', '50 ปี'],
['ระบบติดตั้ง', 'Push Fit (สวมล็อค)'],
],
},
{
tableName: 'ขนาดท่อ XYLENT',
headers: ['ขนาด (mm)', 'การใช้งาน'],
rows: [
['32', 'ท่อระบายน้ำในอาคาร'],
['40', 'ท่อระบายน้ำซักล้าง'],
['50', 'ท่อระบายน้ำในห้องน้ำ'],
['75', 'ท่อระบายน้ำหลัก'],
['90', 'ท่อระบายน้ำฝน'],
['110', 'ท่อระบายน้ำโสโครก'],
],
},
{
tableName: 'อุปกรณ์ติดตั้งท่อ XYLENT',
headers: ['อุปกรณ์', 'รายละเอียด'],
rows: [
['Clips', 'ยึดท่อกับผนัง'],
['Brackets', 'รองรับท่อแนวตั้ง'],
['Expansion Joint', 'รองรับการขยายตัวจากความร้อน'],
['Fire Collar', 'ป้องกันไฟลุกลาม'],
],
},
];
// Realflex Flexible Hose Tables
export const realflexTables: ProductTable[] = [
{
tableName: 'Realflex Flexible Hose - ข้อมูลจำเพาะ',
headers: ['คุณสมบัติ', 'ค่า'],
rows: [
['วัสดุ', 'AISI 304 Stainless Steel'],
['แรงดันใช้งาน', '14 bar / 200 psi'],
['แรงดันทดสอบ', '70 bar / 875 psi'],
['อุณหภูมิใช้งาน', 'สูงสุด 107°C / 225°F'],
['มาตรฐาน', 'NFPA13/13D/13R, EN12845'],
['การทดสอบ', 'UL 2443, FM 1637'],
['ความยาว', '1.2m, 1.5m, 1.8m'],
],
},
{
tableName: 'ขนาด Realflex Hose',
headers: ['ขนาด', 'OD (mm)', 'ID (mm)', 'Bend Radius (mm)'],
rows: [
['½"', '15', '10', '40'],
['¾"', '20', '15', '50'],
['1"', '25', '20', '65'],
],
},
];
// POLOPLAST PP-R/PP-RCT Tables
export const poloplastTables: ProductTable[] = [
{
tableName: 'POLOPLAST PP-R SDR 11 (S 5)',
headers: ['ขนาด (mm)', 'ความหนา (mm)', 'น้ำหนัก (kg/m)', 'PN'],
rows: [
['20', '2.0', '0.11', '10'],
['25', '2.3', '0.16', '10'],
['32', '2.9', '0.26', '10'],
['40', '3.7', '0.42', '10'],
['50', '4.6', '0.65', '10'],
['63', '5.8', '1.03', '10'],
['75', '6.8', '1.44', '10'],
['90', '8.2', '2.09', '10'],
['110', '10.0', '3.13', '10'],
],
},
{
tableName: 'POLOPLAST PP-R SDR 6 (S 2.5)',
headers: ['ขนาด (mm)', 'ความหนา (mm)', 'น้ำหนัก (kg/m)', 'PN'],
rows: [
['20', '3.4', '0.18', '20'],
['25', '4.2', '0.28', '20'],
['32', '5.4', '0.46', '20'],
['40', '6.7', '0.72', '20'],
['50', '8.3', '1.12', '20'],
['63', '10.5', '1.78', '20'],
['75', '12.5', '2.53', '20'],
['90', '15.0', '3.65', '20'],
['110', '18.3', '5.45', '20'],
],
},
{
tableName: 'POLOPLAST PP-RCT FIBER ML 5 (SDR 7.4)',
headers: ['ขนาด (mm)', 'ความหนา (mm)', 'น้ำหนัก (kg/m)', 'PN'],
rows: [
['20', '2.8', '0.15', '16'],
['25', '3.5', '0.23', '16'],
['32', '4.4', '0.37', '16'],
['40', '5.5', '0.58', '16'],
['50', '6.9', '0.92', '16'],
['63', '8.6', '1.45', '16'],
['75', '10.3', '2.07', '16'],
['90', '12.3', '3.00', '16'],
['110', '15.1', '4.48', '16'],
],
},
{
tableName: 'เปรียบเทียบ PP-R vs PP-RCT',
headers: ['คุณสมบัติ', 'PP-R', 'PP-RCT'],
rows: [
['อายุการใช้งาน', '50 ปี', '50 ปี'],
['แรงดันใช้งาน', 'สูงสุด PN20', 'สูงสุด PN25'],
['อุณหภูมิสูงสุด', '95°C', '95°C'],
['การยืดขยายตัว', 'ปกติ', 'ลดลง 5 เท่า'],
['ความต้านทานแรงกระแทก', 'ปกติ', 'สูงกว่า'],
],
},
];
// Export all product tables by product ID
export const allProductTables: Record<string, ProductTable[]> = {
dukelarrsen: dukelarrsenTables,
'ppr-welder': pprWelderTables,
'pvc': pvcTables,
'upvc': pvcTables,
'clevis-hanger': clevisHangerTables,
'split-ring-hanger': splitRingHangerTables,
'beam-clamp': beamClampTables,
'band-hanger': bandHangerTables,
'hanger-clamp-bolt': pipeHangerTables,
'syler': sylerTables,
'xylent': xylentTables,
'realflex': realflexTables,
'poloplast': poloplastTables,
};

View File

@@ -1,5 +1,52 @@
import { SiteConfig, NavItem, ProductCategory, WorkHours } from '@/types';
import { dukelarrsenTables, pprWelderTables, pvcTables, clevisHangerTables, splitRingHangerTables, beamClampTables, bandHangerTables, pipeHangerTables, sylerTables, xylentTables, realflexTables, poloplastTables } from './product-tables';
// Deal Plus Tech - Site Configuration
// Types defined inline to avoid circular imports
export interface SiteConfig {
name: string;
nameTh: string;
url: string;
description: string;
phone: string;
email: string;
lineId: string;
facebookUrl: string;
address: string;
}
export interface NavItem {
label: string;
labelEn: string;
href: string;
children?: Array<{ label: string; labelEn: string; href: string; children?: Array<{ label: string; labelEn: string; href: string }> }>;
}
export interface ProductCategory {
id: string;
name: string;
nameEn: string;
slug: string;
href: string;
image: string;
description: string;
shortDescription?: string;
keywords?: string[];
seoContent?: string;
specifications?: Array<{ label: string; value: string; unit?: string }>;
features?: string[];
applications?: string[];
certifications?: string[];
faq?: Array<{ question: string; answer: string }>;
schemaData?: any;
relatedProductIds?: string[];
productTables?: Array<{ tableName: string; headers: string[]; rows: string[][] }>;
}
export interface WorkHours {
day: string;
hours: string;
isClosed?: boolean;
}
export const siteConfig: SiteConfig = {
name: 'Deal Plus Tech',
@@ -212,7 +259,7 @@ export const productCategories: ProductCategory[] = [
material: 'Metal, Plastic',
},
relatedProductIds: ['ppr-elephant', 'thai-ppr', 'poloplast', 'hdpe-welder'],
productTables: pprWelderTables,
},
{
id: 'poloplast',
@@ -278,7 +325,7 @@ export const productCategories: ProductCategory[] = [
material: 'PP-R / PP-RCT',
category: 'Plumbing Pipe - Premium PPR',
},
productTables: poloplastTables,
relatedProductIds: ['ppr-elephant', 'thai-ppr', 'ppr-welder'],
},
@@ -462,7 +509,7 @@ export const productCategories: ProductCategory[] = [
category: 'Plumbing Pipe - uPVC',
},
relatedProductIds: ['pvc', 'xylent'],
productTables: pvcTables,
},
{
id: 'pvc',
@@ -518,7 +565,7 @@ export const productCategories: ProductCategory[] = [
category: 'Plumbing Pipe - PVC',
},
relatedProductIds: ['upvc', 'xylent'],
productTables: pvcTables,
},
{
id: 'syler',
@@ -572,7 +619,7 @@ export const productCategories: ProductCategory[] = [
material: 'Steel with PE Lining',
category: 'Fire Protection Pipe',
},
productTables: sylerTables,
relatedProductIds: ['realflex', 'groove-coupling'],
},
@@ -631,7 +678,7 @@ export const productCategories: ProductCategory[] = [
material: 'Polypropylene (PP) - Triple Layer',
category: 'Drainage Pipe - Silent',
},
productTables: xylentTables,
relatedProductIds: ['poloplast', 'upvc'],
},
@@ -837,7 +884,7 @@ export const productCategories: ProductCategory[] = [
category: 'Grooved Coupling - Fire Protection',
},
relatedProductIds: ['mech', 'groove-coupling', 'syler'],
productTables: dukelarrsenTables,
},
{
id: 'mech',
@@ -932,7 +979,7 @@ export const productCategories: ProductCategory[] = [
category: 'Pipe Support & Hangers',
},
relatedProductIds: ['clevis-hanger', 'split-ring-hanger', 'u-bolt'],
productTables: pipeHangerTables,
},
{
id: 'clevis-hanger',
@@ -978,7 +1025,7 @@ export const productCategories: ProductCategory[] = [
category: 'Pipe Hanger - Clevis Type',
},
relatedProductIds: ['threaded-rod', 'split-ring-hanger'],
productTables: clevisHangerTables,
},
{
id: 'split-ring-hanger',
@@ -1021,7 +1068,7 @@ export const productCategories: ProductCategory[] = [
category: 'Pipe Hanger - Split Ring',
},
relatedProductIds: ['clevis-hanger', 'threaded-rod'],
productTables: splitRingHangerTables,
},
{
id: 'beam-clamp',
@@ -1066,7 +1113,7 @@ export const productCategories: ProductCategory[] = [
category: 'Beam Clamp',
},
relatedProductIds: ['threaded-rod', 'clevis-hanger'],
productTables: beamClampTables,
},
{
id: 'band-hanger',
@@ -1109,7 +1156,7 @@ export const productCategories: ProductCategory[] = [
category: 'Pipe Hanger - Band Type',
},
relatedProductIds: ['split-ring-hanger', 'clevis-hanger'],
productTables: bandHangerTables,
},
{
id: 'level-clamp',
@@ -1559,7 +1606,7 @@ export const productCategories: ProductCategory[] = [
material: 'Stainless Steel 304',
category: 'Fire Protection - Flexible Hose',
},
productTables: realflexTables,
relatedProductIds: ['syler', 'extinguishers'],
},

View File

@@ -0,0 +1,26 @@
---
import CookieConsentBanner from '../components/CookieConsentBanner.astro';
export interface Props { title: string; description?: string; image?: string; }
const { title, description, image } = Astro.props;
---
<!doctype html>
<html lang="th">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content={description || 'บริษัท ดีล พลัส เทค จำกัด'} />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Kanit:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
<meta property="og:title" content={title} />
<meta property="og:description" content={description || 'Deal Plus Tech'} />
<title>{title} | ดีล พลัส เทค</title>
</head>
<body class="flex flex-col min-h-screen">
<slot />
<CookieConsentBanner />
</body>
</html>
<style is:global>@import "../styles/globals.css";</style>

View File

@@ -1,27 +1,42 @@
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
// Utility functions migrated from Next.js
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
/**
* Combines class names conditionally (like clsx + tailwind-merge)
*/
export function cn(...classes: Array<string | false | null | undefined>): string {
return classes.filter(Boolean).join(' ');
}
export function formatDate(date: string): string {
return new Date(date).toLocaleDateString('th-TH', {
/**
* Format price in Thai Baht
*/
export function formatPrice(price: number): string {
return new Intl.NumberFormat('th-TH', {
style: 'currency',
currency: 'THB',
}).format(price);
}
/**
* Format date to Thai locale
*/
export function formatDateThai(date: Date | string): string {
return new Intl.DateTimeFormat('th-TH', {
year: 'numeric',
month: 'long',
day: 'numeric',
});
}).format(new Date(date));
}
export function truncate(str: string, length: number): string {
if (str.length <= length) return str;
return str.slice(0, length) + '...';
}
export function slugify(str: string): string {
return str
/**
* Generate slug from Thai text
*/
export function generateSlug(text: string): string {
return text
.toLowerCase()
.replace(/[^\w\s-]/g, '')
.replace(/[\s_-]+/g, '-')
.replace(/^-+|-+$/g, '');
.replace(/\s+/g, '-')
.replace(/[^\w-]+/g, '')
.replace(/--+/g, '-')
.replace(/^-+/, '')
.replace(/-+$/, '');
}

View File

@@ -0,0 +1,78 @@
---
import { createClient } from '@libsql/client';
import BaseLayout from '../../layouts/BaseLayout.astro';
let consentLogs: any[] = [];
let dbError: string | null = null;
const ADMIN_PASSWORD = import.meta.env.ADMIN_PASSWORD || 'changeme';
let isAuthenticated = false;
try {
const authCookie = Astro.cookies.get('admin_auth')?.value;
isAuthenticated = authCookie === 'true';
if (Astro.request.method === 'POST') {
const formData = await Astro.request.formData();
const action = formData.get('action');
if (action === 'login' && formData.get('password') === ADMIN_PASSWORD) {
Astro.cookies.set('admin_auth', 'true', { path: '/', httpOnly: true, secure: import.meta.env.PROD, maxAge: 7200 });
isAuthenticated = true;
} else if (action === 'logout') {
Astro.cookies.delete('admin_auth', { path: '/' });
}
}
if (isAuthenticated) {
const client = createClient({ url: import.meta.env.ASTRO_DB_REMOTE_URL || 'file:./data/consent.db' });
const result = await client.execute({ sql: 'SELECT * FROM consent_logs ORDER BY created_at DESC LIMIT 100', args: [] });
consentLogs = result.rows || [];
}
} catch (error) {
dbError = 'Database not initialized yet';
}
---
<BaseLayout title="Consent Logs Admin">
<main class="min-h-screen py-12">
<div class="container mx-auto px-4 max-w-7xl">
<div class="bg-white rounded-2xl shadow-lg p-8">
<h1 class="text-3xl font-bold mb-2">Consent Logs Admin</h1>
<p class="text-secondary-600 mb-8">PDPA Compliance - User Consent Records</p>
{dbError && <div class="mb-6 p-4 bg-yellow-50 border-l-4 border-yellow-500"><p class="text-yellow-800">{dbError}</p></div>}
{!isAuthenticated ? (
<div class="max-w-md mx-auto">
<form method="POST" class="bg-secondary-50 p-6 rounded-xl">
<input type="hidden" name="action" value="login" />
<label class="block text-sm font-semibold mb-2">Password</label>
<input type="password" name="password" class="w-full px-4 py-2 border-2 rounded-lg mb-4" required />
<button type="submit" class="w-full bg-primary-600 text-white py-2 rounded-lg hover:bg-primary-700">Login</button>
</form>
</div>
) : (
<div>
<form method="POST" class="mb-6"><input type="hidden" name="action" value="logout" /><button type="submit" class="text-secondary-600 hover:text-secondary-800">Logout</button></form>
<div class="overflow-x-auto">
<table class="w-full">
<thead><tr class="bg-secondary-800 text-white"><th class="px-4 py-3 text-left">Date</th><th class="px-4 py-3 text-left">Session</th><th class="px-4 py-3 text-left">Essential</th><th class="px-4 py-3 text-left">Analytics</th></tr></thead>
<tbody>
{consentLogs.map((log: any, i: number) => (
<tr class={i % 2 === 0 ? 'bg-white' : 'bg-secondary-50'}>
<td class="px-4 py-3">{new Date(log.timestamp).toLocaleString('th-TH')}</td>
<td class="px-4 py-3 font-mono text-sm">{log.session_id}</td>
<td class="px-4 py-3">{log.essential === 1 ? '✓' : '✗'}</td>
<td class="px-4 py-3">{log.analytics === 1 ? '<span class="text-green-600">Accepted</span>' : '<span class="text-red-600">Rejected</span>'}</td>
</tr>
))}
</tbody>
</table>
</div>
{consentLogs.length === 0 && <p class="text-center py-12 text-secondary-600">No consent logs yet</p>}
</div>
)}
</div>
</div>
</main>
</BaseLayout>

View File

@@ -0,0 +1,57 @@
import { createClient } from '@libsql/client';
import type { APIRoute } from 'astro';
const client = createClient({
url: import.meta.env.ASTRO_DB_REMOTE_URL || 'file:./data/consent.db',
authToken: import.meta.env.ASTRO_DB_APP_TOKEN,
});
await client.execute({
sql: `CREATE TABLE IF NOT EXISTS consent_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT UNIQUE NOT NULL,
timestamp TEXT NOT NULL,
locale TEXT DEFAULT 'th',
essential INTEGER DEFAULT 1,
analytics INTEGER DEFAULT 0,
marketing INTEGER DEFAULT 0,
policy_version TEXT NOT NULL,
ip_hash TEXT,
user_agent TEXT,
created_at TEXT DEFAULT CURRENT_TIMESTAMP
)`,
});
export const POST: APIRoute = async ({ request }) => {
try {
const body = await request.json();
const { sessionId, consent, policyVersion } = body;
if (!sessionId || !consent) {
return new Response(JSON.stringify({ error: 'Missing fields' }), { status: 400 });
}
const ip = request.headers.get('x-forwarded-for') || 'unknown';
const ipHash = await hashIP(ip);
const userAgent = request.headers.get('user-agent') || 'unknown';
const locale = (request.headers.get('accept-language') || 'th').startsWith('th') ? 'th' : 'en';
await client.execute({
sql: `INSERT OR REPLACE INTO consent_logs
(session_id, timestamp, locale, essential, analytics, marketing, policy_version, ip_hash, user_agent)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
args: [sessionId, consent.timestamp, locale, consent.essential ? 1 : 0, consent.analytics ? 1 : 0, consent.marketing ? 1 : 0, policyVersion, ipHash, userAgent],
});
return new Response(JSON.stringify({ success: true }), { status: 200 });
} catch (error) {
console.error('Consent API error:', error);
return new Response(JSON.stringify({ error: 'Internal error' }), { status: 500 });
}
};
async function hashIP(ip: string): Promise<string> {
const encoder = new TextEncoder();
const data = encoder.encode(ip);
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('').substring(0, 16);
}

View File

@@ -0,0 +1,75 @@
---
import { getCollection, render } from 'astro:content';
import BaseLayout from '../../layouts/BaseLayout.astro';
export async function getStaticPaths() {
const posts = await getCollection('blog');
return posts.map((post) => ({
params: { slug: post.id },
props: { post },
}));
}
interface Props {
post: CollectionEntry<'blog'>;
}
const { post } = Astro.props;
const { Content } = await render(post);
const { title, date, author, category, categories, image, featuredImage } = post.data;
// Support both 'category' and 'categories'
const postCategory = category || (Array.isArray(categories) ? categories[0] : 'ทั่วไป');
const postImage = image || featuredImage || '/images/2021/03/ppr-pipe_000C.jpg';
---
<BaseLayout title={title} description={post.data.excerpt}>
<main class="pt-32 pb-16">
<article class="container mx-auto px-4 max-w-4xl">
<!-- Header -->
<header class="mb-8">
<div class="flex items-center gap-4 mb-4">
<span class="industrial-badge">{postCategory}</span>
<time class="text-secondary-500">
{new Date(date).toLocaleDateString('th-TH', {
year: 'numeric',
month: 'long',
day: 'numeric',
})}
</time>
<span class="text-secondary-500">•</span>
<span class="text-secondary-500">{author}</span>
</div>
<h1 class="text-4xl md:text-5xl font-bold text-secondary-900 mb-4">
{title}
</h1>
</header>
<!-- Featured Image -->
<div class="relative aspect-video bg-secondary-100 rounded-xl overflow-hidden mb-8">
<img
src={postImage}
alt={title}
class="object-cover w-full h-full"
loading="lazy"
/>
</div>
<!-- Content -->
<div class="prose prose-lg max-w-none prose-headings:font-bold prose-a:text-primary-600 hover:prose-a:text-primary-700 prose-img:rounded-xl">
<Content />
</div>
<!-- Back to Blog -->
<div class="mt-12 pt-8 border-t border-secondary-200">
<a href="/blog/" class="inline-flex items-center text-primary-600 font-medium hover:text-primary-700 transition-colors">
<svg class="w-5 h-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width={2} d="M10 19l-7-7m0 0l7-7m-7 7h18" />
</svg>
กลับสู่หน้าบทความ
</a>
</div>
</article>
</main>
</BaseLayout>

View File

@@ -0,0 +1,37 @@
---
import { getCollection } from 'astro:content';
import BlogCard from '../../components/BlogCard.astro';
import BaseLayout from '../../layouts/BaseLayout.astro';
const posts = await getCollection('blog');
export const metadata = {
title: 'บทความความรู้',
description: 'บทความความรู้เกี่ยวกับวัสดุท่อ อุปกรณ์ระบบท่อ และเทคนิคการติดตั้ง',
};
export const prerender = true;
---
<BaseLayout title={metadata.title} description={metadata.description}>
<main class="pt-32 pb-16">
<div class="container mx-auto px-4">
<!-- Hero -->
<div class="text-center mb-12">
<h1 class="text-4xl md:text-5xl font-bold text-secondary-900 mb-4">
บทความ<span class="text-primary-600">ความรู้</span>
</h1>
<p class="text-xl text-secondary-600 max-w-2xl mx-auto">
บทความความรู้เกี่ยวกับวัสดุท่อ อุปกรณ์ระบบท่อ และเทคนิคการติดตั้ง
</p>
</div>
<!-- Blog Grid -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{posts.map((post) => (
<BlogCard post={post} />
))}
</div>
</div>
</main>
</BaseLayout>

54
src/pages/index.astro Normal file
View File

@@ -0,0 +1,54 @@
---
import BaseLayout from '../layouts/BaseLayout.astro';
import Header from '../components/Header.astro';
import Footer from '../components/Footer.astro';
import { productCategories } from '../data/site-config';
const featuredProducts = productCategories.slice(0, 6);
---
<BaseLayout title="หน้าแรก" description="บริษัท ดีล พลัส เทค จำกัด - ผู้เชี่ยวชาญด้านระบบท่อ">
<Header />
<main class="pt-20">
<section class="relative h-[70vh] min-h-[500px] bg-secondary-900">
<div class="absolute inset-0 bg-gradient-to-r from-secondary-900 via-secondary-900/90 to-secondary-900/60 z-10"></div>
<img src="/images/2021/03/hdpe_pipe_main.jpg" alt="ท่อคุณภาพสูง" class="absolute inset-0 w-full h-full object-cover opacity-50" />
<div class="relative z-20 container mx-auto px-4 h-full flex items-center">
<div class="max-w-2xl">
<span class="inline-block px-4 py-2 bg-primary-600 text-white font-semibold mb-4 rounded">ผู้เชี่ยวชาญด้านระบบท่อและ HVAC</span>
<h1 class="text-4xl md:text-5xl lg:text-6xl font-bold text-white mb-6">วัสดุท่อ อุปกรณ์ HVAC<span class="text-primary-400 block">และฉนวนหุ้มท่อ</span></h1>
<p class="text-lg text-secondary-200 mb-8">จำหน่ายและติดตั้งท่อ PPR, ท่อ HDPE และอุปกรณ์ระบบท่อครบวงจร</p>
<div class="flex gap-4">
<a href="/products/" class="btn-primary">ดูสินค้าทั้งหมด</a>
<a href="/contact-us/" class="btn-outline border-white text-white hover:bg-white hover:text-secondary-900">ติดต่อเรา</a>
</div>
</div>
</div>
</section>
<section class="py-16 bg-secondary-50">
<div class="container mx-auto px-4">
<div class="text-center mb-12">
<h2 class="section-title">สินค้าแนะนำ</h2>
<p class="section-subtitle">คุณภาพมาตรฐานสากล</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{featuredProducts.map((product) => (
<div class="card">
<a href={`/products/${product.slug}/`} class="block">
<div class="aspect-video bg-secondary-100 flex items-center justify-center p-8">
<img src="/images/2021/03/hdpe001.jpg" alt={product.name} class="max-h-full max-w-full object-contain" />
</div>
<div class="p-6">
<h3 class="text-xl font-bold text-secondary-900 mb-2">{product.name}</h3>
<p class="text-secondary-600 text-sm mb-4 line-clamp-2">{product.description}</p>
<span class="btn-outline text-sm">ดูรายละเอียด</span>
</div>
</a>
</div>
))}
</div>
</div>
</section>
</main>
<Footer />
</BaseLayout>

View File

@@ -0,0 +1,75 @@
---
import BaseLayout from '../layouts/BaseLayout.astro';
---
<BaseLayout title="นโยบายความเป็นส่วนตัว" description="PDPA Privacy Policy">
<main class="py-12 bg-secondary-50 min-h-screen">
<article class="container mx-auto px-4 max-w-4xl">
<div class="bg-white rounded-2xl shadow-lg p-8 md:p-12">
<h1 class="text-4xl font-bold mb-4">นโยบายความเป็นส่วนตัว</h1>
<p class="text-lg text-secondary-600 mb-8">Privacy Policy (PDPA)</p>
<p class="text-sm text-secondary-500 mb-8">Version: 1.0.0 | Last Updated: 12 มีนาคม 2569</p>
<section class="mb-8">
<h2 class="text-2xl font-bold mb-4">1. ผู้ควบคุมข้อมูล</h2>
<p class="text-secondary-700"><strong>บริษัท ดีล พลัส เทค จำกัด</strong> เป็นผู้ควบคุมข้อมูลส่วนบุคคล ตาม พ.ร.บ. คุ้มครองข้อมูลส่วนบุคคล พ.ศ. 2562</p>
<p class="text-secondary-700 mt-2"><strong>ติดต่อ:</strong> info@dealplustech.co.th | 090-555-1415</p>
</section>
<section class="mb-8">
<h2 class="text-2xl font-bold mb-4">2. ข้อมูลที่เก็บรวบรวม</h2>
<ul class="list-disc list-inside space-y-2 text-secondary-700">
<li>ข้อมูลส่วนตัว: ชื่อ, อีเมล, เบอร์โทรศัพท์</li>
<li>ข้อมูลการใช้งาน: IP Address, Browser, พฤติกรรมการใช้งาน</li>
<li>ข้อมูลคุกกี้: การตั้งค่าคุกกี้, Session ID</li>
</ul>
</section>
<section class="mb-8">
<h2 class="text-2xl font-bold mb-4">3. วัตถุประสงค์</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="bg-secondary-50 p-4 rounded-lg"><strong>การให้บริการ</strong><p class="text-sm text-secondary-600">ตอบสนองคำขอ, ให้บริการ</p></div>
<div class="bg-secondary-50 p-4 rounded-lg"><strong>การติดต่อกลับ</strong><p class="text-sm text-secondary-600">ตอบคำถาม, ให้ข้อมูล</p></div>
<div class="bg-secondary-50 p-4 rounded-lg"><strong>การวิเคราะห์</strong><p class="text-sm text-secondary-600">ปรับปรุงเว็บไซต์</p></div>
<div class="bg-secondary-50 p-4 rounded-lg"><strong>ตามกฎหมาย</strong><p class="text-sm text-secondary-600">ปฏิบัติตามกฎหมาย</p></div>
</div>
</section>
<section class="mb-8">
<h2 class="text-2xl font-bold mb-4">4. ระยะเวลาเก็บรักษา</h2>
<ul class="space-y-2">
<li class="flex justify-between"><span>ข้อมูลการใช้งาน</span><span class="font-semibold">10 ปี</span></li>
<li class="flex justify-between"><span>บันทึกการยินยอม</span><span class="font-semibold">10 ปี</span></li>
<li class="flex justify-between"><span>ข้อมูลติดต่อ</span><span class="font-semibold">5 ปี</span></li>
</ul>
</section>
<section class="mb-8">
<h2 class="text-2xl font-bold mb-4">5. สิทธิของคุณ (PDPA)</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="bg-secondary-50 p-4 rounded-lg"><strong>สิทธิขอเข้าถึง</strong></div>
<div class="bg-secondary-50 p-4 rounded-lg"><strong>สิทธิขอแก้ไข</strong></div>
<div class="bg-secondary-50 p-4 rounded-lg"><strong>สิทธิขอลบ</strong></div>
<div class="bg-secondary-50 p-4 rounded-lg"><strong>สิทธิเพิกถอน</strong></div>
</div>
</section>
<section class="mb-8">
<h2 class="text-2xl font-bold mb-4">6. คุกกี้</h2>
<ul class="space-y-3">
<li class="bg-secondary-50 p-4 rounded-lg border-l-4 border-primary-600"><strong>คุกกี้จำเป็น</strong><p class="text-sm">ใช้สำหรับการทำงานของเว็บไซต์</p></li>
<li class="bg-secondary-50 p-4 rounded-lg border-l-4 border-accent-500"><strong>คุกกี้วิเคราะห์</strong><p class="text-sm">Umami Analytics (ไม่ใช้ข้อมูลส่วนตัว)</p></li>
</ul>
</section>
<section>
<h2 class="text-2xl font-bold mb-4">7. ติดต่อ</h2>
<div class="bg-primary-50 p-6 rounded-xl">
<p class="mb-2"><strong>อีเมล:</strong> info@dealplustech.co.th</p>
<p><strong>โทรศัพท์:</strong> 090-555-1415</p>
</div>
</section>
</div>
</article>
</main>
</BaseLayout>

View File

@@ -0,0 +1,62 @@
---
import { getCollection, render } from 'astro:content';
import type { CollectionEntry } from 'astro:content';
import BaseLayout from '../../layouts/BaseLayout.astro';
import Header from '../../components/Header.astro';
import Footer from '../../components/Footer.astro';
import { productCategories } from '../../data/site-config';
export async function getStaticPaths() {
const products = await getCollection('products');
return products.map((product) => ({
params: { slug: product.data.slug },
props: { product },
}));
}
interface Props { product: CollectionEntry<'products'>; }
const { product } = Astro.props;
const { Content } = await render(product);
// Get product tables from site-config
const productData = productCategories.find(p => p.id === product.data.id);
const productTables = productData?.productTables || [];
---
<BaseLayout title={product.data.title} description={product.data.description || ''}>
<Header />
<main class="pt-20">
<article class="container mx-auto px-4 py-12">
<h1 class="text-4xl font-bold mb-6">{product.data.title}</h1>
<div class="prose max-w-none mb-12"><Content /></div>
{productTables.length > 0 && (
<div class="space-y-8">
{productTables.map((table: any) => (
<div>
<h3 class="text-2xl font-bold mb-4">{table.tableName}</h3>
<div class="overflow-x-auto">
<table class="w-full border-collapse border">
<thead>
<tr class="bg-secondary-100">
{table.headers?.map((h: string) => <th class="border p-3 text-left">{h}</th>)}
</tr>
</thead>
<tbody>
{table.rows?.map((row: string[]) => (
<tr>
{row.map((cell: string) => <td class="border p-3">{cell}</td>)}
</tr>
))}
</tbody>
</table>
</div>
</div>
))}
</div>
)}
</article>
</main>
<Footer />
</BaseLayout>

View File

@@ -0,0 +1,21 @@
---
import { getCollection } from 'astro:content';
import ProductCard from '../../components/ProductCard.astro';
import BaseLayout from '../../layouts/BaseLayout.astro';
const products = await getCollection('products');
---
<BaseLayout title="สินค้าทั้งหมด" description="รายการสินค้าทั้งหมดจาก Deal Plus Tech">
<main class="py-12">
<div class="container mx-auto px-4 max-w-6xl">
<h1 class="section-title mb-8">สินค้าทั้งหมด</h1>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{products.map((product) => (
<ProductCard product={product} />
))}
</div>
</div>
</main>
</BaseLayout>

View File

@@ -0,0 +1,65 @@
---
import BaseLayout from '../layouts/BaseLayout.astro';
---
<BaseLayout title="ข้อกำหนดการใช้งาน" description="Terms & Conditions">
<main class="py-12 bg-secondary-50 min-h-screen">
<article class="container mx-auto px-4 max-w-4xl">
<div class="bg-white rounded-2xl shadow-lg p-8 md:p-12">
<h1 class="text-4xl font-bold mb-4">ข้อกำหนดและเงื่อนไข</h1>
<p class="text-lg text-secondary-600 mb-8">Terms & Conditions</p>
<p class="text-sm text-secondary-500 mb-8">Version: 1.0.0 | 12 มีนาคม 2569</p>
<section class="mb-8">
<h2 class="text-2xl font-bold mb-4">1. การยอมรับ</h2>
<p class="text-secondary-700">การใช้เว็บไซต์แสดงว่ายอมรับข้อกำหนดนี้</p>
</section>
<section class="mb-8">
<h2 class="text-2xl font-bold mb-4">2. บริการ</h2>
<p class="text-secondary-700 mb-2">จำหน่ายและให้คำปรึกษาเกี่ยวกับ:</p>
<ul class="list-disc list-inside text-secondary-700">
<li>ท่อ PPR, ท่อ HDPE</li>
<li>อุปกรณ์ระบบน้ำและ HVAC</li>
<li>บริการหลังการขาย</li>
</ul>
</section>
<section class="mb-8">
<h2 class="text-2xl font-bold mb-4">3. ทรัพย์สินทางปัญญา</h2>
<p class="text-secondary-700">เนื้อหาทั้งหมดเป็นทรัพย์สินของบริษัทฯ ห้ามคัดลอกโดยไม่ได้รับอนุญาต</p>
</section>
<section class="mb-8">
<h2 class="text-2xl font-bold mb-4">4. ข้อผูกพันผู้ใช้</h2>
<ul class="list-disc list-inside text-secondary-700 space-y-2">
<li>ใช้งานเพื่อวัตถุประสงค์ที่ชอบด้วยกฎหมาย</li>
<li>ไม่ให้ข้อมูลที่เป็นเท็จ</li>
<li>ไม่พยายามเข้าถึงระบบโดยไม่ได้รับอนุญาต</li>
</ul>
</section>
<section class="mb-8">
<h2 class="text-2xl font-bold mb-4">5. ข้อจำกัดความรับผิด</h2>
<div class="bg-secondary-50 p-6 rounded-xl border-l-4 border-accent-500">
<p class="text-secondary-700">บริษัทฯ ไม่รับผิดชอบต่อความเสียหายใดๆ ที่เกิดจากการใช้เว็บไซต์</p>
</div>
</section>
<section class="mb-8">
<h2 class="text-2xl font-bold mb-4">6. กฎหมายที่ใช้บังคับ</h2>
<p class="text-secondary-700">อยู่ภายใต้กฎหมายราชอาณาจักรไทย</p>
</section>
<section>
<h2 class="text-2xl font-bold mb-4">7. ติดต่อ</h2>
<div class="bg-primary-50 p-6 rounded-xl">
<p><strong>บริษัท ดีล พลัส เทค จำกัด</strong></p>
<p>อีเมล: info@dealplustech.co.th</p>
<p>โทร: 090-555-1415</p>
</div>
</section>
</div>
</article>
</main>
</BaseLayout>

157
src/styles/global.css Normal file
View File

@@ -0,0 +1,157 @@
@import "tailwindcss";
/* Deal Plus Tech - Industrial Theme */
@theme {
/* Primary Colors - Green for trust and growth */
--color-primary-50: #f0fdf4;
--color-primary-100: #dcfce7;
--color-primary-200: #bbf7d0;
--color-primary-300: #86efac;
--color-primary-400: #4ade80;
--color-primary-500: #22c55e;
--color-primary-600: #16a34a;
--color-primary-700: #15803d;
--color-primary-800: #166534;
--color-primary-900: #14532d;
/* Secondary Colors - Slate grays for industrial look */
--color-secondary-50: #f8fafc;
--color-secondary-100: #f1f5f9;
--color-secondary-200: #e2e8f0;
--color-secondary-300: #cbd5e1;
--color-secondary-400: #94a3b8;
--color-secondary-500: #64748b;
--color-secondary-600: #475569;
--color-secondary-700: #334155;
--color-secondary-800: #1e293b;
--color-secondary-900: #0f172a;
/* Accent Colors - Yellow for highlights */
--color-accent-400: #facc15;
--color-accent-500: #eab308;
--color-accent-600: #ca8a04;
/* Industrial custom colors */
--color-industrial-dark: #1a1a1a;
--color-industrial-light: #f5f5f5;
/* Font - Kanit for Thai support */
--font-sans: 'Kanit', system-ui, sans-serif;
--font-mono: ui-monospace, monospace;
/* Shadows */
--shadow-card: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
--shadow-industrial: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
--shadow-bold: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
}
@layer base {
html {
scroll-behavior: smooth;
/* Base font size */
font-size: 16px;
}
/* Responsive font sizes for larger screens */
@media (min-width: 1280px) {
html {
font-size: 18px;
}
}
@media (min-width: 1536px) {
html {
font-size: 20px;
}
}
@media (min-width: 1920px) {
html {
font-size: 22px;
}
}
@media (min-width: 2560px) {
html {
font-size: 24px;
}
}
body {
@apply bg-white text-secondary-900 antialiased;
}
h1, h2, h3, h4, h5, h6 {
@apply font-bold tracking-tight;
}
}
@layer components {
.btn-primary {
@apply inline-flex items-center justify-center px-6 py-3 md:px-8 md:py-4 bg-primary-600 text-white font-semibold rounded-lg
hover:bg-primary-700 transition-all duration-200 shadow-bold hover:shadow-industrial
active:translate-y-0.5 text-base md:text-lg;
}
.btn-secondary {
@apply inline-flex items-center justify-center px-6 py-3 md:px-8 md:py-4 bg-secondary-800 text-white font-semibold rounded-lg
hover:bg-secondary-900 transition-all duration-200 text-base md:text-lg;
}
.btn-outline {
@apply inline-flex items-center justify-center px-6 py-3 md:px-8 md:py-4 border-2 border-primary-600 text-primary-600 font-semibold rounded-lg
hover:bg-primary-600 hover:text-white transition-all duration-200 text-base md:text-lg;
}
.section-title {
@apply text-3xl md:text-4xl lg:text-5xl xl:text-6xl font-bold text-secondary-900;
}
.section-subtitle {
@apply text-lg md:text-xl lg:text-2xl xl:text-3xl text-secondary-600 mt-4;
}
.card {
@apply bg-white rounded-xl shadow-card overflow-hidden transition-all duration-300
hover:shadow-industrial hover:-translate-y-1;
}
.card-industrial {
@apply bg-secondary-800 text-white rounded-xl p-6 border-l-4 border-primary-500;
}
.gradient-overlay {
@apply absolute inset-0 bg-gradient-to-r from-industrial-dark/90 to-industrial-dark/70;
}
.industrial-badge {
@apply inline-flex items-center px-3 py-1 md:px-4 md:py-2 bg-primary-600 text-white text-sm md:text-base font-semibold rounded;
}
}
@layer utilities {
.text-balance {
text-wrap: balance;
}
.text-gradient {
@apply bg-clip-text text-transparent bg-gradient-to-r from-primary-500 to-primary-700;
}
/* Responsive text utilities */
.text-responsive-sm {
@apply text-sm md:text-base lg:text-lg xl:text-xl;
}
.text-responsive-base {
@apply text-base md:text-lg lg:text-xl xl:text-2xl;
}
.text-responsive-lg {
@apply text-lg md:text-xl lg:text-2xl xl:text-3xl;
}
.text-responsive-xl {
@apply text-xl md:text-2xl lg:text-3xl xl:text-4xl;
}
}

View File

@@ -1,77 +1,36 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@import "tailwindcss";
@layer base {
html {
scroll-behavior: smooth;
}
body {
@apply bg-white text-secondary-900 antialiased;
}
h1, h2, h3, h4, h5, h6 {
@apply font-bold tracking-tight;
}
@theme {
--color-primary-500: #22c55e;
--color-primary-600: #16a34a;
--color-primary-700: #15803d;
--color-secondary-50: #f8fafc;
--color-secondary-100: #f1f5f9;
--color-secondary-200: #e2e8f0;
--color-secondary-300: #cbd5e1;
--color-secondary-500: #64748b;
--color-secondary-600: #475569;
--color-secondary-700: #334155;
--color-secondary-800: #1e293b;
--color-secondary-900: #0f172a;
--color-accent-500: #eab308;
}
@layer components {
.btn-primary {
@apply inline-flex items-center justify-center px-6 py-3 bg-primary-600 text-white font-semibold rounded-lg
hover:bg-primary-700 transition-all duration-200 shadow-bold hover:shadow-industrial
active:translate-y-0.5;
}
.btn-secondary {
@apply inline-flex items-center justify-center px-6 py-3 bg-secondary-800 text-white font-semibold rounded-lg
hover:bg-secondary-900 transition-all duration-200;
}
.btn-outline {
@apply inline-flex items-center justify-center px-6 py-3 border-2 border-primary-600 text-primary-600 font-semibold rounded-lg
hover:bg-primary-600 hover:text-white transition-all duration-200;
}
.section-title {
@apply text-3xl md:text-4xl lg:text-5xl font-bold text-secondary-900;
}
.section-subtitle {
@apply text-lg md:text-xl text-secondary-600 mt-4;
}
.card {
@apply bg-white rounded-xl shadow-card overflow-hidden transition-all duration-300
hover:shadow-industrial hover:-translate-y-1;
}
.card-industrial {
@apply bg-secondary-800 text-white rounded-xl p-6 border-l-4 border-primary-500;
}
.gradient-overlay {
@apply absolute inset-0 bg-gradient-to-r from-industrial-dark/90 to-industrial-dark/70;
}
.industrial-badge {
@apply inline-flex items-center px-3 py-1 bg-primary-600 text-white text-sm font-semibold rounded;
}
}
html { font-family: 'Kanit', system-ui, sans-serif; scroll-behavior: smooth; }
body { background-color: white; color: var(--color-secondary-900); -webkit-font-smoothing: antialiased; }
h1, h2, h3, h4, h5, h6 { font-weight: 700; letter-spacing: -0.025em; }
@layer utilities {
.text-balance {
text-wrap: balance;
}
.text-gradient {
@apply bg-clip-text text-transparent bg-gradient-to-r from-primary-500 to-primary-700;
}
}
.btn-primary { display: inline-flex; align-items: center; justify-content: center; padding: 0.75rem 1.5rem; background-color: var(--color-primary-600); color: white; font-weight: 600; border-radius: 0.5rem; transition: all 0.2s; }
.btn-primary:hover { background-color: var(--color-primary-700); }
.btn-secondary { display: inline-flex; align-items: center; justify-content: center; padding: 0.75rem 1.5rem; background-color: var(--color-secondary-800); color: white; font-weight: 600; border-radius: 0.5rem; transition: all 0.2s; }
.btn-secondary:hover { background-color: var(--color-secondary-900); }
.btn-outline { display: inline-flex; align-items: center; justify-content: center; padding: 0.75rem 1.5rem; border: 2px solid var(--color-primary-600); color: var(--color-primary-600); font-weight: 600; border-radius: 0.5rem; background-color: transparent; transition: all 0.2s; }
.btn-outline:hover { background-color: var(--color-primary-600); color: white; }
/*
* Typography Best Practices:
* - Use .text-base (16px) as minimum for readable text
* - Avoid .text-xs (12px) and .text-sm (14px) for body content
* - For less emphasis, use color (text-secondary-600) not smaller size
*/
.section-title { font-size: 1.875rem; font-weight: 700; text-align: center; margin-bottom: 1rem; }
@media (min-width: 768px) { .section-title { font-size: 2.25rem; } }
@media (min-width: 1024px) { .section-title { font-size: 3rem; } }
.section-subtitle { font-size: 1.125rem; text-align: center; color: var(--color-secondary-600); }
.card { background-color: white; border-radius: 0.75rem; overflow: hidden; box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1); transition: all 0.3s; }
.card:hover { box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1); transform: translateY(-0.25rem); }

View File

@@ -1,133 +0,0 @@
// Site Configuration Types
export interface SiteConfig {
name: string;
nameTh: string;
url: string;
description: string;
phone: string;
email: string;
lineId: string;
facebookUrl: string;
address: string;
}
export interface WorkHours {
day: string;
hours: string;
isClosed?: boolean;
}
export interface ProductCategory {
id: string;
name: string;
nameEn: string;
slug: string;
href: string;
image: string;
description: string;
shortDescription?: string;
keywords?: string[];
// Enhanced SEO Content
seoContent?: string;
// Product Specifications
specifications?: ProductSpecification[];
// Key Features
features?: string[];
// Applications/Uses
applications?: string[];
// Standards & Certifications
certifications?: string[];
// FAQ Section
faq?: FAQItem[];
// Schema.org data
schemaData?: {
brand?: string;
manufacturer?: string;
sku?: string;
mpn?: string;
material?: string;
category?: string;
};
// Related product IDs
relatedProductIds?: string[];
// Product Tables (extracted from images)
productTables?: ProductTable[];
}
export interface ProductSpecification {
label: string;
value: string;
unit?: string;
}
export interface FAQItem {
question: string;
answer: string;
}
export interface ProductTable {
tableName: string;
headers: string[];
rows: string[][];
}
export interface NavItem {
label: string;
labelEn: string;
href: string;
children?: NavItem[];
}
// Blog Types
export interface BlogPost {
slug: string;
title: string;
excerpt: string;
content: string;
date: string;
author: string;
category: string;
image?: string;
}
export interface BlogCategory {
name: string;
slug: string;
count: number;
}
// Portfolio Types
export interface PortfolioItem {
id: string;
title: string;
category: string;
image: string;
description: string;
}
export interface PortfolioProject {
id: string;
name: string;
href: string;
image: string;
description: string;
}
// Contact Form Types
export interface ContactFormData {
name: string;
email: string;
phone: string;
subject: string;
message: string;
}
// SEO Types
export interface SEOData {
title: string;
description: string;
keywords?: string[];
ogImage?: string;
canonical?: string;
}