SiamCafe · Blog
Shadcn UI กับ Batch Processing Pipeline —
DevOps และระบบ

Shadcn UI กับ Batch Processing Pipeline —

เผยแพร่ May 28, 2026

Shadcn UI Batch Processing

Shadcn UI กับ Batch Processing Pipeline —

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):

Shadcn UI กับ Batch Processing Pipeline —

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