feat: Migrate Astro to root - Replace Next.js completely

- Removed Next.js project (src, app, components, etc.)
- Moved Astro from dealplustech-astro/ to root
- Updated Dockerfile for Astro root deployment
- All PDPA compliance features preserved:
  * Cookie consent banner
  * Consent logging API with SQLite
  * Admin dashboard (/admin/consent-logs)
  * Privacy Policy (Thai, PDPA-compliant)
  * Terms & Conditions (Thai)
- 15 pages: homepage, 6 products, 3 blog posts, legal pages, admin
- Build: 660ms, all pages generated successfully
This commit is contained in:
Kunthawat
2026-03-10 22:11:57 +07:00
parent 8ffcd8a266
commit 28c4f8d981
13646 changed files with 4003 additions and 1824470 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,247 @@
---
// Cookie Consent Banner Component - PDPA Compliant
// Displays on first visit, allows users to accept/reject cookie categories
---
<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 ease-in-out"
role="region"
aria-labelledby="consent-title"
aria-describedby="consent-description"
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">
<!-- Consent Content -->
<div class="flex-1">
<h2 id="consent-title" class="text-xl md:text-2xl font-bold text-secondary-900 mb-3">
เรายึดถือความเป็นส่วนตัวของคุณ
</h2>
<p id="consent-description" class="text-base md:text-lg text-secondary-700 leading-relaxed mb-4">
เราใช้คุกกี้เพื่อปรับปรุงประสบการณ์การใช้งาน วิเคราะห์การเข้าใช้งาน และแสดงเนื้อหาที่ตรงใจคุณ
คุณสามารถเลือกยอมรับหรือปฏิเสธคุกกี้ที่ไม่จำเป็นได้
</p>
<!-- Cookie Categories -->
<div class="space-y-3 mt-4">
<!-- Essential Cookies (Always On) -->
<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 accent-primary-600 rounded"
/>
<label for="consent-essential" class="flex-1 cursor-pointer">
<span class="font-semibold text-secondary-900">คุกกี้จำเป็น</span>
<span class="text-sm text-secondary-600 block">ใช้สำหรับการทำงานของเว็บไซต์ ไม่สามารถปิดได้</span>
</label>
<span class="text-xs px-2 py-1 bg-primary-600 text-white rounded">จำเป็น</span>
</div>
<!-- Analytics Cookies -->
<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 accent-primary-600 rounded consent-checkbox"
/>
<label for="consent-analytics" class="flex-1 cursor-pointer">
<span class="font-semibold text-secondary-900">คุกกี้วิเคราะห์ข้อมูล</span>
<span class="text-sm text-secondary-600 block">ช่วยให้เราเข้าใจพฤติกรรมการใช้งาน</span>
</label>
</div>
<!-- Marketing Cookies -->
<div class="flex items-center gap-3 bg-secondary-50 p-3 rounded-lg">
<input
type="checkbox"
id="consent-marketing"
class="w-5 h-5 accent-primary-600 rounded consent-checkbox"
/>
<label for="consent-marketing" class="flex-1 cursor-pointer">
<span class="font-semibold text-secondary-900">คุกกี้การตลาด</span>
<span class="text-sm text-secondary-600 block">ใช้สำหรับแสดงโฆษณาที่เกี่ยวข้อง</span>
</label>
</div>
</div>
</div>
<!-- Action Buttons -->
<div class="flex flex-col sm:flex-row gap-3 flex-shrink-0">
<button
id="consent-reject"
type="button"
class="bg-secondary-800 hover:bg-secondary-900 text-white px-6 py-3 rounded-lg font-semibold transition-colors"
>
ปฏิเสธทั้งหมด
</button>
<button
id="consent-accept"
type="button"
class="bg-primary-600 hover:bg-primary-700 text-white px-6 py-3 rounded-lg font-semibold transition-colors"
>
ยอมรับทั้งหมด
</button>
</div>
</div>
<!-- Privacy Policy Link -->
<div class="mt-6 pt-6 border-t border-secondary-200 text-center">
<p class="text-sm text-secondary-600">
การใช้งานคุกกี้ของเราเป็นไปตาม
<a href="/privacy-policy/" class="text-primary-600 hover:underline font-medium">นโยบายความเป็นส่วนตัว</a>
และ
<a href="/terms-and-conditions/" class="text-primary-600 hover:underline font-medium">ข้อกำหนดการใช้งาน</a>
</p>
</div>
</div>
</div>
<script>
interface ConsentPreferences {
essential: boolean;
analytics: boolean;
marketing: boolean;
timestamp: string;
policyVersion: string;
}
const POLICY_VERSION = '1.0.0';
const CONSENT_STORAGE_KEY = 'consent-preferences';
const CONSENT_LOG_API = '/api/consent';
function generateSessionId(): string {
return 'ses_' + Math.random().toString(36).substring(2, 15) +
Math.random().toString(36).substring(2, 15);
}
function getStoredConsent(): ConsentPreferences | null {
const stored = localStorage.getItem(CONSENT_STORAGE_KEY);
return stored ? JSON.parse(stored) : null;
}
async function saveConsent(consent: ConsentPreferences) {
localStorage.setItem(CONSENT_STORAGE_KEY, JSON.stringify(consent));
// Log consent to database (PDPA requirement)
try {
let sessionId = sessionStorage.getItem('consent_session_id');
if (!sessionId) {
sessionId = generateSessionId();
sessionStorage.setItem('consent_session_id', sessionId);
}
await fetch(CONSENT_LOG_API, {
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') as HTMLElement;
if (banner) {
banner.style.display = 'block';
setTimeout(() => {
banner.classList.remove('translate-y-full');
}, 10);
}
}
function hideBanner() {
const banner = document.getElementById('cookie-consent-banner') as HTMLElement;
if (banner) {
banner.classList.add('translate-y-full');
setTimeout(() => {
banner.style.display = 'none';
}, 300);
}
}
function initConsent() {
const stored = getStoredConsent();
if (stored) {
// Consent already given - restore checkboxes
const analyticsCheckbox = document.getElementById('consent-analytics') as HTMLInputElement;
const marketingCheckbox = document.getElementById('consent-marketing') as HTMLInputElement;
if (analyticsCheckbox) analyticsCheckbox.checked = stored.analytics;
if (marketingCheckbox) marketingCheckbox.checked = stored.marketing;
// Load Umami if consented
if (stored.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);
}
return;
}
// First visit - show banner after delay
setTimeout(showBanner, 500);
}
function handleAccept() {
const analytics = (document.getElementById('consent-analytics') as HTMLInputElement)?.checked ?? false;
const marketing = (document.getElementById('consent-marketing') as HTMLInputElement)?.checked ?? false;
const consent: ConsentPreferences = {
essential: true,
analytics: analytics || true,
marketing: marketing || true,
timestamp: new Date().toISOString(),
policyVersion: POLICY_VERSION,
};
saveConsent(consent);
hideBanner();
}
function handleReject() {
const consent: ConsentPreferences = {
essential: true,
analytics: false,
marketing: false,
timestamp: new Date().toISOString(),
policyVersion: POLICY_VERSION,
};
saveConsent(consent);
hideBanner();
}
// Event listeners
document.addEventListener('DOMContentLoaded', () => {
initConsent();
document.getElementById('consent-accept')?.addEventListener('click', handleAccept);
document.getElementById('consent-reject')?.addEventListener('click', handleReject);
});
// Expose function for footer link
(window as any).openConsentPreferences = () => {
showBanner();
};
</script>
<style>
/* Cookie consent banner styles */
#cookie-consent-banner {
bottom: 0;
left: 0;
right: 0;
}
</style>

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

@@ -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-pipe_000C.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/poloplast_000C.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/ppr-pipe_000C.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/syler_000C.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/ppr-pipe_000C.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/xylent_000C.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,47 @@
---
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="generator" content={Astro.generator} />
<meta name="description" content={description || 'บริษัท ดีล พลัส เทค จำกัด - ผู้เชี่ยวชาญด้านระบบน้ำ ท่อ PPR ตราช้าง ท่อพีพีอาร์ ท่อ HDPE'} />
<!-- Favicon -->
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="alternate icon" href="/favicon.ico" sizes="any" />
<link rel="apple-touch-icon" href="/favicon.svg" />
<!-- Google Fonts: Kanit for Thai -->
<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" />
<!-- SEO -->
<meta property="og:title" content={title} />
<meta property="og:description" content={description || 'Deal Plus Tech - ผู้เชี่ยวชาญด้านระบบน้ำ'} />
<meta property="og:image" content={image || '/og-image.jpg'} />
<meta property="og:type" content="website" />
<meta name="twitter:card" content="summary_large_image" />
<title>{title} | ดีล พลัส เทค</title>
</head>
<body class="flex flex-col min-h-screen">
<slot />
</body>
</html>
<style is:global>
html {
font-family: 'Kanit', system-ui, sans-serif;
}
</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,93 @@
---
import BaseLayout from '../../layouts/BaseLayout.astro';
let consentLogs: any[] = [];
let dbError: string | null = null;
let isAuthenticated = false;
const ADMIN_PASSWORD = 'changeme';
try {
if (Astro.request.method === 'POST') {
const formData = await Astro.request.formData();
const action = formData.get('action');
if (action === 'login') {
const password = formData.get('password');
if (password === ADMIN_PASSWORD) {
Astro.cookies.set('admin_auth', 'true', {
path: '/',
httpOnly: true,
secure: import.meta.env.PROD,
maxAge: 60 * 60 * 2
});
isAuthenticated = true;
}
} else if (action === 'logout') {
Astro.cookies.delete('admin_auth', { path: '/' });
isAuthenticated = false;
}
}
const authCookie = Astro.cookies.get('admin_auth')?.value;
if (authCookie === 'true') {
isAuthenticated = true;
}
// Database will be accessed at runtime, not build time
// This page is static, so we just show login form
} catch (error) {
dbError = 'Admin authentication error';
}
---
<BaseLayout title="Consent Logs Admin" description="Admin dashboard for viewing consent logs">
<main class="min-h-screen bg-secondary-50 py-12">
<div class="container mx-auto px-4 max-w-7xl">
<div class="bg-white rounded-2xl shadow-lg p-6 md:p-8">
<h1 class="text-3xl font-bold text-secondary-900 mb-2">Consent Logs Admin</h1>
<p class="text-secondary-600 mb-8">View and manage user consent records (PDPA compliance)</p>
{dbError && (
<div class="mb-6 p-4 bg-yellow-50 border-l-4 border-yellow-500 rounded">
<p class="text-yellow-800">{dbError}</p>
</div>
)}
{!isAuthenticated ? (
<div class="max-w-md mx-auto">
<div class="bg-secondary-50 rounded-xl p-6 border-2 border-secondary-200">
<h2 class="text-xl font-bold text-secondary-900 mb-4">Admin Login</h2>
<p class="text-sm text-secondary-600 mb-4">Default password: changeme</p>
<form method="POST" class="space-y-4">
<input type="hidden" name="action" value="login" />
<div>
<label for="password" class="block text-sm font-semibold text-secondary-700 mb-2">Password</label>
<input
type="password"
id="password"
name="password"
class="w-full px-4 py-2 border-2 border-secondary-300 rounded-lg focus:outline-none focus:border-primary-600"
required
/>
</div>
<button type="submit" class="bg-primary-600 hover:bg-primary-700 text-white px-4 py-2 rounded-lg w-full">
Login
</button>
</form>
</div>
</div>
) : (
<div>
<div class="mb-6 p-4 bg-green-50 border-l-4 border-green-500 rounded">
<p class="text-green-800">Logged in. Database will be initialized on first consent submission.</p>
</div>
<form method="POST">
<input type="hidden" name="action" value="logout" />
<button type="submit" class="text-secondary-600 hover:text-secondary-800">Logout</button>
</form>
</div>
)}
</div>
</div>
</main>
</BaseLayout>

View File

@@ -0,0 +1,92 @@
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 NOT NULL DEFAULT 1,
analytics INTEGER NOT NULL DEFAULT 0,
marketing INTEGER NOT NULL 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 required fields' }), {
status: 400,
headers: { 'Content-Type': 'application/json' },
});
}
const ip = request.headers.get('x-forwarded-for') || 'unknown';
const ipHash = await hashIP(ip);
const userAgent = request.headers.get('user-agent') || 'unknown';
const acceptLanguage = request.headers.get('accept-language') || 'th';
const locale = acceptLanguage.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,
headers: { 'Content-Type': 'application/json' },
});
} catch (error) {
console.error('Consent API error:', error);
return new Response(JSON.stringify({ error: 'Internal server error' }), {
status: 500,
headers: { 'Content-Type': 'application/json' },
});
}
};
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));
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
return hashHex.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>

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

@@ -0,0 +1,186 @@
---
import BaseLayout from '../layouts/BaseLayout.astro';
import FloatingContact from '../components/FloatingContact.astro';
import { productCategories } from '../data/site-config';
// Featured products - key products for homepage
const featuredProducts = productCategories.filter(p =>
['ppr-elephant', 'hdpe', 'poloplast', 'syler', 'xylent'].includes(p.id)
).slice(0, 6);
---
<BaseLayout title="หน้าแรก" description="บริษัท ดีล พลัส เทค จำกัด - ผู้เชี่ยวชาญด้านระบบท่อและ HVAC">
<main>
<!-- Hero Section -->
<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" />
<img
src="/images/2021/03/ppr-pipe_000C.jpg"
alt="ท่อพีพีอาร์คุณภาพสูง"
class="absolute inset-0 w-full h-full object-cover opacity-50"
loading="eager"
/>
<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 leading-tight">
วัสดุท่อ อุปกรณ์ HVAC
<span class="text-primary-400 block">และฉนวนหุ้มท่อ</span>
</h1>
<p class="text-lg md:text-xl text-secondary-200 mb-8">
จำหน่ายและติดตั้งท่อ PPR, ท่อ HDPE, กริลแอร์, เทอร์โมเบรค และอุปกรณ์ระบบท่อครบวงจร พร้อมบริการให้คำปรึกษาจากทีมมืออาชีพ
</p>
<div class="flex flex-wrap 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>
<!-- Features Section -->
<section class="py-16 bg-secondary-800">
<div class="container mx-auto px-4">
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
<div class="text-center p-6">
<div class="w-16 h-16 bg-primary-600 rounded-lg flex items-center justify-center mx-auto mb-4">
<svg class="w-8 h-8 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width={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 class="text-xl font-bold text-white mb-2">สินค้าคุณภาพ</h3>
<p class="text-secondary-300">
สินค้าทุกชิ้นผ่านมาตรฐานคุณภาพ พร้อมรับประกัน
</p>
</div>
<div class="text-center p-6">
<div class="w-16 h-16 bg-primary-600 rounded-lg flex items-center justify-center mx-auto mb-4">
<svg class="w-8 h-8 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width={2} d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
</div>
<h3 class="text-xl font-bold text-white mb-2">จัดส่งรวดเร็ว</h3>
<p class="text-secondary-300">
จัดส่งสินค้าทั่วประเทศ รวดเร็วและปลอดภัย
</p>
</div>
<div class="text-center p-6">
<div class="w-16 h-16 bg-primary-600 rounded-lg flex items-center justify-center mx-auto mb-4">
<svg class="w-8 h-8 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width={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 class="text-xl font-bold text-white mb-2">บริการหลังการขาย</h3>
<p class="text-secondary-300">
ทีมงานพร้อมให้คำปรึกษาและดูแลอย่างต่อเนื่อง
</p>
</div>
</div>
</div>
</section>
<!-- Featured Products -->
<section class="py-16 bg-secondary-50">
<div class="container mx-auto px-4">
<div class="text-center mb-12">
<h2 class="text-3xl md:text-4xl font-bold text-secondary-900 mb-4">
สินค้า<span class="text-primary-600">เด่น</span>
</h2>
<p class="text-secondary-600 text-lg">ผลิตภัณฑ์คุณภาพสูงที่ได้รับความนิยม</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{featuredProducts.map((product) => (
<a href={product.href} class="card group">
<div class="relative aspect-video bg-secondary-100 overflow-hidden">
<img
src={product.image}
alt={product.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">
{product.name}
</h3>
<p class="mt-2 text-sm text-secondary-600 line-clamp-2">
{product.shortDescription || product.description}
</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>
))}
</div>
<div class="text-center mt-12">
<a href="/products/" class="btn-primary">
ดูสินค้าทั้งหมด
</a>
</div>
</div>
</section>
<!-- About Preview -->
<section class="py-16 bg-white">
<div class="container mx-auto px-4">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
<div>
<h2 class="text-3xl md:text-4xl font-bold text-secondary-900 mb-6">
เกี่ยวกับ<span class="text-primary-600">เรา</span>
</h2>
<p class="text-lg text-secondary-600 mb-6">
บริษัท ดีล พลัส เทค จำกัด เราเป็นผู้เชียวชาญด้านระบบน้ำ ให้คำแนะนำและจำหน่ายท่อ PPR ตราช้าง ท่อพีพีอาร์ ท่อ PPR ท่อ HDPE Thai PPR คุณภาพสูง ราคาถูก
</p>
<p class="text-secondary-700 mb-8">
ด้วยประสบการณ์ยาวนาน เราพร้อมให้บริการสินค้าคุณภาพและคำแนะนำจากผู้เชี่ยวชาญ เพื่อให้งานระบบของคุณมีประสิทธิภาพสูงสุด
</p>
<a href="/about-us/" class="btn-secondary">
อ่านเพิ่มเติม
</a>
</div>
<div class="relative aspect-video bg-secondary-100 rounded-xl overflow-hidden">
<img
src="/images/2021/03/ppr-pipe_000C.jpg"
alt="เกี่ยวกับดีลพลัสเทค"
class="object-cover w-full h-full"
loading="lazy"
/>
</div>
</div>
</div>
</section>
<!-- CTA Section -->
<section class="py-16 bg-primary-600">
<div class="container mx-auto px-4 text-center">
<h2 class="text-3xl md:text-4xl font-bold text-white mb-4">
สนใจสินค้าหรือต้องการคำปรึกษา?
</h2>
<p class="text-xl text-primary-100 mb-8 max-w-2xl mx-auto">
ทีมงานของเราพร้อมให้คำแนะนำและช่วยคุณเลือกสินค้าที่เหมาะสมที่สุด
</p>
<div class="flex flex-wrap justify-center gap-4">
<a href="tel:090-555-1415" class="btn-secondary bg-white text-primary-600 hover:bg-primary-50">
โทร: 090-555-1415
</a>
<a href="https://line.me/ti/p/@dealplustech" target="_blank" rel="noopener" class="btn-outline border-white text-white hover:bg-white hover:text-primary-600">
เพิ่มเพื่อน LINE
</a>
</div>
</div>
</section>
</main>
<FloatingContact />
</BaseLayout>

View File

@@ -0,0 +1,176 @@
---
import BaseLayout from '../layouts/BaseLayout.astro';
const POLICY_VERSION = '1.0.0';
const LAST_UPDATED = '2026-03-10';
---
<BaseLayout title="นโยบายความเป็นส่วนตัว" description="นโยบายความเป็นส่วนตัวของดีล พลัส เทค ตาม พ.ร.บ. คุ้มครองข้อมูลส่วนบุคคล พ.ศ. 2562 (PDPA)">
<main class="min-h-screen py-12 bg-secondary-50">
<article class="container mx-auto px-4 max-w-4xl">
<div class="bg-white rounded-2xl shadow-lg p-6 md:p-12">
<header class="mb-12 pb-8 border-b-2 border-secondary-200">
<h1 class="text-4xl font-bold text-secondary-900 mb-4">นโยบายความเป็นส่วนตัว</h1>
<p class="text-lg text-secondary-600">Privacy Policy (Personal Data Protection Policy)</p>
<div class="mt-4 text-sm text-secondary-500">
<p>Version: {POLICY_VERSION}</p>
<p>Last Updated: {new Date(LAST_UPDATED).toLocaleDateString('th-TH', { year: 'numeric', month: 'long', day: 'numeric' })}</p>
</div>
</header>
<section class="mb-12">
<h2 class="text-2xl font-bold text-secondary-900 mb-6">1. ข้อมูลของผู้ควบคุมข้อมูลส่วนบุคคล</h2>
<div class="bg-secondary-50 rounded-xl p-6 border-l-4 border-primary-600">
<p class="text-secondary-700 mb-4">
<strong class="text-secondary-900">บริษัท ดีล พลัส เทค จำกัด</strong> เป็นผู้ควบคุมข้อมูลส่วนบุคคล ตาม พ.ร.บ. คุ้มครองข้อมูลส่วนบุคคล พ.ศ. 2562 (PDPA)
</p>
<ul class="space-y-2 text-secondary-700">
<li><strong>ที่อยู่:</strong> 9/70 ซอยนครลุง 17 แขวงบางไผ่ เขตบางแค กทม. 10160</li>
<li><strong>โทรศัพท์:</strong> 090-555-1415</li>
<li><strong>อีเมล:</strong> info@dealplustech.co.th</li>
</ul>
</div>
</section>
<section class="mb-12">
<h2 class="text-2xl font-bold text-secondary-900 mb-6">2. ประเภทของข้อมูลที่เก็บรวบรวม</h2>
<ul class="space-y-3">
<li class="flex items-start gap-3">
<span class="text-primary-600 mt-1">✓</span>
<div>
<strong class="text-secondary-900">ข้อมูลประจำตัว:</strong>
<span class="text-secondary-700"> ชื่อ, นามสกุล, ที่อยู่อีเมล, เบอร์โทรศัพท์</span>
</div>
</li>
<li class="flex items-start gap-3">
<span class="text-primary-600 mt-1">✓</span>
<div>
<strong class="text-secondary-900">ข้อมูลการใช้งาน:</strong>
<span class="text-secondary-700"> IP Address, ข้อมูลเบราว์เซอร์, อุปกรณ์ที่ใช้</span>
</div>
</li>
<li class="flex items-start gap-3">
<span class="text-primary-600 mt-1">✓</span>
<div>
<strong class="text-secondary-900">ข้อมูลคุกกี้:</strong>
<span class="text-secondary-700"> การตั้งค่าคุกกี้, Session ID</span>
</div>
</li>
</ul>
</section>
<section class="mb-12">
<h2 class="text-2xl font-bold text-secondary-900 mb-6">3. วัตถุประสงค์ในการประมวลผลข้อมูล</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="bg-secondary-50 p-4 rounded-lg">
<strong class="text-secondary-900 block mb-1">การให้บริการ</strong>
<span class="text-secondary-600 text-sm">ตอบสนองคำขอ, ให้บริการลูกค้า</span>
</div>
<div class="bg-secondary-50 p-4 rounded-lg">
<strong class="text-secondary-900 block mb-1">การติดต่อกลับ</strong>
<span class="text-secondary-600 text-sm">ตอบคำถาม, ให้ข้อมูลผลิตภัณฑ์</span>
</div>
<div class="bg-secondary-50 p-4 rounded-lg">
<strong class="text-secondary-900 block mb-1">การวิเคราะห์</strong>
<span class="text-secondary-600 text-sm">ปรับปรุงเว็บไซต์, ประสบการณ์ผู้ใช้</span>
</div>
<div class="bg-secondary-50 p-4 rounded-lg">
<strong class="text-secondary-900 block mb-1">ตามกฎหมาย</strong>
<span class="text-secondary-600 text-sm">ปฏิบัติตามข้อบังคับทางกฎหมาย</span>
</div>
</div>
</section>
<section class="mb-12">
<h2 class="text-2xl font-bold text-secondary-900 mb-6">4. ฐานกฎหมายในการประมวลผลข้อมูล</h2>
<div class="space-y-4">
<div class="border-l-4 border-primary-600 pl-6">
<h3 class="font-bold text-secondary-900 mb-2">4.1 การยินยอม (Consent)</h3>
<p class="text-secondary-700">สำหรับการใช้คุกกี้ที่ไม่จำเป็น, การตลาด</p>
</div>
<div class="border-l-4 border-primary-600 pl-6">
<h3 class="font-bold text-secondary-900 mb-2">4.2 การ履行合同 (Contract)</h3>
<p class="text-secondary-700">เพื่อการให้บริการและดำเนินการตามคำขอของคุณ</p>
</div>
<div class="border-l-4 border-primary-600 pl-6">
<h3 class="font-bold text-secondary-900 mb-2">4.3 ข้อบังคับทางกฎหมาย (Legal Obligation)</h3>
<p class="text-secondary-700">เพื่อปฏิบัติตามกฎหมายและระเบียบที่เกี่ยวข้อง</p>
</div>
</div>
</section>
<section class="mb-12">
<h2 class="text-2xl font-bold text-secondary-900 mb-6">5. ระยะเวลาการเก็บรักษาข้อมูล</h2>
<div class="bg-primary-50 rounded-xl p-6 border-2 border-primary-200">
<ul class="space-y-3">
<li class="flex justify-between items-center">
<span class="text-secondary-700">ข้อมูลการใช้งานเว็บไซต์</span>
<span class="font-semibold text-primary-900">10 ปี</span>
</li>
<li class="flex justify-between items-center">
<span class="text-secondary-700">บันทึกการยินยอมคุกกี้</span>
<span class="font-semibold text-primary-900">10 ปี</span>
</li>
<li class="flex justify-between items-center">
<span class="text-secondary-700">ข้อมูลติดต่อลูกค้า</span>
<span class="font-semibold text-primary-900">5 ปี</span>
</li>
</ul>
</div>
</section>
<section class="mb-12">
<h2 class="text-2xl font-bold text-secondary-900 mb-6">6. สิทธิของเจ้าของข้อมูลส่วนบุคคล</h2>
<p class="text-secondary-700 mb-4">ภายใต้ PDPA คุณมีสิทธิดังนี้:</p>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="bg-secondary-50 p-4 rounded-lg">
<h3 class="font-bold text-secondary-900 mb-2">สิทธิขอเข้าถึง</h3>
<p class="text-sm text-secondary-600">ขอรับสำเนาข้อมูลส่วนบุคคล</p>
</div>
<div class="bg-secondary-50 p-4 rounded-lg">
<h3 class="font-bold text-secondary-900 mb-2">สิทธิขอแก้ไข</h3>
<p class="text-sm text-secondary-600">ขอให้แก้ไขข้อมูลที่ไม่ถูกต้อง</p>
</div>
<div class="bg-secondary-50 p-4 rounded-lg">
<h3 class="font-bold text-secondary-900 mb-2">สิทธิขอลบ</h3>
<p class="text-sm text-secondary-600">ขอให้ลบข้อมูลส่วนบุคคล</p>
</div>
<div class="bg-secondary-50 p-4 rounded-lg">
<h3 class="font-bold text-secondary-900 mb-2">สิทธิเพิกถอน</h3>
<p class="text-sm text-secondary-600">เพิกถอนความยินยอมเมื่อใดก็ได้</p>
</div>
</div>
</section>
<section class="mb-12">
<h2 class="text-2xl font-bold text-secondary-900 mb-6">7. คุกกี้และเทคโนโลยีการติดตาม</h2>
<ul class="space-y-4">
<li class="bg-secondary-50 p-4 rounded-lg border-l-4 border-primary-600">
<h3 class="font-bold text-secondary-900 mb-2">คุกกี้จำเป็น (Essential Cookies)</h3>
<p class="text-sm text-secondary-600">จำเป็นสำหรับการทำงานของเว็บไซต์ ไม่สามารถปิดได้</p>
</li>
<li class="bg-secondary-50 p-4 rounded-lg border-l-4 border-accent-500">
<h3 class="font-bold text-secondary-900 mb-2">คุกกี้วิเคราะห์ (Analytics Cookies)</h3>
<p class="text-sm text-secondary-600">帮助我们了解网站使用情况 (Umami Analytics)</p>
</li>
<li class="bg-secondary-50 p-4 rounded-lg border-l-4 border-secondary-400">
<h3 class="font-bold text-secondary-900 mb-2">คุกกี้การตลาด (Marketing Cookies)</h3>
<p class="text-sm text-secondary-600">ใช้สำหรับแสดงโฆษณาที่เกี่ยวข้อง</p>
</li>
</ul>
</section>
<section class="mb-12">
<h2 class="text-2xl font-bold text-secondary-900 mb-6">8. การติดต่อ</h2>
<div class="bg-primary-50 rounded-xl p-6 border-2 border-primary-200">
<p class="text-secondary-700 mb-4">หากคุณมีคำถามหรือต้องการใช้สิทธิของคุณ กรุณาติดต่อ:</p>
<div class="space-y-2">
<p class="text-secondary-900"><strong>อีเมล:</strong> info@dealplustech.co.th</p>
<p class="text-secondary-900"><strong>โทรศัพท์:</strong> 090-555-1415</p>
</div>
</div>
</section>
</div>
</article>
</main>
</BaseLayout>

View File

@@ -0,0 +1,89 @@
---
import { getCollection, render } from 'astro:content';
import BaseLayout from '../../layouts/BaseLayout.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.name} description={product.data.shortDescription || product.data.description}>
<main class="py-12">
<article class="container mx-auto px-4 max-w-7xl">
<!-- Product Header -->
<header class="mb-12">
<h1 class="text-4xl md:text-5xl lg:text-6xl xl:text-7xl font-bold text-secondary-900 mb-6">
{product.data.name}
</h1>
<p class="text-lg md:text-xl lg:text-2xl xl:text-3xl text-secondary-600 max-w-4xl">
{product.data.description}
</p>
</header>
<!-- Content from Markdown -->
<div class="prose prose-lg md:prose-xl lg:prose-2xl max-w-none mb-12">
<Content />
</div>
<!-- Product Tables Section -->
{productTables.length > 0 && (
<section class="mb-12">
<h2 class="text-3xl md:text-4xl lg:text-5xl font-bold text-secondary-900 mb-8 flex items-center gap-3">
<svg class="w-8 h-8 md:w-10 md:h-10 lg:w-12 lg:h-12 text-primary-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width={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 class="space-y-10">
{productTables.map((table, tableIndex) => (
<div class="bg-white rounded-2xl border-2 border-secondary-200 shadow-lg">
<h3 class="text-lg md:text-xl lg:text-2xl font-semibold text-secondary-800 p-4 md:p-6 bg-secondary-50 border-b-2 border-secondary-200">
{table.tableName}
</h3>
<div class="w-full overflow-x-auto -mx-2 md:mx-0">
<div class="px-2 md:px-0">
<table class="w-full min-w-[600px] border-collapse">
<thead>
<tr class="bg-primary-100">
{table.headers.map((header, headerIndex) => (
<th class="px-3 py-2 md:px-4 md:py-3 text-left text-xs md:text-sm lg:text-base font-bold text-primary-800 border-b-2 border-primary-300 whitespace-nowrap">
{header}
</th>
))}
</tr>
</thead>
<tbody>
{table.rows.map((row, rowIndex) => (
<tr class={rowIndex % 2 === 0 ? 'bg-white' : 'bg-secondary-50'}>
{row.map((cell, cellIndex) => (
<td class="px-3 py-2 md:px-4 md:py-3 text-xs md:text-sm lg:text-base text-secondary-700 border-b border-secondary-100 break-words max-w-[200px] md:max-w-none">
{cell}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
))}
</div>
</section>
)})}

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

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;
}