♻️ Restructure: Move Astro to repository root

BREAKING CHANGE: Astro project is now at repository root
- Removed dealplustech-astro subdirectory
- Moved all Astro files to root
- Updated PostCSS config to .cjs
- Removed old Next.js files

 11 pages built successfully
 Cookie consent banner included
 Privacy/Terms links in footer
 Ready for Easypanel deployment (no root dir needed)

Migration path:
- Old structure: /dealplustech-astro/
- New structure: / (root)
This commit is contained in:
Kunthawat Greethong
2026-03-09 22:00:05 +07:00
parent 5b041a6a44
commit 7a67f68d9f
16524 changed files with 4277 additions and 1983574 deletions

View File

@@ -1 +0,0 @@
export default new Map();

View File

@@ -1 +0,0 @@
export default new Map();

View File

@@ -1,207 +0,0 @@
declare module 'astro:content' {
export interface RenderResult {
Content: import('astro/runtime/server/index.js').AstroComponentFactory;
headings: import('astro').MarkdownHeading[];
remarkPluginFrontmatter: Record<string, any>;
}
interface Render {
'.md': Promise<RenderResult>;
}
export interface RenderedContent {
html: string;
metadata?: {
imagePaths: Array<string>;
[key: string]: unknown;
};
}
}
declare module 'astro:content' {
type Flatten<T> = T extends { [K: string]: infer U } ? U : never;
export type CollectionKey = keyof AnyEntryMap;
export type CollectionEntry<C extends CollectionKey> = Flatten<AnyEntryMap[C]>;
export type ContentCollectionKey = keyof ContentEntryMap;
export type DataCollectionKey = keyof DataEntryMap;
type AllValuesOf<T> = T extends any ? T[keyof T] : never;
type ValidContentEntrySlug<C extends keyof ContentEntryMap> = AllValuesOf<
ContentEntryMap[C]
>['slug'];
export type ReferenceDataEntry<
C extends CollectionKey,
E extends keyof DataEntryMap[C] = string,
> = {
collection: C;
id: E;
};
export type ReferenceContentEntry<
C extends keyof ContentEntryMap,
E extends ValidContentEntrySlug<C> | (string & {}) = string,
> = {
collection: C;
slug: E;
};
export type ReferenceLiveEntry<C extends keyof LiveContentConfig['collections']> = {
collection: C;
id: string;
};
/** @deprecated Use `getEntry` instead. */
export function getEntryBySlug<
C extends keyof ContentEntryMap,
E extends ValidContentEntrySlug<C> | (string & {}),
>(
collection: C,
// Note that this has to accept a regular string too, for SSR
entrySlug: E,
): E extends ValidContentEntrySlug<C>
? Promise<CollectionEntry<C>>
: Promise<CollectionEntry<C> | undefined>;
/** @deprecated Use `getEntry` instead. */
export function getDataEntryById<C extends keyof DataEntryMap, E extends keyof DataEntryMap[C]>(
collection: C,
entryId: E,
): Promise<CollectionEntry<C>>;
export function getCollection<C extends keyof AnyEntryMap, E extends CollectionEntry<C>>(
collection: C,
filter?: (entry: CollectionEntry<C>) => entry is E,
): Promise<E[]>;
export function getCollection<C extends keyof AnyEntryMap>(
collection: C,
filter?: (entry: CollectionEntry<C>) => unknown,
): Promise<CollectionEntry<C>[]>;
export function getLiveCollection<C extends keyof LiveContentConfig['collections']>(
collection: C,
filter?: LiveLoaderCollectionFilterType<C>,
): Promise<
import('astro').LiveDataCollectionResult<LiveLoaderDataType<C>, LiveLoaderErrorType<C>>
>;
export function getEntry<
C extends keyof ContentEntryMap,
E extends ValidContentEntrySlug<C> | (string & {}),
>(
entry: ReferenceContentEntry<C, E>,
): E extends ValidContentEntrySlug<C>
? Promise<CollectionEntry<C>>
: Promise<CollectionEntry<C> | undefined>;
export function getEntry<
C extends keyof DataEntryMap,
E extends keyof DataEntryMap[C] | (string & {}),
>(
entry: ReferenceDataEntry<C, E>,
): E extends keyof DataEntryMap[C]
? Promise<DataEntryMap[C][E]>
: Promise<CollectionEntry<C> | undefined>;
export function getEntry<
C extends keyof ContentEntryMap,
E extends ValidContentEntrySlug<C> | (string & {}),
>(
collection: C,
slug: E,
): E extends ValidContentEntrySlug<C>
? Promise<CollectionEntry<C>>
: Promise<CollectionEntry<C> | undefined>;
export function getEntry<
C extends keyof DataEntryMap,
E extends keyof DataEntryMap[C] | (string & {}),
>(
collection: C,
id: E,
): E extends keyof DataEntryMap[C]
? string extends keyof DataEntryMap[C]
? Promise<DataEntryMap[C][E]> | undefined
: Promise<DataEntryMap[C][E]>
: Promise<CollectionEntry<C> | undefined>;
export function getLiveEntry<C extends keyof LiveContentConfig['collections']>(
collection: C,
filter: string | LiveLoaderEntryFilterType<C>,
): Promise<import('astro').LiveDataEntryResult<LiveLoaderDataType<C>, LiveLoaderErrorType<C>>>;
/** Resolve an array of entry references from the same collection */
export function getEntries<C extends keyof ContentEntryMap>(
entries: ReferenceContentEntry<C, ValidContentEntrySlug<C>>[],
): Promise<CollectionEntry<C>[]>;
export function getEntries<C extends keyof DataEntryMap>(
entries: ReferenceDataEntry<C, keyof DataEntryMap[C]>[],
): Promise<CollectionEntry<C>[]>;
export function render<C extends keyof AnyEntryMap>(
entry: AnyEntryMap[C][string],
): Promise<RenderResult>;
export function reference<C extends keyof AnyEntryMap>(
collection: C,
): import('astro/zod').ZodEffects<
import('astro/zod').ZodString,
C extends keyof ContentEntryMap
? ReferenceContentEntry<C, ValidContentEntrySlug<C>>
: ReferenceDataEntry<C, keyof DataEntryMap[C]>
>;
// Allow generic `string` to avoid excessive type errors in the config
// if `dev` is not running to update as you edit.
// Invalid collection names will be caught at build time.
export function reference<C extends string>(
collection: C,
): import('astro/zod').ZodEffects<import('astro/zod').ZodString, never>;
type ReturnTypeOrOriginal<T> = T extends (...args: any[]) => infer R ? R : T;
type InferEntrySchema<C extends keyof AnyEntryMap> = import('astro/zod').infer<
ReturnTypeOrOriginal<Required<ContentConfig['collections'][C]>['schema']>
>;
type ContentEntryMap = {
};
type DataEntryMap = {
"blog": Record<string, {
id: string;
body?: string;
collection: "blog";
data: any;
rendered?: RenderedContent;
filePath?: string;
}>;
};
type AnyEntryMap = ContentEntryMap & DataEntryMap;
type ExtractLoaderTypes<T> = T extends import('astro/loaders').LiveLoader<
infer TData,
infer TEntryFilter,
infer TCollectionFilter,
infer TError
>
? { data: TData; entryFilter: TEntryFilter; collectionFilter: TCollectionFilter; error: TError }
: { data: never; entryFilter: never; collectionFilter: never; error: never };
type ExtractDataType<T> = ExtractLoaderTypes<T>['data'];
type ExtractEntryFilterType<T> = ExtractLoaderTypes<T>['entryFilter'];
type ExtractCollectionFilterType<T> = ExtractLoaderTypes<T>['collectionFilter'];
type ExtractErrorType<T> = ExtractLoaderTypes<T>['error'];
type LiveLoaderDataType<C extends keyof LiveContentConfig['collections']> =
LiveContentConfig['collections'][C]['schema'] extends undefined
? ExtractDataType<LiveContentConfig['collections'][C]['loader']>
: import('astro/zod').infer<
Exclude<LiveContentConfig['collections'][C]['schema'], undefined>
>;
type LiveLoaderEntryFilterType<C extends keyof LiveContentConfig['collections']> =
ExtractEntryFilterType<LiveContentConfig['collections'][C]['loader']>;
type LiveLoaderCollectionFilterType<C extends keyof LiveContentConfig['collections']> =
ExtractCollectionFilterType<LiveContentConfig['collections'][C]['loader']>;
type LiveLoaderErrorType<C extends keyof LiveContentConfig['collections']> = ExtractErrorType<
LiveContentConfig['collections'][C]['loader']
>;
export type ContentConfig = typeof import("../src/content.config.mjs");
export type LiveContentConfig = never;
}

View File

@@ -1,2 +0,0 @@
/// <reference types="astro/client" />
/// <reference path="content.d.ts" />

View File

@@ -1,14 +0,0 @@
# Astro Production Dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
RUN npm run build
EXPOSE 4321
CMD ["npm", "run", "preview", "--", "--host", "0.0.0.0", "--port", "4321"]

View File

@@ -1,7 +0,0 @@
node_modules
dist
.git
.gitignore
*.md
.env*
!package*.json

View File

@@ -1 +0,0 @@
.node-version

View File

@@ -1,221 +0,0 @@
# 🔬 Easypanel API Testing Results
## ✅ What Works
### 1. Authentication
```bash
curl -H "Authorization: Bearer TOKEN" \
http://110.164.146.46:3000/api/trpc/setup.getStatus
# ✅ Returns: {"result":{"data":{"json":{"isComplete":true}}}}
```
### 2. List Projects
```bash
curl -H "Authorization: Bearer TOKEN" \
http://110.164.146.46:3000/api/trpc/projects.listProjectsAndServices
# ✅ Returns project list including "customerwebsite"
```
### 3. Inspect Service
```bash
curl -H "Authorization: Bearer TOKEN" \
http://110.164.146.46:3000/api/trpc/services.app.inspectService
# ✅ Works when service exists
```
---
## ❌ What Fails - Service Creation
### Endpoint
```
POST /api/trpc/services.app.createService
```
### Schema (from OpenAPI)
```json
{
"input": {
"json": {
"projectName": "customerwebsite",
"serviceName": "my-app",
"source": {
"type": "image" | "github",
// For "image":
"image": "nginx:alpine",
"username": "...",
"password": "...",
// For "github":
"owner": "...",
"repo": "...",
"ref": "main",
"path": "..."
}
}
}
}
```
### Attempts & Errors
#### Attempt 1: Basic Image
```json
{
"input": {
"json": {
"projectName": "customerwebsite",
"serviceName": "test",
"source": {
"type": "image",
"image": "nginx:alpine",
"port": 80
}
}
}
}
```
**Result:** ❌ 500 Error
```json
{
"error": {
"json": {
"message": "[{\"code\":\"invalid_type\",\"expected\":\"object\",\"received\":\"undefined\",\"path\":[],\"message\":\"Required\"}]"
}
}
}
```
#### Attempt 2: With Git Source
```json
{
"input": {
"json": {
"projectName": "customerwebsite",
"serviceName": "test-git",
"source": {
"type": "git",
"repository": "http://...",
"branch": "main"
}
}
}
}
```
**Result:** ❌ Same "Required" error
#### Attempt 3: Using `project` instead of `projectName`
```json
{
"input": {
"json": {
"project": "customerwebsite",
"name": "test"
}
}
}
```
**Result:** ❌ Same error
---
## 🔍 Analysis
The Zod validation error `"expected":"object","received":"undefined","path":[]` suggests:
1. **Missing required field** at root level
2. **Schema mismatch** - the API expects additional fields not in OpenAPI spec
3. **Possible tRPC format issue** - Easypanel might use a different input format
---
## 💡 Hypothesis
Easypanel's `createService` endpoint might require:
### Option A: Additional Required Fields
```json
{
"input": {
"json": {
"projectName": "...",
"serviceName": "...",
"source": {...},
// Missing required fields:
"environmentVariables": [],
"domains": [],
"ports": [],
"mounts": []
}
}
}
```
### Option B: Different tRPC Format
```json
{
"type": "query",
"input": {...}
}
```
### Option C: Requires Project ID (not name)
```json
{
"input": {
"json": {
"project": "cmkw22b00000007tu4gim48q9", // Actual ID
"name": "test"
}
}
}
```
---
## 📋 Next Steps to Debug
### 1. Capture Browser Network Traffic
1. Open Easypanel dashboard
2. Open DevTools → Network tab
3. Create a service manually
4. Inspect the exact API request
5. Copy request payload
### 2. Test with cURL
Use the exact payload from browser
### 3. Alternative: Use Easypanel CLI (if exists)
Check if Easypanel provides official CLI
### 4. Contact Easypanel Support
Ask for correct API schema for `services.app.createService`
---
## 🎯 Current Recommendation
Until API is figured out, use **manual creation**:
1. **Create service via dashboard** (2 minutes)
2. **Copy service ID**
3. **Register with skill**: `./deploy.sh register SERVICE_ID`
4. **Future updates**: Automated via `./deploy.sh update`
This is still 80% automated!
---
## 📞 Need Your Help
**Can you:**
1. Open browser DevTools when creating service in Easypanel
2. Copy the exact API request payload
3. Share it so I can update the skill?
Or if you know the correct schema, let me know!
---
**Status:** ⚠️ API schema unclear
**Workaround:** Manual creation + automated updates
**Automation Level:** 80% (will be 100% with correct schema)

View File

@@ -1,132 +0,0 @@
# 🚀 Fully Automated Deployment - v5.0
## ✅ Complete Workflow
The skill now automates ALL steps:
### Step 1: Create Service
```bash
POST /api/trpc/services.app.createService
{
"json": {
"projectName": "customerwebsite",
"domains": [{"host": "$(EASYPANEL_DOMAIN)"}],
"serviceName": "dealplustech-astro"
}
}
```
### Step 2: Configure Git
```bash
POST /api/trpc/services.app.updateSourceGit
{
"json": {
"projectName": "customerwebsite",
"serviceName": "dealplustech-astro",
"repo": "http://110.164.146.46:3001/dealplustech/dealplustech-astro.git",
"ref": "main",
"path": "/"
}
}
```
### Step 3: Set Build Type
```bash
POST /api/trpc/services.app.updateBuild
{
"json": {
"projectName": "customerwebsite",
"serviceName": "dealplustech-astro",
"build": {"type": "nixpacks"}
}
}
```
### Step 4: Get Primary Domain
```bash
GET /api/trpc/domains.getPrimaryDomain
{
"json": {
"projectName": "customerwebsite",
"serviceName": "dealplustech-astro"
}
}
```
### Step 5: Wait for Deployment
- Polls service status every 10 seconds
- Waits up to 5 minutes (30 attempts)
- Reports when status is "running" or "ready"
---
## 🎯 Usage
### First Deployment
```bash
cd dealplustech-astro
./skills/easypanel-deploy/deploy.sh deploy
```
**That's it!** The script handles everything automatically!
### Redeploy After Changes
```bash
git push # Push your changes
./skills/easypanel-deploy/deploy.sh redeploy # Trigger redeploy
```
### Check Status
```bash
./skills/easypanel-deploy/deploy.sh status
```
---
## ✅ Benefits
| Feature | Before v5.0 | v5.0 |
|---------|-------------|------|
| Manual steps | 4-6 steps | **0 steps** |
| Time required | 5-10 minutes | **1 command** |
| API calls | N/A | **5 automated calls** |
| Error handling | Manual | **Automatic** |
| Wait for deploy | Manual | **Automatic** |
| Domain info | Manual lookup | **Automatic** |
---
## 📋 What Happens
```
1. ./deploy.sh deploy
2. Creates service in Easypanel
3. Configures Git repository
4. Sets build type (nixpacks)
5. Gets auto-generated domain
6. Waits for deployment
7. ✅ Deployment complete!
```
**Zero manual steps!**
---
## 🎉 Ready to Test!
```bash
# Deploy now!
./skills/easypanel-deploy/deploy.sh deploy
```
---
**Status:** ✅ Fully Automated
**Automation Level:** 100%
**Manual Steps:** 0

View File

@@ -1,284 +0,0 @@
# 🚀 Easypanel Deployment Guide
**Project:** Deal Plus Tech Astro Migration
**Deployment Target:** Easypanel (Docker-based)
**Build Command:** `npm run build`
**Output:** Static site served via `npm run preview`
---
## 📋 Prerequisites
1. **Easypanel Account** - Access to Easypanel instance at `http://110.164.146.46:3000`
2. **Git Repository** - Code pushed to Git (Gitea/GitHub/GitLab)
3. **Domain** (optional) - Custom domain for production
---
## 🛠️ Deployment Steps
### Option 1: Deploy from Git Repository (Recommended)
#### Step 1: Connect Git Repository
1. Login to Easypanel: `http://110.164.146.46:3000`
2. Click **"New Project"** or select existing project
3. Click **"New Service"** → **"Git Repository"**
4. Connect your Git provider (Gitea, GitHub, GitLab)
5. Select repository: `dealplustech/dealplustech-astro`
6. Select branch: `main`
#### Step 2: Configure Build Settings
**Build Configuration:**
- **Build Command:** `npm run build`
- **Publish Directory:** `dist`
- **Dockerfile:** (Leave empty - uses auto-detection)
- **Node Version:** `20`
**Environment Variables:**
```bash
NODE_ENV=production
PORT=4321
HOST=0.0.0.0
```
#### Step 3: Deploy
1. Click **"Deploy"**
2. Wait for build to complete (~2-3 minutes)
3. Easypanel will automatically assign a URL (e.g., `dealplustech-astro.easypanel.app`)
4. Test the deployment
#### Step 4: Auto-Deploy (Optional)
Enable auto-deploy on push:
1. Go to Service Settings → **Git**
2. Enable **"Auto Deploy"**
3. Select branch: `main`
Now every push to `main` will trigger automatic deployment!
---
### Option 2: Deploy with Dockerfile
If you prefer using the provided Dockerfile:
#### Step 1: Build Docker Image Locally
```bash
cd dealplustech-astro
# Build Docker image
docker build -t dealplustech-astro:latest .
# Test locally
docker run -p 4321:4321 dealplustech-astro:latest
```
Visit `http://localhost:4321` to test
#### Step 2: Push to Container Registry
```bash
# Tag for your registry
docker tag dealplustech-astro:latest your-registry.com/dealplustech-astro:latest
# Push to registry
docker push your-registry.com/dealplustech-astro:latest
```
#### Step 3: Deploy on Easypanel
1. Login to Easypanel
2. **New Service****"Docker Image"**
3. Enter image URL: `your-registry.com/dealplustech-astro:latest`
4. Configure port: `4321`
5. Click **"Deploy"**
---
## 🔧 Configuration
### Environment Variables
| Variable | Value | Description |
|----------|-------|-------------|
| `NODE_ENV` | `production` | Production mode |
| `PORT` | `4321` | Astro preview server port |
| `HOST` | `0.0.0.0` | Listen on all interfaces |
### Resource Allocation
**Recommended Resources:**
- **CPU:** 0.5 - 1 vCPU
- **Memory:** 512MB - 1GB
- **Storage:** 1GB (for logs and assets)
### Custom Domain
To add a custom domain:
1. Go to Service Settings → **Domains**
2. Click **"Add Domain"**
3. Enter your domain: `dealplustech.co.th`
4. Update DNS records:
```
Type: CNAME
Name: www
Value: your-easypanel-url.easypanel.app
```
5. Enable SSL (Easypanel provides auto-SSL)
---
## 📊 Monitoring
### Health Check
The Dockerfile includes a health check endpoint:
- **URL:** `http://your-domain:4321/`
- **Expected:** HTTP 200 OK
### Logs
View logs in Easypanel:
1. Select Service
2. Click **"Logs"** tab
3. Filter by date/time
### Metrics
Easypanel provides:
- CPU usage
- Memory usage
- Network traffic
- Request count
---
## 🔄 Update Deployment
### Automatic Updates (Git Auto-Deploy)
If auto-deploy is enabled:
1. Push changes to `main` branch
2. Easypanel automatically rebuilds
3. New version deploys in ~2-3 minutes
### Manual Updates
1. Go to Service → **Deployments**
2. Click **"Redeploy"**
3. Select latest commit
4. Click **"Deploy Now"**
---
## 🐛 Troubleshooting
### Build Fails
**Issue:** `npm run build` fails
**Solution:**
1. Check build logs in Easypanel
2. Verify `package.json` scripts
3. Test build locally: `npm run build`
4. Check Node version (must be 20+)
### 502 Bad Gateway
**Issue:** Service returns 502 error
**Solution:**
1. Check if container is running
2. Verify port is 4321
3. Check health check endpoint
4. Review container logs
### Static Assets Not Loading
**Issue:** Images/CSS return 404
**Solution:**
1. Verify `public/` folder is copied in Dockerfile
2. Check asset paths in code
3. Rebuild and redeploy
---
## 📝 Post-Deployment Checklist
- [ ] Test homepage loads
- [ ] Test product pages (6 products)
- [ ] Test blog posts (3 posts)
- [ ] Test mobile responsiveness
- [ ] Verify FloatingContact buttons work
- [ ] Test all navigation links
- [ ] Check SEO metadata
- [ ] Setup custom domain (if needed)
- [ ] Enable SSL certificate
- [ ] Configure CDN (optional)
- [ ] Setup monitoring alerts
---
## 🎯 Production Optimization
### Enable Compression
Easypanel automatically enables gzip compression for static assets.
### CDN Integration
For better performance:
1. Sign up for CDN (Cloudflare, BunnyCDN, etc.)
2. Point CDN to Easypanel URL
3. Update DNS to point to CDN
4. Configure cache rules
### Caching Headers
Astro sets optimal cache headers by default:
- **HTML:** No cache (always fresh)
- **Assets:** 1 year (immutable)
- **Images:** 1 year (immutable)
---
## 📞 Support
**Easypanel Documentation:** https://docs.easypanel.io
**Astro Documentation:** https://docs.astro.build
**Project Repository:** [Your Git Repo]
---
## 🚀 Quick Deploy Commands
```bash
# Build locally
npm run build
# Test production build locally
npm run preview
# Build Docker image
docker build -t dealplustech-astro:latest .
# Run Docker container
docker run -p 4321:4321 dealplustech-astro:latest
# Push to registry
docker push your-registry.com/dealplustech-astro:latest
```
---
**Deployment Status:** ✅ Ready to Deploy
**Estimated Deploy Time:** 2-3 minutes
**First Deploy:** Manual
**Subsequent Deploys:** Automatic (if enabled)

View File

@@ -1,118 +0,0 @@
# 🚢 Image Deployment Options
## The Issue
When you built `dealplustech-astro:latest`, it's only on your **local Docker**. Easypanel server can't access it.
## ✅ Solutions
### Option 1: Easypanel Docker Registry (Recommended)
Easypanel provides a built-in Docker registry.
**Steps:**
```bash
# 1. Get registry URL (usually SERVER_IP:3001)
# For your setup: 110.164.146.46:3001
# 2. Login to registry
docker login 110.164.146.46:3001
# Use Easypanel credentials
# 3. Tag image
docker tag dealplustech-astro:latest 110.164.146.46:3001/customerwebsite/dealplustech-astro:latest
# 4. Push
docker push 110.164.146.46:3001/customerwebsite/dealplustech-astro:latest
# 5. In Easypanel dashboard, use:
# Image: 110.164.146.46:3001/customerwebsite/dealplustech-astro:latest
```
---
### Option 2: Docker Hub (Public)
```bash
# Tag with your Docker Hub username
docker tag dealplustech-astro:latest yourusername/dealplustech-astro:latest
# Push
docker push yourusername/dealplustech-astro:latest
# In Easypanel, use:
# Image: yourusername/dealplustech-astro:latest
```
**Note:** Public repository - anyone can see it
---
### Option 3: Private Registry (Harbor, GitLab, etc.)
```bash
# Tag for your registry
docker tag dealplustech-astro:latest registry.yourcompany.com/project/dealplustech-astro:latest
# Login
docker login registry.yourcompany.com
# Push
docker push registry.yourcompany.com/project/dealplustech-astro:latest
```
---
### Option 4: Direct Server Deployment
If you have SSH access to Easypanel server:
```bash
# Export image
docker save dealplustech-astro:latest > dealplustech-astro.tar
# Copy to server
scp dealplustech-astro.tar user@110.164.146.46:/tmp/
# SSH to server and load
ssh user@110.164.146.46
docker load < /tmp/dealplustech-astro.tar
# In Easypanel, use:
# Image: dealplustech-astro:latest (local)
```
---
## 🎯 Recommended for Your Setup
**Use Easypanel's built-in registry:**
```bash
# 1. Login
docker login 110.164.146.46:3001
# 2. Tag
docker tag dealplustech-astro:latest 110.164.146.46:3001/customerwebsite/dealplustech-astro:latest
# 3. Push
docker push 110.164.146.46:3001/customerwebsite/dealplustech-astro:latest
# 4. Create service in Easypanel with:
# Image: 110.164.146.46:3001/customerwebsite/dealplustech-astro:latest
```
---
## 📋 Checklist
- [ ] Choose registry option
- [ ] Login to registry
- [ ] Tag image correctly
- [ ] Push to registry
- [ ] Use full image name in Easypanel
---
**Next Step:** Push to Easypanel registry, then create service!

View File

@@ -1,177 +0,0 @@
# 🚀 Deployment Summary & Next Steps
## ✅ What's Complete
### 1. Docker Image Built
- **Image:** `dealplustech-astro:latest`
- **Size:** ~564MB
- **Status:** Ready to deploy
### 2. Easypanel Connection
- **URL:** http://110.164.146.46:3000
- **Token:** ✅ Configured
- **Project Found:** `customerwebsite`
### 3. Skill Created
- **Location:** `skills/easypanel-deploy/`
- **Version:** 2.2
- **Features:** State management, lifecycle control
---
## 📋 Manual Steps Required
Easypanel API has limitations for service creation. Follow these steps:
### Step 1: Create Service in Dashboard
1. **Open Easypanel:**
```
http://110.164.146.46:3000
```
2. **Select Project:**
- Click on: `customerwebsite`
3. **Create New Service:**
- Click: **"New Service"**
- Choose: **"Docker image"**
4. **Configure Service:**
```
Name: dealplustech-astro
Image: dealplustech-astro:latest
Port: 4321
```
5. **Deploy:**
- Click: **"Create"** or **"Deploy"**
- Wait for deployment (~2-3 minutes)
6. **Copy Service ID:**
- Go to service settings
- Copy the ID (looks like: `svc_abc123...`)
### Step 2: Register Service ID
```bash
cd dealplustech-astro
# Register the service ID you copied
./skills/easypanel-deploy/deploy.sh register svc_abc123...
```
### Step 3: Verify
```bash
# Check status
./skills/easypanel-deploy/deploy.sh status
# Should show:
# ✅ Service: dealplustech-astro
# ID: svc_abc123...
```
---
## 🔄 Future Updates
After registration, updates are easy:
```bash
# Make code changes
git pull
# Update deployment
./skills/easypanel-deploy/deploy.sh update
# This will:
# 1. Rebuild Docker image
# 2. Ready for deployment
# 3. Click "Deploy" in Easypanel to apply
```
---
## 📊 Quick Commands
| Command | Purpose |
|---------|---------|
| `./deploy.sh deploy` | Initial deployment |
| `./deploy.sh register ID` | Register service |
| `./deploy.sh update` | Update service |
| `./deploy.sh status` | Check status |
| `./deploy.sh list` | List projects |
---
## 🐛 Troubleshooting
### Docker Build Fails
```bash
# Rebuild manually
cd dealplustech-astro
docker build -t dealplustech-astro:latest .
```
### Can't Access Easypanel
- Check VPN/connection
- URL: http://110.164.146.46:3000
- Contact admin if needed
### Service Won't Start
1. Check logs in Easypanel dashboard
2. Verify port 4321 is available
3. Check resource allocation
---
## 📁 Files Created
```
dealplustech-astro/
├── skills/easypanel-deploy/
│ ├── deploy.sh # Main script
│ ├── SKILL_v2.md # Documentation
│ └── README.md # Quick start
├── easypanel.config.json # Configuration
├── Dockerfile # Docker config
└── DEPLOYMENT_SUMMARY.md # This file
~/.easypanel/
├── credentials # API token
└── state.json # Service IDs
```
---
## ✅ Checklist
- [ ] Docker image built ✅
- [ ] Easypanel token configured ✅
- [ ] Project identified (`customerwebsite`) ✅
- [ ] Create service in dashboard ⏳
- [ ] Register service ID ⏳
- [ ] Verify deployment ⏳
---
## 🎯 Next Action
**Go to Easypanel dashboard and create the service:**
1. Open: http://110.164.146.46:3000
2. Select: `customerwebsite` project
3. New Service → Docker image
4. Name: `dealplustech-astro`
5. Image: `dealplustech-astro:latest`
6. Port: `4321`
7. Deploy!
Then run: `./deploy.sh register YOUR_SERVICE_ID`
---
**Status:** Ready for manual service creation
**Estimated Time:** 5 minutes
**Difficulty:** Easy

View File

@@ -1,302 +0,0 @@
# 🚀 Easypanel API Deployment Guide
## Overview
This guide shows how to deploy the Deal Plus Tech Astro site to Easypanel using their API.
---
## Prerequisites
1. **Easypanel Access**
- URL: `http://110.164.146.46:3000`
- Admin credentials
2. **API Token**
- Login to Easypanel
- Go to Settings → API
- Generate new API token
3. **Docker Image**
- Already built locally: `dealplustech-astro:latest`
- Size: 564MB
---
## Method 1: Manual Deployment (Recommended)
### Step 1: Login to Easypanel
```
URL: http://110.164.146.46:3000
```
### Step 2: Create Project
1. Click **"New Project"**
2. Name: `dealplustech`
3. Description: "Deal Plus Tech Websites"
4. Click **"Create"**
### Step 3: Deploy Docker Image
1. Select project: `dealplustech`
2. Click **"New Service"**
3. Choose: **"Docker Image"**
4. Configure:
- **Name:** `dealplustech-astro`
- **Image:** `dealplustech-astro:latest`
- **Port:** `4321`
5. Click **"Deploy"**
### Step 4: Verify Deployment
1. Wait for build to complete (~2-3 minutes)
2. Click on service to view logs
3. Look for: "Astro preview ready"
4. Access URL provided by Easypanel
---
## Method 2: Automated Deployment (API)
### Step 1: Get API Token
```bash
# Login to Easypanel and get token from Settings → API
export EASYPANEL_API_TOKEN="your-api-token-here"
```
### Step 2: Run Deployment Script
```bash
cd dealplustech-astro
# Option A: With token as argument
./deploy-easypanel.sh your-api-token
# Option B: With environment variable
export EASYPANEL_API_TOKEN="your-api-token"
./deploy-easypanel.sh
```
### Step 3: Monitor Deployment
```bash
# View service status
curl -s "http://110.164.146.46:3000/api/trpc/services.app.inspectService" \
-H "Authorization: Bearer $EASYPANEL_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"input":{"json":{"projectName":"dealplustech","serviceName":"dealplustech-astro"}}}' \
--insecure | python3 -m json.tool
```
---
## Method 3: Direct Docker Deployment
If you have direct Docker access to the Easypanel server:
```bash
# SSH to Easypanel server
ssh user@110.164.146.46
# Run container directly
docker run -d \
-p 4321:4321 \
--name dealplustech-astro \
--restart unless-stopped \
-e NODE_ENV=production \
-e PORT=4321 \
-e HOST=0.0.0.0 \
dealplustech-astro:latest
```
---
## API Endpoints
### List Projects
```bash
curl -X GET "http://110.164.146.46:3000/api/trpc/projects.listProjects" \
-H "Authorization: Bearer YOUR_TOKEN" \
--insecure
```
### Create Service
```bash
curl -X POST "http://110.164.146.46:3000/api/trpc/services.app.create" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{
"projectName": "dealplustech",
"name": "dealplustech-astro",
"type": "docker",
"docker": {
"image": "dealplustech-astro:latest",
"port": 4321
},
"env": {
"NODE_ENV": "production",
"PORT": "4321"
}
}' \
--insecure
```
### Inspect Service
```bash
curl -X GET "http://110.164.146.46:3000/api/trpc/services.app.inspectService" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{"input":{"json":{"projectName":"dealplustech","serviceName":"dealplustech-astro"}}}' \
--insecure
```
### Get Service Logs
```bash
curl -X GET "http://110.164.146.46:3000/api/trpc/services.common.getLogs" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{"input":{"json":{"projectName":"dealplustech","serviceName":"dealplustech-astro","lines":50}}}' \
--insecure
```
### Deploy/Redeploy Service
```bash
curl -X POST "http://110.164.146.46:3000/api/trpc/services.app.deploy" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{"input":{"json":{"projectName":"dealplustech","serviceName":"dealplustech-astro"}}}' \
--insecure
```
---
## Environment Variables
| Variable | Value | Required |
|----------|-------|----------|
| `NODE_ENV` | `production` | ✅ Yes |
| `PORT` | `4321` | ✅ Yes |
| `HOST` | `0.0.0.0` | ✅ Yes |
---
## Troubleshooting
### API Returns 401 Unauthorized
**Problem:** Invalid or missing API token
**Solution:**
```bash
# Verify token is set
echo $EASYPANEL_API_TOKEN
# Regenerate token in Easypanel dashboard
```
### Service Won't Start
**Problem:** Container crashes on startup
**Solution:**
```bash
# Check logs
docker logs dealplustech-astro
# Common issues:
# - Port already in use
# - Missing environment variables
# - Image build failed
```
### Build Fails
**Problem:** Docker build errors
**Solution:**
```bash
# Rebuild with verbose output
cd dealplustech-astro
docker build --no-cache -t dealplustech-astro:latest .
# Check for errors in output
```
### Can't Access Easypanel
**Problem:** Connection timeout
**Solution:**
```bash
# Test connection
curl -I http://110.164.146.46:3000
# Check if Easypanel is running
# Contact server administrator if needed
```
---
## Post-Deployment
### 1. Verify Service
```bash
curl http://your-easypanel-url:4321/
```
Should return HTML with Thai content
### 2. Check Health
```bash
curl -I http://your-easypanel-url:4321/
```
Should return `HTTP/1.1 200 OK`
### 3. Setup Domain (Optional)
1. Go to Easypanel → Service Settings → Domains
2. Add domain: `dealplustech.co.th`
3. Update DNS records
4. Enable SSL
### 4. Enable Auto-Deploy
For Git-based auto-deploy:
1. Go to Service Settings → Git
2. Connect repository
3. Enable auto-deploy on push
---
## Resources
- **Easypanel Dashboard:** http://110.164.146.46:3000
- **API Documentation:** http://110.164.146.46:3000/api
- **Swagger UI:** http://110.164.146.46:3000/api (Swagger UI)
- **Easypanel Docs:** https://docs.easypanel.io
---
## Support
For issues:
1. Check service logs in Easypanel
2. Review deployment script output
3. Contact Easypanel support
4. Check project documentation
---
**Last Updated:** 2026-03-02
**Status:** ✅ Ready for Deployment

View File

@@ -1,112 +0,0 @@
# 🚀 Deployment Instructions - Deal Plus Tech Astro
## ✅ Git Pushed Successfully!
**Repository:** https://git.moreminimore.com/kunthawat/dealplustech.git
**Branch:** main
**Commit:** 6402d88 - Complete Astro migration
---
## 📦 Deploy to Easypanel
### Option 1: Via Easypanel UI (Recommended)
1. **Login to Easypanel:** http://110.164.146.46:3000
2. **Go to Project:** `customerwebsite`
3. **Create New Service:**
- Click "New Service" → "Git Repository"
- Repository URL: `https://git.moreminimore.com/kunthawat/dealplustech.git`
- Branch: `main`
- Service Name: `dealplustech-astro`
4. **Configure Build:**
- Build Type: **Dockerfile**
- Dockerfile Path: `./dealplustech-astro/Dockerfile`
- Port: `3000`
5. **Click "Deploy"**
6. **Wait for build** (~2-3 minutes)
7. **Access your site:** Easypanel will provide the URL
---
### Option 2: If Service Already Exists
If you have an existing service for this repo:
1. The git push should trigger **auto-deploy** automatically
2. Check the service deployment logs in Easypanel
3. If auto-deploy didn't trigger, click "Redeploy" manually
---
## ✅ Verify Deployment
After deployment completes:
- [ ] Visit your site URL
- [ ] Cookie consent banner appears
- [ ] Homepage loads correctly
- [ ] Privacy Policy accessible (`/privacy-policy/`)
- [ ] Terms & Conditions accessible (`/terms-and-conditions/`)
- [ ] Blog posts load (`/blog/`)
- [ ] All navigation works
- [ ] Mobile responsive
---
## ⚙️ Post-Deployment Configuration
### Enable Umami Analytics
Edit `src/layouts/BaseLayout.astro` (lines ~103-105):
```javascript
function loadAnalytics() {
const umamiEnabled = true; // Change to true
const umamiWebsiteId = 'xxx-xxx-xxx'; // Your Umami ID
const umamiDomain = 'analytics.moreminimore.com'; // Your domain
if (umamiEnabled && umamiWebsiteId && umamiDomain) {
// Loads Umami script
}
}
```
Then commit and push:
```bash
git add src/layouts/BaseLayout.astro
git commit -m "Enable Umami Analytics"
git push
```
---
## 🎉 What's Deployed
✅ Astro static site (184KB, 11 pages)
✅ PDPA-compliant Privacy Policy (Thai)
✅ PDPA-compliant Terms & Conditions (Thai)
✅ Cookie Policy with disclosure (Thai)
✅ Cookie consent banner (client-side)
✅ Blog with 3 posts
✅ Umami Analytics placeholder
✅ Production Docker configuration
---
## 📞 Support
**Easypanel URL:** http://110.164.146.46:3000
**Git Repository:** https://git.moreminimore.com/kunthawat/dealplustech.git
**If build fails:**
1. Check Easypanel build logs
2. Verify Dockerfile is at correct path
3. Check port is set to 3000
4. Review error messages in logs

View File

@@ -1,15 +0,0 @@
# Production Docker for Astro Static Site
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
RUN npm install -g serve
EXPOSE 3000
ENV NODE_ENV=production PORT=3000 HOST=0.0.0.0
CMD ["serve", "dist", "-l", "3000", "--single"]

View File

@@ -1,310 +0,0 @@
# Easypanel Deployment Skill
**Skill Name:** `easypanel-deploy`
**Description:** Deploy Astro/Next.js/Vite apps to Easypanel with Docker
**Version:** 1.0.0
**Author:** Deal Plus Tech DevOps
---
## Overview
This skill provides a complete deployment workflow for static site generators (Astro, Next.js, Vite) to Easypanel using Docker containers.
---
## Capabilities
-**Dockerfile Generation** - Optimized multi-stage Dockerfile
-**Easypanel Configuration** - Pre-configured for Easypanel deployment
-**Git Integration** - Auto-deploy on push
-**Environment Setup** - Environment variables and secrets
-**Domain Configuration** - Custom domain + SSL setup
-**Health Checks** - Container health monitoring
-**Resource Optimization** - Recommended resource allocation
---
## Usage
### Prerequisites
1. Easypanel instance (self-hosted or cloud)
2. Git repository with Astro/Next.js/Vite project
3. Docker installed (for local testing)
### Step 1: Prepare Project
```bash
# Navigate to project
cd your-project
# Ensure build script exists
npm run build
# Test production build
npm run preview
```
### Step 2: Add Deployment Files
Create these files in project root:
**Dockerfile:**
```dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/public ./public
EXPOSE 4321
CMD ["npm", "run", "preview", "--", "--host", "0.0.0.0", "--port", "4321"]
```
**.dockerignore:**
```
node_modules
dist
*.log
.git
.env
```
### Step 3: Deploy to Easypanel
#### Option A: Git Repository (Recommended)
1. Login to Easypanel
2. **New Service****Git Repository**
3. Select repository and branch
4. Configure:
- **Build Command:** `npm run build`
- **Publish Directory:** `dist`
- **Port:** `4321`
5. Click **Deploy**
#### Option B: Docker Image
1. Build image:
```bash
docker build -t your-app:latest .
docker push your-registry.com/your-app:latest
```
2. Deploy on Easypanel:
- **New Service** → **Docker Image**
- Enter image URL
- Set port: `4321`
- Click **Deploy**
### Step 4: Configure Domain (Optional)
1. Go to Service Settings → **Domains**
2. Add domain: `your-domain.com`
3. Update DNS:
```
Type: CNAME
Name: @
Value: your-service.easypanel.app
```
4. Enable SSL
---
## Environment Variables
| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `NODE_ENV` | No | `production` | Node environment |
| `PORT` | No | `4321` | Server port |
| `HOST` | No | `0.0.0.0` | Bind address |
---
## Resource Recommendations
| Project Size | CPU | Memory | Storage |
|--------------|-----|--------|---------|
| **Small** (< 100MB) | 0.5 vCPU | 512MB | 1GB |
| **Medium** (< 500MB) | 1 vCPU | 1GB | 2GB |
| **Large** (> 500MB) | 2 vCPU | 2GB | 5GB |
---
## Health Check
**Endpoint:** `GET /`
**Expected:** HTTP 200 OK
**Timeout:** 3 seconds
**Interval:** 30 seconds
### Manual Health Check
```bash
curl http://your-service:4321/
```
### Docker Health Check
```bash
docker inspect --format='{{.State.Health.Status}}' your-container
```
---
## Troubleshooting
### Build Fails
**Symptoms:** Build command returns error
**Solutions:**
1. Check build logs
2. Verify `package.json` scripts
3. Test locally: `npm run build`
4. Check Node version compatibility
### Container Crashes
**Symptoms:** Container exits immediately
**Solutions:**
1. Check container logs: `docker logs <container>`
2. Verify port configuration
3. Check environment variables
4. Review Dockerfile CMD instruction
### 502 Bad Gateway
**Symptoms:** Service returns 502 error
**Solutions:**
1. Verify container is running
2. Check port mapping
3. Review health check status
4. Inspect application logs
---
## Best Practices
### Security
- ✅ Use `.dockerignore` to exclude sensitive files
- ✅ Run as non-root user (add `USER node` to Dockerfile)
- ✅ Use production dependencies only
- ✅ Enable SSL for custom domains
- ✅ Regular security updates
### Performance
- ✅ Multi-stage Docker builds
- ✅ Minimize Docker image size
- ✅ Enable compression (Easypanel default)
- ✅ Use CDN for static assets
- ✅ Configure proper caching
### Monitoring
- ✅ Enable health checks
- ✅ Monitor resource usage
- ✅ Set up log aggregation
- ✅ Configure alerts for failures
- ✅ Regular backup strategy
---
## Examples
### Astro Project
```bash
# Astro specific configuration
npm create astro@latest
npm run build # Output: dist/
npm run preview # Port: 4321
```
### Next.js Project
```bash
# Next.js specific configuration
npx create-next-app
npm run build # Output: .next/
npm run start # Port: 3000
```
Update Dockerfile PORT to 3000 for Next.js.
### Vite Project
```bash
# Vite specific configuration
npm create vite@latest
npm run build # Output: dist/
npx serve dist # Port: 3000
```
---
## Integration with CI/CD
### GitHub Actions Example
```yaml
name: Deploy to Easypanel
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build Docker Image
run: docker build -t my-app:latest .
- name: Push to Registry
run: docker push registry.com/my-app:latest
- name: Deploy to Easypanel
run: |
# Use Easypanel API or CLI
easypanel deploy my-app:latest
```
---
## Resources
- **Easypanel Docs:** https://docs.easypanel.io
- **Docker Docs:** https://docs.docker.com
- **Astro Docs:** https://docs.astro.build
- **Node.js Docker Best Practices:** https://github.com/nodejs/docker-node
---
## Support
For issues or questions:
1. Check troubleshooting section
2. Review Easypanel documentation
3. Check project logs
4. Contact DevOps team
---
**Last Updated:** 2026-03-02
**Skill Version:** 1.0.0
**Status:** ✅ Production Ready

View File

@@ -1,164 +0,0 @@
# 🚀 Final Deployment Guide - Deal Plus Tech Astro
## ✅ What's Ready
### 1. Docker Image
- ✅ Built locally: `dealplustech-astro:local`
- ✅ Size: ~564MB
- ✅ Ready to push to registry
### 2. Easypanel Integration
- ✅ API token configured
- ✅ Project identified: `customerwebsite`
- ✅ Deployment script ready
### 3. Registry Options
**Option A: Easypanel Registry** (If available)
```bash
URL: 110.164.146.46:3001
Status: ⚠️ Port 3001 not accessible
```
**Option B: Docker Hub** (Recommended fallback)
```bash
URL: hub.docker.com
Status: ✅ Available
```
**Option C: Direct Server Load** (If SSH access)
```bash
Server: 110.164.146.46
Status: ⚠️ Requires SSH credentials
```
---
## 📋 Recommended Deployment Path
### Step 1: Push to Docker Hub (5 minutes)
```bash
# 1. Login to Docker Hub
docker login
# 2. Tag image
docker tag dealplustech-astro:local yourusername/dealplustech-astro:latest
# 3. Push
docker push yourusername/dealplustech-astro:latest
```
### Step 2: Create Service in Easypanel (2 minutes)
```
1. Open: http://110.164.146.46:3000
2. Project: customerwebsite
3. New Service → Docker image
4. Image: yourusername/dealplustech-astro:latest
5. Port: 4321
6. Deploy
7. Copy Service ID
```
### Step 3: Register Service ID
```bash
cd dealplustech-astro
./skills/easypanel-deploy/deploy.sh register svc_xxx...
```
### Step 4: Verify
```bash
./skills/easypanel-deploy/deploy.sh status
```
---
## 🔄 Future Updates
Once registered:
```bash
# Make changes
git pull
# Update (automatically rebuilds and pushes)
./skills/easypanel-deploy/deploy.sh update
# Redeploy in Easypanel dashboard
```
---
## 🎯 Quick Commands
```bash
# Deploy (builds + instructions)
./skills/easypanel-deploy/deploy.sh deploy
# Register service
./skills/easypanel-deploy/deploy.sh register SERVICE_ID
# Update (rebuild + push)
./skills/easypanel-deploy/deploy.sh update
# Check status
./skills/easypanel-deploy/deploy.sh status
# List projects
./skills/easypanel-deploy/deploy.sh list
```
---
## 📁 Files Created
```
dealplustech-astro/
├── skills/easypanel-deploy/
│ ├── deploy.sh ✅ v3.2 Smart deployment
│ ├── SKILL_v2.md ✅ Full documentation
│ ├── README.md ✅ Quick start
│ └── AUTOMATIC_DEPLOYMENT.md ✅ Automation details
├── DEPLOYMENT_OPTIONS.md ✅ Registry options
├── FINAL_DEPLOYMENT_GUIDE.md ✅ This file
└── easypanel.config.json ✅ Configuration
~/.easypanel/
├── credentials ✅ API token
└── state.json ✅ Service IDs
```
---
## ✅ Checklist
- [x] Docker image built
- [x] Easypanel token configured
- [x] Project identified (customerwebsite)
- [ ] Push to Docker Hub
- [ ] Create service in Easypanel
- [ ] Register service ID
- [ ] Verify deployment
---
## 🎯 Next Action
**Push to Docker Hub:**
```bash
docker login
docker tag dealplustech-astro:local yourusername/dealplustech-astro:latest
docker push yourusername/dealplustech-astro:latest
```
**Then create service in Easypanel!**
---
**Status:** Ready for Docker Hub push
**Estimated Time:** 10 minutes
**Difficulty:** Easy

View File

@@ -1,167 +0,0 @@
# 🔧 Fixes Applied - Product Tables & Responsive Fonts
## ✅ Issue 1: Product Tables Disappeared
### Problem
The product specification tables (extracted from images) were not showing on product pages in the Astro migration.
### Root Cause
- The Astro product detail page template (`[slug].astro`) was missing
- Product tables data exists in `src/data/site-config.ts` but wasn't being rendered
- Markdown files don't include table data (tables are in TypeScript file)
### Solution
Created `src/pages/products/[slug].astro` with:
1. **Table Rendering Section** - Displays all `productTables` from site-config
2. **Proper Styling** - Matches original Next.js design with:
- Table name headers
- Alternating row colors
- Responsive table containers
- Proper borders and shadows
### Code Added
```astro
{productTables.length > 0 && (
<section class="mb-12">
<h2>ตารางข้อมูลผลิตภัณฑ์</h2>
{productTables.map((table) => (
<div class="bg-white rounded-2xl...">
<h3>{table.tableName}</h3>
<table>
<thead>{table.headers.map(...)} </thead>
<tbody>{table.rows.map(...)} </tbody>
</table>
</div>
))}
</section>
)}
```
### Data Source
Tables are loaded from `src/data/site-config.ts` via `productCategories.find(p => p.id === product.data.id).productTables`
Products with tables:
- ✅ ppr-welder (1 table)
- ✅ poloplast (4 tables)
- ✅ syler (2 tables)
- ✅ xylent (3 tables)
- ✅ pvc/upvc (6 tables)
- ✅ realflex (3 tables)
---
## ✅ Issue 2: Font Too Small on Large Screens
### Problem
Text was not scaling properly on larger screens (desktop, 4K monitors).
### Root Cause
- Base font size was fixed at browser default (16px)
- No responsive scaling for larger viewports
- Tailwind classes weren't enough for very large screens
### Solution
Added responsive font scaling in `src/styles/global.css`:
```css
/* Base font size */
html {
font-size: 16px;
}
/* Scale up 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; }
}
```
### Additional Responsive Text Classes
```css
.text-responsive-sm { text-sm md:text-base lg:text-lg xl:text-xl; }
.text-responsive-base { text-base md:text-lg lg:text-xl xl:text-2xl; }
.text-responsive-lg { text-lg md:text-xl lg:text-2xl xl:text-3xl; }
.text-responsive-xl { text-xl md:text-2xl lg:text-3xl xl:text-4xl; }
```
### Updated Components
All major text elements now use responsive sizing:
- **Buttons**: `text-base md:text-lg`
- **Section titles**: `text-3xl md:text-4xl lg:text-5xl xl:text-6xl`
- **Table headers**: `text-xl md:text-2xl lg:text-3xl`
- **Table cells**: `text-base md:text-lg lg:text-xl`
- **Features/FAQ**: `text-base md:text-lg lg:text-xl`
---
## 📊 Before & After Comparison
| Screen Size | Before | After |
|-------------|--------|-------|
| **Mobile (< 768px)** | 16px base | 16px base ✓ |
| **Tablet (768-1280px)** | 16px base | 16px base ✓ |
| **Desktop (1280-1536px)** | 16px base | **18px base** ↑ |
| **Large (1536-1920px)** | 16px base | **20px base** ↑ |
| **XL (1920-2560px)** | 16px base | **22px base** ↑ |
| **4K (> 2560px)** | 16px base | **24px base** ↑ |
### Font Scaling Examples
**Product Title:**
- Before: `text-4xl` (36px fixed)
- After: `text-4xl md:text-5xl lg:text-6xl xl:text-7xl` (36px → 60px)
**Table Headers:**
- Before: `text-lg` (18px fixed)
- After: `text-xl md:text-2xl lg:text-3xl` (20px → 30px)
**Body Text:**
- Before: `text-base` (16px fixed)
- After: Responsive + base font scaling (16px → 24px)
---
## 🎯 Testing Checklist
### Product Tables
- [ ] Visit any product page (e.g., `/pp-r-pp-rct-poloplast/`)
- [ ] Scroll to "ตารางข้อมูลผลิตภัณฑ์" section
- [ ] Verify tables are visible with proper styling
- [ ] Check table headers have green background
- [ ] Check rows alternate white/gray
- [ ] Verify tables scroll horizontally on mobile
### Responsive Fonts
- [ ] Test on mobile (375px) - text should be readable
- [ ] Test on tablet (768px) - text slightly larger
- [ ] Test on desktop (1920px) - text noticeably larger
- [ ] Test on 4K (3840px) - text should be large and clear
- [ ] Check product titles scale properly
- [ ] Check table text scales properly
- [ ] Check buttons scale properly
---
## 📁 Files Modified
| File | Changes |
|------|---------|
| `src/pages/products/[slug].astro` | ✅ Created - product detail page with tables |
| `src/styles/global.css` | ✅ Updated - responsive font scaling |
---
**Status:** ✅ Both issues fixed
**Build:** ✅ Passing
**Next:** Test on actual device/screen sizes

View File

@@ -1,231 +0,0 @@
# 🚀 Gitea Repository Deployment - BEST METHOD!
## ✅ Why This is Better
| Method | Docker Registry | Gitea Repo |
|--------|----------------|------------|
| Build locally | ✅ Required | ❌ Not needed |
| Push to registry | ✅ Required | ❌ Not needed |
| Easypanel builds | ❌ No | ✅ Yes! |
| Auto-deploy on push | ❌ No | ✅ Yes! |
| Version control | ❌ No | ✅ Yes! |
| Rollbacks | ❌ Hard | ✅ Easy |
---
## 🎯 How It Works
```
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ You Git │ │ Easypanel │ │ Deployed │
│ Push │────▶│ Clones & │────▶│ Service │
│ to Gitea │ │ Builds │ │ Running │
└─────────────┘ └──────────────┘ └─────────────┘
```
**Easypanel will:**
1. Clone your Gitea repository
2. Run `npm install`
3. Run `npm run build`
4. Deploy the `dist/` folder
5. Serve on port 4321
---
## 📋 Setup Steps
### Step 1: Run Deploy Script
```bash
cd dealplustech-astro
./skills/easypanel-deploy/deploy.sh deploy
```
**It will show:**
```
Gitea URL: http://110.164.146.46:3001
Repository: dealplustech/dealplustech-astro
Branch: main
Build Command: npm run build
Publish Directory: dist
Port: 4321
```
### Step 2: Create Service in Easypanel
```
1. Open: http://110.164.146.46:3000
2. Select Project: customerwebsite
3. Click: New Service → Git Repository
4. Configure:
┌─────────────────────────────────────┐
│ Repository URL: │
│ http://110.164.146.46:3001/ │
│ dealplustech/dealplustech-astro │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ Branch: main │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ Build Command: npm run build │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ Publish Directory: dist │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ Port: 4321 │
└─────────────────────────────────────┘
5. Click: Deploy
```
### Step 3: Wait for Build
Easypanel will:
- ✅ Clone from Gitea (~10 seconds)
- ✅ Run `npm install` (~30 seconds)
- ✅ Run `npm run build` (~20 seconds)
- ✅ Deploy `dist/` (~5 seconds)
**Total: ~1 minute**
### Step 4: Register Service ID
After deployment completes:
```bash
# Copy Service ID from Easypanel dashboard
./skills/easypanel-deploy/deploy.sh register svc_xxx...
```
---
## 🔄 Automatic Updates
### Enable Auto-Deploy
**In Easypanel:**
1. Go to service settings
2. Click: Git → Auto Deploy
3. Enable: Auto Deploy on Push
**Then:**
```bash
# Make changes
git add .
git commit -m "Update something"
git push
# Easypanel automatically rebuilds! 🎉
```
### Manual Redeploy
```bash
./skills/easypanel-deploy/deploy.sh redeploy
```
---
## ✅ Benefits
### 1. No Docker Registry
- ❌ No `docker build`
- ❌ No `docker push`
- ❌ No registry credentials
### 2. Automatic Deployments
- ✅ Push to Gitea
- ✅ Easypanel rebuilds
- ✅ Zero manual steps
### 3. Version Control
- ✅ Every commit = potential deployment
- ✅ Easy rollbacks
- ✅ Change history
### 4. Build Caching
-`node_modules` cached
- ✅ Faster builds
- ✅ Efficient
---
## 🔧 Configuration
### Gitea Repository
```bash
URL: http://110.164.146.46:3001
Owner: dealplustech
Repo: dealplustech-astro
Branch: main
```
### Build Settings
```bash
Build Command: npm run build
Publish Directory: dist/
Node Version: 20 (from .nvmrc or package.json)
```
### Environment Variables
Add in Easypanel service settings:
```bash
NODE_ENV=production
NEXT_PUBLIC_SITE_URL=https://your-domain.com
```
---
## 📁 Required Files
Your repository should have:
```
dealplustech-astro/
├── package.json ✅ Required (defines build)
├── Dockerfile ⚠️ Optional (for Docker mode)
├── astro.config.mjs ✅ Required
├── src/ ✅ Required
└── .gitignore ✅ Required
```
---
## 🎯 Next Action
**Deploy now:**
```bash
# 1. Run deployment script
./skills/easypanel-deploy/deploy.sh deploy
# 2. Follow instructions to create service in Easypanel
# 3. After deployment:
./skills/easypanel-deploy/deploy.sh register SERVICE_ID
# 4. Enable auto-deploy in Easypanel
# 5. Future updates: just git push!
```
---
**This is THE BEST way to deploy!** 🎉
- No Docker needed
- No registry needed
- Automatic deployments
- Full version control

View File

@@ -1,58 +0,0 @@
# 🚀 Deal Plus Tech - Astro Migration Complete
**Migration Date:** 9 March 2026
**Status:****PRODUCTION READY**
---
## ✅ **MIGRATION COMPLETE**
All core features have been successfully migrated from Next.js to Astro:
### **What's Working:**
1. ✅ Astro static site - 11 pages built
2. ✅ PDPA-compliant Privacy Policy (Thai)
3. ✅ PDPA-compliant Terms & Conditions (Thai)
4. ✅ Cookie Policy (Thai)
5. ✅ Cookie consent banner (client-side)
6. ✅ Blog with 3 posts
7. ✅ Umami Analytics placeholder
8. ✅ Docker configuration ready
9. ✅ Deployment ready for Easypanel
### **Backup:**
Old Next.js backup: `/Users/kunthawatgreethong/Gitea/dealplustech-backup-nextjs-20260309.tar.gz` (62MB)
---
## 🚀 **DEPLOY TO EASYPANEL NOW**
### **Step 1: Push to Git**
```bash
cd /Users/kunthawatgreethong/Gitea/dealplustech/dealplustech-astro
git add .
git commit -m "✅ Astro migration complete - PDPA compliant"
git push origin main
```
### **Step 2: Deploy on Easypanel**
1. Go to Easypanel: `http://110.164.146.46:3000`
2. Click your project
3. **New Service****Git Repository**
4. Connect Gitea: `https://git.moreminimore.com/kunthawat/dealplustech-astro.git`
5. Branch: `main`
6. Port: `3000`
7. **Deploy**
### **Step 3: Verify**
- Visit your Easypanel URL
- Check cookie banner appears
- Test all pages load
- Verify privacy policy works
---
## 🎉 **SUCCESS!**
The Astro site is production-ready and fully PDPA-compliant!

View File

@@ -1,199 +0,0 @@
# Deal Plus Tech - Astro Migration Status
**Generated:** 2026-03-02
**Migration Approach:** Full rewrite (Option C - Focused)
---
## ✅ COMPLETED (4/11 tasks)
### 1. ✅ Project Setup
- Created Astro project: `dealplustech-astro/`
- Tailwind 4 configured
- TypeScript strict mode enabled
- Kanit font support configured
### 2. ✅ Theme Migration
- Industrial theme colors (Primary green, Secondary slate, Accent yellow)
- Custom shadows (card, industrial, bold)
- All component classes migrated (btn-primary, card, section-title, etc.)
- 70 lines of custom CSS
### 3. ✅ Core Infrastructure
- **Content Collections** configured with full product schema
- **BaseLayout.astro** - HTML shell with Thai SEO
- **Header.astro** - Fully functional with mobile menu
- **Footer.astro** - Complete with all sections
- **utils.ts** - Helper functions (cn, formatPrice, etc.)
- **site-config.ts** - Navigation + company info
### 4. ✅ Example Product
- **ppr-elephant.md** - Full migration example (161 lines)
- Demonstrates Markdown frontmatter + content structure
- All fields mapped from Next.js structure
---
## 📁 CREATED FILES
```
dealplustech-astro/
├── src/
│ ├── components/
│ │ ├── Header.astro ✅ 223 lines (mobile menu + JS)
│ │ └── Footer.astro ✅ 115 lines
│ ├── layouts/
│ │ └── BaseLayout.astro ✅ 46 lines
│ ├── pages/
│ │ └── products/
│ │ ├── index.astro ✅ Product listing
│ │ └── [slug].astro ✅ Dynamic product pages
│ ├── content/
│ │ ├── config.ts ✅ Product schema
│ │ └── products/
│ │ └── ppr-elephant.md ✅ Example product
│ ├── data/
│ │ ├── site-config.ts ✅ 116 lines
│ │ └── README.md ⏳ Pending
│ ├── lib/
│ │ └── utils.ts ✅ 43 lines
│ └── styles/
│ └── global.css ✅ 114 lines (theme)
└── astro.config.mjs ✅ Tailwind 4
```
**Total: ~850 lines of code created**
---
## ⏳ REMAINING WORK (7/11 tasks)
### 5. ⏳ Migrate Product Data (36 products)
**Status:** 1/36 complete (ppr-elephant)
**Remaining:** 35 products to convert to Markdown
| Priority | Product ID | Status |
|----------|------------|--------|
| ✅ Done | ppr-elephant | Complete |
| ⏳ High | thai-ppr | Pending |
| ⏳ High | poloplast | Pending |
| ⏳ High | hdpe | Pending |
| ⏳ High | pvc | Pending |
| ⏳ Medium | syler | Pending |
| ⏳ Medium | xylent | Pending |
| ⏳ Medium | realflex | Pending |
| ⏳ Low | (27 more products) | Pending |
**Estimate:** 4-6 hours (10 min/product)
---
### 6. ⏳ Migrate Static Pages
**Status:** 0/7 pages
| Page | Next.js Route | Status |
|------|---------------|--------|
| Homepage | `/` | ⏳ Pending |
| About Us | `/about-us/` | ⏳ Pending |
| Contact Us | `/contact-us/` | ⏳ Pending |
| Portfolio | `/all-projects/` | ⏳ Pending |
| Services | `/services/` | ⏳ Pending |
| Sales Engineer | `/sales-engineer/` | ⏳ Pending |
| Join Us | `/join-us/` | ⏳ Pending |
**Estimate:** 2-3 hours
---
### 7. ⏳ Migrate Blog System
**Status:** Not started
**Tasks:**
- [ ] Create blog collection schema
- [ ] Migrate WordPress integration
- [ ] Create blog listing page
- [ ] Create blog post template
**Estimate:** 2-3 hours
---
### 8. ⏳ Migrate FloatingContact Widget
**Status:** Not started
**Current:** React component (client-side floating widget)
**Migration:** Keep as React island with `client:load`
**Estimate:** 1 hour
---
### 9. ⏳ Easypanel Deployment Setup
**Status:** Not started
**Tasks:**
- [ ] Create Dockerfile
- [ ] Configure build command
- [ ] Setup environment variables
- [ ] Test deployment
**Estimate:** 1-2 hours
---
### 10. ⏳ Create Easypanel Skill
**Status:** Not started
**Estimate:** 2-3 hours
---
## 📊 OVERALL PROGRESS
| Category | Progress | Status |
|----------|----------|--------|
| **Infrastructure** | 100% | ✅ Complete |
| **Layouts** | 100% | ✅ Complete |
| **Products** | 3% | ⏳ 1/36 done |
| **Static Pages** | 0% | ⏳ Pending |
| **Blog** | 0% | ⏳ Pending |
| **Deployment** | 0% | ⏳ Pending |
**Overall: ~36% complete** (infrastructure done, content migration in progress)
---
## 🚀 RECOMMENDED NEXT STEPS
1. **Migrate 5 key products** (PPR, HDPE, PVC, Syler, POLOPLAST) - 1 hour
2. **Create homepage** - 1 hour
3. **Setup deployment** - 1 hour
4. **Test and verify** - 1 hour
**Then:** Migrate remaining products and blog incrementally
---
## 🎯 PRODUCTION READY WHEN
- [ ] 5 core products migrated
- [ ] Homepage + About + Contact pages done
- [ ] Deployment configured and tested
- [ ] All links working
- [ ] SEO metadata verified
**Estimated time to MVP:** 4-6 hours
---
## 📝 NOTES
- Product data conversion is straightforward (copy frontmatter, paste content)
- All styling preserved and working
- Mobile menu tested and functional
- Thai language fully supported
- SEO schema ready to implement
---
**Last Updated:** 2026-03-02 09:00 AM

View File

@@ -1 +0,0 @@
web: npm run preview -- --host 0.0.0.0 --port $PORT

View File

@@ -1,43 +0,0 @@
# Astro Starter Kit: Minimal
```sh
npm create astro@latest -- --template minimal
```
> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun!
## 🚀 Project Structure
Inside of your Astro project, you'll see the following folders and files:
```text
/
├── public/
├── src/
│ └── pages/
│ └── index.astro
└── package.json
```
Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
Any static assets, like images, can be placed in the `public/` directory.
## 🧞 Commands
All commands are run from the root of the project, from a terminal:
| Command | Action |
| :------------------------ | :----------------------------------------------- |
| `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:4321` |
| `npm run build` | Build your production site to `./dist/` |
| `npm run preview` | Preview your build locally, before deploying |
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
| `npm run astro -- --help` | Get help using the Astro CLI |
## 👀 Want to learn more?
Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).

View File

@@ -1,58 +0,0 @@
# 🍪 How to Test Cookie Consent Banner
## The Banner IS Working!
The cookie consent banner code is in the built HTML. If you don't see it, it's because:
- You already accepted/rejected before
- Consent is saved in localStorage permanently
- Banner only shows to NEW visitors
## How to Test (3 Methods):
### Method 1: Clear LocalStorage (Easiest)
1. Open your website in browser
2. Press **F12** to open DevTools
3. Go to **Application** tab (Chrome) or **Storage** tab (Firefox)
4. Click on **Local Storage** → Your site URL
5. Find `consent-preferences` key
6. **Right-click****Delete**
7. **Refresh page** (F5)
8. ✅ Banner should appear at bottom!
### Method 2: Incognito/Private + Clear Cache
1. Close ALL browser windows
2. Open **NEW Incognito/Private** window
3. **IMPORTANT:** Don't visit the site first in regular browser
4. Visit your site in Incognito
5. ✅ Banner should appear
### Method 3: Different Browser
1. If you use Chrome, try Firefox or Safari
2. First-time visit = banner appears
## What the Banner Does:
- Shows at **bottom of page** (fixed position)
- Has 3 buttons: **ยอมรับ** | **ปฏิเสธ** | **ปรับแต่ง**
- Clicking saves preference to localStorage
- Won't show again until you clear storage
## If Banner Still Doesn't Show:
1. **Check browser console** (F12 → Console tab)
2. Look for JavaScript errors
3. Verify page has loaded completely
4. Check if script is blocked by ad blocker
## Code Location:
- File: `src/layouts/BaseLayout.astro`
- Lines: 41-115
- Built output: `dist/index.html` (search for "consent-preferences")
---
**The code is working correctly!** It's designed to not annoy users by showing repeatedly.

View File

@@ -1,9 +0,0 @@
import { defineConfig } from 'astro/config';
import tailwindcss from '@tailwindcss/vite';
export default defineConfig({
vite: {
plugins: [tailwindcss()]
},
output: 'static'
});

View File

@@ -1,101 +0,0 @@
#!/bin/bash
# Easypanel Deployment Script for Deal Plus Tech Astro
# Usage: ./deploy-easypanel.sh [api-token]
set -e
EASYPANEL_URL="http://110.164.146.46:3000"
APP_NAME="dealplustech-astro"
PROJECT_NAME="dealplustech"
DOCKER_IMAGE="dealplustech-astro:latest"
PORT=4321
API_TOKEN="${1:-$EASYPANEL_API_TOKEN}"
echo "============================================================"
echo "🚀 Deploying to Easypanel"
echo "============================================================"
# Check if API token is provided
if [ -z "$API_TOKEN" ]; then
echo "⚠️ No API token provided"
echo ""
echo "📋 Manual Deployment Steps:"
echo "1. Open Easypanel: $EASYPANEL_URL"
echo "2. Create/Select project: $PROJECT_NAME"
echo "3. Click 'New Service' → 'Docker Image'"
echo "4. Enter image: $DOCKER_IMAGE"
echo "5. Set port: $PORT"
echo "6. Click 'Deploy'"
echo ""
echo "💡 To enable automated deployment:"
echo " 1. Get API token from Easypanel (Settings → API)"
echo " 2. Run: export EASYPANEL_API_TOKEN='your-token'"
echo " 3. Run this script again: ./deploy-easypanel.sh"
exit 0
fi
# Build Docker image
echo ""
echo "🐳 Building Docker image..."
cd dealplustech-astro
docker build -t $DOCKER_IMAGE . || {
echo "❌ Docker build failed"
exit 1
}
echo "✅ Docker image built: $DOCKER_IMAGE"
# Deploy via API
echo ""
echo "🚀 Deploying to Easypanel via API..."
# Create deployment payload
cat > /tmp/deploy.json << EOF
{
"projectName": "$PROJECT_NAME",
"name": "$APP_NAME",
"type": "docker",
"docker": {
"image": "$DOCKER_IMAGE",
"port": $PORT
},
"env": {
"NODE_ENV": "production",
"PORT": "$PORT",
"HOST": "0.0.0.0"
}
}
EOF
# Make API call
RESPONSE=$(curl -s -w "\n%{http_code}" -X POST "$EASYPANEL_URL/api/trpc/services.app.create" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $API_TOKEN" \
-d @/tmp/deploy.json \
--insecure)
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
BODY=$(echo "$RESPONSE" | head -n-1)
if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "201" ]; then
echo "✅ Service created successfully"
echo "$BODY" | python3 -m json.tool 2>/dev/null || echo "$BODY"
# Trigger deployment
echo ""
echo "🔄 Triggering deployment..."
# Would need service ID from previous response
echo ""
echo "============================================================"
echo "🎉 DEPLOYMENT COMPLETE!"
echo "============================================================"
echo ""
echo "📍 Service: $APP_NAME"
echo "🔗 Dashboard: $EASYPANEL_URL"
echo ""
echo "💡 Check deployment status in Easypanel dashboard"
else
echo "❌ Deployment failed (HTTP $HTTP_CODE)"
echo "$BODY"
exit 1
fi

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,121 +0,0 @@
<!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 v5.18.0"><meta name="description" content="ท่อ HDPE (High Density Polyethylene) เป็นท่อที่ได้รับความนิยมสูงในงานระบบน้ำ เนื่องจากความทนทานและความยืดหยุ่นที่เหนือกว่าท่อชนิดอื่น"><!-- 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="ข้อดีของท่อ HDPE ในงานระบบน้ำ ทำไมถึงเป็นตัวเลือกยอดนิยม"><meta property="og:description" content="ท่อ HDPE (High Density Polyethylene) เป็นท่อที่ได้รับความนิยมสูงในงานระบบน้ำ เนื่องจากความทนทานและความยืดหยุ่นที่เหนือกว่าท่อชนิดอื่น"><meta property="og:image" content="/og-image.jpg"><meta property="og:type" content="website"><meta name="twitter:card" content="summary_large_image"><title>ข้อดีของท่อ HDPE ในงานระบบน้ำ ทำไมถึงเป็นตัวเลือกยอดนิยม | ดีล พลัส เทค</title><style>html{font-family:Kanit,system-ui,sans-serif;font-size:18px}@media(min-width:1280px){html{font-size:20px}}@media(min-width:1536px){html{font-size:22px}}@media(min-width:1920px){html{font-size:24px}}
</style></head> <body class="flex flex-col min-h-screen"> <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">ท่อ HDPE</span> <time class="text-secondary-500"> 10 มกราคม 2567 </time> <span class="text-secondary-500"></span> <span class="text-secondary-500">Deal Plus Tech</span> </div> <h1 class="text-4xl md:text-5xl font-bold text-secondary-900 mb-4"> ข้อดีของท่อ HDPE ในงานระบบน้ำ ทำไมถึงเป็นตัวเลือกยอดนิยม </h1> </header> <!-- Featured Image --> <div class="relative aspect-video bg-secondary-100 rounded-xl overflow-hidden mb-8"> <img src="/images/2021/03/hdpe-pipe_000C.jpg" alt="ข้อดีของท่อ HDPE ในงานระบบน้ำ ทำไมถึงเป็นตัวเลือกยอดนิยม" 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"> <h2 id="ท่อ-hdpe-คืออะไร">ท่อ HDPE คืออะไร?</h2>
<p>ท่อ HDPE (High Density Polyethylene) หรือท่อเอชดีพีอี เป็นท่อที่ผลิตจากโพลิเอทิลีนความหนาแน่นสูง เป็นวัสดุพลาสติกที่มีความแข็งแรงและทนทานเป็นอย่างมาก</p>
<h2 id="ข้อดีของท่อ-hdpe">ข้อดีของท่อ HDPE</h2>
<h3 id="1-ความยืดหยุ่นสูง">1. ความยืดหยุ่นสูง</h3>
<p>ท่อ HDPE สามารถโค้งงอได้ถึง 45 องศา ทำให้เหมาะสำหรับพื้นที่ติดตั้งจำกัด และสามารถรองรับการเคลื่อนไหวของดินได้ดี</p>
<h3 id="2-ทนทานต่อสารเคมี">2. ทนทานต่อสารเคมี</h3>
<p>ท่อ HDPE ทนทานต่อการกัดกร่อนของสารเคมี กรด และด่าง ทำให้เหมาะสำหรับงานอุตสาหกรรม</p>
<h3 id="3-อายุการใช้งานยาวนาน">3. อายุการใช้งานยาวนาน</h3>
<p>ท่อ HDPE มีอายุการใช้งานมากกว่า 50 ปี เมื่อติดตั้งและใช้งานอย่างถูกต้อง</p>
<h3 id="4-น้ำหนักเบา">4. น้ำหนักเบา</h3>
<p>ท่อ HDPE มีน้ำหนักเบากว่าท่อโลหะ ทำให้ง่ายต่อการขนส่งและติดตั้ง</p>
<h3 id="5-การเชื่อมต่อที่แน่นหนา">5. การเชื่อมต่อที่แน่นหนา</h3>
<p>การเชื่อมท่อ HDPE ด้วยวิธี Butt Fusion ทำให้ท่อเชื่อมต่อกันเป็นเนื้อเดียว ไม่มีรอยต่อ ป้องกันการรั่วซึม</p>
<h3 id="6-ปลอดภัยต่อสุขภาพ">6. ปลอดภัยต่อสุขภาพ</h3>
<p>ท่อ HDPE ไม่เป็นสนิม ไม่ปล่อยสารพิษ ปลอดภัยสำหรับน้ำดื่ม</p>
<h2 id="การใช้งานท่อ-hdpe">การใช้งานท่อ HDPE</h2>
<h3 id="งานประปา">งานประปา</h3>
<ul>
<li>ท่อส่งน้ำประปา</li>
<li>ระบบประปาในบ้านเรือน</li>
<li>ระบบประปาในอาคาร</li>
</ul>
<h3 id="งานเกษตร">งานเกษตร</h3>
<ul>
<li>ระบบน้ำหยด</li>
<li>ระบบสปริงเกลอร์</li>
<li>ระบบน้ำเพื่อการเกษตร</li>
</ul>
<h3 id="งานอุตสาหกรรม">งานอุตสาหกรรม</h3>
<ul>
<li>ท่อส่งสารเคมี</li>
<li>ระบบบำบัดน้ำเสีย</li>
<li>งานโรงงานอุตสาหกรรม</li>
</ul>
<h3 id="งานโครงสร้างพื้นฐาน">งานโครงสร้างพื้นฐาน</h3>
<ul>
<li>งานท่อใต้ดิน</li>
<li>ท่อร้อยสายไฟ</li>
<li>งานสาธารณูปโภค</li>
</ul>
<h2 id="ขนาดท่อ-hdpe-ที่นิยมใช้">ขนาดท่อ HDPE ที่นิยมใช้</h2>
<table><thead><tr><th>ขนาด (มม.)</th><th>การใช้งาน</th></tr></thead><tbody><tr><td>16-32</td><td>งานประปาภายในบ้าน</td></tr><tr><td>40-63</td><td>งานประปาอาคารขนาดเล็ก</td></tr><tr><td>75-110</td><td>งานประปาอาคารขนาดใหญ่</td></tr><tr><td>125-315</td><td>งานท่อส่งน้ำหลัก</td></tr><tr><td>355-1200</td><td>งานโครงสร้างพื้นฐาน</td></tr></tbody></table>
<h2 id="เกรดของท่อ-hdpe">เกรดของท่อ HDPE</h2>
<h3 id="pe80">PE80</h3>
<ul>
<li>เหมาะสำหรับงานทั่วไป</li>
<li>ทนแรงดันสูงสุด 8 MPa</li>
</ul>
<h3 id="pe100">PE100</h3>
<ul>
<li>เหมาะสำหรับงานที่ต้องการความแข็งแรงสูง</li>
<li>ทนแรงดันสูงสุด 10 MPa</li>
<li>เป็นเกรดที่นิยมใช้ในปัจจุบัน</li>
</ul>
<h2 id="การติดตั้งท่อ-hdpe">การติดตั้งท่อ HDPE</h2>
<h3 id="วิธี-butt-fusion">วิธี Butt Fusion</h3>
<ol>
<li>ตัดท่อให้ตรง</li>
<li>ทำความสะอาดผิวท่อ</li>
<li>ใช้เครื่องเชื่อมท่อ HDPE</li>
<li>ให้ความร้อนจนผิวท่อละลาย</li>
<li>กดท่อเข้าด้วยกัน</li>
<li>รอให้เย็นตัวลง</li>
</ol>
<h3 id="วิธี-electrofusion">วิธี Electrofusion</h3>
<ol>
<li>ใช้ข้อต่อแบบ Electrofusion</li>
<li>เสียบปลั๊กไฟเข้ากับข้อต่อ</li>
<li>รอจนกระบวนการเชื่อมเสร็จสิ้น</li>
</ol>
<h2 id="สรุป">สรุป</h2>
<p>ท่อ HDPE เป็นตัวเลือกที่ยอดเยี่ยมสำหรับงานระบบน้ำ เนื่องจากมีความทนทาน ความยืดหยุ่น และอายุการใช้งานที่ยาวนาน ไม่ว่าจะเป็นงานประปา งานเกษตร หรืองานอุตสาหกรรม ท่อ HDPE สามารถตอบโจทย์ได้ทุกการใช้งาน</p>
<hr>
<p><strong>สนใจสินค้าท่อ HDPE?</strong>
ติดต่อเราได้ที่:</p>
<ul>
<li>โทร: 090-555-1415</li>
<li>LINE: jppselection</li>
</ul>
<p><a href="/%E0%B8%97%E0%B9%88%E0%B8%ADhdpe">ดูสินค้าท่อ HDPE ทั้งหมด</a></p> </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"></path> </svg>
กลับสู่หน้าบทความ
</a> </div> </article> </main> <!-- Cookie Consent Banner --> <script type="module">const t=JSON.parse(localStorage.getItem("consent-preferences")||"null");if(t)t.analytics;else{const e=document.createElement("div");e.id="cookie-banner",e.className="fixed bottom-0 left-0 right-0 bg-secondary-900 text-white p-6 z-50 shadow-lg",e.innerHTML=`
<div class="container mx-auto max-w-4xl">
<p class="text-lg mb-4">เราใช้คุกกี้เพื่อปรับปรุงประสบการณ์การใช้งานเว็บไซต์ โดยคลิกยอมรับเพื่อใช้งานคุกกี้ทุกประเภท หรือคลิกปรับแต่งเพื่อเลือกคุกกี้ที่ต้องการ</p>
<div class="flex flex-wrap gap-3">
<button id="accept-all" class="px-6 py-2 bg-primary-600 text-white font-semibold rounded-md hover:bg-primary-700">ยอมรับ</button>
<button id="reject-all" class="px-6 py-2 bg-secondary-700 text-white font-semibold rounded-md hover:bg-secondary-600">ปฏิเสธ</button>
<button id="customize" class="px-6 py-2 border border-white text-white font-semibold rounded-md hover:bg-white hover:text-secondary-900">ปรับแต่ง</button>
</div>
</div>
`,document.body.appendChild(e),document.getElementById("accept-all")?.addEventListener("click",()=>{localStorage.setItem("consent-preferences",JSON.stringify({essential:!0,analytics:!0,marketing:!0,timestamp:new Date().toISOString()})),e.remove()}),document.getElementById("reject-all")?.addEventListener("click",()=>{localStorage.setItem("consent-preferences",JSON.stringify({essential:!0,analytics:!1,marketing:!1,timestamp:new Date().toISOString()})),e.remove()}),document.getElementById("customize")?.addEventListener("click",()=>{localStorage.setItem("consent-preferences",JSON.stringify({essential:!0,analytics:!1,marketing:!1,timestamp:new Date().toISOString()})),e.remove()})}</script> </body> </html>

View File

@@ -1,73 +0,0 @@
<!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 v5.18.0"><meta name="description" content="ท่อ PPR (Polypropylene Random Copolymer) เป็นท่อพลาสติกที่ได้รับความนิยมสูงในการใช้งานระบบประปา บทความนี้จะอธิบายทุกสิ่งที่คุณต้องรู้เกี่ยวกับท่อ PPR"><!-- 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="ท่อ PPR คืออะไร? คู่มือฉบับสมบูรณ์สำหรับการเลือกใช้งาน"><meta property="og:description" content="ท่อ PPR (Polypropylene Random Copolymer) เป็นท่อพลาสติกที่ได้รับความนิยมสูงในการใช้งานระบบประปา บทความนี้จะอธิบายทุกสิ่งที่คุณต้องรู้เกี่ยวกับท่อ PPR"><meta property="og:image" content="/og-image.jpg"><meta property="og:type" content="website"><meta name="twitter:card" content="summary_large_image"><title>ท่อ PPR คืออะไร? คู่มือฉบับสมบูรณ์สำหรับการเลือกใช้งาน | ดีล พลัส เทค</title><style>html{font-family:Kanit,system-ui,sans-serif;font-size:18px}@media(min-width:1280px){html{font-size:20px}}@media(min-width:1536px){html{font-size:22px}}@media(min-width:1920px){html{font-size:24px}}
</style></head> <body class="flex flex-col min-h-screen"> <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">ท่อ PPR</span> <time class="text-secondary-500"> 15 มกราคม 2567 </time> <span class="text-secondary-500"></span> <span class="text-secondary-500">Deal Plus Tech</span> </div> <h1 class="text-4xl md:text-5xl font-bold text-secondary-900 mb-4"> ท่อ PPR คืออะไร? คู่มือฉบับสมบูรณ์สำหรับการเลือกใช้งาน </h1> </header> <!-- Featured Image --> <div class="relative aspect-video bg-secondary-100 rounded-xl overflow-hidden mb-8"> <img src="/images/2021/03/ppr-pipe_000C.jpg" alt="ท่อ PPR คืออะไร? คู่มือฉบับสมบูรณ์สำหรับการเลือกใช้งาน" 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"> <h2 id="ท่อ-ppr-คืออะไร">ท่อ PPR คืออะไร?</h2>
<p>ท่อ PPR (Polypropylene Random Copolymer) หรือท่อพีพีอาร์ เป็นท่อพลาสติกที่ผลิตจากเม็ดพลาสติก PP-R 80 (Polypropylene Random Copolymer 80) ซึ่งเป็นวัสดุพลาสติกคุณภาพสูงที่มีความแข็งแรงและทนทานเป็นอย่างดี</p>
<h2 id="ข้อดีของท่อ-ppr">ข้อดีของท่อ PPR</h2>
<h3 id="1-ทนแรงดันและอุณหภูมิสูง">1. ทนแรงดันและอุณหภูมิสูง</h3>
<p>ท่อ PPR สามารถทนแรงดันได้สูงถึง 20 บาร์ และทนต่ออุณหภูมิได้สูงถึง 95°C ทำให้เหมาะสำหรับใช้งานทั้งระบบน้ำเย็นและน้ำร้อน</p>
<h3 id="2-สะอาดและปลอดภัย">2. สะอาดและปลอดภัย</h3>
<p>ท่อ PPR ไม่เป็นสนิม ปราศจากโลหะหนักและสิ่งปนเปื้อน ทำให้น้ำที่ไหลผ่านสะอาดและปลอดภัยต่อการบริโภค</p>
<h3 id="3-อายุการใช้งานยาวนาน">3. อายุการใช้งานยาวนาน</h3>
<p>ด้วยคุณสมบัติที่ทนทาน ท่อ PPR มีอายุการใช้งานยาวนานกว่า 50 ปี</p>
<h3 id="4-ติดตั้งง่าย">4. ติดตั้งง่าย</h3>
<p>การเชื่อมต่อท่อ PPR ใช้วิธีเชื่อมด้วยความร้อน ทำให้ท่อและข้อต่อเป็นเนื้อเดียวกัน ไม่มีปัญหารั่วซึม</p>
<h3 id="5-ประหยัดพลังงาน">5. ประหยัดพลังงาน</h3>
<p>ท่อ PPR เป็นฉนวนกันความร้อนที่ดี ช่วยรักษาอุณหภูมิของน้ำได้ดีกว่าท่อโลหะ</p>
<h2 id="การเลือกท่อ-ppr-ที่เหมาะสม">การเลือกท่อ PPR ที่เหมาะสม</h2>
<h3 id="ขนาดท่อ">ขนาดท่อ</h3>
<p>เลือกขนาดท่อให้เหมาะสมกับปริมาณน้ำที่ต้องการใช้งาน:</p>
<ul>
<li>ท่อขนาด 20-25 มม. เหมาะสำหรับบ้านเรือนทั่วไป</li>
<li>ท่อขนาด 32-63 มม. เหมาะสำหรับอาคารขนาดใหญ่</li>
</ul>
<h3 id="เกรดของท่อ">เกรดของท่อ</h3>
<ul>
<li><strong>PN10</strong> - สำหรับน้ำเย็น ทนแรงดัน 10 บาร์</li>
<li><strong>PN16</strong> - สำหรับน้ำอุ่น ทนแรงดัน 16 บาร์</li>
<li><strong>PN20</strong> - สำหรับน้ำร้อน ทนแรงดัน 20 บาร์</li>
</ul>
<h2 id="การติดตั้งท่อ-ppr">การติดตั้งท่อ PPR</h2>
<h3 id="ขั้นตอนการเชื่อมท่อ">ขั้นตอนการเชื่อมท่อ</h3>
<ol>
<li>ตัดท่อให้ตรงและเรียบ</li>
<li>ทำความสะอาดผิวท่อและข้อต่อ</li>
<li>ใช้เครื่องเชื่อมท่ออุณหภูมิ 260°C</li>
<li>สอดท่อและข้อต่อเข้าด้วยกัน</li>
<li>รอให้เย็นตัวลงประมาณ 2-3 นาที</li>
</ol>
<h3 id="ข้อควรระวัง">ข้อควรระวัง</h3>
<ul>
<li>หลีกเลี่ยงการติดตั้งในพื้นที่ที่มีแสงแดดโดยตรง</li>
<li>ควรทิ้งระยะห่างสำหรับการขยายตัวของท่อ</li>
<li>ตรวจสอบความร้อนของเครื่องเชื่อมก่อนใช้งาน</li>
</ul>
<h2 id="ท่อ-ppr-ตราช้าง">ท่อ PPR ตราช้าง</h2>
<p>ท่อ PPR ตราช้าง เป็นท่อ PPR คุณภาพสูงที่ผลิตจากเม็ดพลาสติก PP-R 80 วัตถุดิบคุณภาพสูงมาตรฐานยุโรปจาก lyondellbasell</p>
<p><strong>คุณสมบัติเด่น:</strong></p>
<ul>
<li>ทนแรงดันได้สูงสุด 20 บาร์</li>
<li>ทนต่ออุณหภูมิได้สูงถึง 95°C</li>
<li>ผลิตตามมาตรฐาน DIN8077 และ DIN8078 ของประเทศเยอรมัน</li>
<li>รับประกันคุณภาพ</li>
</ul>
<h2 id="สรุป">สรุป</h2>
<p>ท่อ PPR เป็นตัวเลือกที่ดีสำหรับระบบประปาในปัจจุบัน เนื่องจากมีความทนทานสูง ติดตั้งง่าย และมีอายุการใช้งานยาวนาน หากคุณกำลังมองหาท่อสำหรับงานระบบน้ำ ท่อ PPR เป็นตัวเลือกที่คุ้มค่าและเหมาะสม</p>
<hr>
<p><strong>สนใจสินค้าท่อ PPR?</strong>
ติดต่อเราได้ที่:</p>
<ul>
<li>โทร: 090-555-1415</li>
<li>LINE: jppselection</li>
<li>อีเมล: <a href="mailto:dealplustech@gmail.com">dealplustech@gmail.com</a></li>
</ul>
<p><a href="/%E0%B8%97%E0%B9%88%E0%B8%AD%E0%B8%9E%E0%B8%B5%E0%B8%9E%E0%B8%B5%E0%B8%AD%E0%B8%B2%E0%B8%A3%E0%B9%8C%E0%B8%95%E0%B8%A3%E0%B8%B2%E0%B8%8A%E0%B9%89%E0%B8%B2%E0%B8%87">ดูสินค้าท่อ PPR ทั้งหมด</a></p> </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"></path> </svg>
กลับสู่หน้าบทความ
</a> </div> </article> </main> <!-- Cookie Consent Banner --> <script type="module">const t=JSON.parse(localStorage.getItem("consent-preferences")||"null");if(t)t.analytics;else{const e=document.createElement("div");e.id="cookie-banner",e.className="fixed bottom-0 left-0 right-0 bg-secondary-900 text-white p-6 z-50 shadow-lg",e.innerHTML=`
<div class="container mx-auto max-w-4xl">
<p class="text-lg mb-4">เราใช้คุกกี้เพื่อปรับปรุงประสบการณ์การใช้งานเว็บไซต์ โดยคลิกยอมรับเพื่อใช้งานคุกกี้ทุกประเภท หรือคลิกปรับแต่งเพื่อเลือกคุกกี้ที่ต้องการ</p>
<div class="flex flex-wrap gap-3">
<button id="accept-all" class="px-6 py-2 bg-primary-600 text-white font-semibold rounded-md hover:bg-primary-700">ยอมรับ</button>
<button id="reject-all" class="px-6 py-2 bg-secondary-700 text-white font-semibold rounded-md hover:bg-secondary-600">ปฏิเสธ</button>
<button id="customize" class="px-6 py-2 border border-white text-white font-semibold rounded-md hover:bg-white hover:text-secondary-900">ปรับแต่ง</button>
</div>
</div>
`,document.body.appendChild(e),document.getElementById("accept-all")?.addEventListener("click",()=>{localStorage.setItem("consent-preferences",JSON.stringify({essential:!0,analytics:!0,marketing:!0,timestamp:new Date().toISOString()})),e.remove()}),document.getElementById("reject-all")?.addEventListener("click",()=>{localStorage.setItem("consent-preferences",JSON.stringify({essential:!0,analytics:!1,marketing:!1,timestamp:new Date().toISOString()})),e.remove()}),document.getElementById("customize")?.addEventListener("click",()=>{localStorage.setItem("consent-preferences",JSON.stringify({essential:!0,analytics:!1,marketing:!1,timestamp:new Date().toISOString()})),e.remove()})}</script> </body> </html>

View File

@@ -1,165 +0,0 @@
<!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 v5.18.0"><meta name="description" content="ปั๊มน้ำเป็นอุปกรณ์สำคัญในระบบน้ำทุกบ้าน การบำรุงรักษาที่ถูกต้องจะช่วยยืดอายุการใช้งานและประหยัดค่าไฟฟ้า"><!-- 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="การบำรุงรักษาปั๊มน้ำให้มีอายุการใช้งานยาวนาน"><meta property="og:description" content="ปั๊มน้ำเป็นอุปกรณ์สำคัญในระบบน้ำทุกบ้าน การบำรุงรักษาที่ถูกต้องจะช่วยยืดอายุการใช้งานและประหยัดค่าไฟฟ้า"><meta property="og:image" content="/og-image.jpg"><meta property="og:type" content="website"><meta name="twitter:card" content="summary_large_image"><title>การบำรุงรักษาปั๊มน้ำให้มีอายุการใช้งานยาวนาน | ดีล พลัส เทค</title><style>html{font-family:Kanit,system-ui,sans-serif;font-size:18px}@media(min-width:1280px){html{font-size:20px}}@media(min-width:1536px){html{font-size:22px}}@media(min-width:1920px){html{font-size:24px}}
</style></head> <body class="flex flex-col min-h-screen"> <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">ปั๊มน้ำ</span> <time class="text-secondary-500"> 5 มกราคม 2567 </time> <span class="text-secondary-500"></span> <span class="text-secondary-500">Deal Plus Tech</span> </div> <h1 class="text-4xl md:text-5xl font-bold text-secondary-900 mb-4"> การบำรุงรักษาปั๊มน้ำให้มีอายุการใช้งานยาวนาน </h1> </header> <!-- Featured Image --> <div class="relative aspect-video bg-secondary-100 rounded-xl overflow-hidden mb-8"> <img src="/images/2021/02/Water-Pump1.jpg" alt="การบำรุงรักษาปั๊มน้ำให้มีอายุการใช้งานยาวนาน" 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"> <h2 id="ความสำคัญของการบำรุงรักษาปั๊มน้ำ">ความสำคัญของการบำรุงรักษาปั๊มน้ำ</h2>
<p>ปั๊มน้ำเป็นหัวใจของระบบน้ำในบ้าน การบำรุงรักษาอย่างสม่ำเสมอจะช่วย:</p>
<ul>
<li>ยืดอายุการใช้งานของปั๊มน้ำ</li>
<li>ลดปัญหาการเสีย</li>
<li>ประหยัดค่าไฟฟ้า</li>
<li>ป้องกันอุบัติเหตุจากการรั่วซึม</li>
</ul>
<h2 id="การบำรุงรักษาปั๊มน้ำแบบทำเอง">การบำรุงรักษาปั๊มน้ำแบบทำเอง</h2>
<h3 id="1-ตรวจสอบสายไฟและสวิตช์">1. ตรวจสอบสายไฟและสวิตช์</h3>
<ul>
<li>ตรวจสอบสายไฟว่ามีรอยชำรุดหรือไม่</li>
<li>ตรวจสอบสวิตช์ว่าทำงานปกติหรือไม่</li>
<li>หากพบความผิดปกติควรเรียกช่าง</li>
</ul>
<h3 id="2-ทำความสะอาดตัวกรอง">2. ทำความสะอาดตัวกรอง</h3>
<ul>
<li>ปิดวาล์วน้ำเข้าก่อนทำความสะอาด</li>
<li>ถอดตัวกรองออกมาล้าง</li>
<li>ตรวจสอบว่ามีสิ่งปนเปื้อนหรือไม่</li>
<li>ติดตั้งกลับเข้าที่เดิม</li>
</ul>
<h3 id="3-ตรวจสอบแรงดันน้ำ">3. ตรวจสอบแรงดันน้ำ</h3>
<ul>
<li>สังเกตแรงดันน้ำว่าลดลงหรือไม่</li>
<li>ตรวจสอบว่ามีเสียงผิดปกติหรือไม่</li>
<li>หากแรงดันลดลงอาจมีการรั่วซึม</li>
</ul>
<h3 id="4-ตรวจสอบถังแรงดัน-pressure-tank">4. ตรวจสอบถังแรงดัน (Pressure Tank)</h3>
<ul>
<li>ตรวจสอบว่าถังมีอากาศเพียงพอหรือไม่</li>
<li>หากปั๊มเปิด-ปิดบ่อยผิดปกติ อาจต้องเติมอากาศ</li>
<li>ควรตรวจสอบทุก 6 เดือน</li>
</ul>
<h2 id="ปัญหาที่พบบ่อยและวิธีแก้ไข">ปัญหาที่พบบ่อยและวิธีแก้ไข</h2>
<h3 id="ปั๊มไม่ทำงาน">ปั๊มไม่ทำงาน</h3>
<p><strong>สาเหตุ:</strong></p>
<ul>
<li>ไฟดับหรือสายไฟขาด</li>
<li>สวิตช์เสีย</li>
<li>มอเตอร์เสีย</li>
</ul>
<p><strong>วิธีแก้:</strong></p>
<ul>
<li>ตรวจสอบไฟและสายไฟ</li>
<li>เปลี่ยนสวิตช์</li>
<li>เรียกช่างซ่อมมอเตอร์</li>
</ul>
<h3 id="แรงดันน้ำต่ำ">แรงดันน้ำต่ำ</h3>
<p><strong>สาเหตุ:</strong></p>
<ul>
<li>ตัวกรองอุดตัน</li>
<li>ท่อรั่ว</li>
<li>ใบพัดสึกหรอ</li>
</ul>
<p><strong>วิธีแก้:</strong></p>
<ul>
<li>ทำความสะอาดตัวกรอง</li>
<li>ตรวจสอบและซ่อมท่อ</li>
<li>เปลี่ยนใบพัด</li>
</ul>
<h3 id="ปั๊มเปิด-ปิดบ่อย">ปั๊มเปิด-ปิดบ่อย</h3>
<p><strong>สาเหตุ:</strong></p>
<ul>
<li>ถังแรงดันอากาศรั่ว</li>
<li>แผ่นไดอะแฟรมแตก</li>
<li>วาล์วตรวจสอบแรงดันเสีย</li>
</ul>
<p><strong>วิธีแก้:</strong></p>
<ul>
<li>เติมอากาศในถัง</li>
<li>เปลี่ยนแผ่นไดอะแฟรม</li>
<li>เปลี่ยนวาล์ว</li>
</ul>
<h3 id="ปั๊มมีเสียงดังผิดปกติ">ปั๊มมีเสียงดังผิดปกติ</h3>
<p><strong>สาเหตุ:</strong></p>
<ul>
<li>ลูกปืนเสีย</li>
<li>ใบพัดชำรุด</li>
<li>การติดตั้งไม่แน่นหนา</li>
</ul>
<p><strong>วิธีแก้:</strong></p>
<ul>
<li>เปลี่ยนลูกปืน</li>
<li>เปลี่ยนใบพัด</li>
<li>ตรวจสอบการยึดแน่น</li>
</ul>
<h2 id="ตารางการบำรุงรักษา">ตารางการบำรุงรักษา</h2>
<table><thead><tr><th>รายการ</th><th>ความถี่</th><th>หมายเหตุ</th></tr></thead><tbody><tr><td>ตรวจสอบสายไฟ</td><td>ทุกเดือน</td><td>มองหารอยชำรุด</td></tr><tr><td>ทำความสะอาดตัวกรอง</td><td>ทุก 3 เดือน</td><td>หรือเมื่อแรงดันลด</td></tr><tr><td>ตรวจสอบถังแรงดัน</td><td>ทุก 6 เดือน</td><td>เติมอากาศหากจำเป็น</td></tr><tr><td>ตรวจสอบสวิตช์</td><td>ทุกปี</td><td>เปลี่ยนหากเสีย</td></tr><tr><td>ตรวจสอบใบพัด</td><td>ทุก 2 ปี</td><td>โดยช่างผู้เชี่ยวชาญ</td></tr></tbody></table>
<h2 id="เคล็ดลับการใช้งานปั๊มน้ำ">เคล็ดลับการใช้งานปั๊มน้ำ</h2>
<h3 id="ประหยัดไฟฟ้า">ประหยัดไฟฟ้า</h3>
<ul>
<li>เลือกขนาดปั๊มที่เหมาะสมกับการใช้งาน</li>
<li>ติดตั้งถังแรงดันขนาดเหมาะสม</li>
<li>หลีกเลี่ยงการเปิด-ปิดปั๊มบ่อย</li>
</ul>
<h3 id="ป้องกันปัญหา">ป้องกันปัญหา</h3>
<ul>
<li>อย่าให้ปั๊มแห้ง (ทำงานโดยไม่มีน้ำ)</li>
<li>ตรวจสอบรอยรั่วอย่างสม่ำเสมอ</li>
<li>ใช้ตัวกรองเพื่อป้องกันสิ่งสกปรก</li>
</ul>
<h3 id="เมื่อต้องเปลี่ยนปั๊ม">เมื่อต้องเปลี่ยนปั๊ม</h3>
<ul>
<li>เลือกปั๊มที่มีคุณภาพ</li>
<li>พิจารณาขนาดและกำลังที่เหมาะสม</li>
<li>ติดตั้งโดยช่างผู้เชี่ยวชาญ</li>
</ul>
<h2 id="สรุป">สรุป</h2>
<p>การบำรุงรักษาปั๊มน้ำอย่างสม่ำเสมอจะช่วยยืดอายุการใช้งาน ลดปัญหาการเสีย และประหยัดค่าใช้จ่ายในระยะยาว ควรตรวจสอบและบำรุงรักษาตามตารางที่กำหนด และหากพบปัญหาที่ไม่สามารถแก้ไขได้เอง ควรติดต่อช่างผู้เชี่ยวชาญ</p>
<hr>
<p><strong>ต้องการซื้อปั๊มน้ำหรืออุปกรณ์เสริม?</strong>
ติดต่อเราได้ที่:</p>
<ul>
<li>โทร: 090-555-1415</li>
<li>LINE: jppselection</li>
</ul>
<p><a href="/%E0%B8%9B%E0%B8%B1%E0%B9%8A%E0%B8%A1%E0%B8%99%E0%B9%89%E0%B8%B3-pump">ดูสินค้าปั๊มน้ำทั้งหมด</a></p> </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"></path> </svg>
กลับสู่หน้าบทความ
</a> </div> </article> </main> <!-- Cookie Consent Banner --> <script type="module">const t=JSON.parse(localStorage.getItem("consent-preferences")||"null");if(t)t.analytics;else{const e=document.createElement("div");e.id="cookie-banner",e.className="fixed bottom-0 left-0 right-0 bg-secondary-900 text-white p-6 z-50 shadow-lg",e.innerHTML=`
<div class="container mx-auto max-w-4xl">
<p class="text-lg mb-4">เราใช้คุกกี้เพื่อปรับปรุงประสบการณ์การใช้งานเว็บไซต์ โดยคลิกยอมรับเพื่อใช้งานคุกกี้ทุกประเภท หรือคลิกปรับแต่งเพื่อเลือกคุกกี้ที่ต้องการ</p>
<div class="flex flex-wrap gap-3">
<button id="accept-all" class="px-6 py-2 bg-primary-600 text-white font-semibold rounded-md hover:bg-primary-700">ยอมรับ</button>
<button id="reject-all" class="px-6 py-2 bg-secondary-700 text-white font-semibold rounded-md hover:bg-secondary-600">ปฏิเสธ</button>
<button id="customize" class="px-6 py-2 border border-white text-white font-semibold rounded-md hover:bg-white hover:text-secondary-900">ปรับแต่ง</button>
</div>
</div>
`,document.body.appendChild(e),document.getElementById("accept-all")?.addEventListener("click",()=>{localStorage.setItem("consent-preferences",JSON.stringify({essential:!0,analytics:!0,marketing:!0,timestamp:new Date().toISOString()})),e.remove()}),document.getElementById("reject-all")?.addEventListener("click",()=>{localStorage.setItem("consent-preferences",JSON.stringify({essential:!0,analytics:!1,marketing:!1,timestamp:new Date().toISOString()})),e.remove()}),document.getElementById("customize")?.addEventListener("click",()=>{localStorage.setItem("consent-preferences",JSON.stringify({essential:!0,analytics:!1,marketing:!1,timestamp:new Date().toISOString()})),e.remove()})}</script> </body> </html>

View File

@@ -1,35 +0,0 @@
<!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 v5.18.0"><meta name="description" content="นโยบายการใช้งานคุกกี้ของเว็บไซต์บริษัท ดีล พลัส เทค จำกัด"><!-- 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="นโยบายคุกกี้"><meta property="og:description" content="นโยบายการใช้งานคุกกี้ของเว็บไซต์บริษัท ดีล พลัส เทค จำกัด"><meta property="og:image" content="/og-image.jpg"><meta property="og:type" content="website"><meta name="twitter:card" content="summary_large_image"><title>นโยบายคุกกี้ | ดีล พลัส เทค</title><style>html{font-family:Kanit,system-ui,sans-serif;font-size:18px}@media(min-width:1280px){html{font-size:20px}}@media(min-width:1536px){html{font-size:22px}}@media(min-width:1920px){html{font-size:24px}}
</style></head> <body class="flex flex-col min-h-screen"> <main class="min-h-screen bg-secondary-50"> <div class="container mx-auto px-4 py-12"> <div class="max-w-4xl mx-auto bg-white rounded-lg shadow-lg p-8 md:p-12"> <h1 class="text-4xl font-bold text-secondary-900 mb-4">นโยบายคุกกี้</h1> <p class="text-secondary-600 mb-8">Cookie Policy - ปรับปรุงล่าสุด: 9 มีนาคม 2026</p> <div class="prose prose-lg max-w-none text-secondary-700"> <section class="mb-8"> <h2 class="text-2xl font-bold text-secondary-900 mb-4">1. คุกกี้คืออะไร?</h2> <p class="mb-4">
คุกกี้ (Cookie) คือไฟล์ข้อความขนาดเล็กที่เว็บไซต์บันทึกลงบนอุปกรณ์ของท่าน
(คอมพิวเตอร์, แท็บเล็ต, หรือมือถือ) เมื่อท่านเยี่ยมชมเว็บไซต์
คุกกี้ช่วยให้เว็บไซต์จดจำการกระทำและความชอบของท่าน ทำให้ประสบการณ์การใช้งานดีขึ้น
</p> </section> <section class="mb-8"> <h2 class="text-2xl font-bold text-secondary-900 mb-4">2. ประเภทคุกกี้ที่เราใช้</h2> <p class="mb-4">เราใช้คุกกี้ 3 ประเภท:</p> <div class="space-y-4"> <div class="bg-secondary-50 p-4 rounded-lg"> <h3 class="text-lg font-semibold mb-2">🔒 คุกกี้ที่จำเป็น (Essential Cookies)</h3> <p class="text-sm">
คุกกี้เหล่านี้จำเป็นสำหรับการทำงานของเว็บไซต์ ไม่สามารถปิดใช้งานได้
ใช้สำหรับ: การจัดการเซสชัน, ความปลอดภัย, การทำงานพื้นฐานของเว็บไซต์
</p> <p class="text-sm mt-2"><strong>ความยินยอม:</strong> ไม่จำเป็น (เปิดเสมอ)</p> </div> <div class="bg-secondary-50 p-4 rounded-lg"> <h3 class="text-lg font-semibold mb-2">📊 คุกกี้วิเคราะห์ (Analytics Cookies)</h3> <p class="text-sm">
คุกกี้เหล่านี้帮助我们เก็บข้อมูลการใช้งานเว็บไซต์แบบไม่ระบุตัวตน
ใช้สำหรับ: การวิเคราะห์ผู้เยี่ยมชม, หน้าเว็บที่นิยม, อัตราการตีกลับ
</p> <p class="text-sm mt-2"><strong>ความยินยอม:</strong> ต้องเปิดใช้งาน (Opt-in)</p> </div> <div class="bg-secondary-50 p-4 rounded-lg"> <h3 class="text-lg font-semibold mb-2">📢 คุกกี้การตลาด (Marketing Cookies)</h3> <p class="text-sm">
คุกกี้เหล่านี้ใช้เพื่อติดตามผู้ใช้งานบนเว็บไซต์ต่าง ๆ
ใช้สำหรับ: การโฆษณาที่กำหนดเป้าหมาย, การวัดประสิทธิภาพโฆษณา
</p> <p class="text-sm mt-2"><strong>ความยินยอม:</strong> ต้องเปิดใช้งาน (Opt-in)</p> </div> </div> </section> <section class="mb-8"> <h2 class="text-2xl font-bold text-secondary-900 mb-4">3. คุกกี้ที่เราใช้</h2> <div class="overflow-x-auto"> <table class="min-w-full border"> <thead class="bg-secondary-100"> <tr> <th class="px-4 py-2 border text-left">ชื่อคุกกี้</th> <th class="px-4 py-2 border text-left">ประเภท</th> <th class="px-4 py-2 border text-left">ระยะเวลา</th> <th class="px-4 py-2 border text-left">วัตถุประสงค์</th> </tr> </thead> <tbody> <tr> <td class="px-4 py-2 border">session_id</td> <td class="px-4 py-2 border">จำเป็น</td> <td class="px-4 py-2 border">จนกว่าจะปิดเบราว์เซอร์</td> <td class="px-4 py-2 border">จัดการเซสชัน</td> </tr> <tr> <td class="px-4 py-2 border">consent-preferences</td> <td class="px-4 py-2 border">จำเป็น</td> <td class="px-4 py-2 border">1 ปี</td> <td class="px-4 py-2 border">บันทึกการตั้งค่าคุกกี้</td> </tr> <tr> <td class="px-4 py-2 border">umami analytics</td> <td class="px-4 py-2 border">วิเคราะห์</td> <td class="px-4 py-2 border">ไม่ใช้คุกกี้</td> <td class="px-4 py-2 border">วิเคราะห์การใช้งาน (Privacy-first)</td> </tr> </tbody> </table> </div> </section> <section class="mb-8"> <h2 class="text-2xl font-bold text-secondary-900 mb-4">4. การจัดการคุกกี้</h2> <p class="mb-4">ท่านสามารถจัดการการตั้งค่าคุกกี้ได้โดย:</p> <ul class="list-disc pl-6 space-y-2"> <li><strong>แบนเนอร์คุกกี้:</strong> คลิกที่ปุ่ม "ปรับแต่ง" ในแบนเนอร์คุกกี้เพื่อเลือกคุกกี้ที่ต้องการ</li> <li><strong>การตั้งค่าเบราว์เซอร์:</strong> เบราว์เซอร์ส่วนใหญ่ยอมให้ท่านบล็อกหรือลบคุกกี้ได้</li> <li><strong>ลิงก์ในฟุตเตอร์:</strong> คลิก "การตั้งค่าคุกกี้" ที่ด้านล่างของหน้าเว็บ</li> </ul> <p class="mt-4"> <a href="#" id="openPreferences" class="text-primary-600 hover:underline font-semibold">
→ เปิดการตั้งค่าคุกกี้ตอนนี้
</a> </p> </section> <section class="mb-8"> <h2 class="text-2xl font-bold text-secondary-900 mb-4">5. การเพิกถอนความยินยอม</h2> <p class="mb-4">
ท่านสามารถเพิกถอนความยินยอมสำหรับคุกกี้วิเคราะห์และคุกกี้การตลาดเมื่อใดก็ได้
โดยไปที่การตั้งค่าคุกกี้และปิดการใช้งานคุกกี้เหล่านั้น
การเพิกถอนความยินยอมจะไม่มีผลต่อความถูกต้องของการประมวลผลก่อนการเพิกถอน
</p> </section> <section class="mb-8"> <h2 class="text-2xl font-bold text-secondary-900 mb-4">6. การอัปเดตนโยบาย</h2> <p class="mb-4">
เราอาจอัปเดตนโยบายคุกกี้นี้เป็นครั้งคราว การเปลี่ยนแปลงใด ๆ จะถูกเผยแพร่บนหน้านี้
กรุณาตรวจสอบหน้าทนี้เป็นระยะเพื่อดูการเปลี่ยนแปลง
</p> </section> <section class="mb-8"> <h2 class="text-2xl font-bold text-secondary-900 mb-4">7. การติดต่อ</h2> <p class="mb-4">หากมีคำถามเกี่ยวกับนโยบายคุกกี้นี้ กรุณาติดต่อ:</p> <div class="bg-secondary-50 p-4 rounded-lg"> <p><strong>บริษัท ดีล พลัส เทค จำกัด</strong></p> <p>อีเมล: info@dealplustech.co.th</p> <p>โทรศัพท์: 090-555-1415</p> </div> </section> <section class="mt-12 pt-8 border-t border-secondary-200"> <p class="text-sm text-secondary-600">
อ่านเพิ่มเติม: <a href="/privacy-policy/" class="text-primary-600 hover:underline">นโยบายความเป็นส่วนตัว</a> |
<a href="/terms-and-conditions/" class="text-primary-600 hover:underline">ข้อกำหนดและเงื่อนไข</a> </p> </section> </div> </div> </div> </main> <!-- Cookie Consent Banner --> <script type="module">const t=JSON.parse(localStorage.getItem("consent-preferences")||"null");if(t)t.analytics;else{const e=document.createElement("div");e.id="cookie-banner",e.className="fixed bottom-0 left-0 right-0 bg-secondary-900 text-white p-6 z-50 shadow-lg",e.innerHTML=`
<div class="container mx-auto max-w-4xl">
<p class="text-lg mb-4">เราใช้คุกกี้เพื่อปรับปรุงประสบการณ์การใช้งานเว็บไซต์ โดยคลิกยอมรับเพื่อใช้งานคุกกี้ทุกประเภท หรือคลิกปรับแต่งเพื่อเลือกคุกกี้ที่ต้องการ</p>
<div class="flex flex-wrap gap-3">
<button id="accept-all" class="px-6 py-2 bg-primary-600 text-white font-semibold rounded-md hover:bg-primary-700">ยอมรับ</button>
<button id="reject-all" class="px-6 py-2 bg-secondary-700 text-white font-semibold rounded-md hover:bg-secondary-600">ปฏิเสธ</button>
<button id="customize" class="px-6 py-2 border border-white text-white font-semibold rounded-md hover:bg-white hover:text-secondary-900">ปรับแต่ง</button>
</div>
</div>
`,document.body.appendChild(e),document.getElementById("accept-all")?.addEventListener("click",()=>{localStorage.setItem("consent-preferences",JSON.stringify({essential:!0,analytics:!0,marketing:!0,timestamp:new Date().toISOString()})),e.remove()}),document.getElementById("reject-all")?.addEventListener("click",()=>{localStorage.setItem("consent-preferences",JSON.stringify({essential:!0,analytics:!1,marketing:!1,timestamp:new Date().toISOString()})),e.remove()}),document.getElementById("customize")?.addEventListener("click",()=>{localStorage.setItem("consent-preferences",JSON.stringify({essential:!0,analytics:!1,marketing:!1,timestamp:new Date().toISOString()})),e.remove()})}</script> </body> </html> <script type="module">document.getElementById("openPreferences")?.addEventListener("click",e=>{e.preventDefault(),window.dispatchEvent(new CustomEvent("open-cookie-preferences"))});</script>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 655 B

View File

@@ -1,9 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
<path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
<style>
path { fill: #000; }
@media (prefers-color-scheme: dark) {
path { fill: #FFF; }
}
</style>
</svg>

Before

Width:  |  Height:  |  Size: 749 B

File diff suppressed because one or more lines are too long

View File

@@ -1,32 +0,0 @@
<!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 v5.18.0"><meta name="description" content="นโยบายความเป็นส่วนตัวตามกฎหมายคุ้มครองข้อมูลส่วนบุคคล (PDPA) ของบริษัท ดีล พลัส เทค จำกัด"><!-- 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="นโยบายความเป็นส่วนตัว"><meta property="og:description" content="นโยบายความเป็นส่วนตัวตามกฎหมายคุ้มครองข้อมูลส่วนบุคคล (PDPA) ของบริษัท ดีล พลัส เทค จำกัด"><meta property="og:image" content="/og-image.jpg"><meta property="og:type" content="website"><meta name="twitter:card" content="summary_large_image"><title>นโยบายความเป็นส่วนตัว | ดีล พลัส เทค</title><style>html{font-family:Kanit,system-ui,sans-serif;font-size:18px}@media(min-width:1280px){html{font-size:20px}}@media(min-width:1536px){html{font-size:22px}}@media(min-width:1920px){html{font-size:24px}}
</style></head> <body class="flex flex-col min-h-screen"> <main class="min-h-screen bg-secondary-50"> <div class="container mx-auto px-4 py-12"> <div class="max-w-4xl mx-auto bg-white rounded-lg shadow-lg p-8 md:p-12"> <h1 class="text-4xl font-bold text-secondary-900 mb-4">นโยบายความเป็นส่วนตัว</h1> <p class="text-secondary-600 mb-8">Privacy Policy - ปรับปรุงล่าสุด: 9 มีนาคม 2026</p> <nav class="mb-8 p-4 bg-secondary-50 rounded-lg"> <h2 class="text-lg font-semibold mb-3">สารบัญ</h2> <ul class="space-y-1 text-primary-600"> <li><a href="#section1" class="hover:underline">1. ข้อมูลผู้ควบคุมข้อมูลส่วนบุคคล</a></li> <li><a href="#section2" class="hover:underline">2. ประเภทข้อมูลที่เก็บรวบรวม</a></li> <li><a href="#section3" class="hover:underline">3. วัตถุประสงค์การเก็บรวบรวม</a></li> <li><a href="#section4" class="hover:underline">4. ฐานทางกฎหมายสำหรับการประมวลผล</a></li> <li><a href="#section5" class="hover:underline">5. ระยะเวลาเก็บรักษาข้อมูล</a></li> <li><a href="#section6" class="hover:underline">6. การเปิดเผยข้อมูล</a></li> <li><a href="#section9" class="hover:underline">7. คุกกี้และเทคโนโลยีการติดตาม</a></li> <li><a href="#section10" class="hover:underline">8. สิทธิของเจ้าของข้อมูล</a></li> <li><a href="#section11" class="hover:underline">9. มาตรการรักษาความปลอดภัย</a></li> <li><a href="#section13" class="hover:underline">10. สิทธิร้องเรียน</a></li> </ul> </nav> <div class="prose prose-lg max-w-none"> <section id="section1" class="mb-8"> <h2 class="text-2xl font-bold text-secondary-900 mb-4">1. ข้อมูลผู้ควบคุมข้อมูลส่วนบุคคล</h2> <p class="text-secondary-700 mb-4"> <strong>บริษัท ดีล พลัส เทค จำกัด</strong> เป็นผู้ควบคุมข้อมูลส่วนบุคคล (Data Controller)
ซึ่งมีหน้าที่ในการตัดสินใจเกี่ยวกับการเก็บรวบรวม ใช้ หรือเปิดเผยข้อมูลส่วนบุคคล
</p> <div class="bg-secondary-50 p-4 rounded-lg"> <p class="text-secondary-700"><strong>ที่อยู่:</strong> 9/70 ซอยนครลุง 17 แขวงบางไผ่ เขตบางแค กทม. 10160</p> <p class="text-secondary-700"><strong>เบอร์โทรศัพท์:</strong> 090-555-1415</p> <p class="text-secondary-700"><strong>อีเมล:</strong> info@dealplustech.co.th</p> </div> </section> <section id="section2" class="mb-8"> <h2 class="text-2xl font-bold text-secondary-900 mb-4">2. ประเภทข้อมูลที่เก็บรวบรวม</h2> <p class="text-secondary-700 mb-4">เราเก็บรวบรวมข้อมูลส่วนบุคคลดังนี้:</p> <ul class="list-disc pl-6 space-y-2 text-secondary-700"> <li><strong>ข้อมูลส่วนบุคคลทั่วไป:</strong> ชื่อ, นามสกุล, ที่อยู่อีเมล, เบอร์โทรศัพท์</li> <li><strong>ข้อมูลการใช้งาน:</strong> IP Address, Browser Type, Device Information, Cookie Data</li> <li><strong>ข้อมูลการติดต่อ:</strong> ข้อความหรือคำถามที่ท่านส่งถึงเรา</li> </ul> </section> <section id="section3" class="mb-8"> <h2 class="text-2xl font-bold text-secondary-900 mb-4">3. วัตถุประสงค์การเก็บรวบรวม</h2> <p class="text-secondary-700 mb-4">เราเก็บรวบรวมข้อมูลเพื่อวัตถุประสงค์ดังนี้:</p> <ul class="list-disc pl-6 space-y-2 text-secondary-700"> <li>เพื่อให้บริการและตอบคำถามหรือคำขอของท่าน</li> <li>เพื่อปรับปรุงประสบการณ์การใช้งานเว็บไซต์</li> <li>เพื่อส่งข้อมูลข่าวสารและการตลาด (เมื่อได้รับความยินยอม)</li> <li>เพื่อวิเคราะห์และปรับปรุงบริการของเรา</li> <li>เพื่อปฏิบัติตามกฎหมายและข้อบังคับที่เกี่ยวข้อง</li> </ul> </section> <section id="section4" class="mb-8"> <h2 class="text-2xl font-bold text-secondary-900 mb-4">4. ฐานทางกฎหมายสำหรับการประมวลผล</h2> <p class="text-secondary-700 mb-4">เราประมวลผลข้อมูลส่วนบุคคลภายใต้ฐานทางกฎหมายดังนี้:</p> <ul class="list-disc pl-6 space-y-2 text-secondary-700"> <li><strong>ความยินยอม (Consent):</strong> สำหรับการส่งข้อมูลการตลาดและการใช้คุกกี้บางประเภท</li> <li><strong>การปฏิบัติตามสัญญา (Contract):</strong> เพื่อให้บริการที่ท่านร้องขอ</li> <li><strong>ผลประโยชน์โดยชอบด้วยกฎหมาย (Legitimate Interest):</strong> เพื่อปรับปรุงบริการและวิเคราะห์การใช้งาน</li> <li><strong>การปฏิบัติตามกฎหมาย (Legal Obligation):</strong> เมื่อจำเป็นต้องปฏิบัติตามกฎหมาย</li> </ul> </section> <section id="section5" class="mb-8"> <h2 class="text-2xl font-bold text-secondary-900 mb-4">5. ระยะเวลาเก็บรักษาข้อมูล</h2> <p class="text-secondary-700 mb-4">
เราเก็บรักษาข้อมูลส่วนบุคคลเป็นเวลา <strong>10 ปี</strong> ตามข้อกำหนดของกฎหมาย PDPA
หรือตราบเท่าที่จำเป็นสำหรับวัตถุประสงค์ที่ระบุไว้ข้างต้น
หลังจากสิ้นสุดระยะเวลาเก็บรักษา เราจะทำลายหรือทำให้ข้อมูลไม่สามารถระบุตัวตนได้
</p> </section> <section id="section6" class="mb-8"> <h2 class="text-2xl font-bold text-secondary-900 mb-4">6. การเปิดเผยข้อมูล</h2> <p class="text-secondary-700 mb-4">
เราจะไม่เปิดเผยข้อมูลส่วนบุคคลของท่านให้กับบุคคลภายนอก เว้นแต่:
</p> <ul class="list-disc pl-6 space-y-2 text-secondary-700"> <li>ท่านให้ความยินยอมอย่างชัดเจน</li> <li>จำเป็นต้องปฏิบัติตามกฎหมายหรือคำสั่งศาล</li> <li>จำเป็นสำหรับการให้บริการที่ท่านร้องขอ (เช่น ผู้ให้บริการจัดส่ง)</li> <li>เพื่อปกป้องสิทธิและความปลอดภัยของเราและผู้อื่น</li> </ul> </section> <section id="section9" class="mb-8"> <h2 class="text-2xl font-bold text-secondary-900 mb-4">7. คุกกี้และเทคโนโลยีการติดตาม</h2> <p class="text-secondary-700 mb-4">
เราใช้คุกกี้และเทคโนโลยีการติดตามเพื่อปรับปรุงประสบการณ์การใช้งานเว็บไซต์ ท่านสามารถจัดการการตั้งค่าคุกกี้ได้ที่
<a href="/cookie-policy/" class="text-primary-600 hover:underline">นโยบายคุกกี้</a> </p> </section> <section id="section10" class="mb-8"> <h2 class="text-2xl font-bold text-secondary-900 mb-4">8. สิทธิของเจ้าของข้อมูล</h2> <p class="text-secondary-700 mb-4">
ภายใต้กฎหมาย PDPA ท่านมีสิทธิดังต่อไปนี้:
</p> <ul class="list-disc pl-6 space-y-2 text-secondary-700"> <li><strong>สิทธิขอเข้าถึง:</strong> ขอรับสำเนาข้อมูลส่วนบุคคลที่ท่านให้ไว้</li> <li><strong>สิทธิขอแก้ไข:</strong> ขอให้แก้ไขข้อมูลที่ไม่ถูกต้อง</li> <li><strong>สิทธิขอ ลบ:</strong> ขอให้ ลบข้อมูลส่วนบุคคล (Right to Erasure)</li> <li><strong>สิทธิขอระงับ:</strong> ขอให้ระงับการประมวลผลข้อมูล</li> <li><strong>สิทธิขอพกพา:</strong> ขอรับข้อมูลในรูปแบบที่อ่านได้ทั่วไป</li> <li><strong>สิทธิคัดค้าน:</strong> คัดค้านการประมวลผลข้อมูล</li> <li><strong>สิทธิเพิกถอนความยินยอม:</strong> เพิกถอนความยินยอมที่ได้ให้ไว้ก่อนหน้า</li> <li><strong>สิทธิร้องเรียน:</strong> ร้องเรียนต่อคณะกรรมการคุ้มครองข้อมูลส่วนบุคคล</li> </ul> <p class="text-secondary-700 mt-4">
หากท่านต้องการใช้สิทธิเหล่านี้ กรุณาติดต่อเราที่ info@dealplustech.co.th
</p> </section> <section id="section11" class="mb-8"> <h2 class="text-2xl font-bold text-secondary-900 mb-4">9. มาตรการรักษาความปลอดภัย</h2> <p class="text-secondary-700 mb-4">
เราใช้มาตรการรักษาความปลอดภัยที่เหมาะสมเพื่อปกป้องข้อมูลส่วนบุคคลของท่านจากการเข้าถึง
การใช้ การแก้ไข หรือการเปิดเผยโดยไม่ได้รับอนุญาต มาตรการเหล่านี้รวมถึง:
</p> <ul class="list-disc pl-6 space-y-2 text-secondary-700"> <li>การเข้ารหัสข้อมูล (Encryption)</li> <li>การควบคุมการเข้าถึง (Access Control)</li> <li>การสำรองข้อมูลเป็นประจำ</li> <li>การฝึกอบรมพนักงานเรื่องการคุ้มครองข้อมูล</li> </ul> </section> <section id="section13" class="mb-8"> <h2 class="text-2xl font-bold text-secondary-900 mb-4">10. สิทธิร้องเรียน</h2> <p class="text-secondary-700 mb-4">
หากท่านเชื่อว่ามีการละเมิดกฎหมาย PDPA ท่านมีสิทธิร้องเรียนต่อ:
</p> <div class="bg-secondary-50 p-4 rounded-lg"> <p class="text-secondary-700"> <strong>คณะกรรมการคุ้มครองข้อมูลส่วนบุคคล (PDPC)</strong> </p> <p class="text-secondary-700">เว็บไซต์: www.pdpc.or.th</p> <p class="text-secondary-700">อีเมล: info@pdpc.or.th</p> </div> </section> <section class="mt-12 pt-8 border-t border-secondary-200"> <h2 class="text-xl font-bold text-secondary-900 mb-4">การติดต่อ</h2> <p class="text-secondary-700">
หากมีคำถามเกี่ยวกับนโยบายความเป็นส่วนตัวนี้ กรุณาติดต่อ:
</p> <div class="mt-4"> <p class="text-secondary-700"><strong>บริษัท ดีล พลัส เทค จำกัด</strong></p> <p class="text-secondary-700">อีเมล: info@dealplustech.co.th</p> <p class="text-secondary-700">โทรศัพท์: 090-555-1415</p> </div> </section> </div> </div> </div> </main> <!-- Cookie Consent Banner --> <script type="module">const t=JSON.parse(localStorage.getItem("consent-preferences")||"null");if(t)t.analytics;else{const e=document.createElement("div");e.id="cookie-banner",e.className="fixed bottom-0 left-0 right-0 bg-secondary-900 text-white p-6 z-50 shadow-lg",e.innerHTML=`
<div class="container mx-auto max-w-4xl">
<p class="text-lg mb-4">เราใช้คุกกี้เพื่อปรับปรุงประสบการณ์การใช้งานเว็บไซต์ โดยคลิกยอมรับเพื่อใช้งานคุกกี้ทุกประเภท หรือคลิกปรับแต่งเพื่อเลือกคุกกี้ที่ต้องการ</p>
<div class="flex flex-wrap gap-3">
<button id="accept-all" class="px-6 py-2 bg-primary-600 text-white font-semibold rounded-md hover:bg-primary-700">ยอมรับ</button>
<button id="reject-all" class="px-6 py-2 bg-secondary-700 text-white font-semibold rounded-md hover:bg-secondary-600">ปฏิเสธ</button>
<button id="customize" class="px-6 py-2 border border-white text-white font-semibold rounded-md hover:bg-white hover:text-secondary-900">ปรับแต่ง</button>
</div>
</div>
`,document.body.appendChild(e),document.getElementById("accept-all")?.addEventListener("click",()=>{localStorage.setItem("consent-preferences",JSON.stringify({essential:!0,analytics:!0,marketing:!0,timestamp:new Date().toISOString()})),e.remove()}),document.getElementById("reject-all")?.addEventListener("click",()=>{localStorage.setItem("consent-preferences",JSON.stringify({essential:!0,analytics:!1,marketing:!1,timestamp:new Date().toISOString()})),e.remove()}),document.getElementById("customize")?.addEventListener("click",()=>{localStorage.setItem("consent-preferences",JSON.stringify({essential:!0,analytics:!1,marketing:!1,timestamp:new Date().toISOString()})),e.remove()})}</script> </body> </html>

File diff suppressed because one or more lines are too long

View File

@@ -1,52 +0,0 @@
<!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 v5.18.0"><meta name="description" content="บริการครบวงจร จำหน่ายวัสดุท่อ ให้คำปรึกษา ออกแบบระบบ และติดตั้ง"><!-- 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="บริการของเรา"><meta property="og:description" content="บริการครบวงจร จำหน่ายวัสดุท่อ ให้คำปรึกษา ออกแบบระบบ และติดตั้ง"><meta property="og:image" content="/og-image.jpg"><meta property="og:type" content="website"><meta name="twitter:card" content="summary_large_image"><title>บริการของเรา | ดีล พลัส เทค</title><style>html{font-family:Kanit,system-ui,sans-serif;font-size:18px}@media(min-width:1280px){html{font-size:20px}}@media(min-width:1536px){html{font-size:22px}}@media(min-width:1920px){html{font-size:24px}}
</style></head> <body class="flex flex-col min-h-screen"> <main> <!-- Hero Section --> <section class="relative h-[50vh] min-h-[400px] bg-secondary-900"> <div class="absolute inset-0 bg-gradient-to-r from-secondary-900 via-secondary-900/90 to-secondary-900/60 z-10"></div> <img src="/images/2021/03/hdpe-pipe_000C.jpg" alt="บริการของเรา" class="absolute inset-0 w-full h-full object-cover opacity-40" 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">
บริการครบวงจร
</span> <h1 class="text-4xl md:text-5xl lg:text-6xl font-bold text-white mb-6">
บริการ<span class="text-primary-400">ของเรา</span> </h1> <p class="text-xl text-secondary-200">
ตั้งแต่การให้คำปรึกษา ออกแบบ จัดส่ง จนถึงติดตั้ง เราพร้อมดูแลโครงการของคุณครบวงจร
</p> </div> </div> </section> <!-- Services Grid --> <section class="py-20 bg-white"> <div class="container mx-auto px-4"> <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8"> <!-- Service 1 --> <div class="group p-8 bg-secondary-50 rounded-2xl hover:bg-primary-600 transition-all duration-300 hover:shadow-xl"> <div class="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"> <svg class="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="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"></path> </svg> </div> <h3 class="text-xl font-bold text-secondary-900 mb-3 group-hover:text-white transition-colors">
จำหน่ายวัสดุท่อ
</h3> <p class="text-secondary-600 group-hover:text-primary-100 transition-colors">
จำหน่ายท่อพีพีอาร์ ท่อ HDPE ท่อ PVC วาล์ว และอุปกรณ์ต่อท่อครบวงจร สินค้าคุณภาพ ราคาแข่งขันได้
</p> </div> <!-- Service 2 --> <div class="group p-8 bg-secondary-50 rounded-2xl hover:bg-primary-600 transition-all duration-300 hover:shadow-xl"> <div class="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"> <svg class="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="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"></path> </svg> </div> <h3 class="text-xl font-bold text-secondary-900 mb-3 group-hover:text-white transition-colors">
ให้คำปรึกษา
</h3> <p class="text-secondary-600 group-hover:text-primary-100 transition-colors">
ทีมงานมืออาชีพพร้อมให้คำปรึกษาเกี่ยวกับการเลือกวัสดุท่อที่เหมาะสมกับโครงการของคุณ
</p> </div> <!-- Service 3 --> <div class="group p-8 bg-secondary-50 rounded-2xl hover:bg-primary-600 transition-all duration-300 hover:shadow-xl"> <div class="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"> <svg class="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="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"></path> </svg> </div> <h3 class="text-xl font-bold text-secondary-900 mb-3 group-hover:text-white transition-colors">
ออกแบบระบบ
</h3> <p class="text-secondary-600 group-hover:text-primary-100 transition-colors">
บริการออกแบบระบบท่อน้ำ ระบบดับเพลิง และระบบปรับอากาศ โดยวิศวกรผู้เชี่ยวชาญ
</p> </div> <!-- Service 4 --> <div class="group p-8 bg-secondary-50 rounded-2xl hover:bg-primary-600 transition-all duration-300 hover:shadow-xl"> <div class="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"> <svg class="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="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> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path> </svg> </div> <h3 class="text-xl font-bold text-secondary-900 mb-3 group-hover:text-white transition-colors">
ติดตั้งระบบ
</h3> <p class="text-secondary-600 group-hover:text-primary-100 transition-colors">
ทีมช่างผู้เชี่ยวชาญติดตั้งระบบท่อครบวงจร พร้อมรับประกันงาน
</p> </div> <!-- Service 5 --> <div class="group p-8 bg-secondary-50 rounded-2xl hover:bg-primary-600 transition-all duration-300 hover:shadow-xl"> <div class="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"> <svg class="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4"></path> </svg> </div> <h3 class="text-xl font-bold text-secondary-900 mb-3 group-hover:text-white transition-colors">
จัดส่งสินค้า
</h3> <p class="text-secondary-600 group-hover:text-primary-100 transition-colors">
บริการจัดส่งสินค้าทั่วประเทศ รวดเร็ว ปลอดภัย มีประกันความเสียหาย
</p> </div> <!-- Service 6 --> <div class="group p-8 bg-secondary-50 rounded-2xl hover:bg-primary-600 transition-all duration-300 hover:shadow-xl"> <div class="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"> <svg class="w-8 h-8" 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"></path> </svg> </div> <h3 class="text-xl font-bold text-secondary-900 mb-3 group-hover:text-white transition-colors">
บริการหลังการขาย
</h3> <p class="text-secondary-600 group-hover:text-primary-100 transition-colors">
ทีมงานพร้อมให้การดูแลและบริการซ่อมบำรุงหลังการขายตลอดอายุการใช้งาน
</p> </div> </div> </div> </section> <!-- Process Section --> <section class="py-20 bg-secondary-900"> <div class="container mx-auto px-4"> <div class="text-center mb-16"> <h2 class="text-3xl md:text-4xl font-bold text-white mb-4">
ขั้นตอน<span class="text-primary-400">การทำงาน</span> </h2> <p class="text-secondary-300 text-lg max-w-2xl mx-auto">
เราให้ความสำคัญกับทุกขั้นตอน เพื่อให้ลูกค้าได้รับบริการที่ดีที่สุด
</p> </div> <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8"> <!-- Step 1 --> <div class="relative"> <div class="text-center"> <span class="text-6xl font-bold text-primary-600/30">01</span> <h3 class="text-xl font-bold text-white mt-4 mb-2">ปรึกษา</h3> <p class="text-secondary-400">ติดต่อเราเพื่อปรึกษาเกี่ยวกับความต้องการของโครงการ</p> </div> </div> <!-- Step 2 --> <div class="relative"> <div class="text-center"> <span class="text-6xl font-bold text-primary-600/30">02</span> <h3 class="text-xl font-bold text-white mt-4 mb-2">ออกแบบ</h3> <p class="text-secondary-400">ทีมวิศวกรออกแบบระบบให้เหมาะสมกับการใช้งาน</p> </div> </div> <!-- Step 3 --> <div class="relative"> <div class="text-center"> <span class="text-6xl font-bold text-primary-600/30">03</span> <h3 class="text-xl font-bold text-white mt-4 mb-2">เสนอราคา</h3> <p class="text-secondary-400">เสนอราคาสินค้าและบริการอย่างโปร่งใส</p> </div> </div> <!-- Step 4 --> <div class="relative"> <div class="text-center"> <span class="text-6xl font-bold text-primary-600/30">04</span> <h3 class="text-xl font-bold text-white mt-4 mb-2">ติดตั้ง</h3> <p class="text-secondary-400">ทีมช่างติดตั้งโดยมืออาชีพตรงตามกำหนด</p> </div> </div> </div> </div> </section> <!-- Why Choose Us --> <section class="py-20 bg-secondary-50"> <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> <div class="space-y-6"> <div class="flex gap-4"> <div class="w-12 h-12 bg-primary-600 rounded-lg flex items-center justify-center flex-shrink-0"> <svg class="w-6 h-6 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path> </svg> </div> <div> <h3 class="font-bold text-secondary-900 mb-1">ประสบการณ์กว่า 10 ปี</h3> <p class="text-secondary-600">เชี่ยวชาญด้านระบบท่อและอุปกรณ์ครบวงจร</p> </div> </div> <div class="flex gap-4"> <div class="w-12 h-12 bg-primary-600 rounded-lg flex items-center justify-center flex-shrink-0"> <svg class="w-6 h-6 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path> </svg> </div> <div> <h3 class="font-bold text-secondary-900 mb-1">สินค้าคุณภาพ</h3> <p class="text-secondary-600">สินค้าผ่านมาตรฐาน มอก. / FM / UL พร้อมรับประกัน</p> </div> </div> <div class="flex gap-4"> <div class="w-12 h-12 bg-primary-600 rounded-lg flex items-center justify-center flex-shrink-0"> <svg class="w-6 h-6 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path> </svg> </div> <div> <h3 class="font-bold text-secondary-900 mb-1">ทีมงานมืออาชีพ</h3> <p class="text-secondary-600">วิศวกรและช่างผู้เชี่ยวชาญพร้อมให้คำปรึกษา</p> </div> </div> <div class="flex gap-4"> <div class="w-12 h-12 bg-primary-600 rounded-lg flex items-center justify-center flex-shrink-0"> <svg class="w-6 h-6 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path> </svg> </div> <div> <h3 class="font-bold text-secondary-900 mb-1">บริการรวดเร็ว</h3> <p class="text-secondary-600">จัดส่งสินค้าทั่วประเทศ ตรงตามกำหนด</p> </div> </div> </div> </div> <div class="relative aspect-video bg-secondary-200 rounded-2xl overflow-hidden"> <img src="/images/2021/03/hdpe-welding_000C-1.jpg" alt="ทีมงานมืออาชีพ" class="object-cover w-full h-full" loading="lazy"> </div> </div> </div> </section> <!-- CTA --> <section class="py-20 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-primary-100 text-lg mb-8 max-w-">
ต2xl mx-autoิดต่อเราวันนี้เพื่อรับคำปรึกษาและใบเสนอราคาฟรี
</p> <div class="flex flex-wrap justify-center gap-4"> <a href="/contact-us/" class="btn-secondary bg-white text-primary-600 hover:bg-primary-50">
ติดต่อเรา
</a> <a href="tel:090-555-1415" class="btn-outline border-white text-white hover:bg-white hover:text-primary-600">
โทร: 090-555-1415
</a> </div> </div> </section> </main> <div class="fixed bottom-6 right-6 z-40 flex flex-col gap-3"> <!-- LINE --> <a href="https://line.me/ti/p/@dealplustech" 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"></path> </svg> </a> <!-- Phone --> <a href="tel:090-555-1415" 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"></path> </svg> </a> </div> <!-- Cookie Consent Banner --> <script type="module">const t=JSON.parse(localStorage.getItem("consent-preferences")||"null");if(t)t.analytics;else{const e=document.createElement("div");e.id="cookie-banner",e.className="fixed bottom-0 left-0 right-0 bg-secondary-900 text-white p-6 z-50 shadow-lg",e.innerHTML=`
<div class="container mx-auto max-w-4xl">
<p class="text-lg mb-4">เราใช้คุกกี้เพื่อปรับปรุงประสบการณ์การใช้งานเว็บไซต์ โดยคลิกยอมรับเพื่อใช้งานคุกกี้ทุกประเภท หรือคลิกปรับแต่งเพื่อเลือกคุกกี้ที่ต้องการ</p>
<div class="flex flex-wrap gap-3">
<button id="accept-all" class="px-6 py-2 bg-primary-600 text-white font-semibold rounded-md hover:bg-primary-700">ยอมรับ</button>
<button id="reject-all" class="px-6 py-2 bg-secondary-700 text-white font-semibold rounded-md hover:bg-secondary-600">ปฏิเสธ</button>
<button id="customize" class="px-6 py-2 border border-white text-white font-semibold rounded-md hover:bg-white hover:text-secondary-900">ปรับแต่ง</button>
</div>
</div>
`,document.body.appendChild(e),document.getElementById("accept-all")?.addEventListener("click",()=>{localStorage.setItem("consent-preferences",JSON.stringify({essential:!0,analytics:!0,marketing:!0,timestamp:new Date().toISOString()})),e.remove()}),document.getElementById("reject-all")?.addEventListener("click",()=>{localStorage.setItem("consent-preferences",JSON.stringify({essential:!0,analytics:!1,marketing:!1,timestamp:new Date().toISOString()})),e.remove()}),document.getElementById("customize")?.addEventListener("click",()=>{localStorage.setItem("consent-preferences",JSON.stringify({essential:!0,analytics:!1,marketing:!1,timestamp:new Date().toISOString()})),e.remove()})}</script> </body> </html>

View File

@@ -1,42 +0,0 @@
<!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 v5.18.0"><meta name="description" content="ข้อกำหนดและเงื่อนไขการใช้งานเว็บไซต์ของบริษัท ดีล พลัส เทค จำกัด"><!-- 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="ข้อกำหนดและเงื่อนไข"><meta property="og:description" content="ข้อกำหนดและเงื่อนไขการใช้งานเว็บไซต์ของบริษัท ดีล พลัส เทค จำกัด"><meta property="og:image" content="/og-image.jpg"><meta property="og:type" content="website"><meta name="twitter:card" content="summary_large_image"><title>ข้อกำหนดและเงื่อนไข | ดีล พลัส เทค</title><style>html{font-family:Kanit,system-ui,sans-serif;font-size:18px}@media(min-width:1280px){html{font-size:20px}}@media(min-width:1536px){html{font-size:22px}}@media(min-width:1920px){html{font-size:24px}}
</style></head> <body class="flex flex-col min-h-screen"> <main class="min-h-screen bg-secondary-50"> <div class="container mx-auto px-4 py-12"> <div class="max-w-4xl mx-auto bg-white rounded-lg shadow-lg p-8 md:p-12"> <h1 class="text-4xl font-bold text-secondary-900 mb-4">ข้อกำหนดและเงื่อนไข</h1> <p class="text-secondary-600 mb-8">Terms and Conditions - มีผลบังคับใช้ตั้งแต่วันที่ 9 มีนาคม 2026</p> <div class="prose prose-lg max-w-none text-secondary-700"> <section class="mb-8"> <h2 class="text-2xl font-bold text-secondary-900 mb-4">1. การยอมรับข้อกำหนด</h2> <p class="mb-4">
ยินดีต้อนรับสู่เว็บไซต์ของ <strong>บริษัท ดีล พลัส เทค จำกัด</strong> ("เรา", "ของเรา" หรือ "เว็บไซต์")
โดยการเข้าใช้งานเว็บไซต์นี้ ท่านยอมรับว่าท่านได้อ่าน เข้าใจ และตกลงที่จะผูกพันกับข้อกำหนดและเงื่อนไขเหล่านี้
หากท่านไม่ยอมรับข้อกำหนดใด ๆ กรุณาหยุดการใช้งานเว็บไซต์นี้
</p> </section> <section class="mb-8"> <h2 class="text-2xl font-bold text-secondary-900 mb-4">2. คำอธิบายบริการ</h2> <p class="mb-4">
เว็บไซต์นี้ให้บริการข้อมูลเกี่ยวกับสินค้าและบริการของเรา รวมถึง:
</p> <ul class="list-disc pl-6 space-y-2"> <li>ข้อมูลผลิตภัณฑ์ท่อและอุปกรณ์ระบบ HVAC</li> <li>ข้อมูลทางเทคนิคและสเปคสินค้า</li> <li>บริการให้คำปรึกษา</li> <li>ช่องทางการติดต่อและขอใบเสนอราคา</li> </ul> </section> <section class="mb-8"> <h2 class="text-2xl font-bold text-secondary-900 mb-4">3. สิทธิ์ในทรัพย์สินทางปัญญา</h2> <p class="mb-4">
เนื้อหาทั้งหมดบนเว็บไซต์นี้ รวมถึงแต่ไม่จำกัดเพียง ข้อความ รูปภาพ กราฟิก โลโก้ ไอคอน
และซอฟต์แวร์ เป็นทรัพย์สินทางปัญญาของบริษัท ดีล พลัส เทค จำกัด และได้รับความคุ้มครองตามกฎหมายลิขสิทธิ์
ห้ามมิให้ทำซ้ำ ดัดแปลง เผยแพร่ หรือใช้เพื่อการค้าโดยไม่ได้รับอนุญาตเป็นลายลักษณ์อักษร
</p> </section> <section class="mb-8"> <h2 class="text-2xl font-bold text-secondary-900 mb-4">4. ข้อผูกมัดของผู้ใช้</h2> <p class="mb-4">ท่านตกลงที่จะ:</p> <ul class="list-disc pl-6 space-y-2"> <li>ใช้เว็บไซต์นี้เพื่อวัตถุประสงค์ที่ถูกต้องตามกฎหมายเท่านั้น</li> <li>ไม่ให้ข้อมูลที่เป็นเท็จหรือไม่ถูกต้อง</li> <li>ไม่พยายามเข้าถึงระบบหรือข้อมูลโดยไม่ได้รับอนุญาต</li> <li>ไม่ใช้เว็บไซต์ในทางที่ผิดหรือเป็นอันตรายต่อผู้อื่น</li> <li>เคารพสิทธิ์ในทรัพย์สินทางปัญญาของเรา</li> </ul> </section> <section class="mb-8"> <h2 class="text-2xl font-bold text-secondary-900 mb-4">5. การจำกัดความรับผิด</h2> <p class="mb-4">
เราพยายามให้ข้อมูลที่ถูกต้องและทันสมัยบนเว็บไซต์ อย่างไรก็ตาม เราไม่รับประกันว่า:
</p> <ul class="list-disc pl-6 space-y-2"> <li>ข้อมูลบนเว็บไซต์จะถูกต้อง ครบถ้วน หรือเป็นปัจจุบันเสมอ</li> <li>เว็บไซต์จะทำงานได้โดยไม่มีการขัดข้องหรือปราศจากข้อผิดพลาด</li> <li>เว็บไซต์จะปลอดภัยจากการโจมตีทางไซเบอร์หรือไวรัส</li> </ul> <p class="mt-4">
เราจะไม่รับผิดชอบต่อความเสียหายใด ๆ ที่เกิดขึ้นจากการใช้หรือไม่สามารถใช้เว็บไซต์นี้
ไม่ว่ากรณีใด ๆ ทั้งสิ้น
</p> </section> <section class="mb-8"> <h2 class="text-2xl font-bold text-secondary-900 mb-4">6. เงื่อนไขการ终止</h2> <p class="mb-4">
เราขอสงวนสิทธิ์ในการระงับหรือยกเลิกการเข้าถึงเว็บไซต์ของท่าน โดยไม่ต้องแจ้งให้ทราบล่วงหน้า
หาก我们发现ว่าท่านละเมิดข้อกำหนดและเงื่อนไขเหล่านี้
</p> </section> <section class="mb-8"> <h2 class="text-2xl font-bold text-secondary-900 mb-4">7. กฎหมายที่ใช้บังคับ</h2> <p class="mb-4">
ข้อกำหนดและเงื่อนไขนี้ อยู่ภายใต้บังคับของกฎหมายแห่งราชอาณาจักรไทย
และให้ตีความตามกฎหมายดังกล่าว
</p> </section> <section class="mb-8"> <h2 class="text-2xl font-bold text-secondary-900 mb-4">8. การระงับข้อพิพาท</h2> <p class="mb-4">
หากเกิดข้อพิพาทใด ๆ จากข้อกำหนดและเงื่อนไขนี้ คู่สัญญาตกลงที่จะเจรจาไกล่เกลี่ยข้อพิพาท
หากไม่สามารถตกลงกันได้ ให้อยู่ในอำนาจพิจารณาคดีของศาลไทย
</p> </section> <section class="mb-8"> <h2 class="text-2xl font-bold text-secondary-900 mb-4">9. การแก้ไขข้อกำหนด</h2> <p class="mb-4">
เราขอสงวนสิทธิ์ในการแก้ไขข้อกำหนดและเงื่อนไขนี้เมื่อใดก็ได้
โดยจะแจ้งให้ท่านทราบผ่านการเผยแพร่บนเว็บไซต์ การแก้ไขจะมีผลบังคับใช้ทันทีหลังการเผยแพร่
กรุณาตรวจสอบหน้าทนี้เป็นระยะ
</p> </section> <section class="mb-8"> <h2 class="text-2xl font-bold text-secondary-900 mb-4">10. ข้อมูลการติดต่อ</h2> <p class="mb-4">หากมีคำถามเกี่ยวกับข้อกำหนดและเงื่อนไขนี้ กรุณาติดต่อ:</p> <div class="bg-secondary-50 p-4 rounded-lg"> <p><strong>บริษัท ดีล พลัส เทค จำกัด</strong></p> <p>ที่อยู่: 9/70 ซอยนครลุง 17 แขวงบางไผ่ เขตบางแค กทม. 10160</p> <p>โทรศัพท์: 090-555-1415</p> <p>อีเมล: info@dealplustech.co.th</p> </div> </section> <section class="mt-12 pt-8 border-t border-secondary-200"> <p class="text-sm text-secondary-600">
ข้อกำหนดและเงื่อนไขนี้เป็นส่วนหนึ่งของข้อตกลงการใช้งานเว็บไซต์ของเรา
และต้องอ่านร่วมกับ <a href="/privacy-policy/" class="text-primary-600 hover:underline">นโยบายความเป็นส่วนตัว</a>
และ <a href="/cookie-policy/" class="text-primary-600 hover:underline">นโยบายคุกกี้</a> </p> </section> </div> </div> </div> </main> <!-- Cookie Consent Banner --> <script type="module">const t=JSON.parse(localStorage.getItem("consent-preferences")||"null");if(t)t.analytics;else{const e=document.createElement("div");e.id="cookie-banner",e.className="fixed bottom-0 left-0 right-0 bg-secondary-900 text-white p-6 z-50 shadow-lg",e.innerHTML=`
<div class="container mx-auto max-w-4xl">
<p class="text-lg mb-4">เราใช้คุกกี้เพื่อปรับปรุงประสบการณ์การใช้งานเว็บไซต์ โดยคลิกยอมรับเพื่อใช้งานคุกกี้ทุกประเภท หรือคลิกปรับแต่งเพื่อเลือกคุกกี้ที่ต้องการ</p>
<div class="flex flex-wrap gap-3">
<button id="accept-all" class="px-6 py-2 bg-primary-600 text-white font-semibold rounded-md hover:bg-primary-700">ยอมรับ</button>
<button id="reject-all" class="px-6 py-2 bg-secondary-700 text-white font-semibold rounded-md hover:bg-secondary-600">ปฏิเสธ</button>
<button id="customize" class="px-6 py-2 border border-white text-white font-semibold rounded-md hover:bg-white hover:text-secondary-900">ปรับแต่ง</button>
</div>
</div>
`,document.body.appendChild(e),document.getElementById("accept-all")?.addEventListener("click",()=>{localStorage.setItem("consent-preferences",JSON.stringify({essential:!0,analytics:!0,marketing:!0,timestamp:new Date().toISOString()})),e.remove()}),document.getElementById("reject-all")?.addEventListener("click",()=>{localStorage.setItem("consent-preferences",JSON.stringify({essential:!0,analytics:!1,marketing:!1,timestamp:new Date().toISOString()})),e.remove()}),document.getElementById("customize")?.addEventListener("click",()=>{localStorage.setItem("consent-preferences",JSON.stringify({essential:!0,analytics:!1,marketing:!1,timestamp:new Date().toISOString()})),e.remove()})}</script> </body> </html>

View File

@@ -1,16 +0,0 @@
{
"easypanel": {
"url": "",
"project": "customerwebsite",
"app": {
"name": "dealplustech-astro",
"port": 4321,
"image": "dealplustech-astro:latest"
},
"env": {
"NODE_ENV": "production",
"PORT": "4321",
"HOST": "0.0.0.0"
}
}
}

View File

@@ -1,14 +0,0 @@
# Nixpacks Configuration for Astro
# https://nixpacks.com/docs/configuration/file
[phases.setup]
nixPkgs = ["nodejs-20_x"]
[phases.install]
cmds = ["npm ci"]
[phases.build]
cmds = ["npm run build"]
[start]
cmd = "npm run preview -- --host 0.0.0.0 --port $PORT"

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
../acorn/bin/acorn

View File

@@ -1 +0,0 @@
../astro/astro.js

View File

@@ -1 +0,0 @@
../astro-consent/dist/cli.cjs

View File

@@ -1 +0,0 @@
../cssesc/bin/cssesc

View File

@@ -1 +0,0 @@
../esbuild/bin/esbuild

View File

@@ -1 +0,0 @@
../is-docker/cli.js

View File

@@ -1 +0,0 @@
../is-inside-container/cli.js

View File

@@ -1 +0,0 @@
../jiti/lib/jiti-cli.mjs

View File

@@ -1 +0,0 @@
../js-yaml/bin/js-yaml.js

View File

@@ -1 +0,0 @@
../nanoid/bin/nanoid.cjs

View File

@@ -1 +0,0 @@
../@babel/parser/bin/babel-parser.js

View File

@@ -1 +0,0 @@
../rollup/dist/bin/rollup

View File

@@ -1 +0,0 @@
../semver/bin/semver.js

View File

@@ -1 +0,0 @@
../svgo/bin/svgo.js

View File

@@ -1 +0,0 @@
../typescript/bin/tsc

View File

@@ -1 +0,0 @@
../tsconfck/bin/tsconfck.js

View File

@@ -1 +0,0 @@
../typescript/bin/tsserver

View File

@@ -1 +0,0 @@
../vite/bin/vite.js

File diff suppressed because it is too large Load Diff

View File

@@ -1,31 +0,0 @@
{
"hash": "ec7c8616",
"configHash": "06117f6a",
"lockfileHash": "fa2c2659",
"browserHash": "12cbe3a0",
"optimized": {
"astro > cssesc": {
"src": "../../cssesc/cssesc.js",
"file": "astro___cssesc.js",
"fileHash": "3b44b1ed",
"needsInterop": true
},
"astro > aria-query": {
"src": "../../aria-query/lib/index.js",
"file": "astro___aria-query.js",
"fileHash": "4b6e1232",
"needsInterop": true
},
"astro > axobject-query": {
"src": "../../axobject-query/lib/index.js",
"file": "astro___axobject-query.js",
"fileHash": "32d0da0b",
"needsInterop": true
}
},
"chunks": {
"chunk-BUSYA2B4": {
"file": "chunk-BUSYA2B4.js"
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,99 +0,0 @@
import {
__commonJS
} from "./chunk-BUSYA2B4.js";
// node_modules/cssesc/cssesc.js
var require_cssesc = __commonJS({
"node_modules/cssesc/cssesc.js"(exports, module) {
var object = {};
var hasOwnProperty = object.hasOwnProperty;
var merge = function merge2(options, defaults) {
if (!options) {
return defaults;
}
var result = {};
for (var key in defaults) {
result[key] = hasOwnProperty.call(options, key) ? options[key] : defaults[key];
}
return result;
};
var regexAnySingleEscape = /[ -,\.\/:-@\[-\^`\{-~]/;
var regexSingleEscape = /[ -,\.\/:-@\[\]\^`\{-~]/;
var regexExcessiveSpaces = /(^|\\+)?(\\[A-F0-9]{1,6})\x20(?![a-fA-F0-9\x20])/g;
var cssesc = function cssesc2(string, options) {
options = merge(options, cssesc2.options);
if (options.quotes != "single" && options.quotes != "double") {
options.quotes = "single";
}
var quote = options.quotes == "double" ? '"' : "'";
var isIdentifier = options.isIdentifier;
var firstChar = string.charAt(0);
var output = "";
var counter = 0;
var length = string.length;
while (counter < length) {
var character = string.charAt(counter++);
var codePoint = character.charCodeAt();
var value = void 0;
if (codePoint < 32 || codePoint > 126) {
if (codePoint >= 55296 && codePoint <= 56319 && counter < length) {
var extra = string.charCodeAt(counter++);
if ((extra & 64512) == 56320) {
codePoint = ((codePoint & 1023) << 10) + (extra & 1023) + 65536;
} else {
counter--;
}
}
value = "\\" + codePoint.toString(16).toUpperCase() + " ";
} else {
if (options.escapeEverything) {
if (regexAnySingleEscape.test(character)) {
value = "\\" + character;
} else {
value = "\\" + codePoint.toString(16).toUpperCase() + " ";
}
} else if (/[\t\n\f\r\x0B]/.test(character)) {
value = "\\" + codePoint.toString(16).toUpperCase() + " ";
} else if (character == "\\" || !isIdentifier && (character == '"' && quote == character || character == "'" && quote == character) || isIdentifier && regexSingleEscape.test(character)) {
value = "\\" + character;
} else {
value = character;
}
}
output += value;
}
if (isIdentifier) {
if (/^-[-\d]/.test(output)) {
output = "\\-" + output.slice(1);
} else if (/\d/.test(firstChar)) {
output = "\\3" + firstChar + " " + output.slice(1);
}
}
output = output.replace(regexExcessiveSpaces, function($0, $1, $2) {
if ($1 && $1.length % 2) {
return $0;
}
return ($1 || "") + $2;
});
if (!isIdentifier && options.wrap) {
return quote + output + quote;
}
return output;
};
cssesc.options = {
"escapeEverything": false,
"isIdentifier": false,
"quotes": "single",
"wrap": false
};
cssesc.version = "3.0.0";
module.exports = cssesc;
}
});
export default require_cssesc();
/*! Bundled license information:
cssesc/cssesc.js:
(*! https://mths.be/cssesc v3.0.0 by @mathias *)
*/
//# sourceMappingURL=astro___cssesc.js.map

View File

@@ -1,7 +0,0 @@
{
"version": 3,
"sources": ["../../cssesc/cssesc.js"],
"sourcesContent": ["/*! https://mths.be/cssesc v3.0.0 by @mathias */\n'use strict';\n\nvar object = {};\nvar hasOwnProperty = object.hasOwnProperty;\nvar merge = function merge(options, defaults) {\n\tif (!options) {\n\t\treturn defaults;\n\t}\n\tvar result = {};\n\tfor (var key in defaults) {\n\t\t// `if (defaults.hasOwnProperty(key) { … }` is not needed here, since\n\t\t// only recognized option names are used.\n\t\tresult[key] = hasOwnProperty.call(options, key) ? options[key] : defaults[key];\n\t}\n\treturn result;\n};\n\nvar regexAnySingleEscape = /[ -,\\.\\/:-@\\[-\\^`\\{-~]/;\nvar regexSingleEscape = /[ -,\\.\\/:-@\\[\\]\\^`\\{-~]/;\nvar regexAlwaysEscape = /['\"\\\\]/;\nvar regexExcessiveSpaces = /(^|\\\\+)?(\\\\[A-F0-9]{1,6})\\x20(?![a-fA-F0-9\\x20])/g;\n\n// https://mathiasbynens.be/notes/css-escapes#css\nvar cssesc = function cssesc(string, options) {\n\toptions = merge(options, cssesc.options);\n\tif (options.quotes != 'single' && options.quotes != 'double') {\n\t\toptions.quotes = 'single';\n\t}\n\tvar quote = options.quotes == 'double' ? '\"' : '\\'';\n\tvar isIdentifier = options.isIdentifier;\n\n\tvar firstChar = string.charAt(0);\n\tvar output = '';\n\tvar counter = 0;\n\tvar length = string.length;\n\twhile (counter < length) {\n\t\tvar character = string.charAt(counter++);\n\t\tvar codePoint = character.charCodeAt();\n\t\tvar value = void 0;\n\t\t// If its not a printable ASCII character…\n\t\tif (codePoint < 0x20 || codePoint > 0x7E) {\n\t\t\tif (codePoint >= 0xD800 && codePoint <= 0xDBFF && counter < length) {\n\t\t\t\t// Its a high surrogate, and there is a next character.\n\t\t\t\tvar extra = string.charCodeAt(counter++);\n\t\t\t\tif ((extra & 0xFC00) == 0xDC00) {\n\t\t\t\t\t// next character is low surrogate\n\t\t\t\t\tcodePoint = ((codePoint & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000;\n\t\t\t\t} else {\n\t\t\t\t\t// Its an unmatched surrogate; only append this code unit, in case\n\t\t\t\t\t// the next code unit is the high surrogate of a surrogate pair.\n\t\t\t\t\tcounter--;\n\t\t\t\t}\n\t\t\t}\n\t\t\tvalue = '\\\\' + codePoint.toString(16).toUpperCase() + ' ';\n\t\t} else {\n\t\t\tif (options.escapeEverything) {\n\t\t\t\tif (regexAnySingleEscape.test(character)) {\n\t\t\t\t\tvalue = '\\\\' + character;\n\t\t\t\t} else {\n\t\t\t\t\tvalue = '\\\\' + codePoint.toString(16).toUpperCase() + ' ';\n\t\t\t\t}\n\t\t\t} else if (/[\\t\\n\\f\\r\\x0B]/.test(character)) {\n\t\t\t\tvalue = '\\\\' + codePoint.toString(16).toUpperCase() + ' ';\n\t\t\t} else if (character == '\\\\' || !isIdentifier && (character == '\"' && quote == character || character == '\\'' && quote == character) || isIdentifier && regexSingleEscape.test(character)) {\n\t\t\t\tvalue = '\\\\' + character;\n\t\t\t} else {\n\t\t\t\tvalue = character;\n\t\t\t}\n\t\t}\n\t\toutput += value;\n\t}\n\n\tif (isIdentifier) {\n\t\tif (/^-[-\\d]/.test(output)) {\n\t\t\toutput = '\\\\-' + output.slice(1);\n\t\t} else if (/\\d/.test(firstChar)) {\n\t\t\toutput = '\\\\3' + firstChar + ' ' + output.slice(1);\n\t\t}\n\t}\n\n\t// Remove spaces after `\\HEX` escapes that are not followed by a hex digit,\n\t// since theyre redundant. Note that this is only possible if the escape\n\t// sequence isnt preceded by an odd number of backslashes.\n\toutput = output.replace(regexExcessiveSpaces, function ($0, $1, $2) {\n\t\tif ($1 && $1.length % 2) {\n\t\t\t// Its not safe to remove the space, so dont.\n\t\t\treturn $0;\n\t\t}\n\t\t// Strip the space.\n\t\treturn ($1 || '') + $2;\n\t});\n\n\tif (!isIdentifier && options.wrap) {\n\t\treturn quote + output + quote;\n\t}\n\treturn output;\n};\n\n// Expose default options (so they can be overridden globally).\ncssesc.options = {\n\t'escapeEverything': false,\n\t'isIdentifier': false,\n\t'quotes': 'single',\n\t'wrap': false\n};\n\ncssesc.version = '3.0.0';\n\nmodule.exports = cssesc;\n"],
"mappings": ";;;;;AAAA;AAAA;AAGA,QAAI,SAAS,CAAC;AACd,QAAI,iBAAiB,OAAO;AAC5B,QAAI,QAAQ,SAASA,OAAM,SAAS,UAAU;AAC7C,UAAI,CAAC,SAAS;AACb,eAAO;AAAA,MACR;AACA,UAAI,SAAS,CAAC;AACd,eAAS,OAAO,UAAU;AAGzB,eAAO,GAAG,IAAI,eAAe,KAAK,SAAS,GAAG,IAAI,QAAQ,GAAG,IAAI,SAAS,GAAG;AAAA,MAC9E;AACA,aAAO;AAAA,IACR;AAEA,QAAI,uBAAuB;AAC3B,QAAI,oBAAoB;AAExB,QAAI,uBAAuB;AAG3B,QAAI,SAAS,SAASC,QAAO,QAAQ,SAAS;AAC7C,gBAAU,MAAM,SAASA,QAAO,OAAO;AACvC,UAAI,QAAQ,UAAU,YAAY,QAAQ,UAAU,UAAU;AAC7D,gBAAQ,SAAS;AAAA,MAClB;AACA,UAAI,QAAQ,QAAQ,UAAU,WAAW,MAAM;AAC/C,UAAI,eAAe,QAAQ;AAE3B,UAAI,YAAY,OAAO,OAAO,CAAC;AAC/B,UAAI,SAAS;AACb,UAAI,UAAU;AACd,UAAI,SAAS,OAAO;AACpB,aAAO,UAAU,QAAQ;AACxB,YAAI,YAAY,OAAO,OAAO,SAAS;AACvC,YAAI,YAAY,UAAU,WAAW;AACrC,YAAI,QAAQ;AAEZ,YAAI,YAAY,MAAQ,YAAY,KAAM;AACzC,cAAI,aAAa,SAAU,aAAa,SAAU,UAAU,QAAQ;AAEnE,gBAAI,QAAQ,OAAO,WAAW,SAAS;AACvC,iBAAK,QAAQ,UAAW,OAAQ;AAE/B,4BAAc,YAAY,SAAU,OAAO,QAAQ,QAAS;AAAA,YAC7D,OAAO;AAGN;AAAA,YACD;AAAA,UACD;AACA,kBAAQ,OAAO,UAAU,SAAS,EAAE,EAAE,YAAY,IAAI;AAAA,QACvD,OAAO;AACN,cAAI,QAAQ,kBAAkB;AAC7B,gBAAI,qBAAqB,KAAK,SAAS,GAAG;AACzC,sBAAQ,OAAO;AAAA,YAChB,OAAO;AACN,sBAAQ,OAAO,UAAU,SAAS,EAAE,EAAE,YAAY,IAAI;AAAA,YACvD;AAAA,UACD,WAAW,iBAAiB,KAAK,SAAS,GAAG;AAC5C,oBAAQ,OAAO,UAAU,SAAS,EAAE,EAAE,YAAY,IAAI;AAAA,UACvD,WAAW,aAAa,QAAQ,CAAC,iBAAiB,aAAa,OAAO,SAAS,aAAa,aAAa,OAAQ,SAAS,cAAc,gBAAgB,kBAAkB,KAAK,SAAS,GAAG;AAC1L,oBAAQ,OAAO;AAAA,UAChB,OAAO;AACN,oBAAQ;AAAA,UACT;AAAA,QACD;AACA,kBAAU;AAAA,MACX;AAEA,UAAI,cAAc;AACjB,YAAI,UAAU,KAAK,MAAM,GAAG;AAC3B,mBAAS,QAAQ,OAAO,MAAM,CAAC;AAAA,QAChC,WAAW,KAAK,KAAK,SAAS,GAAG;AAChC,mBAAS,QAAQ,YAAY,MAAM,OAAO,MAAM,CAAC;AAAA,QAClD;AAAA,MACD;AAKA,eAAS,OAAO,QAAQ,sBAAsB,SAAU,IAAI,IAAI,IAAI;AACnE,YAAI,MAAM,GAAG,SAAS,GAAG;AAExB,iBAAO;AAAA,QACR;AAEA,gBAAQ,MAAM,MAAM;AAAA,MACrB,CAAC;AAED,UAAI,CAAC,gBAAgB,QAAQ,MAAM;AAClC,eAAO,QAAQ,SAAS;AAAA,MACzB;AACA,aAAO;AAAA,IACR;AAGA,WAAO,UAAU;AAAA,MAChB,oBAAoB;AAAA,MACpB,gBAAgB;AAAA,MAChB,UAAU;AAAA,MACV,QAAQ;AAAA,IACT;AAEA,WAAO,UAAU;AAEjB,WAAO,UAAU;AAAA;AAAA;",
"names": ["merge", "cssesc"]
}

View File

@@ -1,8 +0,0 @@
var __getOwnPropNames = Object.getOwnPropertyNames;
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
export {
__commonJS
};

View File

@@ -1,7 +0,0 @@
{
"version": 3,
"sources": [],
"sourcesContent": [],
"mappings": "",
"names": []
}

View File

@@ -1,3 +0,0 @@
{
"type": "module"
}

View File

@@ -1,53 +0,0 @@
MIT License
Copyright (c) 2021 [Astro contributors](https://github.com/withastro/compiler/graphs/contributors)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
This license applies to parts of the `internal/` subdirectory originating from
the https://cs.opensource.google/go/x/net/+/master:html/ repository:
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -1,72 +0,0 @@
# Astro Compiler
Astros [Go](https://golang.org/) + WASM compiler.
## Install
```
npm install @astrojs/compiler
```
## Usage
#### Transform `.astro` to valid TypeScript
The Astro compiler can convert `.astro` syntax to a TypeScript Module whose default export generates HTML.
**Some notes**...
- TypeScript is valid `.astro` syntax! The output code may need an additional post-processing step to generate valid JavaScript.
- `.astro` files rely on a server implementation exposed as `astro/runtime/server/index.js` in the Node ecosystem. Other runtimes currently need to bring their own rendering implementation and reference it via `internalURL`. This is a pain point we're looking into fixing.
```js
import { transform, type TransformResult } from "@astrojs/compiler";
const result = await transform(source, {
filename: "/Users/astro/Code/project/src/pages/index.astro",
sourcemap: "both",
internalURL: "astro/runtime/server/index.js",
});
```
#### Parse `.astro` and return an AST
The Astro compiler can emit an AST using the `parse` method.
**Some notes**...
- Position data is currently incomplete and in some cases incorrect. We're working on it!
- A `TextNode` can represent both HTML `text` and JavaScript/TypeScript source code.
- The `@astrojs/compiler/utils` entrypoint exposes `walk` and `walkAsync` functions that can be used to traverse the AST. It also exposes the `is` helper which can be used as guards to derive the proper types for each `node`.
```js
import { parse } from "@astrojs/compiler";
import { walk, walkAsync, is } from "@astrojs/compiler/utils";
const result = await parse(source, {
position: false, // defaults to `true`
});
walk(result.ast, (node) => {
// `tag` nodes are `element` | `custom-element` | `component`
if (is.tag(node)) {
console.log(node.name);
}
});
await walkAsync(result.ast, async (node) => {
if (is.tag(node)) {
node.value = await expensiveCalculation(node)
}
});
```
## Develop
### VSCode / CodeSpaces
A `devcontainer` configuration is available for use with VSCode's [Remote Development extension pack](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack) and GitHub CodeSpaces.
## Contributing
[CONTRIBUTING.md](/CONTRIBUTING.md)

File diff suppressed because one or more lines are too long

View File

@@ -1,11 +0,0 @@
import { transform as transform$1, parse as parse$1, convertToTSX as convertToTSX$1, teardown as teardown$1, initialize as initialize$1 } from '../shared/types.js';
import '../shared/ast.js';
import '../shared/diagnostics.js';
declare const transform: typeof transform$1;
declare const parse: typeof parse$1;
declare const convertToTSX: typeof convertToTSX$1;
declare const teardown: typeof teardown$1;
declare const initialize: typeof initialize$1;
export { convertToTSX, initialize, parse, teardown, transform };

View File

@@ -1 +0,0 @@
import{a as f}from"../chunk-QR6QDSEV.js";var u=(t,e)=>p().transform(t,e),S=(t,e)=>p().parse(t,e),v=(t,e)=>p().convertToTSX(t,e),a,i,h=()=>{a=void 0,i=void 0,globalThis["@astrojs/compiler"]=void 0},T=async t=>{let e=t.wasmURL;if(!e)throw new Error('Must provide the "wasmURL" option');e+="",a||(a=m(e).catch(n=>{throw a=void 0,n})),i=i||await a},p=()=>{if(!a)throw new Error('You need to call "initialize" before calling this');if(!i)throw new Error('You need to wait for the promise returned from "initialize" to be resolved before calling this');return i},y=async(t,e)=>{let n;return WebAssembly.instantiateStreaming?n=await WebAssembly.instantiateStreaming(fetch(t),e):n=await(async()=>{let s=await fetch(t).then(o=>o.arrayBuffer());return WebAssembly.instantiate(s,e)})(),n},m=async t=>{let e=new f,n=await y(t,e.importObject);e.run(n.instance);let c=globalThis["@astrojs/compiler"];return{transform:(s,o)=>new Promise(r=>r(c.transform(s,o||{}))),convertToTSX:(s,o)=>new Promise(r=>r(c.convertToTSX(s,o||{}))).then(r=>({...r,map:JSON.parse(r.map)})),parse:(s,o)=>new Promise(r=>r(c.parse(s,o||{}))).then(r=>({...r,ast:JSON.parse(r.ast)}))}};export{v as convertToTSX,T as initialize,S as parse,h as teardown,u as transform};

View File

@@ -1,3 +0,0 @@
"use strict";var c=Object.defineProperty;var d=Object.getOwnPropertyDescriptor;var p=Object.getOwnPropertyNames;var N=Object.prototype.hasOwnProperty;var u=(o,e)=>{for(var t in e)c(o,t,{get:e[t],enumerable:!0})},f=(o,e,t,a)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of p(e))!N.call(o,r)&&r!==t&&c(o,r,{get:()=>e[r],enumerable:!(a=d(e,r))||a.enumerable});return o};var y=o=>f(c({},"__esModule",{value:!0}),o);var v={};u(v,{is:()=>s,serialize:()=>k,walk:()=>h,walkAsync:()=>x});module.exports=y(v);function n(o){return e=>e.type===o}var s={parent(o){return Array.isArray(o.children)},literal(o){return typeof o.value=="string"},tag(o){return o.type==="element"||o.type==="custom-element"||o.type==="component"||o.type==="fragment"},whitespace(o){return o.type==="text"&&o.value.trim().length===0},root:n("root"),element:n("element"),customElement:n("custom-element"),component:n("component"),fragment:n("fragment"),expression:n("expression"),text:n("text"),doctype:n("doctype"),comment:n("comment"),frontmatter:n("frontmatter")},l=class{constructor(e){this.callback=e}async visit(e,t,a){if(await this.callback(e,t,a),s.parent(e)){let r=[];for(let i=0;i<e.children.length;i++){let m=e.children[i];r.push(this.callback(m,e,i))}await Promise.all(r)}}};function h(o,e){new l(e).visit(o)}function x(o,e){return new l(e).visit(o)}function g(o){let e="";for(let t of o.attributes)switch(e+=" ",t.kind){case"empty":{e+=`${t.name}`;break}case"expression":{e+=`${t.name}={${t.value}}`;break}case"quoted":{e+=`${t.name}=${t.raw}`;break}case"template-literal":{e+=`${t.name}=\`${t.value}\``;break}case"shorthand":{e+=`{${t.name}}`;break}case"spread":{e+=`{...${t.value}}`;break}}return e}function k(o,e={selfClose:!0}){let t="";function a(r){if(s.root(r))for(let i of r.children)a(i);else if(s.frontmatter(r))t+=`---${r.value}---
`;else if(s.comment(r))t+=`<!--${r.value}-->`;else if(s.expression(r)){t+="{";for(let i of r.children)a(i);t+="}"}else if(s.literal(r))t+=r.value;else if(s.tag(r))if(t+=`<${r.name}`,t+=g(r),r.children.length===0&&e.selfClose)t+=" />";else{t+=">";for(let i of r.children)a(i);t+=`</${r.name}>`}}return a(o),t}0&&(module.exports={is,serialize,walk,walkAsync});

View File

@@ -1,29 +0,0 @@
import { Node, ParentNode, LiteralNode, TagLikeNode, TextNode, RootNode, ElementNode, CustomElementNode, ComponentNode, FragmentNode, ExpressionNode, DoctypeNode, CommentNode, FrontmatterNode } from '../shared/ast.js';
type Visitor = (node: Node, parent?: ParentNode, index?: number) => void | Promise<void>;
declare const is: {
parent(node: Node): node is ParentNode;
literal(node: Node): node is LiteralNode;
tag(node: Node): node is TagLikeNode;
whitespace(node: Node): node is TextNode;
root: (node: Node) => node is RootNode;
element: (node: Node) => node is ElementNode;
customElement: (node: Node) => node is CustomElementNode;
component: (node: Node) => node is ComponentNode;
fragment: (node: Node) => node is FragmentNode;
expression: (node: Node) => node is ExpressionNode;
text: (node: Node) => node is TextNode;
doctype: (node: Node) => node is DoctypeNode;
comment: (node: Node) => node is CommentNode;
frontmatter: (node: Node) => node is FrontmatterNode;
};
declare function walk(node: ParentNode, callback: Visitor): void;
declare function walkAsync(node: ParentNode, callback: Visitor): Promise<void>;
interface SerializeOptions {
selfClose: boolean;
}
/** @deprecated Please use `SerializeOptions` */
type SerializeOtions = SerializeOptions;
declare function serialize(root: Node, opts?: SerializeOptions): string;
export { SerializeOptions, SerializeOtions, Visitor, is, serialize, walk, walkAsync };

View File

@@ -1,3 +0,0 @@
function n(o){return t=>t.type===o}var a={parent(o){return Array.isArray(o.children)},literal(o){return typeof o.value=="string"},tag(o){return o.type==="element"||o.type==="custom-element"||o.type==="component"||o.type==="fragment"},whitespace(o){return o.type==="text"&&o.value.trim().length===0},root:n("root"),element:n("element"),customElement:n("custom-element"),component:n("component"),fragment:n("fragment"),expression:n("expression"),text:n("text"),doctype:n("doctype"),comment:n("comment"),frontmatter:n("frontmatter")},l=class{constructor(t){this.callback=t}async visit(t,e,s){if(await this.callback(t,e,s),a.parent(t)){let r=[];for(let i=0;i<t.children.length;i++){let c=t.children[i];r.push(this.callback(c,t,i))}await Promise.all(r)}}};function N(o,t){new l(t).visit(o)}function u(o,t){return new l(t).visit(o)}function m(o){let t="";for(let e of o.attributes)switch(t+=" ",e.kind){case"empty":{t+=`${e.name}`;break}case"expression":{t+=`${e.name}={${e.value}}`;break}case"quoted":{t+=`${e.name}=${e.raw}`;break}case"template-literal":{t+=`${e.name}=\`${e.value}\``;break}case"shorthand":{t+=`{${e.name}}`;break}case"spread":{t+=`{...${e.value}}`;break}}return t}function f(o,t={selfClose:!0}){let e="";function s(r){if(a.root(r))for(let i of r.children)s(i);else if(a.frontmatter(r))e+=`---${r.value}---
`;else if(a.comment(r))e+=`<!--${r.value}-->`;else if(a.expression(r)){e+="{";for(let i of r.children)s(i);e+="}"}else if(a.literal(r))e+=r.value;else if(a.tag(r))if(e+=`<${r.name}`,e+=m(r),r.children.length===0&&t.selfClose)e+=" />";else{e+=">";for(let i of r.children)s(i);e+=`</${r.name}>`}}return s(o),e}export{a as is,f as serialize,N as walk,u as walkAsync};

File diff suppressed because one or more lines are too long

View File

@@ -1,37 +0,0 @@
declare class Go {
importObject: {
gojs: {
'runtime.wasmExit': (sp: any) => void;
'runtime.wasmWrite': (sp: any) => void;
'runtime.resetMemoryDataView': (sp: any) => void;
'runtime.nanotime1': (sp: any) => void;
'runtime.walltime': (sp: any) => void;
'runtime.scheduleTimeoutEvent': (sp: any) => void;
'runtime.clearTimeoutEvent': (sp: any) => void;
'runtime.getRandomData': (sp: any) => void;
'syscall/js.finalizeRef': (sp: any) => void;
'syscall/js.stringVal': (sp: any) => void;
'syscall/js.valueGet': (sp: any) => void;
'syscall/js.valueSet': (sp: any) => void;
'syscall/js.valueDelete': (sp: any) => void;
'syscall/js.valueIndex': (sp: any) => void;
'syscall/js.valueSetIndex': (sp: any) => void;
'syscall/js.valueCall': (sp: any) => void;
'syscall/js.valueInvoke': (sp: any) => void;
'syscall/js.valueNew': (sp: any) => void;
'syscall/js.valueLength': (sp: any) => void;
'syscall/js.valuePrepareString': (sp: any) => void;
'syscall/js.valueLoadString': (sp: any) => void;
'syscall/js.valueInstanceOf': (sp: any) => void;
'syscall/js.copyBytesToGo': (sp: any) => void;
'syscall/js.copyBytesToJS': (sp: any) => void;
debug: (value: any) => void;
};
};
constructor();
run(instance: any): Promise<void>;
private _resume;
private _makeFuncWrapper;
}
export { Go as default };

View File

@@ -1 +0,0 @@
import{a}from"../chunk-QR6QDSEV.js";export{a as default};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,12 +0,0 @@
import { transform as transform$1, parse as parse$1, convertToTSX as convertToTSX$1, teardown as teardown$1 } from '../shared/types.js';
export { HoistedScript, ParseOptions, ParseResult, PreprocessorResult, TransformOptions, TransformResult } from '../shared/types.js';
import '../shared/ast.js';
import '../shared/diagnostics.js';
declare const transform: typeof transform$1;
declare const parse: typeof parse$1;
declare const convertToTSX: typeof convertToTSX$1;
declare const compile: (template: string) => Promise<string>;
declare const teardown: typeof teardown$1;
export { compile, convertToTSX, parse, teardown, transform };

View File

@@ -1 +0,0 @@
import{a as c}from"../chunk-W5DTLHV4.js";import{promises as m}from"fs";import{fileURLToPath as f}from"url";var w=async(t,s)=>i().then(r=>r.transform(t,s)),l=async(t,s)=>i().then(r=>r.parse(t,s)),b=async(t,s)=>i().then(r=>r.convertToTSX(t,s)),P=async t=>{let{default:s}=await import(`data:text/javascript;charset=utf-8;base64,${Buffer.from(t).toString("base64")}`);return s},n,g=()=>{n=void 0,globalThis["@astrojs/compiler"]=void 0},i=()=>(n||(n=d().catch(t=>{throw n=void 0,t})),n),y=async(t,s)=>{let r;return r=await(async()=>{let o=await m.readFile(t).then(e=>e.buffer);return WebAssembly.instantiate(new Uint8Array(o),s)})(),r},d=async()=>{let t=new c,s=await y(f(new URL("../astro.wasm",import.meta.url)),t.importObject);t.run(s.instance);let r=globalThis["@astrojs/compiler"];return{transform:(a,o)=>new Promise(e=>{try{e(r.transform(a,o||{}))}catch(p){throw n=void 0,p}}),parse:(a,o)=>new Promise(e=>e(r.parse(a,o||{}))).catch(e=>{throw n=void 0,e}).then(e=>({...e,ast:JSON.parse(e.ast)})),convertToTSX:(a,o)=>new Promise(e=>e(r.convertToTSX(a,o||{}))).catch(e=>{throw n=void 0,e}).then(e=>({...e,map:JSON.parse(e.map)}))}};export{P as compile,b as convertToTSX,l as parse,g as teardown,w as transform};

File diff suppressed because one or more lines are too long

View File

@@ -1,16 +0,0 @@
import { TransformOptions, TransformResult, ParseOptions, ParseResult, ConvertToTSXOptions, TSXResult, transform as transform$1, parse as parse$1, convertToTSX as convertToTSX$1 } from '../shared/types.js';
import '../shared/ast.js';
import '../shared/diagnostics.js';
type UnwrappedPromise<T> = T extends (...params: any) => Promise<infer Return> ? (...params: Parameters<T>) => Return : T;
interface Service {
transform: UnwrappedPromise<typeof transform$1>;
parse: UnwrappedPromise<typeof parse$1>;
convertToTSX: UnwrappedPromise<typeof convertToTSX$1>;
}
declare const transform: (input: string, options: TransformOptions | undefined) => TransformResult;
declare const parse: (input: string, options: ParseOptions | undefined) => ParseResult;
declare const convertToTSX: (input: string, options: ConvertToTSXOptions | undefined) => TSXResult;
declare function startRunningService(): Service;
export { convertToTSX, parse, startRunningService, transform };

View File

@@ -1 +0,0 @@
import{a as c}from"../chunk-W5DTLHV4.js";import{readFileSync as p}from"fs";import{fileURLToPath as m}from"url";function i(){return s||(s=f()),s}var s,l=(e,t)=>i().transform(e,t),w=(e,t)=>i().parse(e,t),h=(e,t)=>i().convertToTSX(e,t);function f(){let e=new c,t=v(m(new URL("../astro.wasm",import.meta.url)),e.importObject);e.run(t);let o=globalThis["@astrojs/compiler"];return{transform:(n,a)=>{try{return o.transform(n,a||{})}catch(r){throw s=void 0,r}},parse:(n,a)=>{try{let r=o.parse(n,a||{});return{...r,ast:JSON.parse(r.ast)}}catch(r){throw s=void 0,r}},convertToTSX:(n,a)=>{try{let r=o.convertToTSX(n,a||{});return{...r,map:JSON.parse(r.map)}}catch(r){throw s=void 0,r}}}}function v(e,t){let o=p(e);return new WebAssembly.Instance(new WebAssembly.Module(o),t)}export{h as convertToTSX,w as parse,f as startRunningService,l as transform};

View File

@@ -1,3 +0,0 @@
"use strict";var m=Object.defineProperty;var d=Object.getOwnPropertyDescriptor;var p=Object.getOwnPropertyNames;var N=Object.prototype.hasOwnProperty;var u=(o,e)=>{for(var t in e)m(o,t,{get:e[t],enumerable:!0})},f=(o,e,t,a)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of p(e))!N.call(o,r)&&r!==t&&m(o,r,{get:()=>e[r],enumerable:!(a=d(e,r))||a.enumerable});return o};var y=o=>f(m({},"__esModule",{value:!0}),o);var v={};u(v,{is:()=>s,serialize:()=>k,walk:()=>h,walkAsync:()=>x});module.exports=y(v);function n(o){return e=>e.type===o}var s={parent(o){return Array.isArray(o.children)},literal(o){return typeof o.value=="string"},tag(o){return o.type==="element"||o.type==="custom-element"||o.type==="component"||o.type==="fragment"},whitespace(o){return o.type==="text"&&o.value.trim().length===0},root:n("root"),element:n("element"),customElement:n("custom-element"),component:n("component"),fragment:n("fragment"),expression:n("expression"),text:n("text"),doctype:n("doctype"),comment:n("comment"),frontmatter:n("frontmatter")},l=class{constructor(e){this.callback=e}async visit(e,t,a){if(await this.callback(e,t,a),s.parent(e)){let r=[];for(let i=0;i<e.children.length;i++){let c=e.children[i];r.push(this.callback(c,e,i))}await Promise.all(r)}}};function h(o,e){new l(e).visit(o)}function x(o,e){return new l(e).visit(o)}function g(o){let e="";for(let t of o.attributes)switch(e+=" ",t.kind){case"empty":{e+=`${t.name}`;break}case"expression":{e+=`${t.name}={${t.value}}`;break}case"quoted":{e+=`${t.name}=${t.raw}`;break}case"template-literal":{e+=`${t.name}=\`${t.value}\``;break}case"shorthand":{e+=`{${t.name}}`;break}case"spread":{e+=`{...${t.name}}`;break}}return e}function k(o,e={selfClose:!0}){let t="";function a(r){if(s.root(r))for(let i of r.children)a(i);else if(s.frontmatter(r))t+=`---${r.value}---
`;else if(s.comment(r))t+=`<!--${r.value}-->`;else if(s.expression(r)){t+="{";for(let i of r.children)a(i);t+="}"}else if(s.literal(r))t+=r.value;else if(s.tag(r))if(t+=`<${r.name}`,t+=g(r),r.children.length===0&&e.selfClose)t+=" />";else{t+=">";for(let i of r.children)a(i);t+=`</${r.name}>`}}return a(o),t}0&&(module.exports={is,serialize,walk,walkAsync});

View File

@@ -1,29 +0,0 @@
import { Node, ParentNode, LiteralNode, TagLikeNode, TextNode, RootNode, ElementNode, CustomElementNode, ComponentNode, FragmentNode, ExpressionNode, DoctypeNode, CommentNode, FrontmatterNode } from '../shared/ast.js';
type Visitor = (node: Node, parent?: ParentNode, index?: number) => void | Promise<void>;
declare const is: {
parent(node: Node): node is ParentNode;
literal(node: Node): node is LiteralNode;
tag(node: Node): node is TagLikeNode;
whitespace(node: Node): node is TextNode;
root: (node: Node) => node is RootNode;
element: (node: Node) => node is ElementNode;
customElement: (node: Node) => node is CustomElementNode;
component: (node: Node) => node is ComponentNode;
fragment: (node: Node) => node is FragmentNode;
expression: (node: Node) => node is ExpressionNode;
text: (node: Node) => node is TextNode;
doctype: (node: Node) => node is DoctypeNode;
comment: (node: Node) => node is CommentNode;
frontmatter: (node: Node) => node is FrontmatterNode;
};
declare function walk(node: ParentNode, callback: Visitor): void;
declare function walkAsync(node: ParentNode, callback: Visitor): Promise<void>;
interface SerializeOptions {
selfClose: boolean;
}
/** @deprecated Please use `SerializeOptions` */
type SerializeOtions = SerializeOptions;
declare function serialize(root: Node, opts?: SerializeOptions): string;
export { SerializeOptions, SerializeOtions, Visitor, is, serialize, walk, walkAsync };

View File

@@ -1,3 +0,0 @@
function n(o){return t=>t.type===o}var a={parent(o){return Array.isArray(o.children)},literal(o){return typeof o.value=="string"},tag(o){return o.type==="element"||o.type==="custom-element"||o.type==="component"||o.type==="fragment"},whitespace(o){return o.type==="text"&&o.value.trim().length===0},root:n("root"),element:n("element"),customElement:n("custom-element"),component:n("component"),fragment:n("fragment"),expression:n("expression"),text:n("text"),doctype:n("doctype"),comment:n("comment"),frontmatter:n("frontmatter")},l=class{constructor(t){this.callback=t}async visit(t,e,s){if(await this.callback(t,e,s),a.parent(t)){let r=[];for(let i=0;i<t.children.length;i++){let m=t.children[i];r.push(this.callback(m,t,i))}await Promise.all(r)}}};function N(o,t){new l(t).visit(o)}function u(o,t){return new l(t).visit(o)}function c(o){let t="";for(let e of o.attributes)switch(t+=" ",e.kind){case"empty":{t+=`${e.name}`;break}case"expression":{t+=`${e.name}={${e.value}}`;break}case"quoted":{t+=`${e.name}=${e.raw}`;break}case"template-literal":{t+=`${e.name}=\`${e.value}\``;break}case"shorthand":{t+=`{${e.name}}`;break}case"spread":{t+=`{...${e.name}}`;break}}return t}function f(o,t={selfClose:!0}){let e="";function s(r){if(a.root(r))for(let i of r.children)s(i);else if(a.frontmatter(r))e+=`---${r.value}---
`;else if(a.comment(r))e+=`<!--${r.value}-->`;else if(a.expression(r)){e+="{";for(let i of r.children)s(i);e+="}"}else if(a.literal(r))e+=r.value;else if(a.tag(r))if(e+=`<${r.name}`,e+=c(r),r.children.length===0&&t.selfClose)e+=" />";else{e+=">";for(let i of r.children)s(i);e+=`</${r.name}>`}}return s(o),e}export{a as is,f as serialize,N as walk,u as walkAsync};

File diff suppressed because one or more lines are too long

View File

@@ -1,37 +0,0 @@
declare class Go {
importObject: {
gojs: {
'runtime.wasmExit': (sp: any) => void;
'runtime.wasmWrite': (sp: any) => void;
'runtime.resetMemoryDataView': (sp: any) => void;
'runtime.nanotime1': (sp: any) => void;
'runtime.walltime': (sp: any) => void;
'runtime.scheduleTimeoutEvent': (sp: any) => void;
'runtime.clearTimeoutEvent': (sp: any) => void;
'runtime.getRandomData': (sp: any) => void;
'syscall/js.finalizeRef': (sp: any) => void;
'syscall/js.stringVal': (sp: any) => void;
'syscall/js.valueGet': (sp: any) => void;
'syscall/js.valueSet': (sp: any) => void;
'syscall/js.valueDelete': (sp: any) => void;
'syscall/js.valueIndex': (sp: any) => void;
'syscall/js.valueSetIndex': (sp: any) => void;
'syscall/js.valueCall': (sp: any) => void;
'syscall/js.valueInvoke': (sp: any) => void;
'syscall/js.valueNew': (sp: any) => void;
'syscall/js.valueLength': (sp: any) => void;
'syscall/js.valuePrepareString': (sp: any) => void;
'syscall/js.valueLoadString': (sp: any) => void;
'syscall/js.valueInstanceOf': (sp: any) => void;
'syscall/js.copyBytesToGo': (sp: any) => void;
'syscall/js.copyBytesToJS': (sp: any) => void;
debug: (value: any) => void;
};
};
constructor();
run(instance: any): Promise<void>;
private _resume;
private _makeFuncWrapper;
}
export { Go as default };

View File

@@ -1 +0,0 @@
import{a}from"../chunk-W5DTLHV4.js";export{a as default};

View File

@@ -1 +0,0 @@
"use strict";var r=Object.defineProperty;var a=Object.getOwnPropertyDescriptor;var i=Object.getOwnPropertyNames;var N=Object.prototype.hasOwnProperty;var p=(t,e,d,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of i(e))!N.call(t,o)&&o!==d&&r(t,o,{get:()=>e[o],enumerable:!(n=a(e,o))||n.enumerable});return t};var s=t=>p(r({},"__esModule",{value:!0}),t);var m={};module.exports=s(m);

View File

@@ -1,74 +0,0 @@
type ParentNode = RootNode | ElementNode | ComponentNode | CustomElementNode | FragmentNode | ExpressionNode;
type LiteralNode = TextNode | DoctypeNode | CommentNode | FrontmatterNode;
type Node = RootNode | ElementNode | ComponentNode | CustomElementNode | FragmentNode | ExpressionNode | TextNode | FrontmatterNode | DoctypeNode | CommentNode;
interface Position {
start: Point;
end?: Point;
}
interface Point {
/** 1-based line number */
line: number;
/** 1-based column number, per-line */
column: number;
/** 0-based byte offset */
offset: number;
}
interface BaseNode {
type: string;
position?: Position;
}
interface ParentLikeNode extends BaseNode {
type: 'element' | 'component' | 'custom-element' | 'fragment' | 'expression' | 'root';
children: Node[];
}
interface ValueNode extends BaseNode {
value: string;
}
interface RootNode extends ParentLikeNode {
type: 'root';
}
interface AttributeNode extends BaseNode {
type: 'attribute';
kind: 'quoted' | 'empty' | 'expression' | 'spread' | 'shorthand' | 'template-literal';
name: string;
value: string;
raw?: string;
}
interface TextNode extends ValueNode {
type: 'text';
}
interface ElementNode extends ParentLikeNode {
type: 'element';
name: string;
attributes: AttributeNode[];
}
interface FragmentNode extends ParentLikeNode {
type: 'fragment';
name: string;
attributes: AttributeNode[];
}
interface ComponentNode extends ParentLikeNode {
type: 'component';
name: string;
attributes: AttributeNode[];
}
interface CustomElementNode extends ParentLikeNode {
type: 'custom-element';
name: string;
attributes: AttributeNode[];
}
type TagLikeNode = ElementNode | FragmentNode | ComponentNode | CustomElementNode;
interface DoctypeNode extends ValueNode {
type: 'doctype';
}
interface CommentNode extends ValueNode {
type: 'comment';
}
interface FrontmatterNode extends ValueNode {
type: 'frontmatter';
}
interface ExpressionNode extends ParentLikeNode {
type: 'expression';
}
export { AttributeNode, BaseNode, CommentNode, ComponentNode, CustomElementNode, DoctypeNode, ElementNode, ExpressionNode, FragmentNode, FrontmatterNode, LiteralNode, Node, ParentLikeNode, ParentNode, Point, Position, RootNode, TagLikeNode, TextNode, ValueNode };

Some files were not shown because too many files have changed in this diff Show More