Solution: Admin Dashboard
Try the Complete App
Section titled “Try the Complete App”ลองเล่น Admin Dashboard ที่สมบูรณ์แล้ว — ลองคลิก sidebar, ดู data table, และลองบนมือถือดู
| Name | Role | Status | |
|---|---|---|---|
| Somchai K. | somchai@mail.com | Admin | Active |
| Nattaya P. | nattaya@mail.com | Editor | Active |
| Prawit S. | prawit@mail.com | Viewer | Inactive |
| Mayuree T. | mayuree@mail.com | Editor | Pending |
| Wichai R. | wichai@mail.com | Admin | Active |
Final Project Structure
Section titled “Final Project Structure”Directoryadmin-dashboard/
- index.html หน้า Dashboard หลัก พร้อม Sidebar, Table, Modal
- style.css Tailwind CDN + custom animations
- script.js JavaScript สำหรับ Sidebar toggle, Modal, Mobile menu
Solution Code
Section titled “Solution Code”Show index.html
<!DOCTYPE html><html lang="th"><head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Admin Dashboard</title> <script src="https://cdn.tailwindcss.com"></script> <link rel="stylesheet" href="style.css" /> <script> tailwind.config = { theme: { extend: { fontFamily: { sans: ['Inter', 'Noto Sans Thai', 'system-ui', 'sans-serif'], }, }, }, } </script></head><body class="bg-gray-50 text-gray-900 font-sans">
<div class="flex min-h-screen">
<!-- ==================== SIDEBAR (Desktop) ==================== --> <aside id="sidebar" class="hidden lg:flex lg:flex-col lg:w-64 bg-gray-900 text-white fixed inset-y-0 left-0 z-40"> <!-- Brand --> <div class="flex items-center gap-3 px-6 h-16 border-b border-gray-800"> <div class="flex items-center justify-center w-8 h-8 bg-cyan-500 rounded-lg"> <svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6z M14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6z M4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2z M14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z" /> </svg> </div> <span class="text-lg font-bold">AdminPanel</span> </div>
<!-- Navigation --> <nav class="flex flex-col gap-1 p-4 flex-1"> <a href="#" class="flex items-center gap-3 px-3 py-2.5 bg-gray-800 text-white rounded-lg text-sm font-medium"> <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-4 0h4" /> </svg> Dashboard </a> <a href="#" class="flex items-center gap-3 px-3 py-2.5 text-gray-400 hover:bg-gray-800 hover:text-white rounded-lg text-sm transition-colors"> <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197m13.5-9a2.25 2.25 0 11-4.5 0 2.25 2.25 0 014.5 0z" /> </svg> Users </a> <a href="#" class="flex items-center gap-3 px-3 py-2.5 text-gray-400 hover:bg-gray-800 hover:text-white rounded-lg text-sm transition-colors"> <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4" /> </svg> Products </a> <a href="#" class="flex items-center gap-3 px-3 py-2.5 text-gray-400 hover:bg-gray-800 hover:text-white rounded-lg text-sm transition-colors"> <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" /> </svg> Analytics </a> <a href="#" class="flex items-center gap-3 px-3 py-2.5 text-gray-400 hover:bg-gray-800 hover:text-white rounded-lg text-sm transition-colors"> <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <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.066 2.573c1.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.573 1.066c-.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.066-2.573c-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 stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /> </svg> Settings </a> </nav>
<!-- User Info --> <div class="flex items-center gap-3 px-6 py-4 border-t border-gray-800"> <div class="flex items-center justify-center w-8 h-8 bg-cyan-600 rounded-full text-sm font-bold"> A </div> <div class="min-w-0 flex-1"> <p class="text-sm font-medium truncate">Admin User</p> <p class="text-xs text-gray-400 truncate">admin@example.com</p> </div> </div> </aside>
<!-- ==================== MOBILE SIDEBAR OVERLAY ==================== --> <div id="mobile-overlay" class="fixed inset-0 z-50 hidden lg:hidden"> <!-- Backdrop --> <div class="fixed inset-0 bg-black/50 backdrop-blur-sm" onclick="closeMobileSidebar()"></div>
<!-- Drawer --> <aside class="fixed inset-y-0 left-0 w-72 bg-gray-900 text-white shadow-xl transform transition-transform duration-300"> <div class="flex items-center justify-between px-6 h-16 border-b border-gray-800"> <div class="flex items-center gap-3"> <div class="flex items-center justify-center w-8 h-8 bg-cyan-500 rounded-lg"> <svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6z M14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6z M4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2z M14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z" /> </svg> </div> <span class="text-lg font-bold">AdminPanel</span> </div> <button onclick="closeMobileSidebar()" class="p-2 text-gray-400 hover:text-white rounded-lg transition-colors"> <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /> </svg> </button> </div>
<nav class="flex flex-col gap-1 p-4"> <a href="#" class="flex items-center gap-3 px-3 py-2.5 bg-gray-800 text-white rounded-lg text-sm font-medium"> <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-4 0h4" /> </svg> Dashboard </a> <a href="#" class="flex items-center gap-3 px-3 py-2.5 text-gray-400 hover:bg-gray-800 hover:text-white rounded-lg text-sm transition-colors"> <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197m13.5-9a2.25 2.25 0 11-4.5 0 2.25 2.25 0 014.5 0z" /> </svg> Users </a> <a href="#" class="flex items-center gap-3 px-3 py-2.5 text-gray-400 hover:bg-gray-800 hover:text-white rounded-lg text-sm transition-colors"> <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4" /> </svg> Products </a> <a href="#" class="flex items-center gap-3 px-3 py-2.5 text-gray-400 hover:bg-gray-800 hover:text-white rounded-lg text-sm transition-colors"> <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <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.066 2.573c1.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.573 1.066c-.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.066-2.573c-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 stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /> </svg> Settings </a> </nav> </aside> </div>
<!-- ==================== MAIN CONTENT ==================== --> <div class="flex-1 lg:ml-64">
<!-- Top Header --> <header class="sticky top-0 z-30 flex items-center justify-between h-16 px-4 sm:px-6 lg:px-8 bg-white/80 backdrop-blur-md border-b border-gray-200"> <!-- Mobile Menu Button --> <button onclick="openMobileSidebar()" class="lg:hidden p-2 -ml-2 text-gray-600 hover:text-gray-900 hover:bg-gray-100 rounded-lg transition-colors"> <svg 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>
<!-- Page Title --> <h1 class="text-lg font-bold text-gray-900 lg:text-xl">Dashboard</h1>
<!-- Right Side: Search + Profile --> <div class="flex items-center gap-3"> <!-- Search (hidden on small mobile) --> <div class="hidden sm:flex items-center gap-2 px-3 py-1.5 bg-gray-100 rounded-lg"> <svg class="w-4 h-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" /> </svg> <input type="text" placeholder="Search..." class="bg-transparent border-none outline-none text-sm text-gray-600 w-40 lg:w-56" /> </div>
<!-- Notification Bell --> <button class="relative p-2 text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded-lg transition-colors"> <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" /> </svg> <span class="absolute top-1.5 right-1.5 w-2 h-2 bg-red-500 rounded-full"></span> </button>
<!-- Avatar --> <div class="flex items-center justify-center w-8 h-8 bg-cyan-600 rounded-full text-white text-sm font-bold cursor-pointer"> A </div> </div> </header>
<!-- Dashboard Content --> <main class="p-4 sm:p-6 lg:p-8">
<!-- Stats Cards --> <div class="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4 gap-4 mb-8"> <!-- Total Users --> <div class="flex items-center gap-4 p-5 bg-white border border-gray-200 rounded-xl hover:shadow-md transition-shadow"> <div class="flex items-center justify-center w-12 h-12 bg-cyan-50 text-cyan-600 rounded-xl"> <svg 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="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197m13.5-9a2.25 2.25 0 11-4.5 0 2.25 2.25 0 014.5 0z" /> </svg> </div> <div> <p class="text-2xl font-bold text-gray-900">2,451</p> <p class="text-sm text-gray-500">Total Users</p> </div> </div> <!-- Revenue --> <div class="flex items-center gap-4 p-5 bg-white border border-gray-200 rounded-xl hover:shadow-md transition-shadow"> <div class="flex items-center justify-center w-12 h-12 bg-green-50 text-green-600 rounded-xl"> <svg 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="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /> </svg> </div> <div> <p class="text-2xl font-bold text-gray-900">$45,231</p> <p class="text-sm text-gray-500">Revenue</p> </div> </div> <!-- Orders --> <div class="flex items-center gap-4 p-5 bg-white border border-gray-200 rounded-xl hover:shadow-md transition-shadow"> <div class="flex items-center justify-center w-12 h-12 bg-purple-50 text-purple-600 rounded-xl"> <svg 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="M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z" /> </svg> </div> <div> <p class="text-2xl font-bold text-gray-900">1,327</p> <p class="text-sm text-gray-500">Orders</p> </div> </div> <!-- Active Now --> <div class="flex items-center gap-4 p-5 bg-white border border-gray-200 rounded-xl hover:shadow-md transition-shadow"> <div class="flex items-center justify-center w-12 h-12 bg-amber-50 text-amber-600 rounded-xl"> <svg 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="M13 10V3L4 14h7v7l9-11h-7z" /> </svg> </div> <div> <p class="text-2xl font-bold text-gray-900">573</p> <p class="text-sm text-gray-500">Active Now</p> </div> </div> </div>
<!-- Users Table Section --> <div class="bg-white border border-gray-200 rounded-xl overflow-hidden"> <!-- Table Header --> <div class="flex items-center justify-between px-6 py-4 border-b border-gray-200"> <div> <h2 class="text-lg font-bold text-gray-900">Users</h2> <p class="text-sm text-gray-500">Manage your team members</p> </div> <button onclick="openModal()" class="inline-flex items-center gap-2 px-4 py-2 bg-cyan-600 hover:bg-cyan-700 text-white text-sm font-medium rounded-lg transition-colors"> <svg 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="M12 4v16m8-8H4" /> </svg> Add User </button> </div>
<!-- Desktop Table (hidden on mobile) --> <div class="hidden md:block overflow-x-auto"> <table class="w-full"> <thead> <tr class="border-b border-gray-100 bg-gray-50/50"> <th class="px-6 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider">Name</th> <th class="px-6 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider">Email</th> <th class="px-6 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider">Role</th> <th class="px-6 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider">Status</th> <th class="px-6 py-3 text-right text-xs font-semibold text-gray-500 uppercase tracking-wider">Actions</th> </tr> </thead> <tbody class="divide-y divide-gray-100"> <tr class="hover:bg-gray-50 transition-colors"> <td class="px-6 py-4"> <div class="flex items-center gap-3"> <div class="flex items-center justify-center w-9 h-9 bg-blue-100 text-blue-700 rounded-full text-sm font-bold">S</div> <span class="text-sm font-medium text-gray-900">Somchai K.</span> </div> </td> <td class="px-6 py-4 text-sm text-gray-500">somchai@example.com</td> <td class="px-6 py-4 text-sm text-gray-500">Admin</td> <td class="px-6 py-4"> <span class="inline-flex items-center gap-1 px-2.5 py-1 text-xs font-medium bg-green-50 text-green-700 rounded-full"> <span class="w-1.5 h-1.5 bg-green-500 rounded-full"></span> Active </span> </td> <td class="px-6 py-4 text-right"> <button onclick="openModal()" class="text-sm text-cyan-600 hover:text-cyan-800 font-medium transition-colors">View</button> </td> </tr> <tr class="hover:bg-gray-50 transition-colors"> <td class="px-6 py-4"> <div class="flex items-center gap-3"> <div class="flex items-center justify-center w-9 h-9 bg-purple-100 text-purple-700 rounded-full text-sm font-bold">N</div> <span class="text-sm font-medium text-gray-900">Nattaya P.</span> </div> </td> <td class="px-6 py-4 text-sm text-gray-500">nattaya@example.com</td> <td class="px-6 py-4 text-sm text-gray-500">Editor</td> <td class="px-6 py-4"> <span class="inline-flex items-center gap-1 px-2.5 py-1 text-xs font-medium bg-green-50 text-green-700 rounded-full"> <span class="w-1.5 h-1.5 bg-green-500 rounded-full"></span> Active </span> </td> <td class="px-6 py-4 text-right"> <button onclick="openModal()" class="text-sm text-cyan-600 hover:text-cyan-800 font-medium transition-colors">View</button> </td> </tr> <tr class="hover:bg-gray-50 transition-colors"> <td class="px-6 py-4"> <div class="flex items-center gap-3"> <div class="flex items-center justify-center w-9 h-9 bg-amber-100 text-amber-700 rounded-full text-sm font-bold">P</div> <span class="text-sm font-medium text-gray-900">Prawit S.</span> </div> </td> <td class="px-6 py-4 text-sm text-gray-500">prawit@example.com</td> <td class="px-6 py-4 text-sm text-gray-500">Viewer</td> <td class="px-6 py-4"> <span class="inline-flex items-center gap-1 px-2.5 py-1 text-xs font-medium bg-red-50 text-red-700 rounded-full"> <span class="w-1.5 h-1.5 bg-red-500 rounded-full"></span> Inactive </span> </td> <td class="px-6 py-4 text-right"> <button onclick="openModal()" class="text-sm text-cyan-600 hover:text-cyan-800 font-medium transition-colors">View</button> </td> </tr> <tr class="hover:bg-gray-50 transition-colors"> <td class="px-6 py-4"> <div class="flex items-center gap-3"> <div class="flex items-center justify-center w-9 h-9 bg-green-100 text-green-700 rounded-full text-sm font-bold">M</div> <span class="text-sm font-medium text-gray-900">Mayuree T.</span> </div> </td> <td class="px-6 py-4 text-sm text-gray-500">mayuree@example.com</td> <td class="px-6 py-4 text-sm text-gray-500">Editor</td> <td class="px-6 py-4"> <span class="inline-flex items-center gap-1 px-2.5 py-1 text-xs font-medium bg-green-50 text-green-700 rounded-full"> <span class="w-1.5 h-1.5 bg-green-500 rounded-full"></span> Active </span> </td> <td class="px-6 py-4 text-right"> <button onclick="openModal()" class="text-sm text-cyan-600 hover:text-cyan-800 font-medium transition-colors">View</button> </td> </tr> <tr class="hover:bg-gray-50 transition-colors"> <td class="px-6 py-4"> <div class="flex items-center gap-3"> <div class="flex items-center justify-center w-9 h-9 bg-cyan-100 text-cyan-700 rounded-full text-sm font-bold">W</div> <span class="text-sm font-medium text-gray-900">Wichai R.</span> </div> </td> <td class="px-6 py-4 text-sm text-gray-500">wichai@example.com</td> <td class="px-6 py-4 text-sm text-gray-500">Admin</td> <td class="px-6 py-4"> <span class="inline-flex items-center gap-1 px-2.5 py-1 text-xs font-medium bg-green-50 text-green-700 rounded-full"> <span class="w-1.5 h-1.5 bg-green-500 rounded-full"></span> Active </span> </td> <td class="px-6 py-4 text-right"> <button onclick="openModal()" class="text-sm text-cyan-600 hover:text-cyan-800 font-medium transition-colors">View</button> </td> </tr> </tbody> </table> </div>
<!-- Mobile Cards (hidden on desktop) --> <div class="md:hidden divide-y divide-gray-100"> <!-- Card 1 --> <div class="flex items-center justify-between p-4 hover:bg-gray-50 transition-colors"> <div class="flex items-center gap-3 min-w-0"> <div class="flex items-center justify-center w-10 h-10 bg-blue-100 text-blue-700 rounded-full text-sm font-bold shrink-0">S</div> <div class="min-w-0"> <p class="text-sm font-medium text-gray-900 truncate">Somchai K.</p> <p class="text-xs text-gray-500 truncate">Admin</p> </div> </div> <div class="flex items-center gap-3 shrink-0"> <span class="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-green-50 text-green-700 rounded-full">Active</span> <button onclick="openModal()" class="p-1.5 text-gray-400 hover:text-cyan-600 transition-colors"> <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" /> </svg> </button> </div> </div> <!-- Card 2 --> <div class="flex items-center justify-between p-4 hover:bg-gray-50 transition-colors"> <div class="flex items-center gap-3 min-w-0"> <div class="flex items-center justify-center w-10 h-10 bg-purple-100 text-purple-700 rounded-full text-sm font-bold shrink-0">N</div> <div class="min-w-0"> <p class="text-sm font-medium text-gray-900 truncate">Nattaya P.</p> <p class="text-xs text-gray-500 truncate">Editor</p> </div> </div> <div class="flex items-center gap-3 shrink-0"> <span class="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-green-50 text-green-700 rounded-full">Active</span> <button onclick="openModal()" class="p-1.5 text-gray-400 hover:text-cyan-600 transition-colors"> <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" /> </svg> </button> </div> </div> <!-- Card 3 --> <div class="flex items-center justify-between p-4 hover:bg-gray-50 transition-colors"> <div class="flex items-center gap-3 min-w-0"> <div class="flex items-center justify-center w-10 h-10 bg-amber-100 text-amber-700 rounded-full text-sm font-bold shrink-0">P</div> <div class="min-w-0"> <p class="text-sm font-medium text-gray-900 truncate">Prawit S.</p> <p class="text-xs text-gray-500 truncate">Viewer</p> </div> </div> <div class="flex items-center gap-3 shrink-0"> <span class="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-red-50 text-red-700 rounded-full">Inactive</span> <button onclick="openModal()" class="p-1.5 text-gray-400 hover:text-cyan-600 transition-colors"> <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" /> </svg> </button> </div> </div> <!-- Card 4 --> <div class="flex items-center justify-between p-4 hover:bg-gray-50 transition-colors"> <div class="flex items-center gap-3 min-w-0"> <div class="flex items-center justify-center w-10 h-10 bg-green-100 text-green-700 rounded-full text-sm font-bold shrink-0">M</div> <div class="min-w-0"> <p class="text-sm font-medium text-gray-900 truncate">Mayuree T.</p> <p class="text-xs text-gray-500 truncate">Editor</p> </div> </div> <div class="flex items-center gap-3 shrink-0"> <span class="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-green-50 text-green-700 rounded-full">Active</span> <button onclick="openModal()" class="p-1.5 text-gray-400 hover:text-cyan-600 transition-colors"> <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" /> </svg> </button> </div> </div> <!-- Card 5 --> <div class="flex items-center justify-between p-4 hover:bg-gray-50 transition-colors"> <div class="flex items-center gap-3 min-w-0"> <div class="flex items-center justify-center w-10 h-10 bg-cyan-100 text-cyan-700 rounded-full text-sm font-bold shrink-0">W</div> <div class="min-w-0"> <p class="text-sm font-medium text-gray-900 truncate">Wichai R.</p> <p class="text-xs text-gray-500 truncate">Admin</p> </div> </div> <div class="flex items-center gap-3 shrink-0"> <span class="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-green-50 text-green-700 rounded-full">Active</span> <button onclick="openModal()" class="p-1.5 text-gray-400 hover:text-cyan-600 transition-colors"> <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" /> </svg> </button> </div> </div> </div>
<!-- Table Footer / Pagination --> <div class="flex items-center justify-between px-6 py-3 border-t border-gray-100 bg-gray-50/50"> <p class="text-sm text-gray-500">Showing 1-5 of 24 users</p> <div class="flex items-center gap-1"> <button class="px-3 py-1.5 text-sm text-gray-500 hover:bg-gray-200 rounded-lg transition-colors">Prev</button> <button class="px-3 py-1.5 text-sm font-medium bg-cyan-600 text-white rounded-lg">1</button> <button class="px-3 py-1.5 text-sm text-gray-500 hover:bg-gray-200 rounded-lg transition-colors">2</button> <button class="px-3 py-1.5 text-sm text-gray-500 hover:bg-gray-200 rounded-lg transition-colors">3</button> <button class="px-3 py-1.5 text-sm text-gray-500 hover:bg-gray-200 rounded-lg transition-colors">Next</button> </div> </div> </div>
</main> </div> </div>
<!-- ==================== MODAL ==================== --> <div id="modal" class="fixed inset-0 z-50 hidden items-center justify-center p-4"> <!-- Backdrop --> <div class="fixed inset-0 bg-black/50 backdrop-blur-sm modal-backdrop" onclick="closeModal()"></div>
<!-- Modal Panel --> <div class="relative w-full max-w-lg bg-white rounded-2xl shadow-2xl transform transition-all duration-300 modal-panel"> <!-- Close Button --> <button onclick="closeModal()" class="absolute top-4 right-4 p-1.5 text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded-lg transition-colors"> <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /> </svg> </button>
<!-- Modal Header --> <div class="px-6 pt-6 pb-4"> <h3 class="text-lg font-bold text-gray-900">User Details</h3> <p class="text-sm text-gray-500">View and manage user information</p> </div>
<!-- Modal Body --> <div class="px-6 pb-4"> <div class="flex items-center gap-4 p-4 bg-gray-50 rounded-xl mb-4"> <div class="flex items-center justify-center w-14 h-14 bg-blue-100 text-blue-700 rounded-full text-xl font-bold"> S </div> <div> <p class="text-base font-bold text-gray-900">Somchai K.</p> <p class="text-sm text-gray-500">somchai@example.com</p> </div> </div>
<div class="grid grid-cols-2 gap-4"> <div class="p-3 bg-gray-50 rounded-lg"> <p class="text-xs font-medium text-gray-500 uppercase mb-1">Role</p> <p class="text-sm font-semibold text-gray-900">Admin</p> </div> <div class="p-3 bg-gray-50 rounded-lg"> <p class="text-xs font-medium text-gray-500 uppercase mb-1">Status</p> <span class="inline-flex items-center gap-1 text-sm font-semibold text-green-700"> <span class="w-1.5 h-1.5 bg-green-500 rounded-full"></span> Active </span> </div> <div class="p-3 bg-gray-50 rounded-lg"> <p class="text-xs font-medium text-gray-500 uppercase mb-1">Joined</p> <p class="text-sm font-semibold text-gray-900">Jan 15, 2024</p> </div> <div class="p-3 bg-gray-50 rounded-lg"> <p class="text-xs font-medium text-gray-500 uppercase mb-1">Last Active</p> <p class="text-sm font-semibold text-gray-900">2 hours ago</p> </div> </div> </div>
<!-- Modal Footer --> <div class="flex items-center justify-end gap-3 px-6 py-4 border-t border-gray-100"> <button onclick="closeModal()" class="px-4 py-2 text-sm font-medium text-gray-700 bg-gray-100 hover:bg-gray-200 rounded-lg transition-colors"> Close </button> <button class="px-4 py-2 text-sm font-medium text-white bg-cyan-600 hover:bg-cyan-700 rounded-lg transition-colors"> Edit User </button> </div> </div> </div>
<script src="script.js"></script></body></html>Show style.css
/* ========================================== Admin Dashboard - Custom Styles Tailwind CDN handles all utility classes. This file is for custom animations only. ========================================== */
/* Smooth page transitions */* { scroll-behavior: smooth;}
/* Modal animation */.modal-backdrop { animation: fadeIn 0.2s ease-out;}
.modal-panel { animation: slideUp 0.3s ease-out;}
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; }}
@keyframes slideUp { from { opacity: 0; transform: translateY(16px) scale(0.96); } to { opacity: 1; transform: translateY(0) scale(1); }}
/* Mobile sidebar slide-in */#mobile-overlay aside { animation: slideInLeft 0.3s ease-out;}
@keyframes slideInLeft { from { transform: translateX(-100%); } to { transform: translateX(0); }}Show script.js
// ==========================================// Admin Dashboard - JavaScript// Handles: Mobile Sidebar, Modal// ==========================================
// --- Mobile Sidebar ---
function openMobileSidebar() { const overlay = document.getElementById('mobile-overlay'); overlay.classList.remove('hidden'); // Prevent body scroll when sidebar is open document.body.style.overflow = 'hidden';}
function closeMobileSidebar() { const overlay = document.getElementById('mobile-overlay'); overlay.classList.add('hidden'); // Restore body scroll document.body.style.overflow = '';}
// --- Modal ---
function openModal() { const modal = document.getElementById('modal'); modal.classList.remove('hidden'); modal.classList.add('flex'); // Prevent body scroll document.body.style.overflow = 'hidden';}
function closeModal() { const modal = document.getElementById('modal'); modal.classList.add('hidden'); modal.classList.remove('flex'); // Restore body scroll document.body.style.overflow = '';}
// --- Keyboard Support ---
document.addEventListener('keydown', function (e) { if (e.key === 'Escape') { // Close modal if open const modal = document.getElementById('modal'); if (!modal.classList.contains('hidden')) { closeModal(); return; } // Close mobile sidebar if open const overlay = document.getElementById('mobile-overlay'); if (!overlay.classList.contains('hidden')) { closeMobileSidebar(); } }});Code Explanation
Section titled “Code Explanation”-
Sidebar Navigation
ใช้
hidden lg:flexเพื่อซ่อน sidebar บน mobile และแสดงบน desktop (1024px+) sidebar มีfixed inset-y-0 left-0 w-64เพื่อยึดด้านซ้ายตลอด ส่วน main content ใช้lg:ml-64เพื่อเว้นที่ให้ sidebarPreview Output -
Mobile Sidebar (Drawer)
สร้าง overlay แยกสำหรับ mobile ใช้
fixed inset-0 z-50ครอบทั้งจอ มี backdrop สีดำ semi-transparent (bg-black/50) และ drawer panel ที่เลื่อนเข้าจากซ้าย ควบคุม show/hide ด้วย JavaScript toggle classhiddenPreview OutputDashboardbg-black/50AdminDashboardUsersProductsdrawer fixed z-50 -
Data Table + Mobile Cards
ใช้
hidden md:blockสำหรับ table view บน desktop และmd:hiddenสำหรับ card view บน mobile ทำให้ responsive โดยไม่ต้องเขียน CSS ซับซ้อน แค่สลับระหว่างสอง layout ด้วย Tailwind breakpointsPreview OutputDesktop: hidden md:blockNameRoleStatusS Somchai Admin ActiveN Nattaya Editor ActiveP Prawit Viewer InactiveMobile: md:hiddenSActiveSomchai K.AdminNActiveNattaya P.EditorPInactivePrawit S.Viewer -
Modal (Dialog)
Modal ใช้
fixed inset-0 z-50ครอบทั้งจอ มี backdrop กับ modal panel แยกกัน ใช้ JavaScript สลับhidden/flexพร้อม CSS animations สำหรับ fade-in และ slide-up รองรับปิดด้วย Escape key ผ่าน keyboard event listenerPreview OutputPage Content (grayed out)Backdrop bg-black/50User Details ✕Modal z-50 (top layer) -
Stats Cards
ใช้ CSS Grid responsive
grid-cols-1 sm:grid-cols-2 xl:grid-cols-4เพื่อแสดง 1 column บน mobile, 2 columns บน tablet, 4 columns บน desktop แต่ละ card มี hover effect ด้วยhover:shadow-md transition-shadowPreview Outputgrid-cols-12,451 Userssm:grid-cols-2UsersRevenuexl:grid-cols-4UsersRevenueOrdersActive -
Tailwind Patterns ที่ใช้
ตลอดทั้งโปรเจกต์ใช้ pattern จาก shadcn/ui ได้แก่ Button variants (primary กับ ghost), Status badges ด้วย colored dots, Card layouts ที่มี border + rounded corners, และ consistent spacing system (p-4, p-6, gap-3, gap-4)
Preview OutputButton VariantsPrimary Secondary GhostStatus BadgesActive Pending InactiveCard PatternCard Titleborder + rounded-xl + shadow
What You Learned
Section titled “What You Learned”- ใช้ Tailwind CSS utility classes สร้าง layout จริงได้ (Sidebar, Table, Modal)
- ทำ Responsive Design ด้วย Tailwind breakpoints (
sm:,md:,lg:,xl:) - สร้าง shadcn/ui-style components ด้วย Tailwind (Button variants, Badges, Cards)
- ใช้ JavaScript ควบคุม UI state (show/hide sidebar, modal)
- เข้าใจ Design System patterns ผ่านโค้ดจริง (consistent spacing, colors, typography)