Eight queries across the v2 modules counted only status = 'Finished' and so reported zero on live data, where the current ride pipeline writes 'completed': realtime revenue for today and yesterday, financial stats, settlements, driver scorecard, driver ranking, and both revenue queries. All now match either spelling. Growth and Advanced Analytics rendered through the generic shape-detecting renderer, which produced raw tables that said little. Both now have purpose- built views: - Growth: totals, 30-day joins, and a two-series daily chart. growth.php only returns days that had signups, so the series is expanded to a continuous 30-day axis with explicit zeros — plotting the returned rows directly would hide the gaps and make a quiet month look like steady growth. A caption states how many days actually had a signup. - Analytics: revenue summary tiles, a daily revenue trend, and the captain ranking, with a note explaining that platform share is what remains after the captain's cut. Null aggregates render as "—" rather than 0.00, and markers are drawn only on days with a value so a flat zero line stays readable. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
615 lines
26 KiB
HTML
615 lines
26 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Siro Admin | Operations Console</title>
|
|
|
|
<!-- Fonts & Icons -->
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Outfit:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
<script src="https://unpkg.com/@phosphor-icons/web"></script>
|
|
|
|
<!-- Styles -->
|
|
<!-- ?v= must be bumped whenever css/main.css or js/app.js changes: the files
|
|
are served straight off a bind mount, so without it browsers keep
|
|
running the previously cached build after a deploy. -->
|
|
<link rel="stylesheet" href="css/main.css?v=2026-07-25-8">
|
|
</head>
|
|
<body>
|
|
|
|
<!-- ================= AUTH OVERLAY ================= -->
|
|
<div class="auth-wrapper" id="authWrapper">
|
|
<div class="auth-card">
|
|
<div class="auth-header">
|
|
<div class="auth-logo">
|
|
<i class="ph-bold ph-car"></i>
|
|
<span>SIRO ADMIN</span>
|
|
</div>
|
|
<p class="auth-subtitle" data-i18n="Operations & Control Console">Operations & Control Console</p>
|
|
</div>
|
|
|
|
<form id="loginForm">
|
|
<div class="secure-strip">
|
|
<i class="ph-fill ph-shield-check"></i>
|
|
<span data-i18n="Secure session · device-bound token · WhatsApp OTP">Secure session · device-bound token · WhatsApp OTP</span>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label" for="loginEmail" data-i18n="Admin Email / Phone">Admin Email / Phone</label>
|
|
<div class="form-input-group">
|
|
<input type="text" id="loginEmail" class="form-input" placeholder="you@siromove.com or +9627..." autocomplete="username" required>
|
|
<i class="ph ph-envelope"></i>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label" for="loginPass" data-i18n="Password">Password</label>
|
|
<div class="form-input-group">
|
|
<input type="password" id="loginPass" class="form-input" placeholder="Enter your password" autocomplete="current-password" required>
|
|
<i class="ph ph-lock-key"></i>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Stays on screen until the next attempt: a toast disappears before
|
|
the operator can read why the sign-in failed. -->
|
|
<div class="login-error" id="loginError" hidden></div>
|
|
|
|
<button type="submit" class="btn-primary" id="loginSubmitBtn" style="margin-top: 1.25rem;">
|
|
<i class="ph-bold ph-sign-in"></i> <span data-i18n="Sign In">Sign In</span>
|
|
</button>
|
|
|
|
<div class="device-note">
|
|
<i class="ph ph-fingerprint"></i>
|
|
<span>This browser is registered as a trusted device: <code id="fpPreview">…</code></span>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ================= APP CONTAINER ================= -->
|
|
<div class="app-container">
|
|
|
|
<!-- Sidebar Navigation -->
|
|
<aside class="sidebar" id="sidebar">
|
|
<div class="sidebar-header">
|
|
<a href="#" class="brand">
|
|
<div class="brand-icon">
|
|
<i class="ph-bold ph-lightning"></i>
|
|
</div>
|
|
<span>SIRO</span>
|
|
<span class="brand-badge">ADMIN</span>
|
|
</a>
|
|
</div>
|
|
|
|
<nav class="sidebar-menu">
|
|
<div class="menu-label" data-i18n="Operations">Operations</div>
|
|
<a class="nav-item active" data-view="dashboardView">
|
|
<i class="ph ph-squares-four"></i>
|
|
<span data-i18n="Overview">Overview</span>
|
|
</a>
|
|
<a class="nav-item" data-view="ridesView">
|
|
<i class="ph ph-path"></i>
|
|
<span data-i18n="Rides">Rides</span>
|
|
</a>
|
|
<a class="nav-item" data-view="driversView">
|
|
<i class="ph ph-steering-wheel"></i>
|
|
<span data-i18n="Captains">Captains</span>
|
|
</a>
|
|
<a class="nav-item" data-view="passengersView">
|
|
<i class="ph ph-users"></i>
|
|
<span data-i18n="Passengers">Passengers</span>
|
|
</a>
|
|
|
|
<div class="menu-label" data-i18n="Business">Business</div>
|
|
<a class="nav-item" data-view="financialsView">
|
|
<i class="ph ph-currency-circle-dollar"></i>
|
|
<span data-i18n="Financials">Financials</span>
|
|
</a>
|
|
<a class="nav-item" data-view="approvalsView">
|
|
<i class="ph ph-user-check"></i>
|
|
<span data-i18n="Approvals">Approvals</span>
|
|
<span class="nav-count" id="approvalsCount" hidden>0</span>
|
|
</a>
|
|
<a class="nav-item" data-view="complaintsView">
|
|
<i class="ph ph-warning-circle"></i>
|
|
<span data-i18n="Complaints">Complaints</span>
|
|
</a>
|
|
<a class="nav-item" data-view="systemView">
|
|
<i class="ph ph-shield-check"></i>
|
|
<span data-i18n="Session & Security">Session & Security</span>
|
|
</a>
|
|
</nav>
|
|
|
|
<div class="sidebar-footer">
|
|
<div class="user-profile-mini">
|
|
<div class="avatar" id="userAvatar">SA</div>
|
|
<div class="user-info">
|
|
<div class="name" id="userName">Admin</div>
|
|
<div class="role" id="userRole">—</div>
|
|
</div>
|
|
</div>
|
|
<button class="btn-icon" id="logoutBtn" title="Sign Out" data-i18n-title="Sign Out">
|
|
<i class="ph ph-sign-out"></i>
|
|
</button>
|
|
</div>
|
|
</aside>
|
|
|
|
<!-- Main Content Body -->
|
|
<div class="main-wrapper">
|
|
|
|
<!-- Top Sticky Header -->
|
|
<header class="top-header">
|
|
<div class="header-left">
|
|
<button class="btn-icon toggle-sidebar-btn" id="toggleSidebar">
|
|
<i class="ph ph-list"></i>
|
|
</button>
|
|
<div class="search-box">
|
|
<i class="ph ph-magnifying-glass"></i>
|
|
<input type="text" id="globalSearch" placeholder="Filter the current table…" data-i18n-ph="Filter the current table…">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="header-right">
|
|
<div class="live-indicator" id="connectionPill">
|
|
<span class="pulse-dot"></span> <span id="connectionText" data-i18n="Connecting…">Connecting…</span>
|
|
</div>
|
|
<button class="btn-icon lang-toggle" id="langToggle" title="العربية / English">ع</button>
|
|
<button class="btn-icon" id="refreshBtn" title="Refresh data" data-i18n-title="Refresh data">
|
|
<i class="ph ph-arrows-clockwise"></i>
|
|
</button>
|
|
</div>
|
|
</header>
|
|
|
|
<main class="content-body">
|
|
|
|
<!-- ================= 1. OVERVIEW ================= -->
|
|
<section class="page-view active" id="dashboardView">
|
|
<div class="page-header">
|
|
<div class="page-title">
|
|
<h1 data-i18n="Operations Overview">Operations Overview</h1>
|
|
<p data-i18n="Live figures read directly from the production database">Live figures read directly from the production database</p>
|
|
</div>
|
|
<div class="page-actions">
|
|
<span class="stamp" id="lastUpdated">—</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Primary KPIs -->
|
|
<div class="stats-grid">
|
|
<div class="stat-card">
|
|
<div class="stat-header">
|
|
<div>
|
|
<div class="stat-value" data-kpi="rides">—</div>
|
|
<div class="stat-title" data-i18n="Total Rides">Total Rides</div>
|
|
</div>
|
|
<div class="stat-icon primary"><i class="ph ph-path"></i></div>
|
|
</div>
|
|
<div class="stat-footer">
|
|
<span class="trend-badge up"><i class="ph ph-plus"></i> <span data-kpi="ridesMonth">—</span></span>
|
|
<span class="trend-text" data-i18n="this month">this month</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="stat-card">
|
|
<div class="stat-header">
|
|
<div>
|
|
<div class="stat-value" data-kpi="completed">—</div>
|
|
<div class="stat-title" data-i18n="Completed Rides">Completed Rides</div>
|
|
</div>
|
|
<div class="stat-icon success"><i class="ph ph-check-circle"></i></div>
|
|
</div>
|
|
<div class="stat-footer">
|
|
<span class="trend-badge up"><i class="ph ph-percent"></i> <span data-kpi="completionRate">—</span></span>
|
|
<span class="trend-text" data-i18n="completion rate">completion rate</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="stat-card">
|
|
<div class="stat-header">
|
|
<div>
|
|
<div class="stat-value" data-kpi="drivers">—</div>
|
|
<div class="stat-title" data-i18n="Registered Captains">Registered Captains</div>
|
|
</div>
|
|
<div class="stat-icon info"><i class="ph ph-steering-wheel"></i></div>
|
|
</div>
|
|
<div class="stat-footer">
|
|
<span class="trend-badge up"><i class="ph ph-plus"></i> <span data-kpi="driversMonth">—</span></span>
|
|
<span class="trend-text" data-i18n="joined this month">joined this month</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="stat-card">
|
|
<div class="stat-header">
|
|
<div>
|
|
<div class="stat-value" data-kpi="passengers">—</div>
|
|
<div class="stat-title" data-i18n="Registered Passengers">Registered Passengers</div>
|
|
</div>
|
|
<div class="stat-icon warning"><i class="ph ph-users"></i></div>
|
|
</div>
|
|
<div class="stat-footer">
|
|
<span class="trend-badge up"><i class="ph ph-plus"></i> <span data-kpi="passengersMonth">—</span></span>
|
|
<span class="trend-text" data-i18n="joined this month">joined this month</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Charts -->
|
|
<div class="dashboard-grid">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3 class="card-title"><span data-i18n="Rides per Day">Rides per Day</span> <span class="card-sub" data-i18n="last 21 days">last 21 days</span></h3>
|
|
</div>
|
|
<div class="chart-container">
|
|
<canvas id="ridesTrendChart"></canvas>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3 class="card-title" data-i18n="Ride Outcomes">Ride Outcomes</h3>
|
|
</div>
|
|
<div class="chart-container donut-wrap">
|
|
<canvas id="statusDonut"></canvas>
|
|
</div>
|
|
<div class="legend" id="statusLegend"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="dashboard-grid">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3 class="card-title" data-i18n="Demand by Time of Day">Demand by Time of Day</h3>
|
|
</div>
|
|
<div class="chart-container" style="height:200px;">
|
|
<canvas id="timeOfDayChart"></canvas>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3 class="card-title" data-i18n="Service Mix">Service Mix</h3>
|
|
</div>
|
|
<div class="mini-list" id="serviceMix"></div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ================= 2. RIDES ================= -->
|
|
<section class="page-view" id="ridesView">
|
|
<div class="page-header">
|
|
<div class="page-title">
|
|
<h1>Rides</h1>
|
|
<p data-i18n="Trips, assigned captains, fares and live status">Trips, assigned captains, fares and live status</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="filter-bar">
|
|
<div class="filter-inputs">
|
|
<select class="select-input" id="rideStatusFilter">
|
|
<option value="All">All statuses</option>
|
|
<option value="Pending">Waiting for a captain</option>
|
|
<option value="Begin">In progress</option>
|
|
<option value="Completed">Completed</option>
|
|
<option value="Canceled">Cancelled</option>
|
|
</select>
|
|
<div class="search-inline">
|
|
<input type="text" class="form-input" id="ridesSearch" placeholder="Search rides by phone number">
|
|
<button class="btn btn-secondary btn-sm" id="ridesSearchBtn"><i class="ph ph-magnifying-glass"></i></button>
|
|
</div>
|
|
</div>
|
|
<span class="stamp" id="ridesMeta">—</span>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="table-responsive">
|
|
<table class="data-table">
|
|
<thead>
|
|
<tr>
|
|
<th data-i18n="Trip">Trip</th>
|
|
<th data-i18n="Passenger">Passenger</th>
|
|
<th data-i18n="Captain">Captain</th>
|
|
<th data-i18n="Route">Route</th>
|
|
<th data-i18n="Fare">Fare</th>
|
|
<th>Status</th>
|
|
<th data-i18n="Created">Created</th>
|
|
<th></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="ridesTableBody"></tbody>
|
|
</table>
|
|
</div>
|
|
<div class="load-more">
|
|
<button class="btn btn-secondary btn-sm" id="ridesMore" hidden>
|
|
<i class="ph ph-plus"></i> Show more
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ================= 3. CAPTAINS ================= -->
|
|
<section class="page-view" id="driversView">
|
|
<div class="page-header">
|
|
<div class="page-title">
|
|
<h1>Captains</h1>
|
|
<p data-i18n="Fleet roster, ratings and trip volume">Fleet roster, ratings and trip volume</p>
|
|
</div>
|
|
<div class="page-actions">
|
|
<button class="btn btn-secondary btn-sm" id="driversPrev"><i class="ph ph-caret-left"></i></button>
|
|
<span class="stamp" id="driversMeta">—</span>
|
|
<button class="btn btn-secondary btn-sm" id="driversNext"><i class="ph ph-caret-right"></i></button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="filter-bar">
|
|
<div class="search-inline">
|
|
<input type="text" class="form-input" id="driversSearch" placeholder="Search by captain ID, phone or email">
|
|
<button class="btn btn-secondary btn-sm" id="driversSearchBtn"><i class="ph ph-magnifying-glass"></i></button>
|
|
</div>
|
|
<span class="stamp">Click any row to open the full profile</span>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="table-responsive">
|
|
<table class="data-table">
|
|
<thead>
|
|
<tr>
|
|
<th data-i18n="ID">ID</th>
|
|
<th>Name</th>
|
|
<th>Phone</th>
|
|
<th>Email</th>
|
|
<th>Rating</th>
|
|
<th>Trips</th>
|
|
<th>Cancellations</th>
|
|
<th>Status</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="driversTableBody"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ================= 4. PASSENGERS ================= -->
|
|
<section class="page-view" id="passengersView">
|
|
<div class="page-header">
|
|
<div class="page-title">
|
|
<h1>Passengers</h1>
|
|
<p data-i18n="Most active customer accounts by trip count">Most active customer accounts by trip count</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="filter-bar">
|
|
<div class="search-inline">
|
|
<input type="text" class="form-input" id="passengersSearch" placeholder="Search by passenger ID, phone or email">
|
|
<button class="btn btn-secondary btn-sm" id="passengersSearchBtn"><i class="ph ph-magnifying-glass"></i></button>
|
|
</div>
|
|
<span class="stamp">Click any row to open the full profile</span>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="table-responsive">
|
|
<table class="data-table">
|
|
<thead>
|
|
<tr>
|
|
<th data-i18n="ID">ID</th>
|
|
<th>Name</th>
|
|
<th>Contact</th>
|
|
<th>Trips</th>
|
|
<th>Rating</th>
|
|
<th>Cancellations</th>
|
|
<th>Joined</th>
|
|
<th>Status</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="passengersTableBody"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ================= 5. FINANCIALS ================= -->
|
|
<section class="page-view" id="financialsView">
|
|
<div class="page-header">
|
|
<div class="page-title">
|
|
<h1>Financials</h1>
|
|
<p data-i18n="Earnings, fares and distance aggregates across all recorded trips">Earnings, fares and distance aggregates across all recorded trips</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="stats-grid">
|
|
<div class="stat-card">
|
|
<div class="stat-value" data-kpi="driverEarnings">—</div>
|
|
<div class="stat-title" data-i18n="Captain earnings (completed trips)">Captain earnings (completed trips)</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-value" data-kpi="avgFare">—</div>
|
|
<div class="stat-title" data-i18n="Average passenger fare">Average passenger fare</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-value" data-kpi="totalDistance">—</div>
|
|
<div class="stat-title" data-i18n="Total distance covered">Total distance covered</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card-header"><h3 class="card-title" data-i18n="Trip Performance">Trip Performance</h3></div>
|
|
<div class="mini-list" id="tripPerformance"></div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ================= 6. APPROVALS ================= -->
|
|
<section class="page-view" id="approvalsView">
|
|
<div class="page-header">
|
|
<div class="page-title">
|
|
<h1 data-i18n="Pending Approvals">Pending Approvals</h1>
|
|
<p data-i18n="Admin and service accounts awaiting activation">Admin and service accounts awaiting activation</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="table-responsive">
|
|
<table class="data-table">
|
|
<thead>
|
|
<tr>
|
|
<th data-i18n="ID">ID</th>
|
|
<th>Name</th>
|
|
<th>Phone</th>
|
|
<th data-i18n="Type">Type</th>
|
|
<th data-i18n="Requested role">Requested role</th>
|
|
<th data-i18n="Requested at">Requested at</th>
|
|
<th></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="approvalsTableBody"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ================= 7. COMPLAINTS ================= -->
|
|
<section class="page-view" id="complaintsView">
|
|
<div class="page-header">
|
|
<div class="page-title">
|
|
<h1>Complaints</h1>
|
|
<p data-i18n="Volume of customer complaints filed">Volume of customer complaints filed</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="stats-grid">
|
|
<div class="stat-card">
|
|
<div class="stat-value" data-kpi="complaintsToday">—</div>
|
|
<div class="stat-title" data-i18n="Filed today">Filed today</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-value" data-kpi="complaintsWeek">—</div>
|
|
<div class="stat-title" data-i18n="This week">This week</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-value" data-kpi="complaintsMonth">—</div>
|
|
<div class="stat-title" data-i18n="This month">This month</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ================= 8. SESSION & SECURITY ================= -->
|
|
<section class="page-view" id="systemView">
|
|
<div class="page-header">
|
|
<div class="page-title">
|
|
<h1>Session & Security</h1>
|
|
<p data-i18n="Details of the credential currently authorising this console">Details of the credential currently authorising this console</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card-header"><h3 class="card-title" data-i18n="Current session">Current session</h3></div>
|
|
<div class="mini-list" id="sessionInfo"></div>
|
|
</div>
|
|
|
|
<div class="card" data-requires-super hidden>
|
|
<div class="card-header">
|
|
<h3 class="card-title">Send WhatsApp message <span class="card-sub">super admin</span></h3>
|
|
</div>
|
|
<p class="card-note">
|
|
Sends a single message through the platform's WhatsApp provider. This leaves the system
|
|
and reaches a real person — you will be asked to confirm before it is sent.
|
|
</p>
|
|
<div class="form-group">
|
|
<input type="text" class="form-input" id="waReceiver" placeholder="Recipient phone, e.g. 962798583052" style="padding-left:1rem;">
|
|
</div>
|
|
<div class="form-group">
|
|
<textarea class="form-input decrypt-area" id="waMessage" rows="3" placeholder="Message text"></textarea>
|
|
</div>
|
|
<div class="api-base-row">
|
|
<button class="btn btn-secondary btn-sm" id="waSendBtn"><i class="ph ph-paper-plane-tilt"></i> <span>Send message</span></button>
|
|
<span class="stamp" id="waStatus"></span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card" data-requires-super hidden>
|
|
<div class="card-header">
|
|
<h3 class="card-title">Encryption tool <span class="card-sub">super admin</span></h3>
|
|
</div>
|
|
<p class="card-note">
|
|
Decrypts a value taken straight from the database (or encrypts one for a manual query).
|
|
The server authorises this by admin phone number against its own allow-list.
|
|
</p>
|
|
<div class="form-group">
|
|
<input type="text" class="form-input" id="decryptPhone" placeholder="Your admin phone (as registered on the server)" style="padding-left:1rem;">
|
|
</div>
|
|
<div class="form-group">
|
|
<textarea class="form-input decrypt-area" id="decryptInput" rows="3" placeholder="Paste the encrypted (or plain) value here"></textarea>
|
|
</div>
|
|
<div class="api-base-row">
|
|
<button class="btn btn-secondary btn-sm" id="decryptBtn" data-action="decrypt"><i class="ph ph-lock-open"></i> <span>Decrypt</span></button>
|
|
<button class="btn btn-secondary btn-sm" id="encryptBtn" data-action="encrypt"><i class="ph ph-lock"></i> Encrypt</button>
|
|
</div>
|
|
<pre class="diagnostics" id="decryptOutput" style="margin-top:1rem;">Result appears here.</pre>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3 class="card-title" data-i18n="API endpoint">API endpoint</h3>
|
|
</div>
|
|
<p class="card-note">
|
|
The mobile admin app points at a different host per country. If every panel is empty,
|
|
the console is most likely calling a backend that does not exist on this domain.
|
|
</p>
|
|
<div class="api-base-row">
|
|
<select class="select-input" id="apiBaseSelect"></select>
|
|
<input type="text" class="form-input" id="apiBaseCustom" placeholder="https://host/backend" hidden>
|
|
<button class="btn btn-primary btn-sm" id="saveApiBaseBtn"><i class="ph ph-check"></i> Apply</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3 class="card-title" data-i18n="Server diagnostics">Server diagnostics</h3>
|
|
<div style="display:flex; gap:0.5rem;">
|
|
<button class="btn btn-secondary btn-sm" id="copyDiagnosticsBtn"><i class="ph ph-copy"></i> Copy</button>
|
|
<button class="btn btn-secondary btn-sm" id="runDiagnosticsBtn"><i class="ph ph-play"></i> <span>Run diagnostics</span></button>
|
|
</div>
|
|
</div>
|
|
<pre class="diagnostics" id="diagnosticsOutput">Press “Run diagnostics” to call every endpoint and print the raw server reply.</pre>
|
|
</div>
|
|
</section>
|
|
|
|
</main>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Details Modal -->
|
|
<div class="modal-overlay" id="detailsModal">
|
|
<div class="modal-content">
|
|
<div id="modalBodyContent"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- OTP Verification Modal -->
|
|
<div class="modal-overlay" id="otpModal">
|
|
<div class="modal-content" style="max-width: 400px; text-align: center;">
|
|
<div class="otp-icon"><i class="ph ph-whatsapp-logo"></i></div>
|
|
<h3 style="color: #fff; margin-bottom: 0.5rem;" data-i18n="WhatsApp Verification">WhatsApp Verification</h3>
|
|
<p style="color: var(--text-muted); font-size: 0.88rem; margin-bottom: 1.5rem;" id="otpPhoneText">
|
|
A 3-digit verification code was sent to your WhatsApp.
|
|
</p>
|
|
|
|
<div style="display: flex; gap: 0.5rem; justify-content: center; margin-bottom: 1.5rem;">
|
|
<input type="text" maxlength="3" inputmode="numeric" id="otpInput" class="form-input otp-input" placeholder="123">
|
|
</div>
|
|
|
|
<button type="button" class="btn-primary" id="submitOtpBtn">
|
|
<i class="ph-bold ph-check"></i> <span data-i18n="Verify & Sign In">Verify & Sign In</span>
|
|
</button>
|
|
|
|
<button type="button" class="btn btn-secondary" onclick="closeOtpModal()" style="width: 100%; margin-top: 0.75rem; justify-content: center;">
|
|
Cancel
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="js/app.js?v=2026-07-25-8"></script>
|
|
</body>
|
|
</html>
|