Fix website-creator: add missing functions and templates with IDs
This commit is contained in:
@@ -28,8 +28,11 @@ import sys
|
||||
import argparse
|
||||
import shutil
|
||||
import subprocess
|
||||
import json
|
||||
import time
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
from urllib.parse import urlparse
|
||||
|
||||
|
||||
# ============================================================================
|
||||
@@ -425,7 +428,269 @@ def save_analytics_config(output_path: str, config: dict):
|
||||
print(f" ✓ Analytics config saved to context/data-services.json")
|
||||
|
||||
|
||||
# ... (rest of functions remain the same - create_project, sync_to_gitea, etc.)
|
||||
# ============================================================================
|
||||
# PROJECT CREATION FUNCTIONS
|
||||
# ============================================================================
|
||||
|
||||
def create_project(args, languages, default_locale, features):
|
||||
"""Create the Astro project structure with templates."""
|
||||
output_path = Path(args.output)
|
||||
project_name = args.name.lower().replace(' ', '-')
|
||||
site_url = f"https://{project_name}.moreminimore.com"
|
||||
|
||||
# Get template directory
|
||||
script_dir = Path(__file__).parent
|
||||
template_dir = script_dir / 'templates'
|
||||
|
||||
print("\n📁 Creating project structure...")
|
||||
|
||||
# Create directories
|
||||
dirs = [
|
||||
output_path / 'public' / 'images',
|
||||
output_path / 'src' / 'components' / 'common',
|
||||
output_path / 'src' / 'components' / 'consent',
|
||||
output_path / 'src' / 'components' / 'ui',
|
||||
output_path / 'src' / 'layouts',
|
||||
output_path / 'src' / 'pages',
|
||||
output_path / 'src' / 'pages' / default_locale,
|
||||
output_path / 'src' / 'styles',
|
||||
output_path / 'src' / 'content' / 'blog',
|
||||
output_path / 'src' / 'lib',
|
||||
output_path / 'db',
|
||||
]
|
||||
|
||||
for d in dirs:
|
||||
d.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
print(" ✓ Directory structure created")
|
||||
|
||||
# Copy templates if they exist
|
||||
if template_dir.exists():
|
||||
print(" 📦 Copying templates with IDs...")
|
||||
|
||||
# Copy layouts
|
||||
layout_src = template_dir / 'layouts' / 'BaseLayout.astro'
|
||||
if layout_src.exists():
|
||||
content = layout_src.read_text(encoding='utf-8')
|
||||
content = content.replace("const siteName = 'Website Name'", f"const siteName = '{args.name}'")
|
||||
content = content.replace("const siteUrl = 'https://example.com'", f"const siteUrl = '{site_url}'")
|
||||
(output_path / 'src' / 'layouts' / 'BaseLayout.astro').write_text(content, encoding='utf-8')
|
||||
|
||||
# Copy Header
|
||||
header_src = template_dir / 'components' / 'common' / 'Header.astro'
|
||||
if header_src.exists():
|
||||
shutil.copy(header_src, output_path / 'src' / 'components' / 'common' / 'Header.astro')
|
||||
|
||||
# Copy Footer
|
||||
footer_src = template_dir / 'components' / 'common' / 'Footer.astro'
|
||||
if footer_src.exists():
|
||||
shutil.copy(footer_src, output_path / 'src' / 'components' / 'common' / 'Footer.astro')
|
||||
|
||||
# Copy page templates
|
||||
page_src = template_dir / 'pages' / 'index.astro'
|
||||
if page_src.exists():
|
||||
shutil.copy(page_src, output_path / 'src' / 'pages' / default_locale / 'index.astro')
|
||||
|
||||
# Copy styles
|
||||
style_src = template_dir / 'styles' / 'global.css'
|
||||
if style_src.exists():
|
||||
shutil.copy(style_src, output_path / 'src' / 'styles' / 'global.css')
|
||||
|
||||
print(" ✓ Templates copied")
|
||||
|
||||
# Create astro.config.mjs
|
||||
locales_str = ', '.join([f"'{lang}'" for lang in languages])
|
||||
astro_config = ASTRO_CONFIG_TEMPLATE.format(
|
||||
site_url=site_url,
|
||||
locales=locales_str,
|
||||
default_locale=default_locale
|
||||
)
|
||||
(output_path / 'astro.config.mjs').write_text(astro_config, encoding='utf-8')
|
||||
print(" ✓ astro.config.mjs created")
|
||||
|
||||
# Create package.json
|
||||
package_json = PACKAGE_JSON_TEMPLATE.format(name=project_name)
|
||||
(output_path / 'package.json').write_text(package_json, encoding='utf-8')
|
||||
print(" ✓ package.json created")
|
||||
|
||||
# Create tsconfig.json
|
||||
tsconfig = """{
|
||||
"extends": "astro/tsconfigs/strict",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["src/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
(output_path / 'tsconfig.json').write_text(tsconfig, encoding='utf-8')
|
||||
|
||||
# Create env file
|
||||
env_content = f"""# Website Configuration
|
||||
SITE_NAME={args.name}
|
||||
SITE_URL={site_url}
|
||||
|
||||
# Umami Analytics (optional - get from Umami dashboard)
|
||||
# UMAMI_WEBSITE_ID=
|
||||
# UMAMI_URL=
|
||||
"""
|
||||
(output_path / '.env').write_text(env_content, encoding='utf-8')
|
||||
print(" ✓ Configuration files created")
|
||||
|
||||
# Create basic index page if no template
|
||||
if not (output_path / 'src' / 'pages' / default_locale / 'index.astro').exists():
|
||||
index_content = f"""---
|
||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
import Header from '../components/common/Header.astro';
|
||||
import Footer from '../components/common/Footer.astro';
|
||||
---
|
||||
|
||||
<BaseLayout title="Home" description="Welcome to {args.name}">
|
||||
<Header />
|
||||
<main id="main-content">
|
||||
<section id="hero-section" class="hero">
|
||||
<h1 id="hero-title">Welcome to {args.name}</h1>
|
||||
<p id="hero-subtitle">Your trusted partner</p>
|
||||
</section>
|
||||
</main>
|
||||
<Footer />
|
||||
</BaseLayout>
|
||||
"""
|
||||
(output_path / 'src' / 'pages' / default_locale / 'index.astro').write_text(index_content, encoding='utf-8')
|
||||
|
||||
print(" ✓ Basic pages created")
|
||||
|
||||
# Create Dockerfile
|
||||
dockerfile = f"""FROM node:20-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install dependencies
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
|
||||
# Copy source
|
||||
COPY . .
|
||||
|
||||
# Build
|
||||
RUN npm run build
|
||||
|
||||
# Serve
|
||||
EXPOSE 80
|
||||
CMD ["npm", "run", "preview"]
|
||||
"""
|
||||
(output_path / 'Dockerfile').write_text(dockerfile, encoding='utf-8')
|
||||
print(" ✓ Dockerfile created")
|
||||
|
||||
# Create .gitignore
|
||||
gitignore = """# Dependencies
|
||||
node_modules/
|
||||
|
||||
# Build output
|
||||
dist/
|
||||
|
||||
# Environment
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# IDE
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
"""
|
||||
(output_path / '.gitignore').write_text(gitignore, encoding='utf-8')
|
||||
print(" ✓ .gitignore created")
|
||||
|
||||
return output_path
|
||||
|
||||
|
||||
def sync_to_gitea(output_path: str, repo_name: str) -> str:
|
||||
"""Sync project to Gitea repository."""
|
||||
try:
|
||||
# Import gitea sync functionality
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent / 'gitea-sync' / 'scripts'))
|
||||
from sync import sync_repo
|
||||
|
||||
# Use the gitea-sync script
|
||||
result = sync_repo(
|
||||
repo_name=repo_name,
|
||||
repo_path=output_path,
|
||||
description=f"Website: {repo_name}",
|
||||
auto_push=True
|
||||
)
|
||||
if result.get('success'):
|
||||
return result.get('url', f"https://git.moreminimore.com/user/{repo_name}.git")
|
||||
else:
|
||||
print(f" ⚠ Gitea sync failed: {result.get('error')}")
|
||||
return f"https://git.moreminimore.com/user/{repo_name}.git"
|
||||
except Exception as e:
|
||||
print(f" ⚠ Gitea sync error: {e}")
|
||||
print(" Continuing without Gitea sync...")
|
||||
# Return a dummy URL so deployment can continue
|
||||
return f"https://git.moreminimore.com/user/{repo_name}.git"
|
||||
|
||||
|
||||
def deploy_to_easypanel(output_path: str, project_name: str, git_url: str) -> str:
|
||||
"""Deploy project to Easypanel."""
|
||||
try:
|
||||
# Import easypanel deploy functionality
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent / 'easypanel-deploy' / 'scripts'))
|
||||
from deploy import (
|
||||
get_session_token,
|
||||
create_service,
|
||||
update_git_source,
|
||||
update_build_type,
|
||||
deploy_service,
|
||||
load_env
|
||||
)
|
||||
|
||||
# Load credentials
|
||||
env = load_env()
|
||||
username = env.get('EASYPANEL_USERNAME', '')
|
||||
password = env.get('EASYPANEL_PASSWORD', '')
|
||||
|
||||
if not username or not password:
|
||||
print(" ⚠ Easypanel credentials not found")
|
||||
print(" Skipping deployment - you can deploy manually later")
|
||||
return f"https://{project_name}.moreminimore.com"
|
||||
|
||||
# Get session token
|
||||
token = get_session_token(username, password)
|
||||
if not token:
|
||||
print(" ⚠ Failed to get Easypanel session")
|
||||
return f"https://{project_name}.moreminimore.com"
|
||||
|
||||
# Create service
|
||||
create_service(project_name, 'web', token)
|
||||
|
||||
# Update git source
|
||||
update_git_source(project_name, 'web', git_url, 'main', token)
|
||||
|
||||
# Set build type to dockerfile
|
||||
update_build_type(project_name, 'web', token, 'dockerfile')
|
||||
|
||||
# Deploy
|
||||
deploy_service(project_name, 'web', token)
|
||||
|
||||
return f"https://{project_name}.moreminimore.com"
|
||||
except Exception as e:
|
||||
print(f" ⚠ Easypanel deployment error: {e}")
|
||||
print(" Continuing without deployment...")
|
||||
return f"https://{project_name}.moreminimore.com"
|
||||
|
||||
|
||||
def monitor_deployment(project_name: str):
|
||||
"""Monitor deployment status."""
|
||||
print(f" 📊 Monitoring deployment for {project_name}...")
|
||||
print(" (Deployment is running in background)")
|
||||
print(" Check status at: https://panelwebsite.moreminimore.com")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
---
|
||||
const currentYear = new Date().getFullYear();
|
||||
|
||||
const quickLinks = [
|
||||
{ name: 'หน้าแรก', href: '/' },
|
||||
{ name: 'เกี่ยวกับเรา', href: '/about' },
|
||||
{ name: 'บริการ', href: '/services' },
|
||||
{ name: 'สินค้า', href: '/products' },
|
||||
{ name: 'ติดต่อเรา', href: '/contact' },
|
||||
];
|
||||
|
||||
const services = [
|
||||
{ name: 'บริการติดตั้ง', href: '/services/installation' },
|
||||
{ name: 'บริการให้คำปรึกษา', href: '/services/consultation' },
|
||||
{ name: 'บริการซ่อมบำรุง', href: '/services/maintenance' },
|
||||
];
|
||||
|
||||
const legalLinks = [
|
||||
{ name: 'นโยบายความเป็นส่วนตัว', href: '/privacy-policy' },
|
||||
{ name: 'ข้อกำหนดและเงื่อนไข', href: '/terms-and-conditions' },
|
||||
{ name: 'นโยบายคุกกี้', href: '/cookie-policy' },
|
||||
];
|
||||
|
||||
const socialLinks = [
|
||||
{ name: 'Facebook', href: 'https://facebook.com', icon: 'facebook' },
|
||||
{ name: 'Line', href: 'https://line.me', icon: 'line' },
|
||||
{ name: 'YouTube', href: 'https://youtube.com', icon: 'youtube' },
|
||||
];
|
||||
---
|
||||
|
||||
<footer id="footer-component" class="bg-secondary-900 text-white pt-16 pb-8">
|
||||
<div id="footer-container" class="container-custom">
|
||||
<!-- Main Footer Content -->
|
||||
<div id="footer-grid" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8 mb-12">
|
||||
<!-- Company Info -->
|
||||
<div id="footer-company">
|
||||
<div id="footer-logo-container" class="mb-4">
|
||||
<img id="footer-logo" src="/images/logo.png" alt="Logo" class="h-12" />
|
||||
</div>
|
||||
<div id="footer-description">
|
||||
<p class="text-secondary-300 mb-4">
|
||||
บริษัท ดีล พลัส เทค จำกัด ผู้เชี่ยวชาญด้านระบบท่อและอุปกรณ์ติดตั้งคุณภาพสูง
|
||||
</p>
|
||||
</div>
|
||||
<!-- Social Links -->
|
||||
<div id="footer-social">
|
||||
<div id="social-links-container" class="flex space-x-4">
|
||||
{socialLinks.map((social, index) => (
|
||||
<a id={`social-${social.icon}`} href={social.href} target="_blank" rel="noopener noreferrer" class="w-10 h-10 bg-secondary-800 rounded-full flex items-center justify-center hover:bg-primary-600 transition-colors" aria-label={social.name}>
|
||||
<span class="text-sm font-medium">{social.name[0]}</span>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quick Links -->
|
||||
<div id="footer-quick-links">
|
||||
<h3 id="quick-links-title" class="text-lg font-bold mb-4">ลิงก์ด่วน</h3>
|
||||
<ul id="quick-links-list" class="space-y-2">
|
||||
{quickLinks.map((link, index) => (
|
||||
<li>
|
||||
<a id={`quick-link-${index}`} href={link.href} class="text-secondary-300 hover:text-white transition-colors">
|
||||
{link.name}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Services -->
|
||||
<div id="footer-services">
|
||||
<h3 id="services-title" class="text-lg font-bold mb-4">บริการ</h3>
|
||||
<ul id="services-list" class="space-y-2">
|
||||
{services.map((service, index) => (
|
||||
<li>
|
||||
<a id={`service-link-${index}`} href={service.href} class="text-secondary-300 hover:text-white transition-colors">
|
||||
{service.name}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Contact Info -->
|
||||
<div id="footer-contact">
|
||||
<h3 id="contact-title" class="text-lg font-bold mb-4">ติดต่อเรา</h3>
|
||||
<div id="contact-info" class="space-y-3">
|
||||
<div id="contact-address" class="flex items-start">
|
||||
<span class="text-secondary-400 mr-2">📍</span>
|
||||
<span class="text-secondary-300">123 ถนนสุขุมวิท กรุงเทพมหานคร 10110</span>
|
||||
</div>
|
||||
<div id="contact-phone" class="flex items-center">
|
||||
<span class="text-secondary-400 mr-2">📞</span>
|
||||
<a href="tel:021234567" class="text-secondary-300 hover:text-white transition-colors">02-123-4567</a>
|
||||
</div>
|
||||
<div id="contact-email" class="flex items-center">
|
||||
<span class="text-secondary-400 mr-2">✉️</span>
|
||||
<a href="mailto:info@example.com" class="text-secondary-300 hover:text-white transition-colors">info@example.com</a>
|
||||
</div>
|
||||
<div id="contact-hours" class="flex items-center">
|
||||
<span class="text-secondary-400 mr-2">🕐</span>
|
||||
<span class="text-secondary-300">วันจันทร์-เสาร์ 08:00-18:00 น.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bottom Footer -->
|
||||
<div id="footer-bottom" class="border-t border-secondary-800 pt-8">
|
||||
<div id="footer-bottom-content" class="flex flex-col md:flex-row justify-between items-center gap-4">
|
||||
<div id="copyright">
|
||||
<p class="text-secondary-400 text-sm">
|
||||
© {currentYear} บริษัท ดีล พลัส เทค จำกัด สงวนลิขสิทธิ์
|
||||
</p>
|
||||
</div>
|
||||
<div id="footer-legal-links">
|
||||
<ul id="legal-links-list" class="flex flex-wrap gap-4 text-sm">
|
||||
{legalLinks.map((link, index) => (
|
||||
<li>
|
||||
<a id={`legal-link-${index}`} href={link.href} class="text-secondary-400 hover:text-white transition-colors">
|
||||
{link.name}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
@@ -0,0 +1,122 @@
|
||||
---
|
||||
const navItems = [
|
||||
{ name: 'หน้าแรก', href: '/' },
|
||||
{ name: 'เกี่ยวกับเรา', href: '/about' },
|
||||
{ name: 'บริการ', href: '/services' },
|
||||
{ name: 'ติดต่อเรา', href: '/contact' },
|
||||
];
|
||||
|
||||
const categories = [
|
||||
{ name: 'สินค้า', href: '/products', hasDropdown: true },
|
||||
];
|
||||
---
|
||||
|
||||
<header id="header-component" class="fixed w-full top-0 z-40 bg-white shadow-md">
|
||||
<nav id="navbar" class="container-custom">
|
||||
<div id="navbar-container" class="flex items-center justify-between h-16 md:h-20">
|
||||
<!-- Logo -->
|
||||
<div id="logo-container">
|
||||
<a id="logo-link" href="/" class="flex items-center">
|
||||
<img id="logo-image" src="/images/logo.png" alt="Logo" class="h-10 md:h-12" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Desktop Navigation -->
|
||||
<div id="desktop-nav" class="hidden md:flex items-center space-x-6">
|
||||
<div id="nav-items-container">
|
||||
{navItems.map((item) => (
|
||||
<a id={`nav-${item.name.replace(' ', '-').toLowerCase()}`} href={item.href} class="nav-link text-secondary-700 hover:text-primary-600 font-medium transition-colors">
|
||||
{item.name}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<!-- Categories Dropdown -->
|
||||
<div id="categories-dropdown" class="relative group">
|
||||
<button id="categories-btn" class="nav-link flex items-center text-secondary-700 hover:text-primary-600 font-medium transition-colors">
|
||||
สินค้า
|
||||
<svg id="categories-chevron" class="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
<div id="categories-menu" class="absolute left-0 mt-2 w-48 bg-white rounded-lg shadow-xl opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 transform origin-top-left">
|
||||
<div id="categories-menu-container" class="py-2">
|
||||
<a id="category-all-products" href="/products" class="block px-4 py-2 text-secondary-700 hover:bg-primary-50 hover:text-primary-600">
|
||||
สินค้าทั้งหมด
|
||||
</a>
|
||||
<a id="category-pipes" href="/products/pipes" class="block px-4 py-2 text-secondary-700 hover:bg-primary-50 hover:text-primary-600">
|
||||
ท่อ
|
||||
</a>
|
||||
<a id="category-valves" href="/products/valves" class="block px-4 py-2 text-secondary-700 hover:bg-primary-50 hover:text-primary-600">
|
||||
วาล์ว
|
||||
</a>
|
||||
<a id="category-fittings" href="/products/fittings" class="block px-4 py-2 text-secondary-700 hover:bg-primary-50 hover:text-primary-600">
|
||||
ข้อต่อ
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CTA Button -->
|
||||
<div id="cta-container">
|
||||
<a id="cta-button" href="/contact" class="btn-primary px-4 py-2 rounded-lg font-medium">
|
||||
ติดต่อเรา
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Menu Button -->
|
||||
<div id="mobile-menu-btn-container" class="md:hidden">
|
||||
<button id="mobile-menu-btn" class="p-2 text-secondary-700 hover:text-primary-600" aria-label="เมนู">
|
||||
<svg id="menu-icon" class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Menu -->
|
||||
<div id="mobile-menu" class="hidden md:hidden bg-white border-t">
|
||||
<div id="mobile-menu-container" class="px-4 py-4 space-y-2">
|
||||
{navItems.map((item, index) => (
|
||||
<a id={`mobile-nav-${index}`} href={item.href} class="block py-2 text-secondary-700 hover:text-primary-600 font-medium">
|
||||
{item.name}
|
||||
</a>
|
||||
))}
|
||||
<div id="mobile-categories-container">
|
||||
<button id="mobile-categories-btn" class="flex items-center justify-between w-full py-2 text-secondary-700 font-medium">
|
||||
สินค้า
|
||||
<svg id="mobile-chevron" class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
<div id="mobile-categories-menu" class="hidden pl-4 space-y-2">
|
||||
<a id="mobile-all-products" href="/products" class="block py-2 text-secondary-600">- สินค้าทั้งหมด</a>
|
||||
<a id="mobile-pipes" href="/products/pipes" class="block py-2 text-secondary-600">- ท่อ</a>
|
||||
<a id="mobile-valves" href="/products/valves" class="block py-2 text-secondary-600">- วาล์ว</a>
|
||||
<a id="mobile-fittings" href="/products/fittings" class="block py-2 text-secondary-600">- ข้อต่อ</a>
|
||||
</div>
|
||||
</div>
|
||||
<a id="mobile-cta" href="/contact" class="block w-full text-center btn-primary px-4 py-3 rounded-lg font-medium mt-4">
|
||||
ติดต่อเรา
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<script>
|
||||
// Mobile Menu Toggle
|
||||
const mobileMenuBtn = document.getElementById('mobile-menu-btn');
|
||||
const mobileMenu = document.getElementById('mobile-menu');
|
||||
const mobileCategoriesBtn = document.getElementById('mobile-categories-btn');
|
||||
const mobileCategoriesMenu = document.getElementById('mobile-categories-menu');
|
||||
|
||||
mobileMenuBtn?.addEventListener('click', () => {
|
||||
mobileMenu?.classList.toggle('hidden');
|
||||
});
|
||||
|
||||
mobileCategoriesBtn?.addEventListener('click', () => {
|
||||
mobileCategoriesMenu?.classList.toggle('hidden');
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,190 @@
|
||||
---
|
||||
import '../styles/global.css';
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
description?: string;
|
||||
image?: string;
|
||||
canonicalURL?: string;
|
||||
}
|
||||
|
||||
const { title, description = '', image = '/images/logo.png', canonicalURL = Astro.url } = Astro.props;
|
||||
|
||||
const siteName = 'Website Name';
|
||||
const siteUrl = 'https://example.com';
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
<html lang="th">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
|
||||
<!-- SEO Meta Tags -->
|
||||
<title>{title} | {siteName}</title>
|
||||
<meta name="title" content={`${title} | ${siteName}`} />
|
||||
<meta name="description" content={description} />
|
||||
<link rel="canonical" href={canonicalURL} />
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content={canonicalURL} />
|
||||
<meta property="og:title" content={`${title} | ${siteName}`} />
|
||||
<meta property="og:description" content={description} />
|
||||
<meta property="og:image" content={new URL(image, siteUrl)} />
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta property="twitter:card" content="summary_large_image" />
|
||||
<meta property="twitter:url" content={canonicalURL} />
|
||||
<meta property="twitter:title" content={`${title} | ${siteName}`} />
|
||||
<meta property="twitter:description" content={description} />
|
||||
<meta property="twitter:image" content={new URL(image, siteUrl)} />
|
||||
|
||||
<!-- Favicon -->
|
||||
<link rel="icon" type="image/png" href="/favicon.ico" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
|
||||
<!-- Preconnect -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
</head>
|
||||
|
||||
<body id="body" class="flex flex-col min-h-screen">
|
||||
<div id="page-wrapper">
|
||||
<header id="header">
|
||||
<slot name="header" />
|
||||
</header>
|
||||
|
||||
<main id="main-content" class="flex-grow">
|
||||
<slot />
|
||||
</main>
|
||||
|
||||
<footer id="footer">
|
||||
<slot name="footer" />
|
||||
</footer>
|
||||
|
||||
<!-- Cookie Consent Banner -->
|
||||
<div id="cookie-consent-banner" class="fixed bottom-0 left-0 right-0 z-50 bg-secondary-900 text-white p-6 shadow-2xl transform translate-y-full transition-transform duration-500">
|
||||
<div id="cookie-consent-container" class="container-custom max-w-6xl">
|
||||
<div id="cookie-consent-content" class="flex flex-col md:flex-row items-center justify-between gap-6">
|
||||
<div id="cookie-consent-text" class="flex-1">
|
||||
<h3 id="cookie-consent-title" class="text-xl font-bold mb-2">เราใช้คุกกี้เพื่อประสบการณ์ที่ดีที่สุด</h3>
|
||||
<p id="cookie-consent-description" class="text-secondary-300 text-base">
|
||||
เว็บไซต์ของเราใช้คุกกี้เพื่อเพิ่มประสิทธิภาพการใช้งาน คุณสามารถยอมรับหรือปฏิเสธได้
|
||||
</p>
|
||||
</div>
|
||||
<div id="cookie-consent-buttons" class="flex flex-wrap gap-4">
|
||||
<button id="cookie-reject-btn" class="btn-secondary px-6 py-3 text-sm">
|
||||
ปฏิเสธทั้งหมด
|
||||
</button>
|
||||
<button id="cookie-accept-btn" class="btn-primary px-6 py-3 text-sm">
|
||||
ยอมรับทั้งหมด
|
||||
</button>
|
||||
<button id="cookie-settings-btn" class="btn-outline px-6 py-3 text-sm">
|
||||
ตั้งค่า
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Cookie Preferences Modal -->
|
||||
<div id="cookie-preferences-modal" class="fixed inset-0 z-50 hidden bg-black/50">
|
||||
<div id="cookie-modal-content" class="flex items-center justify-center min-h-screen p-4">
|
||||
<div id="cookie-modal-box" class="bg-white rounded-2xl shadow-2xl max-w-lg w-full p-6">
|
||||
<div id="cookie-modal-header" class="flex justify-between items-center mb-4">
|
||||
<h2 id="cookie-modal-title" class="text-xl font-bold">ตั้งค่าคุกกี้</h2>
|
||||
<button id="cookie-modal-close" class="text-gray-500 hover:text-gray-700">
|
||||
<span id="cookie-close-icon">✕</span>
|
||||
</button>
|
||||
</div>
|
||||
<div id="cookie-modal-body">
|
||||
<div id="cookie-necessary" class="mb-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<h3 class="font-medium">คุกกี้ที่จำเป็น</h3>
|
||||
<p class="text-sm text-gray-600">จำเป็นสำหรับการทำงานของเว็บไซต์</p>
|
||||
</div>
|
||||
<input type="checkbox" checked disabled class="w-5 h-5" />
|
||||
</div>
|
||||
</div>
|
||||
<div id="cookie-analytics" class="mb-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<h3 class="font-medium">คุกกี้วิเคราะห์</h3>
|
||||
<p class="text-sm text-gray-600">ช่วยให้เราเข้าใจผู้ใช้งาน</p>
|
||||
</div>
|
||||
<input type="checkbox" id="cookie-analytics-checkbox" class="w-5 h-5" />
|
||||
</div>
|
||||
</div>
|
||||
<div id="cookie-marketing" class="mb-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<h3 class="font-medium">คุกกี้การตลาด</h3>
|
||||
<p class="text-sm text-gray-600">ใช้สำหรับโฆษณา</p>
|
||||
</div>
|
||||
<input type="checkbox" id="cookie-marketing-checkbox" class="w-5 h-5" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="cookie-modal-footer" class="flex justify-end gap-4 mt-6">
|
||||
<button id="cookie-save-btn" class="btn-primary px-6 py-3">บันทึก</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Cookie Consent Logic
|
||||
const banner = document.getElementById('cookie-consent-banner');
|
||||
const acceptBtn = document.getElementById('cookie-accept-btn');
|
||||
const rejectBtn = document.getElementById('cookie-reject-btn');
|
||||
const settingsBtn = document.getElementById('cookie-settings-btn');
|
||||
const modal = document.getElementById('cookie-preferences-modal');
|
||||
const closeModal = document.getElementById('cookie-modal-close');
|
||||
const saveBtn = document.getElementById('cookie-save-btn');
|
||||
|
||||
function showBanner() {
|
||||
banner?.classList.remove('translate-y-full');
|
||||
}
|
||||
|
||||
function hideBanner() {
|
||||
banner?.classList.add('translate-y-full');
|
||||
}
|
||||
|
||||
acceptBtn?.addEventListener('click', () => {
|
||||
localStorage.setItem('cookie-consent', 'accepted');
|
||||
hideBanner();
|
||||
});
|
||||
|
||||
rejectBtn?.addEventListener('click', () => {
|
||||
localStorage.setItem('cookie-consent', 'rejected');
|
||||
hideBanner();
|
||||
});
|
||||
|
||||
settingsBtn?.addEventListener('click', () => {
|
||||
modal?.classList.remove('hidden');
|
||||
});
|
||||
|
||||
closeModal?.addEventListener('click', () => {
|
||||
modal?.classList.add('hidden');
|
||||
});
|
||||
|
||||
saveBtn?.addEventListener('click', () => {
|
||||
const analytics = (document.getElementById('cookie-analytics-checkbox') as HTMLInputElement)?.checked;
|
||||
const marketing = (document.getElementById('cookie-marketing-checkbox') as HTMLInputElement)?.checked;
|
||||
localStorage.setItem('cookie-consent', JSON.stringify({ analytics, marketing }));
|
||||
modal?.classList.add('hidden');
|
||||
hideBanner();
|
||||
});
|
||||
|
||||
// Check consent on load
|
||||
const consent = localStorage.getItem('cookie-consent');
|
||||
if (!consent) {
|
||||
showBanner();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
183
skills/website-creator/scripts/templates/pages/index.astro
Normal file
183
skills/website-creator/scripts/templates/pages/index.astro
Normal file
@@ -0,0 +1,183 @@
|
||||
---
|
||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
import Header from '../components/common/Header.astro';
|
||||
import Footer from '../components/common/Footer.astro';
|
||||
|
||||
const pageTitle = 'หน้าแรก';
|
||||
const pageDescription = 'ผู้เชี่ยวชาญด้านระบบท่อและอุปกรณ์ติดตั้งคุณภาพสูง ราคาโรงงาน';
|
||||
---
|
||||
|
||||
<BaseLayout title={pageTitle} description={pageDescription}>
|
||||
<Header slot="header" />
|
||||
|
||||
<!-- Hero Section -->
|
||||
<section id="hero-section" class="relative bg-white section overflow-hidden pt-24 md:pt-32">
|
||||
<div id="hero-container" class="container-custom">
|
||||
<div id="hero-grid" class="grid md:grid-cols-2 gap-8 md:gap-12 items-center">
|
||||
<div id="hero-content" class="animate-fade-in">
|
||||
<h1 id="hero-title" class="text-2xl sm:text-3xl md:text-4xl lg:text-5xl xl:text-6xl font-bold text-secondary-900 mb-4 md:mb-6 leading-tight">
|
||||
ผู้เชี่ยวชาญระบบน้ำ<br/>
|
||||
<span class="text-green-600">คุณภาพสูง ราคาโรงงาน</span>
|
||||
</h1>
|
||||
<p id="hero-description" class="text-base sm:text-lg md:text-xl text-secondary-600 mb-6 md:mb-8 leading-relaxed">
|
||||
เราเป็นผู้เชี่ยวชาญด้านระบบน้ำ ให้คำแนะนำและจำหน่ายท่อ PPR ท่อ HDPE ท่อ PVC และอุปกรณ์ติดตั้งคุณภาพสูง ราคาถูก
|
||||
</p>
|
||||
<div id="hero-buttons" class="flex flex-wrap justify-center gap-3 md:gap-4">
|
||||
<a id="hero-cta-products" href="/products" class="bg-green-600 hover:bg-green-700 text-white px-5 py-3 md:px-8 md:py-4 rounded-xl font-medium transition-all hover:shadow-lg active:scale-95 text-sm md:text-lg">
|
||||
ดูสินค้าทั้งหมด
|
||||
</a>
|
||||
<a id="hero-cta-contact" href="/contact" class="bg-white text-green-600 px-5 py-3 md:px-8 md:py-4 rounded-xl border-2 border-green-500 font-medium transition-all hover:shadow-lg active:scale-95 text-sm md:text-lg">
|
||||
ติดต่อเรา
|
||||
</a>
|
||||
</div>
|
||||
<div id="hero-stats" class="flex items-center sm:space-x-8 space-x-4 sm:mt-12 mt-8 justify-center">
|
||||
<div id="stat-experience">
|
||||
<div id="stat-experience-value" class="text-xl sm:text-2xl md:text-3xl font-bold text-green-600">10+</div>
|
||||
<div id="stat-experience-label" class="text-secondary-600 text-xs sm:text-base">ปีประสบการณ์</div>
|
||||
</div>
|
||||
<div id="stat-projects">
|
||||
<div id="stat-projects-value" class="text-xl sm:text-2xl md:text-3xl font-bold text-green-600">1000+</div>
|
||||
<div id="stat-projects-label" class="text-secondary-600 text-xs sm:text-base">โปรเจคต์</div>
|
||||
</div>
|
||||
<div id="stat-products">
|
||||
<div id="stat-products-value" class="text-xl sm:text-2xl md:text-3xl font-bold text-green-600">500+</div>
|
||||
<div id="stat-products-label" class="text-secondary-600 text-xs sm:text-base">สินค้า</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="hero-image-container" class="relative animate-slide-up mt-6 md:mt-0">
|
||||
<div id="hero-image-wrapper" class="absolute inset-0 bg-gradient-to-br from-green-500/20 to-accent-500/20 rounded-3xl blur-3xl"></div>
|
||||
<div id="hero-image-grid" class="grid grid-cols-3 gap-2 md:gap-4 relative">
|
||||
<div id="hero-image-main" class="col-span-2 row-span-2">
|
||||
<img id="hero-img-1" src="/images/hero-1.jpg" alt="Products" class="w-full h-full object-cover rounded-2xl shadow-xl" />
|
||||
</div>
|
||||
<div id="hero-image-2">
|
||||
<img id="hero-img-2" src="/images/hero-2.jpg" alt="Products" class="w-full h-full object-cover rounded-2xl shadow-xl" />
|
||||
</div>
|
||||
<div id="hero-image-3">
|
||||
<img id="hero-img-3" src="/images/hero-3.jpg" alt="Products" class="w-full h-full object-cover rounded-2xl shadow-xl" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Categories Section -->
|
||||
<section id="categories-section" class="py-16 md:py-24 bg-gray-50">
|
||||
<div id="categories-container" class="container-custom">
|
||||
<div id="categories-header" class="text-center mb-12">
|
||||
<h2 id="categories-title" class="text-2xl md:text-3xl lg:text-4xl font-bold text-secondary-900 mb-4">
|
||||
หมวดสินค้า
|
||||
</h2>
|
||||
<p id="categories-subtitle" class="text-secondary-600 text-lg max-w-2xl mx-auto">
|
||||
สินค้าคุณภาพสูงสำหรับทุกการใช้งาน
|
||||
</p>
|
||||
</div>
|
||||
<div id="categories-grid" class="grid grid-cols-2 md:grid-cols-4 gap-4 md:gap-6">
|
||||
{['ท่อ PPR', 'ท่อ HDPE', 'ท่อ UPVC', 'วาล์ว', 'ข้อต่อ', 'อุปกรณ์ติดตั้ง', 'ปั๊มน้ำ', 'อุปกรณ์ดับเพลิง'].map((category, index) => (
|
||||
<a id={`category-card-${index}`} href={`/products/${category.toLowerCase().replace(' ', '-')}`} class="group bg-white rounded-xl shadow-md hover:shadow-xl transition-all duration-300 p-6 text-center">
|
||||
<div id={`category-icon-${index}`} class="w-16 h-16 mx-auto mb-4 bg-primary-100 rounded-full flex items-center justify-center group-hover:bg-primary-600 transition-colors">
|
||||
<span class="text-2xl">{category[0]}</span>
|
||||
</div>
|
||||
<h3 id={`category-name-${index}`} class="font-bold text-secondary-900 group-hover:text-primary-600 transition-colors">
|
||||
{category}
|
||||
</h3>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Featured Products Section -->
|
||||
<section id="featured-products-section" class="py-16 md:py-24 bg-white">
|
||||
<div id="featured-products-container" class="container-custom">
|
||||
<div id="featured-products-header" class="text-center mb-12">
|
||||
<h2 id="featured-products-title" class="text-2xl md:text-3xl lg:text-4xl font-bold text-secondary-900 mb-4">
|
||||
สินค้าแนะนำ
|
||||
</h2>
|
||||
<p id="featured-products-subtitle" class="text-secondary-600 text-lg max-w-2xl mx-auto">
|
||||
สินค้ายอดนิยมจากลูกค้า
|
||||
</p>
|
||||
</div>
|
||||
<div id="featured-products-grid" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6 md:gap-8">
|
||||
{[
|
||||
{ name: 'ท่อ PPR ตราช้าง', description: 'ท่อ PPR คุณภาพสูง มาตรฐาน', image: '/images/products/ppr.jpg' },
|
||||
{ name: 'วาล์วน้ำดับเพลิง', description: 'วาล์วคุณภาพสูง ทนทาน', image: '/images/products/valve.jpg' },
|
||||
{ name: 'ข้อต่อ HDPE', description: 'ข้อต่อสำหรับท่อ HDPE', image: '/images/products/fitting.jpg' },
|
||||
].map((product, index) => (
|
||||
<div id={`featured-product-card-${index}`} class="bg-white rounded-xl shadow-md hover:shadow-xl transition-all duration-300 overflow-hidden">
|
||||
<div id={`featured-product-image-${index}`} class="aspect-video overflow-hidden">
|
||||
<img id={`featured-product-img-${index}`} src={product.image} alt={product.name} class="w-full h-full object-cover hover:scale-105 transition-transform duration-300" />
|
||||
</div>
|
||||
<div id={`featured-product-content-${index}`} class="p-6">
|
||||
<h3 id={`featured-product-title-${index}`} class="font-bold text-lg text-secondary-900 mb-2">
|
||||
{product.name}
|
||||
</h3>
|
||||
<p id={`featured-product-desc-${index}`} class="text-secondary-600 mb-4">
|
||||
{product.description}
|
||||
</p>
|
||||
<a id={`featured-product-link-${index}`} href={`/products/${product.name.toLowerCase().replace(' ', '-')}`} class="text-primary-600 font-medium hover:text-primary-700 transition-colors">
|
||||
ดูรายละเอียด →
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div id="featured-products-cta" class="text-center mt-12">
|
||||
<a id="featured-products-all" href="/products" class="btn-primary px-8 py-3 text-lg rounded-xl">
|
||||
ดูสินค้าทั้งหมด
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Why Choose Us Section -->
|
||||
<section id="why-choose-us-section" class="py-16 md:py-24 bg-primary-50">
|
||||
<div id="why-choose-us-container" class="container-custom">
|
||||
<div id="why-choose-us-header" class="text-center mb-12">
|
||||
<h2 id="why-choose-us-title" class="text-2xl md:text-3xl lg:text-4xl font-bold text-secondary-900 mb-4">
|
||||
ทำไมต้องเลือกเรา
|
||||
</h2>
|
||||
<p id="why-choose-us-subtitle" class="text-secondary-600 text-lg max-w-2xl mx-auto">
|
||||
เรามีความมุ่งมั่นในการให้บริการที่ดีที่สุด
|
||||
</p>
|
||||
</div>
|
||||
<div id="why-choose-us-grid" class="grid grid-cols-1 md:grid-cols-3 gap-6 md:gap-8">
|
||||
{[
|
||||
{ icon: '🏭', title: 'โรงงานผู้ผลิต', description: 'สินค้าจากโรงงานโดยตรง ราคาถูก' },
|
||||
{ icon: '✅', title: 'มาตรฐาน', description: 'ผ่านการรับรอง มอก.' },
|
||||
{ icon: '🚚', title: 'จัดส่งรวดเร็ว', description: 'ส่งทั่วประเทศไทย' },
|
||||
].map((feature, index) => (
|
||||
<div id={`why-choose-us-card-${index}`} class="bg-white rounded-xl shadow-md p-8 text-center">
|
||||
<div id={`why-choose-us-icon-${index}`} class="text-4xl mb-4">{feature.icon}</div>
|
||||
<h3 id={`why-choose-us-feature-title-${index}`} class="font-bold text-xl text-secondary-900 mb-2">{feature.title}</h3>
|
||||
<p id={`why-choose-us-feature-desc-${index}`} class="text-secondary-600">{feature.description}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- CTA Section -->
|
||||
<section id="cta-section" class="py-16 md:py-24 bg-green-600">
|
||||
<div id="cta-container" class="container-custom text-center">
|
||||
<h2 id="cta-title" class="text-2xl md:text-3xl lg:text-4xl font-bold text-white mb-4">
|
||||
ต้องการคำปรึกษาฟรี?
|
||||
</h2>
|
||||
<p id="cta-description" class="text-white/80 text-lg mb-8 max-w-2xl mx-auto">
|
||||
ทีมงานของเราพร้อมให้คำปรึกษาฟรี ไม่มีค่าใช้จ่าย
|
||||
</p>
|
||||
<div id="cta-buttons" class="flex flex-wrap justify-center gap-4">
|
||||
<a id="cta-contact-btn" href="/contact" class="bg-white text-green-600 px-8 py-3 rounded-xl font-bold hover:bg-gray-100 transition-colors">
|
||||
ติดต่อเราวันนี้
|
||||
</a>
|
||||
<a id="cta-line-btn" href="https://line.me" target="_blank" class="bg-green-500 text-white px-8 py-3 rounded-xl font-bold hover:bg-green-400 transition-colors">
|
||||
ติดต่อผ่าน LINE
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Footer slot="footer" />
|
||||
</BaseLayout>
|
||||
298
skills/website-creator/scripts/templates/styles/global.css
Normal file
298
skills/website-creator/scripts/templates/styles/global.css
Normal file
@@ -0,0 +1,298 @@
|
||||
/* Global Styles */
|
||||
|
||||
/* Base Typography */
|
||||
html {
|
||||
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; }
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* Minimum font sizes */
|
||||
.text-base { font-size: 1rem; }
|
||||
.text-lg { font-size: 1.125rem; }
|
||||
.text-xl { font-size: 1.25rem; }
|
||||
|
||||
/* Container */
|
||||
.container-custom {
|
||||
max-width: 1280px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.container-custom {
|
||||
padding-left: 1.5rem;
|
||||
padding-right: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Section */
|
||||
.section {
|
||||
padding-top: 3rem;
|
||||
padding-bottom: 3rem;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.section {
|
||||
padding-top: 4rem;
|
||||
padding-bottom: 4rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.btn-primary {
|
||||
display: inline-block;
|
||||
padding: 0.75rem 1.5rem;
|
||||
background-color: #16a34a;
|
||||
color: white;
|
||||
border-radius: 0.5rem;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background-color: #15803d;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
display: inline-block;
|
||||
padding: 0.75rem 1.5rem;
|
||||
background-color: #374151;
|
||||
color: white;
|
||||
border-radius: 0.5rem;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background-color: #4b5563;
|
||||
}
|
||||
|
||||
.btn-outline {
|
||||
display: inline-block;
|
||||
padding: 0.75rem 1.5rem;
|
||||
background-color: transparent;
|
||||
color: white;
|
||||
border: 2px solid white;
|
||||
border-radius: 0.5rem;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.btn-outline:hover {
|
||||
background-color: white;
|
||||
color: #16a34a;
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from { transform: translateY(20px); opacity: 0; }
|
||||
to { transform: translateY(0); opacity: 1; }
|
||||
}
|
||||
|
||||
.animate-fade-in {
|
||||
animation: fadeIn 0.5s ease-out;
|
||||
}
|
||||
|
||||
.animate-slide-up {
|
||||
animation: slideUp 0.5s ease-out;
|
||||
}
|
||||
|
||||
/* Utility Classes */
|
||||
.flex { display: flex; }
|
||||
.flex-col { flex-direction: column; }
|
||||
.items-center { align-items: center; }
|
||||
.justify-center { justify-content: center; }
|
||||
.justify-between { justify-content: space-between; }
|
||||
.gap-2 { gap: 0.5rem; }
|
||||
.gap-4 { gap: 1rem; }
|
||||
.gap-6 { gap: 1.5rem; }
|
||||
.gap-8 { gap: 2rem; }
|
||||
|
||||
.grid { display: grid; }
|
||||
.grid-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); }
|
||||
.grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
.grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
||||
.grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.sm\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.md\:flex { display: flex; }
|
||||
.md\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
.md\:grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.lg\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
||||
.lg\:grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
|
||||
}
|
||||
|
||||
/* Spacing */
|
||||
.mt-2 { margin-top: 0.5rem; }
|
||||
.mt-4 { margin-top: 1rem; }
|
||||
.mt-6 { margin-top: 1.5rem; }
|
||||
.mt-8 { margin-top: 2rem; }
|
||||
.mb-2 { margin-bottom: 0.5rem; }
|
||||
.mb-4 { margin-bottom: 1rem; }
|
||||
.mb-6 { margin-bottom: 1.5rem; }
|
||||
.mb-8 { margin-bottom: 2rem; }
|
||||
|
||||
.p-2 { padding: 0.5rem; }
|
||||
.p-4 { padding: 1rem; }
|
||||
.p-6 { padding: 1.5rem; }
|
||||
.p-8 { padding: 2rem; }
|
||||
|
||||
.py-2 { padding-top: 0.5rem; padding-bottom: 0.5rem; }
|
||||
.py-3 { padding-top: 0.75rem; padding-bottom: 0.75rem; }
|
||||
.py-4 { padding-top: 1rem; padding-bottom: 1rem; }
|
||||
|
||||
/* Colors */
|
||||
.text-white { color: white; }
|
||||
.text-black { color: black; }
|
||||
.text-gray-500 { color: #6b7280; }
|
||||
.text-gray-600 { color: #4b5563; }
|
||||
.text-gray-700 { color: #374151; }
|
||||
.text-gray-900 { color: #111827; }
|
||||
.text-green-500 { color: #22c55e; }
|
||||
.text-green-600 { color: #16a34a; }
|
||||
|
||||
.bg-white { background-color: white; }
|
||||
.bg-gray-50 { background-color: #f9fafb; }
|
||||
.bg-gray-100 { background-color: #f3f4f6; }
|
||||
.bg-black { background-color: black; }
|
||||
.bg-green-500 { background-color: #22c55e; }
|
||||
.bg-green-600 { background-color: #16a34a; }
|
||||
|
||||
/* Border Radius */
|
||||
.rounded { border-radius: 0.25rem; }
|
||||
.rounded-lg { border-radius: 0.5rem; }
|
||||
.rounded-xl { border-radius: 0.75rem; }
|
||||
.rounded-2xl { border-radius: 1rem; }
|
||||
.rounded-full { border-radius: 9999px; }
|
||||
|
||||
/* Shadows */
|
||||
.shadow-md { box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); }
|
||||
.shadow-xl { box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); }
|
||||
.shadow-2xl { box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); }
|
||||
|
||||
/* Typography */
|
||||
.font-bold { font-weight: 700; }
|
||||
.font-medium { font-weight: 500; }
|
||||
.font-semibold { font-weight: 600; }
|
||||
|
||||
.text-xs { font-size: 0.75rem; }
|
||||
.text-sm { font-size: 0.875rem; }
|
||||
.text-lg { font-size: 1.125rem; }
|
||||
.text-xl { font-size: 1.25rem; }
|
||||
.text-2xl { font-size: 1.5rem; }
|
||||
.text-3xl { font-size: 1.875rem; }
|
||||
.text-4xl { font-size: 2.25rem; }
|
||||
|
||||
.leading-tight { line-height: 1.25; }
|
||||
.leading-relaxed { line-height: 1.625; }
|
||||
|
||||
/* Width/Height */
|
||||
.w-full { width: 100%; }
|
||||
.h-full { height: 100%; }
|
||||
.h-10 { height: 2.5rem; }
|
||||
.h-12 { height: 3rem; }
|
||||
.h-16 { height: 4rem; }
|
||||
|
||||
.min-h-screen { min-height: 100vh; }
|
||||
|
||||
/* Position */
|
||||
.relative { position: relative; }
|
||||
.absolute { position: absolute; }
|
||||
.fixed { position: fixed; }
|
||||
.inset-0 { top: 0; right: 0; bottom: 0; left: 0; }
|
||||
|
||||
.top-0 { top: 0; }
|
||||
.right-0 { right: 0; }
|
||||
.bottom-0 { bottom: 0; }
|
||||
.left-0 { left: 0; }
|
||||
.z-40 { z-index: 40; }
|
||||
.z-50 { z-index: 50; }
|
||||
|
||||
/* Overflow */
|
||||
.overflow-hidden { overflow: hidden; }
|
||||
|
||||
/* Transitions */
|
||||
.transition-all { transition: all 0.2s; }
|
||||
.transition-colors { transition: color 0.2s, background-color 0.2s; }
|
||||
.transition-transform { transition: transform 0.2s; }
|
||||
|
||||
/* Transform */
|
||||
.translate-y-full { transform: translateY(100%); }
|
||||
|
||||
/* Misc */
|
||||
.cursor-pointer { cursor: pointer; }
|
||||
.hover\:scale-105:hover { transform: scale(1.05); }
|
||||
.active\:scale-95:active { transform: scale(0.95); }
|
||||
|
||||
/* Hidden by default */
|
||||
.hidden { display: none; }
|
||||
|
||||
/* Space-x for flex items */
|
||||
.space-x-4 > * + * { margin-left: 1rem; }
|
||||
.space-x-6 > * + * { margin-left: 1.5rem; }
|
||||
.space-x-8 > * + * { margin-left: 2rem; }
|
||||
|
||||
/* Space-y for flex/grid items */
|
||||
.space-y-2 > * + * { margin-top: 0.5rem; }
|
||||
.space-y-4 > * + * { margin-top: 1rem; }
|
||||
.space-y-6 > * + * { margin-top: 1.5rem; }
|
||||
|
||||
/* Aspect ratio */
|
||||
.aspect-video {
|
||||
aspect-ratio: 16 / 9;
|
||||
}
|
||||
|
||||
/* Object fit */
|
||||
.object-cover {
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
/* Border */
|
||||
.border {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
.border-t {
|
||||
border-top-width: 1px;
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
/* Text align */
|
||||
.text-center { text-align: center; }
|
||||
|
||||
/* Max width */
|
||||
.max-w-lg { max-width: 32rem; }
|
||||
.max-w-2xl { max-width: 42rem; }
|
||||
.max-w-6xl { max-width: 72rem; }
|
||||
.mx-auto { margin-left: auto; margin-right: auto; }
|
||||
Reference in New Issue
Block a user