// CMS admin shell: sidebar + topbar + dashboard
const CmsShell = ({ state, cmsRoute, setCmsRoute, children }) => {
const nav = [
{ group: 'Overview', items: [
{ id: 'dashboard', label: 'Dashboard', icon: 'layout-dashboard' },
]},
{ group: 'Content', items: [
{ id: 'packages', label: 'Tour packages', icon: 'compass', count: state.packages.length },
{ id: 'vehicles', label: 'Vehicles', icon: 'car', count: state.vehicles.length },
{ id: 'blogs', label: 'Blog', icon: 'book-open', count: state.blogs.length },
{ id: 'pages', label: 'Pages', icon: 'file-text', count: state.pages.length },
]},
{ group: 'Operations', items: [
{ id: 'inquiries', label: 'Inquiries', icon: 'mail', count: state.inquiries.filter(i => i.status === 'new').length, badge: 'new' },
{ id: 'menu', label: 'Navigation', icon: 'menu' },
]},
{ group: 'Admin', items: [
{ id: 'users', label: 'Users', icon: 'users' },
{ id: 'settings', label: 'Settings', icon: 'settings' },
]},
];
return (
{/* Sidebar */}
{/* Main */}
{children}
);
};
// -------- DASHBOARD --------
const CmsDashboard = ({ state, setCmsRoute }) => {
const newInquiries = state.inquiries.filter(i => i.status === 'new').length;
const published = state.packages.filter(p => p.status === 'published').length;
const drafts = state.packages.filter(p => p.status === 'draft').length;
const blogDrafts = state.blogs.filter(b => b.status === 'draft').length;
const stats = [
{ label: 'Tour packages', value: state.packages.length, delta: '+2 this month', icon: 'compass' },
{ label: 'Published', value: published, delta: drafts + ' in draft', icon: 'check-circle' },
{ label: 'New inquiries', value: newInquiries, delta: 'needs attention', icon: 'mail', highlight: true },
{ label: 'Blog posts', value: state.blogs.length, delta: blogDrafts + ' in draft', icon: 'book-open' },
];
return (
{new Date().toLocaleDateString('en-MY', { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' })}
Selamat pagi, Peter.
You have {newInquiries} new inquiries waiting for a reply.
setCmsRoute({ route: 'packages-edit', id: 'new' })}>New package
setCmsRoute({ route: 'blogs-edit', id: 'new' })}>New blog post
setCmsRoute({ route: 'pages-edit', id: 'new' })}>New page
{/* Stats */}
{stats.map(s => (
{s.highlight &&
}
{s.value}
{s.delta}
))}
{/* Recent inquiries */}
Recent inquiries
{state.inquiries.slice(0, 6).map(i => (
setCmsRoute({ route: 'inquiries', openId: i.id })} style={{ padding: '14px 20px', borderBottom: '1px solid #f3ecd9', display: 'flex', justifyContent: 'space-between', alignItems: 'center', cursor: 'pointer' }}>
{i.name.split(' ').map(x => x[0]).join('').slice(0,2)}
{i.name}
{i.status === 'new' && }
{i.service} · {i.message.slice(0, 70)}...
{relTime(i.date)}
))}
{/* Activity */}
Quick links
{[
{ route: 'packages', icon: 'compass', label: 'Manage tour packages' },
{ route: 'blogs', icon: 'book-open', label: 'Blog posts' },
{ route: 'menu', icon: 'menu', label: 'Edit site navigation' },
{ route: 'settings', icon: 'settings', label: 'Site settings' },
].map(x => (
))}
This month
147
inquiries received · +22% vs last month
{[45, 52, 38, 60, 48, 72, 66, 80, 58, 90, 72, 85].map((h, i) => (
7 ? '#E85D2F' : 'rgba(245,238,222,0.35)', borderRadius: 2 }} />
))}
);
};
// ---- Shared CMS chrome bits ----
const CmsPageHeader = ({ title, subtitle, breadcrumb, actions }) => (
{breadcrumb &&
{breadcrumb}
}
{title}
{subtitle &&
{subtitle}
}
{actions}
);
const CmsStatusBadge = ({ status }) => {
const map = {
published: { tone: 'green', label: 'Published' },
draft: { tone: 'neutral', label: 'Draft' },
new: { tone: 'orange', label: 'New' },
read: { tone: 'blue', label: 'Read' },
responded: { tone: 'green', label: 'Responded' },
};
const m = map[status] || { tone: 'neutral', label: status };
return
{m.label};
};
Object.assign(window, { CmsShell, CmsDashboard, CmsPageHeader, CmsStatusBadge });