@extends('app-admin.layout') @section('title', $module->name.' — Wavora Admin') @section('page-title', $module->name) @section('page-sub', 'Module: '.$module->code) @php use App\Models\Tenant; use App\Models\WaDevice; use App\Models\Plan; use App\Models\Message; use App\Models\Webhook; use App\Models\ApiKey; use App\Models\Subscription; use App\Models\Invoice; use App\Models\GatewayNode; use App\Models\AuditLog; use App\Models\User; $code = $module->code; @endphp @section('topbar-actions') @if($code === 'tenants') @elseif($code === 'plans') @if(auth()->user()->canDoAction('plans', 'edit')) @endif @elseif($code === 'devices') @endif @endsection @section('content') {{-- ─── TENANTS ─── --}} @if($code === 'tenants') @php $tenants = Tenant::withCount(['waDevices','members'])->latest()->paginate(20); @endphp
All Tenants {{ $tenants->total() }}
@forelse($tenants as $t) @empty @endforelse
TenantEmailDevicesMembersStatusJoined
{{ strtoupper(substr($t->name,0,1)) }}
{{ $t->name }}
{{ $t->slug ?? $t->id }}
{{ $t->email ?? '—' }} {{ $t->wa_devices_count }} {{ $t->members_count }} @php $s = $t->status ?? 'active'; @endphp {{ $s }} {{ $t->created_at?->format('d M Y') }}
corporate_fare

No tenants yet

@if($tenants->hasPages())
{{ $tenants->links() }}
@endif
{{-- ─── USERS ─── --}} @elseif($code === 'users') @php $users = User::latest()->paginate(20); @endphp
Platform Users {{ $users->total() }}
@forelse($users as $u) @empty @endforelse
NameEmailRoleJoined
{{ strtoupper(substr($u->name,0,1)) }}
{{ $u->name }}
{{ $u->email }} {{ $u->group?->name ?? 'admin' }} {{ $u->created_at?->format('d M Y') }}
group

No users

{{-- ─── DEVICES ─── --}} @elseif($code === 'devices') @php $devices = WaDevice::with('tenant', 'devicePlans.plan')->latest()->paginate(20); @endphp
WA Devices {{ $devices->total() }}
@forelse($devices as $d) @empty @endforelse
LabelPhoneTenantPlanStatusAction
smartphone
{{ $d->label ?? $d->name ?? 'Device' }}
{{ $d->phone_number ?? $d->phone ?? '—' }} {{ $d->tenant?->name ?? '—' }} @php $activePlan = $d->devicePlans->where('status', 'active')->first(); $planName = $activePlan ? $activePlan->plan->name : 'No Plan'; @endphp {{ $planName }} @php $s = $d->status ?? 'unknown'; @endphp {{ $s }} settings Manage
smartphone

No devices

{{-- ─── MESSAGES ─── --}} @elseif($code === 'messages') @php $msgs = Message::with('tenant','waDevice')->latest()->paginate(30); @endphp
Messages {{ $msgs->total() }}
@forelse($msgs as $m) @empty @endforelse
ToTypeTenantDeviceStatusTime
{{ $m->to ?? $m->recipient ?? '—' }} {{ $m->type ?? 'text' }} {{ $m->tenant?->name ?? '—' }} {{ $m->waDevice?->label ?? '—' }} @php $s=$m->status??'queued'; $c=match($s){'sent'=>'badge-green','failed'=>'badge-red','queued'=>'badge-yellow','processing'=>'badge-blue',default=>'badge-gray'}; @endphp {{ $s }} {{ $m->created_at?->format('d M H:i') }}
forum

No messages

{{-- ─── PLANS ─── --}} @elseif($code === 'plans') @php $plans = Plan::orderBy('id', 'asc')->get(); @endphp
@forelse($plans as $p)
{{ $p->name }}
{{ $p->code }}
{{ $p->is_active?'Active':'Inactive' }}
@if($p->price_monthly == 0) Free @else Rp{{ number_format($p->price_monthly,0,',','.') }}/mo @endif
@if(isset($p->limits['messages_per_day']))
check_circle {{ number_format($p->limits['messages_per_day']) }} msg/day
@else
check_circle Unlimited msg/day
@endif @if(isset($p->limits['messages_per_month']))
check_circle {{ number_format($p->limits['messages_per_month']) }} msg/month
@else
info Unlimited msg/month
@endif
check_circle {{ number_format($p->limits['operators'] ?? 1) }} operator limit
@if(in_array('mandatory_footer', $p->features ?? [])) info Branding required @else check_circle No branding footer @endif
@if(auth()->user()->canDoAction('plans', 'edit'))
@endif
@empty
layers

No plans yet. Seed the database.

@endforelse
Tabel Perbandingan Paket
@foreach($plans as $p) @endforeach @foreach($plans as $p) @endforeach @foreach($plans as $p) @endforeach @foreach($plans as $p) @endforeach @foreach($plans as $p) @endforeach @foreach($plans as $p) @endforeach @foreach($plans as $p) @endforeach @foreach($plans as $p) @endforeach @foreach($plans as $p) @endforeach @foreach($plans as $p) @endforeach
Fitur & Batasan
{{ $p->name }}
{{ $p->code }}
Harga Bulanan @if($p->price_monthly == 0) Free @else Rp{{ number_format($p->price_monthly,0,',','.') }} @endif
Harga 3 Bulan @if(($p->price_3_months ?? 0) == 0) Free @else Rp{{ number_format($p->price_3_months,0,',','.') }} @endif
Harga 6 Bulan @if(($p->price_6_months ?? 0) == 0) Free @else Rp{{ number_format($p->price_6_months,0,',','.') }} @endif
Harga 12 Bulan @if(($p->price_12_months ?? 0) == 0) Free @else Rp{{ number_format($p->price_12_months,0,',','.') }} @endif
Pesan Per Hari @if(isset($p->limits['messages_per_day']) && $p->limits['messages_per_day'] !== '' && $p->limits['messages_per_day'] > 0) {{ number_format($p->limits['messages_per_day']) }} / hari @else Unlimited @endif
Pesan Per Bulan @if(isset($p->limits['messages_per_month']) && $p->limits['messages_per_month'] !== '' && $p->limits['messages_per_month'] > 0) {{ number_format($p->limits['messages_per_month']) }} / bulan @else Unlimited @endif
Batas Operator{{ number_format($p->limits['operators'] ?? 1) }} Operator
Tanpa Branding Footer @if(in_array('mandatory_footer', $p->features ?? [])) Wajib Branding @else Tanpa Branding @endif
Status Aktif {{ $p->is_active?'Aktif':'Nonaktif' }}
@if(auth()->user()->canDoAction('plans', 'edit')) @endif {{-- ─── WEBHOOKS ─── --}} @elseif($code === 'webhooks') @php $hooks = Webhook::with('tenant')->latest()->paginate(20); @endphp
Webhooks {{ $hooks->total() }}
@forelse($hooks as $h) @empty @endforelse
URLTenantEventsStatusCreated
{{ $h->url }} {{ $h->tenant?->name ?? '—' }} {{ is_array($h->events)?implode(', ',$h->events):($h->events??'all') }} {{ $h->is_active?'Active':'Disabled' }} {{ $h->created_at?->format('d M Y') }}
webhook

No webhooks

{{-- ─── API KEYS ─── --}} @elseif($code === 'api_keys') @php $keys = ApiKey::with('tenant')->latest()->paginate(20); @endphp
API Keys {{ $keys->total() }}
@forelse($keys as $k) @empty @endforelse
NameTenantKey (masked)StatusLast Used
{{ $k->name ?? 'API Key' }} {{ $k->tenant?->name ?? '—' }} {{ '••••'.substr($k->key_hash??'',0,8) }} {{ $k->is_active?'Active':'Revoked' }} {{ $k->last_used_at?->diffForHumans() ?? '—' }}
key

No API keys

{{-- ─── SUBSCRIPTIONS ─── --}} @elseif($code === 'subscriptions') @php $subs = Subscription::with('tenant','plan')->latest()->paginate(20); @endphp
Subscriptions {{ $subs->total() }}
@forelse($subs as $s) @empty @endforelse
TenantPlanStatusExpiresCreated
{{ $s->tenant?->name ?? '—' }} {{ $s->plan?->name ?? '—' }} @php $st=$s->status??'active'; $sc=match($st){'active'=>'badge-green','past_due'=>'badge-yellow','suspended','cancelled','expired'=>'badge-red',default=>'badge-gray'}; @endphp {{ $st }} {{ $s->expires_at?->format('d M Y') ?? '∞' }} {{ $s->created_at?->format('d M Y') }}
receipt_long

No subscriptions

{{-- ─── INVOICES ─── --}} @elseif($code === 'invoices') @php $invoices = Invoice::with('tenant')->latest()->paginate(20); @endphp
Invoices {{ $invoices->total() }}
@forelse($invoices as $inv) @empty @endforelse
Invoice #TenantAmountStatusDueCreated
#{{ $inv->invoice_number ?? substr($inv->id,0,8) }} {{ $inv->tenant?->name ?? '—' }} Rp{{ number_format($inv->grand_total??0,0,',','.') }} @php $st=$inv->status??'pending'; $sc=match($st){'paid'=>'badge-green','pending'=>'badge-yellow','overdue'=>'badge-red','cancelled'=>'badge-gray',default=>'badge-gray'}; @endphp {{ $st }} {{ $inv->due_date?->format('d M Y') ?? '—' }} {{ $inv->created_at?->format('d M Y') }}
description

No invoices

{{-- ─── GATEWAY NODES ─── --}} @elseif($code === 'gateway_nodes') @php $nodes = GatewayNode::all(); @endphp
Gateway Nodes {{ $nodes->count() }}
@forelse($nodes as $n) @empty @endforelse
NameBase URLStatusCapabilitiesWeight
{{ $n->name }} {{ $n->base_url }} {{ $n->status }} {{ implode(', ',$n->capabilities??[]) }} {{ $n->weight ?? 1 }}
hub

No gateway nodes

{{-- ─── AUDIT LOGS ─── --}} @elseif($code === 'audit_logs') @php $logs = AuditLog::with('tenant')->latest()->paginate(50); @endphp
Audit Logs
@forelse($logs as $log) @empty @endforelse
ActionActorTenantIPTime
{{ $log->action ?? '—' }}
@if($log->description)
{{ Str::limit($log->description,80) }}
@endif
{{ $log->actor_email ?? $log->user_id ?? '—' }} {{ $log->tenant?->name ?? '—' }} {{ $log->ip_address ?? '—' }} {{ $log->created_at?->format('d M H:i') }}
history

No audit logs

{{-- ─── SETUP GUIDE ─── --}} @elseif($code === 'setup_guide')
checklist Setup Checklist
@php $checks = [ ['label' => 'Laravel app connected to PostgreSQL', 'ok' => true], ['label' => 'Redis queue connection', 'ok' => config('queue.default') === 'redis'], ['label' => 'Platform settings seeded', 'ok' => \App\Models\PlatformSetting::count() > 0], ['label' => 'Plans seeded (incl. Free plan)', 'ok' => \App\Models\Plan::count() > 0], ['label' => 'Platform admin user created', 'ok' => \App\Models\MUser::count() > 0], ['label' => 'Gateway node configured', 'ok' => \App\Models\GatewayNode::count() > 0], ]; @endphp @foreach($checks as $check)
{{ $check['ok'] ? 'check_circle' : 'radio_button_unchecked' }} {{ $check['label'] }}
@endforeach
bolt Quick Actions
settings Platform Settings layers Manage Plans hub Gateway Nodes shield_lock Permission Matrix
{{-- ─── DEFAULT / FALLBACK ─── --}} @else
info {{ $module->name }}

Module code: {{ $module->code }}

Allowed Actions
@foreach($actions as $action)
{{ $action->name }}
{{ $action->code }}
{{ auth()->user()->canDoAction($module->code,$action->code)?'Allowed':'Denied' }}
@endforeach
construction

Full CRUD for this module will be implemented in the next phase.

@endif @endsection