refactor(admin): CRM을 apps/admin 독립 프로젝트로 분리
- apps/web에서 admin 라우트/가드/sidebar 링크 제거 - apps/admin: 독립 Next.js 앱 (포트 3001) - 자체 login/unauthorized/auth callback - admin-sidebar: Overview/Users/Subscriptions/Usage - requireAdmin() 가드: profile.role='admin' 체크 - monorepo workspace에 apps/admin 등록
This commit is contained in:
parent
daed9f90d5
commit
46673ee941
23 changed files with 536 additions and 343 deletions
96
apps/admin/src/components/admin-sidebar.tsx
Normal file
96
apps/admin/src/components/admin-sidebar.tsx
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
'use client'
|
||||
|
||||
// apps/admin/src/components/admin-sidebar.tsx
|
||||
// Admin CRM 사이드바
|
||||
|
||||
import { usePathname, useRouter } from 'next/navigation'
|
||||
import { Box, List, ListItem, ListItemButton, ListItemIcon, ListItemText, Button } from '@mui/material'
|
||||
import DashboardIcon from '@mui/icons-material/Dashboard'
|
||||
import PeopleIcon from '@mui/icons-material/People'
|
||||
import SubscriptionsIcon from '@mui/icons-material/Subscriptions'
|
||||
import BarChartIcon from '@mui/icons-material/BarChart'
|
||||
import LogoutIcon from '@mui/icons-material/Logout'
|
||||
import { PhosphorText } from '@d3ro/ui/components/ds'
|
||||
import { d3roPalette, d3roFontMono } from '@d3ro/ui/theme'
|
||||
import { getSupabaseBrowserClient } from '@/lib/supabase-browser'
|
||||
|
||||
const NAV_ITEMS = [
|
||||
{ key: 'overview', path: '/', label: 'Overview', icon: <DashboardIcon /> },
|
||||
{ key: 'users', path: '/users', label: 'Users', icon: <PeopleIcon /> },
|
||||
{ key: 'subscriptions', path: '/subscriptions', label: 'Subscriptions', icon: <SubscriptionsIcon /> },
|
||||
{ key: 'usage', path: '/usage', label: 'Usage', icon: <BarChartIcon /> },
|
||||
]
|
||||
|
||||
export function AdminSidebar(): React.ReactElement {
|
||||
const pathname = usePathname()
|
||||
const router = useRouter()
|
||||
|
||||
const handleLogout = async (): Promise<void> => {
|
||||
const supabase = getSupabaseBrowserClient()
|
||||
await supabase.auth.signOut()
|
||||
router.replace('/login')
|
||||
}
|
||||
|
||||
return (
|
||||
<Box sx={{
|
||||
width: 220,
|
||||
minHeight: '100vh',
|
||||
bgcolor: d3roPalette.bg.sidebar,
|
||||
borderRight: `1px solid ${d3roPalette.border.default}`,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
}}>
|
||||
<Box sx={{ p: 2.5, borderBottom: `1px solid ${d3roPalette.border.default}` }}>
|
||||
<PhosphorText variant="heading">D3RO ADMIN</PhosphorText>
|
||||
<PhosphorText variant="dim" sx={{ display: 'block', mt: 0.5, fontSize: 10 }}>CRM Console</PhosphorText>
|
||||
</Box>
|
||||
|
||||
<List sx={{ flex: 1, py: 1 }}>
|
||||
{NAV_ITEMS.map((item) => {
|
||||
const active = item.path === '/'
|
||||
? pathname === '/'
|
||||
: pathname.startsWith(item.path)
|
||||
return (
|
||||
<ListItem key={item.key} disablePadding>
|
||||
<ListItemButton
|
||||
selected={active}
|
||||
onClick={() => router.push(item.path)}
|
||||
sx={{
|
||||
fontFamily: d3roFontMono,
|
||||
'&.Mui-selected': {
|
||||
bgcolor: d3roPalette.bg.inset,
|
||||
borderLeft: `3px solid ${d3roPalette.accent.amber}`,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ListItemIcon sx={{ minWidth: 36, color: active ? d3roPalette.accent.amber : d3roPalette.text.inactive }}>
|
||||
{item.icon}
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={item.label}
|
||||
primaryTypographyProps={{
|
||||
fontFamily: d3roFontMono,
|
||||
fontSize: 13,
|
||||
color: active ? d3roPalette.text.primary : d3roPalette.text.secondary,
|
||||
}}
|
||||
/>
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
)
|
||||
})}
|
||||
</List>
|
||||
|
||||
<Box sx={{ p: 2, borderTop: `1px solid ${d3roPalette.border.default}` }}>
|
||||
<Button
|
||||
fullWidth
|
||||
size="small"
|
||||
startIcon={<LogoutIcon />}
|
||||
onClick={() => void handleLogout()}
|
||||
sx={{ fontFamily: d3roFontMono, fontSize: 12, color: d3roPalette.text.secondary }}
|
||||
>
|
||||
Logout
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue