diff --git a/UPDATE_RESPONSIVE.md b/UPDATE_RESPONSIVE.md index d9d15e9..15c90f1 100644 --- a/UPDATE_RESPONSIVE.md +++ b/UPDATE_RESPONSIVE.md @@ -19,10 +19,14 @@ Web javaherbal sekarang sudah **responsive** dan tidak terpotong di HP! ### 2. **Navbar.tsx** - ✅ Responsive height: 64px → 56px (mobile) - ✅ Responsive padding: 2rem → 1rem (mobile) -- ✅ Hide navigation links di mobile -- ✅ Hide user name di mobile -- ✅ Smaller buttons dan icons di mobile -- ✅ Hide "Keluar" text di mobile (icon only) +- ✅ **Hamburger menu di mobile** (icon Menu/X dari lucide-react) +- ✅ Mobile menu dropdown dengan semua navigation links +- ✅ Show user info di mobile menu +- ✅ Full-width login/logout button di mobile menu +- ✅ Auto-close menu saat route berubah +- ✅ Auto-close menu saat resize ke desktop +- ✅ Smooth transitions dan hover effects +- ✅ Active link highlighting di mobile menu ### 3. **Footer.tsx** - ✅ Grid responsive: 4 kolom → 1 kolom (mobile) @@ -73,11 +77,12 @@ Web javaherbal sekarang sudah **responsive** dan tidak terpotong di HP! ### Mobile (≤ 768px) - Navbar: 56px height +- **Hamburger menu icon** (Menu/X toggle) +- **Mobile dropdown menu** dengan semua navigation links - Grid: 1 kolom - Padding: 3rem 1rem - Font size: Smaller - Input font: 16px (prevent zoom) -- Hide navigation links - Compact buttons --- @@ -119,12 +124,13 @@ style={{ ### Mobile 1. Buka web di HP -2. **Expected:** Tidak ada horizontal scroll -3. **Expected:** UI tidak terpotong -4. **Expected:** Text readable -5. **Expected:** Input tidak zoom saat focus -6. **Expected:** Navbar compact dengan icon only -7. **Expected:** Footer stacked vertical +2. **Expected:** Hamburger menu icon (☰) muncul di kanan atas +3. **Expected:** Klik hamburger, dropdown menu muncul dengan semua links +4. **Expected:** Klik link, menu auto-close dan navigate +5. **Expected:** Tidak ada horizontal scroll +6. **Expected:** UI tidak terpotong +7. **Expected:** Text readable +8. **Expected:** Input tidak zoom saat focus --- @@ -132,6 +138,9 @@ style={{ - [x] Prevent horizontal scroll - [x] Responsive navbar (hide links, compact buttons) +- [x] **Hamburger menu untuk mobile navigation** +- [x] Mobile menu dropdown dengan semua links +- [x] Auto-close menu on route change - [x] Responsive footer (4 col → 1 col) - [x] Responsive grid (2-4 col → 1 col) - [x] Responsive padding (all pages) @@ -153,8 +162,25 @@ Semua halaman sudah disesuaikan: - ✅ Home (Hero, Dashboard) - ✅ Products - ✅ About -- ✅ Control Panel +- ✅ Control Panel (gauge tidak terpotong) - ✅ Login - ✅ Register -- ✅ Navbar +- ✅ **Navbar dengan Hamburger Menu** 🍔 - ✅ Footer + +## 🍔 Fitur Hamburger Menu + +Di mobile (≤ 768px): +1. **Icon hamburger** (☰) muncul di kanan atas navbar +2. **Klik icon** → Dropdown menu muncul dari atas +3. **Menu berisi:** + - User info (jika login) + - Semua navigation links (Beranda, Produk, Tentang Kami) + - Control Panel link (jika login) + - Button Login/Logout +4. **Auto-close** saat: + - Klik link (navigate ke halaman lain) + - Resize window ke desktop + - Klik icon X +5. **Smooth animations** dan hover effects +6. **Active link highlighting** (warna hijau untuk halaman aktif) diff --git a/src/components/ControlPanel.tsx b/src/components/ControlPanel.tsx index f08b675..38b2cb1 100644 --- a/src/components/ControlPanel.tsx +++ b/src/components/ControlPanel.tsx @@ -23,9 +23,9 @@ interface SensorData { function Gauge({ value, max }: { value: number; max: number }) { - const cx = 90 - const cy = 95 - const r = 68 + const cx = 100 + const cy = 100 + const r = 70 const startDeg = -210 const endDeg = 30 const totalSweep = endDeg - startDeg @@ -45,7 +45,7 @@ function Gauge({ value, max }: { value: number; max: number }) { const fillAngle = startDeg + (value / max) * totalSweep return ( - + @@ -53,12 +53,12 @@ function Gauge({ value, max }: { value: number; max: number }) { {/* Track */} - + {/* Fill */} - - SUHU - SAAT INI: - {value.toFixed(1)}°C + + SUHU + SAAT INI: + {value.toFixed(1)}°C ) } @@ -334,7 +334,13 @@ export default function ControlPanel() { ) : ( <> -
+
diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index 553e3a8..072ee70 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -1,6 +1,6 @@ import React, { useState } from 'react' import { Link, useLocation, useNavigate } from 'react-router-dom' -import { Leaf, LogOut } from 'lucide-react' +import { Leaf, LogOut, Menu, X } from 'lucide-react' const NAV_LINKS = [ { label: 'Beranda', path: '/' }, @@ -37,23 +37,38 @@ function NavLink({ label, path }: { label: string; path: string }) { export default function Navbar() { const navigate = useNavigate() + const location = useLocation() const isAuthenticated = localStorage.getItem('isAuthenticated') === 'true' const currentUser = JSON.parse(localStorage.getItem('currentUser') || '{}') const [isMobile, setIsMobile] = useState(window.innerWidth <= 768) + const [menuOpen, setMenuOpen] = useState(false) React.useEffect(() => { - const handleResize = () => setIsMobile(window.innerWidth <= 768) + const handleResize = () => { + setIsMobile(window.innerWidth <= 768) + if (window.innerWidth > 768) { + setMenuOpen(false) // Close menu when resizing to desktop + } + } window.addEventListener('resize', handleResize) return () => window.removeEventListener('resize', handleResize) }, []) + // Close menu when route changes + React.useEffect(() => { + setMenuOpen(false) + }, [location.pathname]) + function handleLogout() { localStorage.removeItem('isAuthenticated') localStorage.removeItem('currentUser') + localStorage.removeItem('token') + setMenuOpen(false) navigate('/') } function handleLogin() { + setMenuOpen(false) navigate('/login') } @@ -98,7 +113,7 @@ export default function Navbar() { - {/* Links - Hide on mobile */} + {/* Links - Desktop only */} {!isMobile && (
{NAV_LINKS.map((link) => ( @@ -112,21 +127,23 @@ export default function Navbar() { {/* Actions */}
- {isAuthenticated ? ( - <> - {!isMobile && currentUser.name && ( - - {currentUser.name} - - )} + {/* Desktop: Show user name and logout */} + {!isMobile && isAuthenticated && currentUser.name && ( + + {currentUser.name} + + )} + + {!isMobile && ( + isAuthenticated ? ( - - ) : ( + ) : ( + + ) + )} + + {/* Mobile: Hamburger menu */} + {isMobile && ( )}
+ + {/* Mobile Menu Dropdown */} + {isMobile && menuOpen && ( +
+ {/* User info if logged in */} + {isAuthenticated && currentUser.name && ( +
+ + Masuk sebagai + + + {currentUser.name} + +
+ )} + + {/* Navigation Links */} +
+ {NAV_LINKS.map((link) => ( + + {link.label} + + ))} + + {isAuthenticated && PROTECTED_LINKS.map((link) => ( + + {link.label} + + ))} +
+ + {/* Login/Logout Button */} +
+ {isAuthenticated ? ( + + ) : ( + + )} +
+
+ )} ) }