@extends('layouts.admin') @section('content') @php use App\Support\Ui; @endphp {{-- Helper macros for the settings page (mockup fieldRow / toggle / select / masked input) --}} @php $fname = fn(string $key) => str_replace('.', '__', $key); $row = function(string $label, string $desc, string $control) { return '
'.e($label).'
'.($desc ? '
'.e($desc).'
' : '').'
'.$control.'
'; }; $toggle = function(string $key) use ($s, $fname) { $on = $s->bool($key); return ''; }; $select = function(string $key, array $options) use ($s, $fname) { $cur = (string) $s->get($key); $h = ''; }; $text = function(string $key, string $type = 'text') use ($s, $fname) { return ''; }; $secret = function(string $key) use ($s, $fname) { return ''; }; $conn = fn(bool $on, ?string $label = null) => ''.e($label ?? ($on ? 'Connected' : 'Not connected')).''; $save = fn(string $panel) => '
'; @endphp
@foreach($kpis as $k)@include('admin.partials.kpi', ['k' => $k])@endforeach
@foreach([['general','General','gear'],['payments','Payments','card'],['sms','SMS & Notifications','bolt'],['maps','Maps & Geolocation','globe'],['social','Social Login','users'],['security','Security','shield']] as [$id,$label,$ic]) @endforeach
{{-- GENERAL --}}
@csrf

Platform identity

Core details shown across the web app, mobile apps and emails.

{!! $row('Platform name', 'Displayed in the header, emails and SMS sender templates.', $text('general.platform_name')) !!} {!! $row('Support email', 'Where user enquiries and escalations are routed.', $text('general.support_email', 'email')) !!} {!! $row('Default currency', 'Used for pricing display when a user has no regional override.', $select('general.default_currency', ['USD','KES','TZS'])) !!} {!! $row('Timezone', 'Used for scheduling, reports and election windows.', $select('general.timezone', ['Africa/Nairobi','Africa/Dar_es_Salaam','UTC'])) !!}

Localisation

Languages available to end users, per the Multi-Language & Accessibility module.

{!! $row('English', 'Default platform language.', $toggle('general.lang_en')) !!} {!! $row('Swahili', 'Available at launch across web and SMS templates.', $toggle('general.lang_sw')) !!} {!! $row('Low-bandwidth Lite Mode', 'Serves a lighter web experience on slow connections.', $toggle('general.lite_mode')) !!}

Maintenance

Temporarily take the public platform offline for maintenance.

{!! $row('Maintenance mode', 'Shows a maintenance page to all non-admin users when enabled.', $toggle('general.maintenance_mode')) !!} {!! $row('Sample-data banner', 'Shows the “SAMPLE DATA · PRE-LAUNCH” pill in the console header.', $toggle('general.demo_mode')) !!}

Module roadmap

Status shown on the dashboard module map and adoption bars.

@foreach($modules as $m) @php $cur = $moduleOverrides[$m['key']] ?? $m['status']; @endphp
{{ $m['name'] }}
{{ $m['group'] }}
@endforeach
{!! $save('general') !!}

Hybrid storage — MariaDB ↔ JSON

Live sync status, queue depth and last reconciliation (Design Doc §4).

{!! $conn($status['database']['up'], $status['database']['up'] ? 'MariaDB online' : 'MariaDB offline') !!}
Mode
{{ $status['mode'] }}
Queued ops
{{ $status['queue']['depth'] }}
Last sync
{{ $status['queue']['last_sync_at'] ? date('H:i:s', $status['queue']['last_sync_at']) : '—' }}
Mirrored records
{{ number_format($status['json']['records']) }}
Connection
{{ $status['database']['connection'] }} · node {{ $status['node'] }}
JSON path
{{ $status['json']['path'] }} ({{ $status['json']['writable'] ? 'writable' : 'NOT WRITABLE' }})
Outages
{{ $status['database']['outages'] }} recorded{{ $status['database']['reason'] ? ' · last: '.\Illuminate\Support\Str::limit($status['database']['reason'], 80) : '' }}
Last result
{{ $status['queue']['last_sync_result'] ? json_encode(collect($status['queue']['last_sync_result'])->only(['status','applied','skipped','conflicts','failed','remaining'])) : '—' }}
@csrf
@csrf
{!! Ui::icon('globe') !!} Status JSON
@if($conflicts->count())
Conflicts awaiting review {{ $conflicts->count() }}
@foreach($conflicts as $c)
{{ $c->entity }} · {{ $c->record_id }} · {{ $c->kind }} · {{ Ui::ago($c->created_at) }}
@csrf
{{ $c->note }}
Offline / JSON version
{{ json_encode($c->local_data, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES) }}
MariaDB version
{{ json_encode($c->db_data, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES) }}
@endforeach @endif
{{-- PAYMENTS --}}
@csrf

M-PESA Daraja

STK Push, C2B and B2C payouts for subscriptions, orders and affiliate payments.

{!! $conn($mpesaOk) !!}
{!! $row('Environment', 'Switch between sandbox testing and live transactions.', $select('payments.mpesa_env', ['Live','Sandbox'])) !!} {!! $row('Consumer key', 'Issued by Safaricom Daraja developer portal.', $secret('payments.mpesa_consumer_key')) !!} {!! $row('Consumer secret', 'Kept encrypted; never shown in full.', $secret('payments.mpesa_consumer_secret')) !!} {!! $row('Business shortcode', 'Paybill or till number used for STK push.', $text('payments.mpesa_shortcode')) !!} {!! $row('Passkey', 'Lipa na M-PESA online passkey.', $secret('payments.mpesa_passkey')) !!}

Card payments — Pesapal

Card and international payment gateway for clients outside M-PESA coverage.

{!! $conn($pesapalOk) !!}
{!! $row('API key', 'Hosted payment page credential.', $secret('payments.pesapal_api_key')) !!} {!! $row('Consumer secret', 'Kept encrypted.', $secret('payments.pesapal_consumer_secret')) !!} {!! $row('Settlement account', 'Bank account payouts are swept to.', $secret('payments.settlement_account')) !!}

Paystack

Card, bank and mobile-money across Africa. Webhook: {{ url('/api/v1/payments/paystack/webhook') }}

{!! $conn($paystackOk) !!}
{!! $row('Public key', 'Used by the app/web checkout widget.', $text('payments.paystack_public_key')) !!} {!! $row('Secret key', 'Server key — kept encrypted. Also verifies webhooks.', $secret('payments.paystack_secret_key')) !!}

PayPal

International card / PayPal balance. Webhook: {{ url('/api/v1/payments/paypal/webhook') }}

{!! $conn($paypalOk) !!}
{!! $row('Environment', 'Sandbox for testing, Live for production.', $select('payments.paypal_env', ['sandbox','live'])) !!} {!! $row('Client ID', 'REST app client id.', $text('payments.paypal_client_id')) !!} {!! $row('Secret', 'REST app secret — kept encrypted.', $secret('payments.paypal_secret')) !!} {!! $row('Webhook ID', 'From the PayPal dashboard; used to verify webhooks.', $text('payments.paypal_webhook_id')) !!}

Marketplace payment rules

Escrow and tax handling for brokered jobs and orders.

{!! $row('Escrow for high-value jobs', 'Hold funds until the client confirms job completion.', $toggle('payments.escrow_enabled')) !!} {!! $row('VAT rate (%)', 'Applied to applicable invoices and receipts.', $text('payments.vat_rate', 'number')) !!} {!! $row('Payout schedule', 'How often engineer/vendor balances are paid out.', $select('payments.payout_schedule', ['Weekly','Bi-weekly','Monthly'])) !!}
{!! $save('payments') !!}
{{-- SMS --}}
@csrf

Africa's Talking — Primary

OTP verification, transactional alerts and USSD.

{!! $conn($atOk) !!}
{!! $row('API key', 'Used to authenticate outbound SMS/USSD requests.', $secret('sms.at_api_key')) !!} {!! $row('Sender ID', 'Shown to recipients as the message sender.', $text('sms.at_sender_id')) !!} {!! $row('USSD short code', 'Feature-phone access to core directory search.', $text('sms.ussd_code')) !!}

Twilio — Secondary

Automatic fallback provider for international coverage and redundancy.

{!! $conn($twilioOk) !!}
{!! $row('Account SID', 'Twilio account identifier.', $secret('sms.twilio_sid')) !!} {!! $row('Auth token', 'Kept encrypted.', $secret('sms.twilio_token')) !!} {!! $row('Auto-failover', "Switch to Twilio automatically if Africa's Talking delivery drops below threshold.", $toggle('sms.auto_failover')) !!}

Notification rules

Controls inherited by the Unified Notifications Hub.

{!! $row('Require OTP at signup', 'Two-factor verification for new accounts.', $toggle('sms.otp_at_signup')) !!} {!! $row('Marketing SMS — default opt-in', 'New users start opted out unless they choose in.', $toggle('sms.marketing_default_optin')) !!} {!! $row('Digest mode for low-priority alerts', 'Batch non-urgent notifications rather than sending instantly.', $toggle('sms.digest_mode')) !!}

Mobile push — Firebase Cloud Messaging

Android & iOS push for the Flutter app. Paste the Firebase service-account JSON (kept encrypted).

{!! $conn($fcmOk) !!}
{!! $row('Service-account JSON', 'Firebase console → Project settings → Service accounts → Generate new private key.', '') !!} {!! $row('Minimum app version', 'Older builds are asked to update.', $text('mobile.min_app_version')) !!} {!! $row('Force update', 'Block older builds until they update.', $toggle('mobile.force_update')) !!} {!! $row('Play Store URL', 'Shown in update prompts.', $text('mobile.android_store_url', 'url')) !!} {!! $row('App Store URL', 'Shown in update prompts.', $text('mobile.ios_store_url', 'url')) !!} {!! $row('Privacy policy URL', 'Required by both app stores; returned by /api/v1/app/config.', $text('legal.privacy_url', 'url')) !!} {!! $row('Terms of service URL', 'Returned by /api/v1/app/config.', $text('legal.terms_url', 'url')) !!}
{!! $save('SMS') !!}
{{-- MAPS --}}
@csrf @php $quota = max(1, (int) $s->get('maps.monthly_quota')); $used = (int) $s->get('maps.quota_used'); $pct = min(100, (int) round($used / $quota * 100)); @endphp

Google Maps Platform

Primary geolocation provider for search, service areas and distance sorting.

{!! $conn($mapsOk) !!}
{!! $row('API key', "Restricted to this platform's domains and mobile app bundle IDs.", $secret('maps.google_key')) !!} {!! $row('Monthly quota', 'Requests allowed before overflow to the fallback provider.', $text('maps.monthly_quota', 'number')) !!} {!! $row('Quota used this month', 'Refreshes on the 1st of each month.', '
'.number_format($used).' / '.number_format($quota).' requests ('.$pct.'%)
') !!}

Fallback & defaults

Keeps geolocation working even if the primary quota is exceeded.

{!! $row('OpenStreetMap fallback', 'Automatically used once the Google Maps quota is exceeded.', $toggle('maps.osm_fallback')) !!} {!! $row('Default map center', 'Used when a user has not granted location access.', $text('maps.default_center')) !!} {!! $row('Geofenced job alerts', 'Notify nearby technicians when an urgent job is posted.', $toggle('maps.geofence_alerts')) !!}
{!! $save('maps') !!}
{{-- SOCIAL --}}
@csrf

Google Sign-In

Faster registration and login for clients and engineers.

{!! $conn($googleOk) !!}
{!! $row('Client ID', 'OAuth 2.0 client for web and mobile.', $secret('social.google_client_id')) !!} {!! $row('Client secret', 'Kept encrypted.', $secret('social.google_client_secret')) !!} {!! $row('Enabled on', 'Where this login option is offered.', $select('social.google_enabled_on', ['Web + Mobile','Web only','Mobile only'])) !!}

Facebook Login

Optional secondary social login provider.

{!! $conn($fbOk) !!}
{!! $row('Client ID', 'Add credentials from the Facebook Developer console to enable.', $secret('social.facebook_client_id')) !!} {!! $row('Client secret', 'Kept encrypted.', $secret('social.facebook_client_secret')) !!}

Sign-in rules

Applies to all social login providers.

{!! $row('Require email verification', 'Send a confirmation email even after social sign-in.', $toggle('social.require_email_verification')) !!} {!! $row('Auto-create client accounts', 'Skip manual approval for client (non-professional) sign-ups.', $toggle('social.auto_create_clients')) !!}
{!! $save('social login') !!}
{{-- SECURITY --}}
@csrf

Access control

Applies to all administrator and finance accounts.

{!! $row('Enforce 2FA for admin accounts', 'Mandatory for Super Admin, Finance and Compliance roles.', $toggle('security.admin_2fa')) !!} {!! $row('Session timeout', 'Automatically sign out inactive admin sessions.', $select('security.session_timeout', ['15 minutes','30 minutes','1 hour'])) !!} {!! $row('Admin IP allowlist', 'Restrict admin console access to approved office/VPN IPs.', $toggle('security.ip_allowlist_enabled')) !!} {!! $row('Allowed IPs', 'Comma-separated IPv4/IPv6 addresses or CIDR ranges.', $text('security.ip_allowlist')) !!}

Monitoring & audit

Cross-cutting fraud and integrity safeguards.

{!! $row('Audit log retention', 'How long admin action history is kept.', $select('security.audit_retention', ['6 months','12 months','24 months'])) !!} {!! $row('Failed login lockout (attempts)', 'Lock an account after repeated failed attempts.', $text('security.failed_login_lockout', 'number')) !!} {!! $row('Fraud flags on payouts', 'Auto-flag unusual affiliate or vendor payout patterns for review.', $toggle('security.fraud_flags')) !!}

Data protection

Kenya Data Protection Act, 2019 compliance controls.

{!! $row('Right-to-erasure requests', 'Allow users to request full account deletion from settings.', $toggle('security.erasure_requests')) !!} {!! $row('Data retention (inactive accounts)', 'Auto-archive accounts inactive beyond this period.', $select('security.inactive_retention', ['12 months','24 months','36 months'])) !!}
{!! $save('security') !!}
@endsection