WellNuo/admin/components/Sidebar.js
Sergei ec63a2c1e2 Add admin panel, optimized API, OTP auth, migrations
Admin Panel (Next.js):
- Dashboard with stats
- Users list with relationships (watches/watched_by)
- User detail pages
- Deployments list and detail pages
- Devices, Orders, Subscriptions pages
- OTP-based admin authentication

Backend Optimizations:
- Fixed N+1 query problem in admin APIs
- Added pagination support
- Added .range() and count support to Supabase wrapper
- Optimized batch queries with lookup maps

Database:
- Added migrations for schema evolution
- New tables: push_tokens, notification_settings
- Updated access model

iOS Build Scripts:
- build-ios.sh, clear-apple-cache.sh
- EAS configuration updates

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-20 11:05:39 -08:00

129 lines
2.9 KiB
JavaScript

'use client';
import Link from 'next/link';
import { usePathname, useRouter } from 'next/navigation';
const navItems = [
{ href: '/dashboard', label: 'Dashboard', icon: '📊' },
{ href: '/orders', label: 'Orders', icon: '📦' },
{ href: '/users', label: 'Users', icon: '👥' },
{ href: '/deployments', label: 'Deployments', icon: '🏠' },
{ href: '/devices', label: 'Devices', icon: '📡' },
{ href: '/subscriptions', label: 'Subscriptions', icon: '💳' },
];
export default function Sidebar() {
const pathname = usePathname();
const router = useRouter();
const handleLogout = () => {
localStorage.removeItem('adminToken');
localStorage.removeItem('adminUser');
window.location.href = '/admin/login';
};
return (
<aside style={styles.sidebar}>
<div style={styles.logo}>
<span style={styles.logoText}>WellNuo</span>
<span style={styles.badge}>Admin</span>
</div>
<nav style={styles.nav}>
{navItems.map((item) => (
<Link
key={item.href}
href={item.href}
style={{
...styles.navItem,
...(pathname === item.href ? styles.navItemActive : {}),
}}
>
<span style={styles.navIcon}>{item.icon}</span>
{item.label}
</Link>
))}
</nav>
<div style={styles.footer}>
<button onClick={handleLogout} style={styles.logoutBtn}>
Sign Out
</button>
</div>
</aside>
);
}
const styles = {
sidebar: {
width: '240px',
height: '100vh',
background: 'white',
borderRight: '1px solid var(--border)',
display: 'flex',
flexDirection: 'column',
position: 'fixed',
left: 0,
top: 0,
},
logo: {
padding: '24px',
display: 'flex',
alignItems: 'center',
gap: '8px',
borderBottom: '1px solid var(--border)',
},
logoText: {
fontSize: '20px',
fontWeight: '700',
color: 'var(--primary)',
},
badge: {
fontSize: '11px',
fontWeight: '600',
color: 'var(--text-muted)',
background: 'var(--surface)',
padding: '2px 8px',
borderRadius: '4px',
},
nav: {
flex: 1,
padding: '16px 12px',
display: 'flex',
flexDirection: 'column',
gap: '4px',
},
navItem: {
display: 'flex',
alignItems: 'center',
gap: '12px',
padding: '12px 16px',
borderRadius: '8px',
fontSize: '14px',
fontWeight: '500',
color: 'var(--text-secondary)',
transition: 'all 0.2s',
},
navItemActive: {
background: 'var(--surface)',
color: 'var(--primary)',
},
navIcon: {
fontSize: '18px',
},
footer: {
padding: '16px',
borderTop: '1px solid var(--border)',
},
logoutBtn: {
width: '100%',
padding: '10px',
fontSize: '14px',
color: 'var(--text-muted)',
background: 'transparent',
border: '1px solid var(--border)',
borderRadius: '8px',
cursor: 'pointer',
},
};