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>
51 lines
1.1 KiB
JavaScript
51 lines
1.1 KiB
JavaScript
'use client';
|
|
|
|
import { useEffect, useState } from 'react';
|
|
import { useRouter } from 'next/navigation';
|
|
import Sidebar from './Sidebar';
|
|
|
|
export default function AdminLayout({ children }) {
|
|
const router = useRouter();
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
const token = localStorage.getItem('adminToken');
|
|
console.log('[LAYOUT] Checking token:', token ? 'exists' : 'missing');
|
|
if (!token) {
|
|
console.log('[LAYOUT] No token, redirecting to login');
|
|
window.location.href = '/admin/login';
|
|
} else {
|
|
console.log('[LAYOUT] Token found, allowing access');
|
|
setLoading(false);
|
|
}
|
|
}, [router]);
|
|
|
|
if (loading) {
|
|
return (
|
|
<div style={{
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
minHeight: '100vh',
|
|
color: 'var(--text-muted)'
|
|
}}>
|
|
Loading...
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div style={{ display: 'flex' }}>
|
|
<Sidebar />
|
|
<main style={{
|
|
marginLeft: '240px',
|
|
flex: 1,
|
|
minHeight: '100vh',
|
|
padding: '32px',
|
|
}}>
|
|
{children}
|
|
</main>
|
|
</div>
|
|
);
|
|
}
|