@props(['active' => 'overview'])
@php
$supportOpen = $supportUnassigned = 0;
try {
$supportOpen = (int) \App\Models\SupportTicket::whereIn('status', ['open', 'in_progress', 'pending'])->count();
$supportUnassigned = (int) \App\Models\SupportTicket::whereNull('assigned_agent_id')
->whereIn('status', ['open', 'pending'])
->count();
} catch (\Throwable $e) {
}
// Live counts for the rest of the sidebar — every "static" 4 / 142
// / 18k / 1.2k pill was hard-coded prototype text. These now read
// from the actual tables. Each is wrapped so a missing table /
// schema mismatch never takes the sidebar down.
$fmtCount = function (int $n): string {
if ($n >= 1000000) {
return number_format($n / 1000000, $n >= 10000000 ? 0 : 1) . 'M';
}
if ($n >= 1000) {
return number_format($n / 1000, $n >= 10000 ? 0 : 1) . 'k';
}
return (string) $n;
};
$usersCount = $rolesCount = $permsCount = $workspacesCount = 0;
$packagesCount = $creditPackagesCount = $couponsCount = $billingHistoryCount = $orderHistoryCount = $invoicesCount = 0;
$supportTeamCount = $supportAgentsCount = $supportSlaCount = $supportCustomersCount = $supportPlaybooksCount = 0;
$announcementsCount = $guidebookCount = 0;
$contactUnread = 0;
try {
$contactUnread = (int) \App\Models\ContactMessage::where('is_read', false)->count();
} catch (\Throwable $e) {
}
try {
$usersCount = (int) \App\Models\User::query()->count();
} catch (\Throwable $e) {
}
try {
$rolesCount = (int) \DB::table('roles')->count();
} catch (\Throwable $e) {
}
try {
$permsCount = (int) \DB::table('permissions')->count();
} catch (\Throwable $e) {
}
try {
$workspacesCount = (int) \App\Models\Workspace::query()->count();
} catch (\Throwable $e) {
}
try {
$packagesCount = (int) \App\Models\Package::query()->count();
} catch (\Throwable $e) {
}
try {
$creditPackagesCount = (int) \App\Models\CreditPackage::query()->count();
} catch (\Throwable $e) {
}
// Coupons badge = active coupons only — expired/disabled coupons
// are noise on the sidebar. "active" means is_active + within
// valid_from/valid_to window when those columns exist.
try {
$couponsCount = (int) \App\Models\Coupon::query()
->when(\Schema::hasColumn('coupons', 'is_active'), fn($q) => $q->where('is_active', true))
->count();
} catch (\Throwable $e) {
}
try {
$billingHistoryCount = (int) \DB::table('orders')->count();
} catch (\Throwable $e) {
}
try {
$orderHistoryCount = (int) \DB::table('orders')->count();
} catch (\Throwable $e) {
}
try {
$invoicesCount = (int) \DB::table('orders')->where('status', 'paid')->count();
} catch (\Throwable $e) {
}
// Support group children. Each lookup is independent so a missing
// table on a fresh install just leaves that badge blank.
try {
$supportTeamCount = (int) \App\Models\SupportTicket::whereNull('assigned_agent_id')
->whereIn('status', ['open', 'pending', 'in_progress'])
->count();
} catch (\Throwable $e) {
}
try {
$supportAgentsCount = (int) \App\Models\SupportAgent::where('is_active', true)->count();
} catch (\Throwable $e) {
}
// SLA badge = tickets with first_response/resolution SLA at risk
// or already breached. Cheap heuristic: open tickets without a
// first_response_at older than the workspace SLA window. Falls
// back to all open tickets if the SLA column isn't there.
try {
$supportSlaCount = (int) \App\Models\SupportTicket::query()
->whereIn('status', ['open', 'pending', 'in_progress'])
->whereNull('first_response_at')
->count();
} catch (\Throwable $e) {
}
// Customers = distinct visitor emails seen in support tickets —
// proxy for "people we've supported" until the explicit
// support_customers table is populated.
try {
$supportCustomersCount = (int) \App\Models\SupportTicket::query()
->whereNotNull('email')
->distinct('email')
->count('email');
} catch (\Throwable $e) {
}
try {
$supportPlaybooksCount = (int) \App\Models\Playbook::where('is_active', true)->count();
} catch (\Throwable $e) {
}
// Marketing group — only count "live" rows so the badge tells
// admins what visitors actually see.
try {
$announcementsCount = (int) \App\Models\Announcement::query()
->when(\Schema::hasColumn('announcements', 'is_active'), fn($q) => $q->where('is_active', true))
->count();
} catch (\Throwable $e) {
}
try {
$guidebookCount = (int) \App\Models\GuidebookArticle::query()
->when(\Schema::hasColumn('guidebook_articles', 'is_published'), fn($q) => $q->where('is_published', true))
->count();
} catch (\Throwable $e) {
}
$nav = [
[
'type' => 'leaf',
'key' => 'overview',
'href' => url('/admin'),
'label' => __('Overview'),
'icon' =>
'',
'sw' => 1.6,
],
[
'type' => 'leaf',
'key' => 'financial',
'href' => url('/admin/financial'),
'label' => __('Financial'),
'icon' => '',
'sw' => 1.6,
],
[
'type' => 'leaf',
'key' => 'premium',
'href' => url('/admin/premium'),
'label' => __('Premium'),
'icon' =>
'',
'sw' => 1.4,
],
[
'type' => 'leaf',
'key' => 'analytics',
'href' => url('/admin/analytics'),
'label' => __('Analytics'),
'icon' => '',
'sw' => 1.6,
],
[
'type' => 'group',
'key' => 'access',
'label' => __('Users & access'),
'icon' => '',
'sw' => 1.5,
'children' => [
[
'key' => 'users',
'href' => url('/admin/users'),
'label' => __('Users'),
'badge' => $usersCount > 0 ? $fmtCount($usersCount) : null,
],
[
'key' => 'roles',
'href' => url('/admin/roles'),
'label' => __('Roles'),
'badge' => $rolesCount > 0 ? (string) $rolesCount : null,
],
[
'key' => 'permissions',
'href' => url('/admin/permissions'),
'label' => __('Permissions'),
'badge' => $permsCount > 0 ? (string) $permsCount : null,
],
],
],
[
'type' => 'leaf',
'key' => 'workspaces',
'href' => url('/admin/workspaces'),
'label' => __('Workspaces'),
'icon' => '',
'sw' => 1.6,
'badge' => $workspacesCount > 0 ? $fmtCount($workspacesCount) : null,
],
// ─────────────────────────────────────────────────────────────
// Commented out — these items already exist on the user side
// (top nav + /more page), so the admin console doesn't need to
// duplicate them. Restore by un-commenting the block(s) below.
// ─────────────────────────────────────────────────────────────
/* Contacts — duplicates user /contacts
['type' => 'leaf', 'key' => 'contacts', 'href' => url('/admin/contacts'), 'label' => __('Contacts'),
'icon' => '',
'sw' => 1.5],
*/
/* Meta Ads — duplicates user top-nav Meta Ads
['type' => 'leaf', 'key' => 'metaads', 'href' => url('/admin/meta-ads'), 'label' => __('Meta Ads'),
'icon' => '', 'sw' => 1.6],
*/
/* Messaging group — Campaigns/Broadcasts/Templates/Flows/Auto-replies
all duplicate the user side
['type' => 'group', 'key' => 'messaging', 'label' => __('Messaging'),
'icon' => '', 'sw' => 1.5,
'children' => [
['key' => 'campaigns', 'href' => url('/admin/campaigns'), 'label' => __('Campaigns')],
['key' => 'broadcasts', 'href' => url('/admin/broadcasts'), 'label' => __('Broadcasts')],
['key' => 'templates', 'href' => url('/admin/templates'), 'label' => __('Templates'), 'badge' => '38'],
['key' => 'flows', 'href' => url('/admin/flows'), 'label' => __('Flows')],
['key' => 'autoreplies', 'href' => url('/admin/auto-replies'), 'label' => __('Auto-replies')],
]],
*/
/* Devices — duplicates user /devices
['type' => 'leaf', 'key' => 'devices', 'href' => url('/admin/devices'), 'label' => __('Devices'),
'icon' => '',
'sw' => 1.6, 'badge' => '312'],
*/
/* Platform group — Integrations + Webhooks both duplicate user side
['type' => 'group', 'key' => 'platform', 'label' => __('Platform'),
'icon' => '', 'sw' => 1.5,
'children' => [
['key' => 'integrations', 'href' => url('/admin/integrations'), 'label' => __('Integrations')],
['key' => 'webhooks', 'href' => url('/admin/webhooks'), 'label' => __('Webhooks')],
]],
*/
[
'type' => 'group',
'key' => 'billing',
'label' => __('Billing & plans'),
'icon' => '',
'sw' => 1.6,
'children' => [
[
'key' => 'packages',
'href' => url('/admin/packages'),
'label' => __('Packages'),
'badge' => $packagesCount > 0 ? (string) $packagesCount : null,
],
[
'key' => 'credit-packages',
'href' => url('/admin/credit-packages'),
'label' => __('Credit packages'),
'badge' => $creditPackagesCount > 0 ? (string) $creditPackagesCount : null,
],
[
'key' => 'coupons',
'href' => url('/admin/coupons'),
'label' => __('Coupons'),
'badge' => $couponsCount > 0 ? (string) $couponsCount : null,
],
[
'key' => 'billing-history',
'href' => url('/admin/billing-history'),
'label' => __('Billing history'),
'badge' => $billingHistoryCount > 0 ? $fmtCount($billingHistoryCount) : null,
],
[
'key' => 'order-history',
'href' => url('/admin/order-history'),
'label' => __('Order history'),
'badge' => $orderHistoryCount > 0 ? $fmtCount($orderHistoryCount) : null,
],
[
'key' => 'invoices',
'href' => url('/admin/invoices'),
'label' => __('Invoices'),
'badge' => $invoicesCount > 0 ? $fmtCount($invoicesCount) : null,
],
// Checkout / pricing controls — tax, refund window, yearly
// discount, company invoice identity, AND the auto-renew
// (recurring subscriptions) switch.
[
'key' => 'checkout-settings',
'href' => url('/admin/checkout-settings'),
'label' => __('Billing settings'),
],
// Wallet rules + affiliate credits — kept inside Billing
// because they only control billing-side math (referral
// signup credits, credits_per_message, credits_per_currency_minor).
[
'key' => 'wallet-rules',
'href' => url('/admin/settings/wallet-rules'),
'label' => __('Wallet & affiliate'),
],
],
],
[
'type' => 'group',
'key' => 'support',
'label' => __('Support'),
'icon' => '',
'sw' => 1.6,
'badge' => $supportOpen + $contactUnread > 0 ? (string) ($supportOpen + $contactUnread) : null,
'badgeStyle' => 'coral',
'children' => [
[
'key' => 'support',
'href' => url('/admin/support'),
'label' => __('Inbox'),
'badge' => $supportOpen > 0 ? (string) $supportOpen : null,
'badgeStyle' => 'coral',
],
[
'key' => 'contact-messages',
'href' => url('/admin/contact-messages'),
'label' => __('Contact inbox'),
'badge' => $contactUnread > 0 ? (string) $contactUnread : null,
'badgeStyle' => 'coral',
],
[
'key' => 'support-team',
'href' => url('/admin/support/team-inbox'),
'label' => __('Team inbox'),
'badge' => $supportTeamCount > 0 ? (string) $supportTeamCount : null,
],
[
'key' => 'support-agents',
'href' => url('/admin/support/agents'),
'label' => __('Agents'),
'badge' => $supportAgentsCount > 0 ? (string) $supportAgentsCount : null,
],
[
'key' => 'support-sla',
'href' => url('/admin/support/sla'),
'label' => __('SLA board'),
'badge' => $supportSlaCount > 0 ? (string) $supportSlaCount : null,
'badgeStyle' => $supportSlaCount > 0 ? 'coral' : null,
],
[
'key' => 'support-customers',
'href' => url('/admin/support/customers'),
'label' => __('Customers'),
'badge' => $supportCustomersCount > 0 ? $fmtCount($supportCustomersCount) : null,
],
[
'key' => 'support-playbooks',
'href' => url('/admin/support/playbooks'),
'label' => __('Playbooks'),
'badge' => $supportPlaybooksCount > 0 ? (string) $supportPlaybooksCount : null,
],
['key' => 'support-reports', 'href' => url('/admin/support/reports'), 'label' => __('Reports')],
],
],
[
'type' => 'group',
'key' => 'marketing',
'label' => __('Marketing'),
'icon' => '',
'sw' => 1.5,
'children' => [
[
'key' => 'announcements',
'href' => url('/admin/announcements'),
'label' => __('Announcements'),
'badge' => $announcementsCount > 0 ? (string) $announcementsCount : null,
],
[
'key' => 'guidebook',
'href' => url('/admin/guidebook'),
'label' => __('Guidebook'),
'badge' => $guidebookCount > 0 ? (string) $guidebookCount : null,
],
],
],
/* Analytics — duplicates user /analytics
['type' => 'leaf', 'key' => 'analytics-dup', 'href' => url('/admin/analytics'), 'label' => __('Analytics'),
'icon' => '', 'sw' => 1.6],
*/
];
$sys = [
[
'key' => 'wadesk-message',
'href' => url('/admin/settings/wadesk-message'),
'label' => __('System Message Setting'),
'icon' =>
'',
'sw' => 1.6,
],
// Currencies, Languages, Payment gateways, AI/API keys, Audit
// log all live under Settings as tiles now — removed from the
// System rail to keep that rail short and focused on what's
// genuinely "system": WaDesk message provider, Security, and
// the Settings hub itself.
[
'key' => 'frontend',
'href' => url('/admin/frontend'),
'label' => __('Frontend editor'),
'icon' => '',
'sw' => 1.6,
],
[
'key' => 'security',
'href' => url('/admin/security'),
'label' => __('Security'),
'icon' => '',
'sw' => 1.6,
],
[
'key' => 'settings',
'href' => url('/admin/settings'),
'label' => __('Settings'),
'icon' =>
'',
'sw' => 1.5,
],
];
@endphp