@extends('layouts.platform') @section('title', 'Platform Settings') @section('header', 'Platform Settings') @section('content') @if(session('status'))
{{ session('status') }}
@endif @php $tokens = [ ':appName' => config('app.name'), ':baseDomain' => $baseDomain, ]; $render = function ($value) use ($tokens) { return is_string($value) ? strtr($value, $tokens) : $value; }; $activeTab = in_array(request('tab'), ['hero', 'features', 'content', 'whatsapp', 'billing', 'payments', 'auth'], true) ? request('tab') : 'hero'; $whatsappNumber = $settings['whatsapp.number'] ?? ''; $whatsappPreview = preg_replace('/\D/', '', (string) $whatsappNumber) !== '' ? 'https://wa.me/' . preg_replace('/\D/', '', (string) $whatsappNumber) . '?text=' . rawurlencode((string) ($settings['whatsapp.message'] ?? '')) : 'Set a number above to preview the link.'; // Resolved feature-section colours (falls back to the Pishon defaults). $fb = []; foreach ($featureColorDefaults as $colorKey => $colorDefault) { $fb[$colorKey] = \App\Services\Platform\PlatformSettings::hexColor($settings[$colorKey] ?? null, $colorDefault); } $featureColorFields = [ 'features_section_bg' => ['Section background', 'features.section_bg'], 'features_heading_color' => ['Heading', 'features.heading_color'], 'features_eyebrow_color' => ['Eyebrow / accent', 'features.eyebrow_color'], 'features_subtitle_color' => ['Supporting text', 'features.subtitle_color'], 'features_card_bg' => ['Card background', 'features.card_bg'], 'features_card_title_color' => ['Card title', 'features.card_title_color'], 'features_card_text_color' => ['Card body text', 'features.card_text_color'], 'features_icon_bg' => ['Icon background', 'features.icon_bg'], 'features_icon_color' => ['Icon / function colour', 'features.icon_color'], 'features_border_color' => ['Border / divider', 'features.border_color'], ]; @endphp {{-- Tab bar --}}
@foreach([ 'hero' => 'Hero Orbit', 'features' => 'Orbit Features', 'content' => 'Content', 'whatsapp' => 'WhatsApp', 'billing' => 'Billing & Trials', 'payments' => 'Payments', 'auth' => 'Authentication & Email', ] as $tab => $label) @endforeach
{{-- Settings form: hero / content / whatsapp / billing are one unit so a single PUT never wipes a hidden section. The features tab carries its own separate CRUD forms and therefore lives outside this form. --}}
@csrf @method('PUT')
{{-- ===================== HERO ORBIT ===================== --}}

Hero Orbit

Copy, call-to-action buttons and the rotating "School Management Orbit" visual.

A phrase inside the headline to show in the brand colour, e.g. "Empowering Schools."

Lower spins faster. The outer rings move proportionally slower.

Leave as the platform logo unless a custom mark is needed.

PNG / JPG / WEBP / GIF, up to 2 MB. Stored on the public disk.

Used instead of the orbit when "Show the rotating orbit" is off.

{{-- ===================== CONTENT ===================== --}}

Content

Feature grid text, how-it-works steps, about, pricing, contact, social links, stats and the announcement banner.

One per line, format Title | Description. Managed records on the "Orbit Features" tab take priority.

How it works

Final call-to-action (dark banner before the contact section)

Cards, prices and trial days come from the live Plans — no prices are hardcoded here.

Social links (shown in the footer)

@foreach(['facebook', 'twitter', 'linkedin', 'instagram'] as $network)
@endforeach

One per line, format Value | Label — e.g. +40 | Schools on record.

{{-- ===================== WHATSAPP ===================== --}}

WhatsApp Chat

A floating WhatsApp button on the public landing page. The link is built from the number and message — never hardcoded.

Country code + number, no leading +. Only digits are kept.

Public link preview: {{ $whatsappPreview }}

{{-- ===================== BILLING & TRIALS ===================== --}}

Billing & Trials

Platform-wide trial configuration. Plan prices and per-plan trial overrides are managed under Plans.

Applies to new trials. Existing schools keep the duration they were granted.

Service charge (Pishon money)

Applied when quoting a plan. Empty values fall back to env defaults (currently {{ config('subscription.service_charge_rate') }}% + {{ config('subscription.gateway_fee_rate') }}% gateway + ₦{{ config('subscription.gateway_fee_fixed') }} fixed).

Pishon's own processing cost estimate.

{{-- ===================== PAYMENTS (Pishon money) ===================== --}} @php $paymentProviders = [ 'paystack' => ['name' => 'Paystack', 'modeValue' => $settings['gateway.paystack.mode'] ?? 'test', 'publicValue' => $settings['gateway.paystack.public_key'] ?? '', 'secretStored' => filled($settings['gateway.paystack.secret_key'] ?? ''), 'webhookStored' => filled($settings['gateway.paystack.webhook_secret'] ?? '')], 'flutterwave' => ['name' => 'Flutterwave', 'modeValue' => $settings['gateway.flutterwave.mode'] ?? 'test', 'publicValue' => $settings['gateway.flutterwave.public_key'] ?? '', 'secretStored' => filled($settings['gateway.flutterwave.secret_key'] ?? ''), 'webhookStored' => filled($settings['gateway.flutterwave.webhook_secret'] ?? '')], ]; @endphp

Payments

Pishon's own gateway credentials. Schools in "Pishon gateway" mode use these keys (settled to the school's profile); platform subscription invoices are charged through them too. Values override the .env keys, which remain the fallback.

@foreach($paymentProviders as $provider => $gw)

{{ $gw['name'] }}

{{ filled($gw['publicValue']) || $gw['secretStored'] || $gw['webhookStored'] ? 'Configured' : 'Using .env fallback' }}

Secret fields are encrypted at rest and never re-rendered. Leave them blank to keep the currently stored value.

@endforeach

Webhook URLs: {{ url('/api/webhooks/payments/paystack') }} (Paystack) · {{ url('/api/webhooks/payments/flutterwave') }} (Flutterwave).

{{-- ===================== AUTHENTICATION & EMAIL ===================== --}} @php $mailConfigured = app(\App\Services\Platform\PlatformSettings::class)->isEmailConfigured(); @endphp

Authentication & Email

Central email sending (SMTP), email verification and platform sign-in with Google or Facebook. When email is configured, every verification, password-reset and notification email is sent from here. The platform login page shows social buttons whenever a provider is enabled below.

{{-- SMTP --}}

SMTP email server

Used for all system emails — verification, password reset, receipts and notifications.

@if($mailConfigured) Configured @else Not configured @endif

Test connection — use the Send test email box below the form to verify the server above works before saving.

@if($mailConfigured)

Current SMTP configuration is live — this status reflects the saved settings.

@endif
{{-- Email verification --}}

Email verification

When enabled, new platform accounts must verify their email address before they can sign in. Existing verified users are unaffected.

{{-- Google --}}

Sign in with Google

Create credentials in the Google Cloud console and paste them here.

@if(app(\App\Services\Platform\PlatformSettings::class)->isGoogleConfigured()) Enabled @else Disabled @endif

Authorised redirect URI (paste into Google): {{ route('platform.social.callback', 'google') }}

{{-- Facebook --}}

Sign in with Facebook

Create an app in the Meta Developer portal and paste its credentials here.

@if(app(\App\Services\Platform\PlatformSettings::class)->isFacebookConfigured()) Enabled @else Disabled @endif

Valid OAuth redirect URI (paste into Meta): {{ route('platform.social.callback', 'facebook') }}

{{-- ===================== FEATURES SECTION COLOURS ===================== --}}

Features Section Colours

Brand the blue “Everything a school needs” section on the platform landing page. Defaults are sampled from the Pishon logo (#0171BC). This affects the platform landing page only — individual school sites keep their own branding.

@foreach($featureColorFields as $input => [$fieldLabel, $colorKey]) @php $def = $featureColorDefaults[$colorKey] ?? '#000000'; $val = $fb[$colorKey] ?? $def; @endphp

{{ $val }}

Default {{ $def }}

@error($input)

{{ $message }}

@enderror
@endforeach
{{-- Live preview --}}

Preview

Features

{{ $settings['features.headline'] ?? 'Everything a school needs' }}

{{ $settings['features.subtitle'] ?? 'Each tenant gets a complete, self-contained school system.' }}

Result Management

Generate professional results and report cards.

View landing page →
{{-- ===================== SEND TEST EMAIL (separate form) ===================== --}}

Send test email

Validates the saved SMTP settings by sending a real message. Configure your SMTP values in the form above, hit Save Settings, then try the live server here.

@csrf
@if(session('test_email_status'))

{{ session('test_email_status') }}

@endif
{{-- ===================== ORBIT FEATURES (separate forms) ===================== --}}

Orbit Features

Database-driven chips for the hero orbit and the landing-page feature grid. Up to 22 appear in the orbit (ring 1 = 4, ring 2 = 8, ring 3 = 10); the rest appear only in the grid.

Add a feature
@csrf
@if($features->isEmpty())

No feature records yet

Add one above, or run php artisan db:seed --class=LandingContentSeeder to restore the defaults.

@else @foreach($features as $feature) @endforeach
Order Feature Orbit Highlight Status Actions
{{ $feature->sort_order }}
{!! $feature->iconSvg() !!}

{{ $feature->title }}

{{ $feature->description }}

@if($feature->link)

{{ $feature->link }}

@endif
@if($feature->in_orbit)In orbit@elseGrid only@endif @if($feature->is_highlighted)Highlight@else—@endif @if($feature->is_active)Active@elseInactive@endif
Edit

Edit: {{ $feature->title }}

@csrf @method('PUT')
@if($feature->image_url)
@endif
in_orbit ? 'checked' : '' }} class="h-4 w-4 rounded border-gray-300 text-primary-600 focus:ring-primary-500">
is_highlighted ? 'checked' : '' }} class="h-4 w-4 rounded border-gray-300 text-primary-600 focus:ring-primary-500">
is_active ? 'checked' : '' }} class="h-4 w-4 rounded border-gray-300 text-primary-600 focus:ring-primary-500">
@csrf @method('DELETE')
@endif
@endsection @push('scripts') @endpush