
Shadcn UI กับ Batch Processing Pipeline —
Shadcn UI Batch Processing

Shadcn UI Component Library React Radix UI Tailwind CSS Copy-paste Components ปรับแต่งได้ทุกอย่าง Dark Mode Accessibility TypeScript
Batch Processing Pipeline ประมวลผลข้อมูลจำนวนมากเป็นชุด ETL Data Aggregation Report Generation Apache Spark Airflow Dagster dbt
Shadcn UI Setup และ Components
=== Shadcn UI Setup ===
1. สร้าง Next.js Project
npx create-next-app@latest batch-dashboard --typescript --tailwind --eslint
cd batch-dashboard
2. ติดตั้ง Shadcn UI
npx shadcn-ui@latest init
- Style: Default
- Base Color: Slate
- CSS Variables: Yes
3. เพิ่ม Components
npx shadcn-ui@latest add button
npx shadcn-ui@latest add table
npx shadcn-ui@latest add badge
npx shadcn-ui@latest add card
npx shadcn-ui@latest add dialog
npx shadcn-ui@latest add chart
npx shadcn-ui@latest add tabs
npx shadcn-ui@latest add progress
npx shadcn-ui@latest add dropdown-menu
npx shadcn-ui@latest add toast
4. Project Structure
batch-dashboard/
├── src/
│ ├── app/
│ │ ├── page.tsx # Dashboard
│ │ ├── jobs/page.tsx # Jobs List
│ │ └── layout.tsx # Layout
│ ├── components/
│ │ ├── ui/ # Shadcn Components
│ │ │ ├── button.tsx
│ │ │ ├── table.tsx
│ │ │ ├── badge.tsx
│ │ │ ├── card.tsx
│ │ │ └── chart.tsx
│ │ ├── job-table.tsx # Batch Jobs Table
│ │ ├── job-chart.tsx # Processing Charts
│ │ └── job-dialog.tsx # Job Config Dialog
│ └── lib/
│ └── utils.ts
├── tailwind.config.ts
└── package.json
components_used = {
"Table": "แสดง Batch Jobs: ID, Name, Status, Duration, Records",
"Badge": "แสดง Status: Running (blue), Success (green), Failed (red)",
"Card": "Summary Cards: Total Jobs, Success Rate, Avg Duration",
"Chart": "Bar Chart: Jobs per Hour, Line Chart: Processing Time",
"Dialog": "Job Configuration: Schedule, Parameters, Notifications",
"Progress": "แสดง Progress ของ Running Jobs",
"Tabs": "แยก View: All Jobs, Running, Completed, Failed",
"Toast": "แจ้งเตือนเมื่อ Job เสร็จหรือ Failed",
"DropdownMenu": "Actions: Retry, Cancel, View Logs, Delete",
}
print("Shadcn UI Components for Batch Dashboard:")
for comp, desc in components_used.items():
print(f" [{comp}]")
print(f" {desc}")
Batch Jobs Dashboard Code
job-table.tsx — Batch Jobs Table Component
"use client"
import {
Table, TableBody, TableCell, TableHead,
TableHeader, TableRow,
} from "@/components/ui/table"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import {
DropdownMenu, DropdownMenuContent,
DropdownMenuItem, DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import { MoreHorizontal, Play, XCircle, Eye } from "lucide-react"
interface BatchJob {
id: string
name: string
status: "running" | "success" | "failed" | "pending"
startTime: string
duration: string
records: number
progress: number
}
const statusStyles = {
running: "bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300",
success: "bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300",
failed: "bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300",
pending: "bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-300",
}
export function JobTable({ jobs }: { jobs: BatchJob[] }) {
return (
<Table>
<TableHeader>
<TableRow>
<TableHead>Job ID</TableHead>
<TableHead>Name</TableHead>
<TableHead>Status</TableHead>
<TableHead>Start Time</TableHead>
<TableHead>Duration</TableHead>
<TableHead>Records</TableHead>
<TableHead>Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{jobs.map((job) => (
<TableRow key={job.id}>
<TableCell className="font-mono">{job.id}</TableCell>
<TableCell>{job.name}</TableCell>
<TableCell>
<Badge className={statusStyles[job.status]}>
{job.status}
</Badge>
</TableCell>
<TableCell>{job.startTime}</TableCell>
<TableCell>{job.duration}</TableCell>
<TableCell>{job.records.toLocaleString()}</TableCell>
<TableCell>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="sm">
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem><Eye /> View Logs</DropdownMenuItem>
<DropdownMenuItem><Play /> Retry</DropdownMenuItem>
<DropdownMenuItem><XCircle /> Cancel</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}
Batch Processing Pipeline Backend
from dataclasses import dataclass, field
from typing import List, Dict
from datetime import datetime, timedelta
import random
@dataclass
class BatchJob:
id: str
name: str
status: str
start_time: str
duration: str
records: int
progress: int
class BatchPipeline:
"""Batch Processing Pipeline"""
def __init__(self):
self.jobs: List[BatchJob] = []
def add_job(self, job: BatchJob):

self.jobs.append(job)
def summary(self):
total = len(self.jobs)
success = sum(1 for j in self.jobs if j.status == "success")
failed = sum(1 for j in self.jobs if j.status == "failed")
running = sum(1 for j in self.jobs if j.status == "running")
print(f"\n{'='*55}")
print(f"Batch Pipeline Dashboard")
print(f"{'='*55}")
print(f" Total Jobs: {total}")
print(f" Success: {success} ({success/total*100:.0f}%)")
print(f" Failed: {failed} ({failed/total*100:.0f}%)")
print(f" Running: {running}")
total_records = sum(j.records for j in self.jobs)
print(f" Total Records: {total_records:,}")
def show_jobs(self):
print(f"\n {'ID':<10} {'Name':<25} {'Status':<10} {'Records':>10}")
print(f" {'-'*55}")
for job in self.jobs[:10]:
print(f" {job.id:<10} {job.name:<25} {job.status:<10} {job.records:>10,}")
pipeline = BatchPipeline()
job_names = [
"ETL Daily Sales", "User Analytics", "Report Generation",
"Data Warehouse Load", "ML Feature Pipeline",
"Log Aggregation", "Email Campaign Batch",
"Invoice Processing", "Inventory Sync", "Backup Database",
]
statuses = ["success"] * 7 + ["failed"] * 2 + ["running"]
for i in range(10):
job = BatchJob(
id=f"JOB-{1000+i}",
name=job_names[i],
status=random.choice(statuses),
start_time=f"2024-01-{15+i} 06:00",
duration=f"{random.randint(5,120)} min",
records=random.randint(10000, 500000),
progress=random.randint(0, 100),
)
pipeline.add_job(job)
pipeline.summary()
pipeline.show_jobs()
API สำหรับ Dashboard
batch_api.py — API Endpoints for Dashboard
Next.js API Routes
app/api/jobs/route.ts
import { NextResponse } from 'next/server'
export async function GET(request: Request) {
const { searchParams } = new URL(request.url)
const status = searchParams.get('status')
const page = parseInt(searchParams.get('page') || '1')
const limit = parseInt(searchParams.get('limit') || '20')
Query database
const jobs = await db.batchJobs.findMany({
where: status ? { status } : undefined,
skip: (page - 1) * limit,
take: limit,
orderBy: { startTime: 'desc' },
})
const total = await db.batchJobs.count({
where: status ? { status } : undefined,
})
return NextResponse.json({
jobs,
pagination: { page, limit, total, pages: Math.ceil(total / limit) },
})
}
export async function POST(request: Request) {
const body = await request.json()
const job = await db.batchJobs.create({ data: body })
Trigger batch processing
await triggerBatchJob(job.id)
return NextResponse.json(job, { status: 201 })
}
app/api/jobs/[id]/route.ts
export async function GET(request: Request, { params }) {
const job = await db.batchJobs.findUnique({
where: { id: params.id },
include: { logs: true, metrics: true },
})
return NextResponse.json(job)
}
export async function PATCH(request: Request, { params }) {
const body = await request.json()
if (body.action === 'retry') {
await retryBatchJob(params.id)
} else if (body.action === 'cancel') {
await cancelBatchJob(params.id)
}
return NextResponse.json({ ok: true })
}
api_endpoints = {
"GET /api/jobs": "ดึง Jobs ทั้งหมด filter ตาม status, pagination",
"POST /api/jobs": "สร้าง Job ใหม่ trigger batch processing",
"GET /api/jobs/:id": "ดึง Job detail + logs + metrics",
"PATCH /api/jobs/:id": "Retry, Cancel, Update Job",
"GET /api/jobs/stats": "Summary statistics ของ Jobs",
"GET /api/jobs/:id/logs": "ดึง Logs ของ Job",
"WebSocket /ws/jobs": "Real-time Job status updates",
}
print("API Endpoints:")
for endpoint, desc in api_endpoints.items():
print(f" {endpoint}")
print(f" -> {desc}")
Best Practices
- Shadcn CLI: ใช้ CLI เพิ่ม Components ไม่ต้อง Copy เอง
- Dark Mode: รองรับ Dark Mode ด้วย next-themes
- Data Table: ใช้ @tanstack/react-table กับ Shadcn Table
- Real-time: ใช้ WebSocket หรือ SSE แสดง Status แบบ Real-time
- Pagination: ใช้ Server-side Pagination สำหรับข้อมูลเยอะ
- Error Handling: แสดง Toast เมื่อ Job Failed พร้อม Retry Button
Shadcn UI คืออะไร
Component Library React Radix UI Tailwind CSS Copy-paste Components ปรับแต่งได้ Dark Mode Accessibility TypeScript ไม่ใช่ NPM Package