1057 lines
50 KiB
HTML
1057 lines
50 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="ar" dir="rtl">
|
||
<head>
|
||
<meta charset="UTF-8"/>
|
||
<meta name="viewport" content="width=device-width,initial-scale=1.0"/>
|
||
<title>Siro — محاكاة تسجيل السائق (سوريا)</title>
|
||
<link href="https://fonts.googleapis.com/css2?family=Tajawal:wght@300;400;500;700;900&display=swap" rel="stylesheet"/>
|
||
<style>
|
||
:root{
|
||
--bg:#080c14;--s1:#0f1423;--s2:#161d30;--s3:#1e273d;
|
||
--bdr:#252f4a;--bdr2:#2d3a58;
|
||
--pri:#5b8dff;--pri2:#4070e0;
|
||
--amber:#f59e0b;--amber2:#d97706;
|
||
--green:#22c55e;--red:#ef4444;
|
||
--purple:#a855f7;--teal:#14b8a6;--cyan:#06b6d4;
|
||
--txt:#e2e8f0;--muted:#64748b;--muted2:#475569;
|
||
}
|
||
*{box-sizing:border-box;margin:0;padding:0;}
|
||
body{font-family:'Tajawal',sans-serif;background:var(--bg);color:var(--txt);min-height:100vh;}
|
||
|
||
/* ── Header ─────────────────────────────────────── */
|
||
.hdr{
|
||
background:linear-gradient(135deg,#0d1630 0%,#141e38 100%);
|
||
border-bottom:1px solid var(--bdr);
|
||
padding:14px 24px;display:flex;align-items:center;justify-content:space-between;
|
||
position:sticky;top:0;z-index:200;
|
||
}
|
||
.hdr-brand{display:flex;align-items:center;gap:12px;}
|
||
.hdr-logo{width:42px;height:42px;border-radius:12px;
|
||
background:linear-gradient(135deg,var(--pri),var(--purple));
|
||
display:flex;align-items:center;justify-content:center;font-size:18px;font-weight:900;}
|
||
.hdr h1{font-size:17px;font-weight:700;line-height:1.3;}
|
||
.hdr p{font-size:11px;color:var(--muted);margin-top:2px;}
|
||
.speed-row{display:flex;align-items:center;gap:10px;background:var(--s2);
|
||
border:1px solid var(--bdr);border-radius:10px;padding:7px 14px;}
|
||
.speed-row label{font-size:12px;color:var(--muted);}
|
||
.speed-row input{width:80px;accent-color:var(--pri);}
|
||
.speed-row span{font-size:13px;font-weight:700;color:var(--pri);min-width:30px;}
|
||
|
||
/* ── Layout ─────────────────────────────────────── */
|
||
.main{display:grid;grid-template-columns:300px 1fr 320px;height:calc(100vh - 67px);}
|
||
|
||
/* ── Left panel ─────────────────────────────────── */
|
||
.lpanel{background:var(--s1);border-left:1px solid var(--bdr);
|
||
overflow-y:auto;padding:18px 16px;display:flex;flex-direction:column;gap:6px;}
|
||
.panel-title{font-size:11px;font-weight:700;text-transform:uppercase;
|
||
letter-spacing:1px;color:var(--muted);margin-bottom:8px;}
|
||
|
||
/* Phase step */
|
||
.step{display:flex;align-items:flex-start;gap:10px;padding:10px 12px;
|
||
border-radius:10px;border:1.5px solid transparent;
|
||
background:var(--s2);cursor:pointer;transition:all .2s;}
|
||
.step:hover{border-color:var(--pri);}
|
||
.step.active{border-color:var(--pri);background:rgba(91,141,255,.12);}
|
||
.step.done{opacity:.5;}
|
||
.step-num{width:28px;height:28px;border-radius:50%;display:flex;align-items:center;
|
||
justify-content:center;font-size:12px;font-weight:700;flex-shrink:0;}
|
||
.step-info{flex:1;min-width:0;}
|
||
.step-name{font-size:13px;font-weight:600;line-height:1.3;}
|
||
.step-sub{font-size:11px;color:var(--muted);margin-top:2px;line-height:1.4;}
|
||
.step-badge{font-size:10px;font-weight:700;padding:2px 7px;border-radius:12px;flex-shrink:0;}
|
||
|
||
/* ── Center panel ────────────────────────────────── */
|
||
.center{background:var(--s1);overflow-y:auto;position:relative;padding:20px;}
|
||
|
||
/* Phone mockup */
|
||
.phone-wrap{display:flex;justify-content:center;padding:10px 0 20px;}
|
||
.phone{
|
||
width:320px;min-height:580px;
|
||
background:var(--s3);
|
||
border:2px solid var(--bdr2);border-radius:38px;
|
||
box-shadow:0 24px 60px rgba(0,0,0,.5),
|
||
inset 0 1px 0 rgba(255,255,255,.06);
|
||
overflow:hidden;position:relative;display:flex;flex-direction:column;
|
||
}
|
||
.phone-bar{height:36px;background:rgba(0,0,0,.3);
|
||
display:flex;align-items:center;justify-content:center;gap:8px;}
|
||
.phone-dot{width:10px;height:10px;border-radius:50%;background:rgba(255,255,255,.1);}
|
||
.phone-notch{width:90px;height:18px;background:#000;border-radius:0 0 12px 12px;}
|
||
|
||
.phone-screen{flex:1;overflow:hidden;position:relative;}
|
||
.screen{
|
||
display:none;flex-direction:column;height:100%;
|
||
animation:screenIn .35s ease;
|
||
}
|
||
.screen.active{display:flex;}
|
||
@keyframes screenIn{from{opacity:0;transform:translateX(-20px);}to{opacity:1;transform:none;}}
|
||
|
||
/* App topbar */
|
||
.app-bar{
|
||
background:linear-gradient(135deg,var(--pri2),var(--purple));
|
||
padding:16px 18px 12px;
|
||
}
|
||
.app-bar h2{font-size:16px;font-weight:700;}
|
||
.app-bar p{font-size:11px;opacity:.8;margin-top:2px;}
|
||
|
||
/* Screen content */
|
||
.screen-body{flex:1;overflow-y:auto;padding:16px;}
|
||
.screen-body::-webkit-scrollbar{width:3px;}
|
||
.screen-body::-webkit-scrollbar-thumb{background:var(--bdr2);}
|
||
|
||
/* Form fields simulation */
|
||
.field{margin-bottom:12px;}
|
||
.field label{font-size:11px;color:var(--muted);margin-bottom:4px;display:block;}
|
||
.field-input{
|
||
background:var(--s2);border:1px solid var(--bdr);border-radius:8px;
|
||
padding:10px 12px;font-size:13px;color:var(--txt);font-family:inherit;
|
||
width:100%;transition:border-color .2s;
|
||
}
|
||
.field-input:focus{outline:none;border-color:var(--pri);}
|
||
.field-input.filled{border-color:var(--green);color:var(--green);}
|
||
.field-input.error{border-color:var(--red);}
|
||
|
||
/* OTP circles */
|
||
.otp-row{display:flex;gap:10px;justify-content:center;padding:20px 0;}
|
||
.otp-box{
|
||
width:48px;height:56px;background:var(--s2);border:2px solid var(--bdr);
|
||
border-radius:12px;display:flex;align-items:center;justify-content:center;
|
||
font-size:22px;font-weight:700;color:var(--txt);transition:all .3s;
|
||
}
|
||
.otp-box.filled{border-color:var(--pri);background:rgba(91,141,255,.15);color:var(--pri);}
|
||
.otp-box.ok{border-color:var(--green);background:rgba(34,197,94,.12);color:var(--green);}
|
||
|
||
/* Doc upload cards */
|
||
.doc-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:14px;}
|
||
.doc-card{
|
||
background:var(--s2);border:1.5px dashed var(--bdr);
|
||
border-radius:10px;padding:12px 8px;
|
||
display:flex;flex-direction:column;align-items:center;gap:6px;
|
||
font-size:11px;color:var(--muted);text-align:center;cursor:pointer;
|
||
transition:all .25s;min-height:80px;justify-content:center;
|
||
}
|
||
.doc-card.uploading{border-color:var(--amber);color:var(--amber);}
|
||
.doc-card.done{border-color:var(--green);background:rgba(34,197,94,.08);color:var(--green);}
|
||
.doc-card.error{border-color:var(--red);color:var(--red);}
|
||
.doc-icon{font-size:22px;}
|
||
|
||
/* AI badge */
|
||
.ai-badge{
|
||
background:linear-gradient(135deg,rgba(168,85,247,.25),rgba(91,141,255,.25));
|
||
border:1px solid rgba(168,85,247,.5);border-radius:8px;
|
||
padding:8px 12px;font-size:11px;margin-bottom:12px;
|
||
display:flex;align-items:center;gap:8px;
|
||
}
|
||
.ai-spin{width:14px;height:14px;border-radius:50%;
|
||
border:2px solid rgba(168,85,247,.3);border-top-color:var(--purple);
|
||
animation:spin .6s linear infinite;}
|
||
@keyframes spin{to{transform:rotate(360deg);}}
|
||
|
||
/* Submit btn */
|
||
.btn-submit{
|
||
width:100%;padding:13px;border-radius:12px;border:none;
|
||
background:linear-gradient(135deg,var(--pri),var(--purple));
|
||
color:#fff;font-family:inherit;font-size:14px;font-weight:700;
|
||
cursor:pointer;transition:all .2s;
|
||
}
|
||
.btn-submit:hover{transform:translateY(-1px);box-shadow:0 8px 20px rgba(91,141,255,.3);}
|
||
.btn-submit:disabled{opacity:.5;cursor:not-allowed;transform:none;}
|
||
|
||
/* Progress bar in phone */
|
||
.steps-bar{display:flex;gap:4px;padding:10px 16px;}
|
||
.bar-seg{height:4px;border-radius:2px;flex:1;background:var(--bdr);transition:background .4s;}
|
||
.bar-seg.done{background:var(--green);}
|
||
.bar-seg.active{background:var(--pri);}
|
||
|
||
/* Status card */
|
||
.status-card{
|
||
background:var(--s3);border:1px solid var(--bdr2);border-radius:12px;
|
||
padding:14px;margin-bottom:12px;display:flex;gap:12px;align-items:center;
|
||
}
|
||
.status-icon{font-size:28px;}
|
||
.status-info h3{font-size:13px;font-weight:700;}
|
||
.status-info p{font-size:11px;color:var(--muted);margin-top:3px;}
|
||
|
||
/* Controls */
|
||
.ctrl-row{display:flex;gap:10px;justify-content:center;margin-top:16px;}
|
||
.btn{padding:9px 18px;border-radius:10px;border:none;
|
||
font-family:inherit;font-size:13px;font-weight:600;cursor:pointer;transition:all .2s;}
|
||
.btn:hover{transform:translateY(-1px);}
|
||
.btn:disabled{opacity:.45;cursor:not-allowed;transform:none;}
|
||
.btn-pri{background:var(--pri);color:#fff;}
|
||
.btn-amb{background:var(--amber);color:#000;}
|
||
.btn-red{background:var(--red);color:#fff;}
|
||
.btn-ghost{background:var(--s3);color:var(--txt);border:1px solid var(--bdr);}
|
||
|
||
/* ── Right panel ─────────────────────────────────── */
|
||
.rpanel{background:var(--s1);border-right:1px solid var(--bdr);
|
||
overflow-y:auto;padding:18px 16px;}
|
||
|
||
/* Stats grid */
|
||
.stats{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:14px;}
|
||
.stat{background:var(--s2);border:1px solid var(--bdr);border-radius:10px;padding:10px 12px;}
|
||
.stat-lbl{font-size:10px;color:var(--muted);}
|
||
.stat-val{font-size:16px;font-weight:700;margin-top:2px;}
|
||
|
||
/* Flow diagram */
|
||
.flow{display:flex;flex-direction:column;gap:4px;margin-bottom:14px;}
|
||
.flow-node{
|
||
background:var(--s2);border:1px solid var(--bdr);border-radius:8px;
|
||
padding:8px 12px;font-size:11px;display:flex;align-items:center;gap:8px;
|
||
transition:all .25s;
|
||
}
|
||
.flow-node.active{border-color:var(--pri);background:rgba(91,141,255,.1);}
|
||
.flow-node.done{border-color:var(--green);opacity:.7;}
|
||
.flow-node.err{border-color:var(--red);}
|
||
.flow-dot{width:8px;height:8px;border-radius:50%;background:var(--bdr);flex-shrink:0;}
|
||
.flow-node.active .flow-dot{background:var(--pri);}
|
||
.flow-node.done .flow-dot{background:var(--green);}
|
||
.flow-node.err .flow-dot{background:var(--red);}
|
||
.flow-arrow{text-align:center;font-size:10px;color:var(--muted);}
|
||
|
||
/* API log */
|
||
.api-log{display:flex;flex-direction:column;gap:5px;}
|
||
.api-entry{
|
||
background:var(--s2);border-radius:7px;padding:8px 10px;
|
||
font-size:11px;line-height:1.5;border-right:3px solid var(--bdr);
|
||
animation:fadeIn .3s ease;
|
||
}
|
||
.api-entry.req{border-color:var(--cyan);}
|
||
.api-entry.res{border-color:var(--green);}
|
||
.api-entry.err{border-color:var(--red);}
|
||
.api-entry.ai{border-color:var(--purple);}
|
||
.api-entry.info{border-color:var(--amber);}
|
||
.api-time{color:var(--muted);font-size:9px;margin-bottom:2px;}
|
||
@keyframes fadeIn{from{opacity:0;transform:translateX(6px);}to{opacity:1;transform:none;}}
|
||
|
||
/* DB table preview */
|
||
.db-preview{background:var(--s2);border:1px solid var(--bdr);border-radius:10px;
|
||
overflow:hidden;margin-bottom:12px;}
|
||
.db-header{background:var(--s3);padding:8px 12px;font-size:11px;font-weight:700;
|
||
color:var(--cyan);border-bottom:1px solid var(--bdr);display:flex;align-items:center;gap:6px;}
|
||
.db-row{padding:7px 12px;font-size:11px;display:flex;justify-content:space-between;
|
||
border-bottom:1px solid rgba(255,255,255,.03);}
|
||
.db-row:last-child{border-bottom:none;}
|
||
.db-key{color:var(--muted);}
|
||
.db-val{color:var(--green);max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
||
.db-val.enc{color:var(--amber);}
|
||
.db-val.pending{color:var(--amber);}
|
||
|
||
/* Toast */
|
||
.toast-wrap{position:fixed;bottom:24px;left:50%;transform:translateX(-50%);
|
||
z-index:999;display:flex;flex-direction:column;gap:8px;align-items:center;pointer-events:none;}
|
||
.toast{background:rgba(22,29,48,.97);border:1px solid var(--bdr2);
|
||
border-radius:12px;padding:10px 20px;font-size:13px;font-weight:500;
|
||
backdrop-filter:blur(12px);white-space:nowrap;pointer-events:auto;}
|
||
|
||
/* Divider */
|
||
.div{height:1px;background:var(--bdr);margin:12px 0;}
|
||
|
||
/* Country badge */
|
||
.country-badge{display:inline-flex;align-items:center;gap:6px;
|
||
background:rgba(239,68,68,.15);border:1px solid rgba(239,68,68,.3);
|
||
border-radius:20px;padding:3px 10px;font-size:11px;font-weight:700;color:var(--red);}
|
||
|
||
/* Security badge */
|
||
.sec-badges{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:12px;}
|
||
.sec-badge{font-size:10px;font-weight:700;padding:3px 8px;border-radius:8px;}
|
||
.sec-badge.jwt{background:rgba(91,141,255,.15);color:var(--pri);}
|
||
.sec-badge.hmac{background:rgba(245,158,11,.15);color:var(--amber);}
|
||
.sec-badge.enc{background:rgba(20,184,166,.15);color:var(--teal);}
|
||
.sec-badge.rate{background:rgba(239,68,68,.15);color:var(--red);}
|
||
.sec-badge.ai{background:rgba(168,85,247,.15);color:var(--purple);}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<!-- Header -->
|
||
<header class="hdr">
|
||
<div class="hdr-brand">
|
||
<div class="hdr-logo">S</div>
|
||
<div>
|
||
<h1>Siro Driver — محاكاة تسجيل السائق <span class="country-badge">🇸🇾 سوريا</span></h1>
|
||
<p>OTP • Document Upload • AI Gemini • DB Encryption • CarRegistration + driver tables</p>
|
||
</div>
|
||
</div>
|
||
<div class="speed-row">
|
||
<label>سرعة</label>
|
||
<input type="range" id="spdSlider" min="0.5" max="5" step="0.5" value="1.5" oninput="setSpeed(this.value)"/>
|
||
<span id="spdLbl">1.5×</span>
|
||
</div>
|
||
</header>
|
||
|
||
<div class="main">
|
||
|
||
<!-- ── LEFT PANEL : Steps ── -->
|
||
<div class="lpanel">
|
||
<div class="panel-title">خطوات التسجيل</div>
|
||
<div id="stepList"></div>
|
||
</div>
|
||
|
||
<!-- ── CENTER : Phone mockup ── -->
|
||
<div class="center">
|
||
<div class="phone-wrap">
|
||
<div class="phone">
|
||
<div class="phone-bar">
|
||
<div class="phone-dot"></div>
|
||
<div class="phone-notch"></div>
|
||
<div class="phone-dot"></div>
|
||
</div>
|
||
|
||
<!-- Steps progress bar -->
|
||
<div class="steps-bar" id="stepsBar"></div>
|
||
|
||
<div class="phone-screen">
|
||
|
||
<!-- SCREEN 0: Welcome -->
|
||
<div class="screen" id="sc0">
|
||
<div class="app-bar">
|
||
<h2>مرحباً بك في سيرو 🚗</h2>
|
||
<p>ابدأ رحلتك معنا كسائق محترف</p>
|
||
</div>
|
||
<div class="screen-body">
|
||
<div style="text-align:center;padding:20px 0 10px;">
|
||
<div style="font-size:52px;margin-bottom:12px;">🇸🇾</div>
|
||
<div style="font-size:15px;font-weight:700;margin-bottom:6px;">تسجيل سائق — سوريا</div>
|
||
<div style="font-size:12px;color:var(--muted);line-height:1.6">
|
||
سيتم التحقق من رقم هاتفك أولاً<br/>ثم رفع وثائقك للمراجعة
|
||
</div>
|
||
</div>
|
||
<div class="div"></div>
|
||
<div class="sec-badges">
|
||
<span class="sec-badge jwt">JWT Bearer</span>
|
||
<span class="sec-badge hmac">HMAC Auth</span>
|
||
<span class="sec-badge enc">AES Encrypt</span>
|
||
<span class="sec-badge rate">Rate Limit</span>
|
||
<span class="sec-badge ai">Gemini AI</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- SCREEN 1: Phone entry -->
|
||
<div class="screen" id="sc1">
|
||
<div class="app-bar">
|
||
<h2>رقم الهاتف</h2>
|
||
<p>أدخل رقمك السوري للتحقق</p>
|
||
</div>
|
||
<div class="screen-body">
|
||
<div class="field">
|
||
<label>رقم الجوال</label>
|
||
<input class="field-input" id="phoneInput" value="963944xxxxxx" readonly/>
|
||
</div>
|
||
<div style="font-size:11px;color:var(--muted);margin-bottom:14px;line-height:1.6;">
|
||
📌 <b>Phone Formatting Logic:</b><br/>
|
||
<code style="color:var(--cyan);">09xxxxxxx</code> → <code style="color:var(--green);">9639xxxxxxx</code><br/>
|
||
<code style="color:var(--cyan);">00963xxxxxxx</code> → <code style="color:var(--green);">963xxxxxxx</code>
|
||
</div>
|
||
<button class="btn-submit" onclick="nextStep()">إرسال OTP عبر WhatsApp ▶</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- SCREEN 2: OTP -->
|
||
<div class="screen" id="sc2">
|
||
<div class="app-bar">
|
||
<h2>رمز التحقق</h2>
|
||
<p>تم الإرسال عبر Intaleq WhatsApp</p>
|
||
</div>
|
||
<div class="screen-body">
|
||
<div style="text-align:center;font-size:12px;color:var(--muted);margin-bottom:4px;">
|
||
أدخل الرمز المرسل لـ <b style="color:var(--txt);">+963944xxx</b>
|
||
</div>
|
||
<div class="otp-row" id="otpRow">
|
||
<div class="otp-box" id="ob0">_</div>
|
||
<div class="otp-box" id="ob1">_</div>
|
||
<div class="otp-box" id="ob2">_</div>
|
||
</div>
|
||
<div style="text-align:center;font-size:11px;color:var(--muted);margin-bottom:14px;">
|
||
⏱️ عداد إعادة الإرسال: <b id="otpTimer" style="color:var(--amber);">120</b> ثانية
|
||
</div>
|
||
<div style="background:var(--s2);border:1px solid var(--bdr);border-radius:8px;
|
||
padding:10px 12px;font-size:11px;line-height:1.7;margin-bottom:14px;">
|
||
<div><b style="color:var(--cyan);">POST</b> /auth/otp/request.php</div>
|
||
<div>📱 country: Syria → Intaleq WhatsApp</div>
|
||
<div>🔁 Failover: Nabeh JWT → SMS</div>
|
||
<div>⏱️ صلاحية: 5 دقائق</div>
|
||
<div>🔒 OTP مشفر في DB (AES-GCM)</div>
|
||
</div>
|
||
<button class="btn-submit" onclick="nextStep()">تحقق من الرمز ▶</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- SCREEN 3: Driver Info -->
|
||
<div class="screen" id="sc3">
|
||
<div class="app-bar">
|
||
<h2>المعلومات الشخصية</h2>
|
||
<p>الخطوة 1 من 3</p>
|
||
</div>
|
||
<div class="screen-body">
|
||
<div class="field"><label>الاسم الأول</label>
|
||
<input class="field-input filled" value="محمد" readonly/></div>
|
||
<div class="field"><label>اسم العائلة</label>
|
||
<input class="field-input filled" value="الأحمد" readonly/></div>
|
||
<div class="field"><label>رقم الهوية الوطنية</label>
|
||
<input class="field-input filled" value="012345678" readonly/></div>
|
||
<div class="field"><label>تاريخ الميلاد</label>
|
||
<input class="field-input filled" value="1990" readonly/></div>
|
||
<div class="field"><label>انتهاء رخصة القيادة</label>
|
||
<input class="field-input filled" value="2026-12-31" readonly/></div>
|
||
<button class="btn-submit" onclick="nextStep()">التالي: معلومات المركبة ▶</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- SCREEN 4: Car Info -->
|
||
<div class="screen" id="sc4">
|
||
<div class="app-bar">
|
||
<h2>معلومات المركبة</h2>
|
||
<p>الخطوة 2 من 3</p>
|
||
</div>
|
||
<div class="screen-body">
|
||
<div class="field"><label>لوحة السيارة</label>
|
||
<input class="field-input filled" value="155186 درعا" readonly/></div>
|
||
<div class="field"><label>الشركة المصنعة</label>
|
||
<input class="field-input filled" value="Hyundai" readonly/></div>
|
||
<div class="field"><label>الموديل</label>
|
||
<input class="field-input filled" value="Elantra" readonly/></div>
|
||
<div class="field"><label>سنة الصنع</label>
|
||
<input class="field-input filled" value="2019" readonly/></div>
|
||
<div class="field"><label>نوع الوقود</label>
|
||
<input class="field-input filled" value="Petrol (fuel_type_id: 1)" readonly/></div>
|
||
<div class="field"><label>تصنيف المركبة</label>
|
||
<input class="field-input filled" value="سيارة (vehicle_category_id: 1)" readonly/></div>
|
||
<button class="btn-submit" onclick="nextStep()">التالي: رفع الوثائق ▶</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- SCREEN 5: Doc Upload -->
|
||
<div class="screen" id="sc5">
|
||
<div class="app-bar">
|
||
<h2>الوثائق المطلوبة</h2>
|
||
<p>الخطوة 3 من 3 — سوريا</p>
|
||
</div>
|
||
<div class="screen-body">
|
||
<div class="doc-grid" id="docGrid">
|
||
<div class="doc-card" id="doc_id_front">
|
||
<div class="doc-icon">🪪</div>
|
||
<div>هوية — وجه</div>
|
||
</div>
|
||
<div class="doc-card" id="doc_id_back">
|
||
<div class="doc-icon">🪪</div>
|
||
<div>هوية — خلف</div>
|
||
</div>
|
||
<div class="doc-card" id="doc_driver_license">
|
||
<div class="doc-icon">📋</div>
|
||
<div>رخصة القيادة — وجه</div>
|
||
</div>
|
||
<div class="doc-card" id="doc_driver_license_back">
|
||
<div class="doc-icon">📋</div>
|
||
<div>رخصة القيادة — خلف <br/><small style="color:var(--amber)">🇸🇾 إلزامي</small></div>
|
||
</div>
|
||
<div class="doc-card" id="doc_profile_picture">
|
||
<div class="doc-icon">🤳</div>
|
||
<div>صورة شخصية</div>
|
||
</div>
|
||
<div class="doc-card" id="doc_criminal_record">
|
||
<div class="doc-icon">📄</div>
|
||
<div>لا حكم عليه</div>
|
||
</div>
|
||
<div class="doc-card" id="doc_car_license_front">
|
||
<div class="doc-icon">🚗</div>
|
||
<div>ترخيص المركبة — وجه</div>
|
||
</div>
|
||
<div class="doc-card" id="doc_car_license_back">
|
||
<div class="doc-icon">🚗</div>
|
||
<div>ترخيص المركبة — خلف</div>
|
||
</div>
|
||
</div>
|
||
<button class="btn-submit" id="docBtn" onclick="nextStep()" disabled>في انتظار رفع الوثائق...</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- SCREEN 6: AI Processing -->
|
||
<div class="screen" id="sc6">
|
||
<div class="app-bar" style="background:linear-gradient(135deg,#6b21a8,#4338ca);">
|
||
<h2>🤖 معالجة الذكاء الاصطناعي</h2>
|
||
<p>Gemini Flash — تحليل الوثائق</p>
|
||
</div>
|
||
<div class="screen-body">
|
||
<div class="ai-badge">
|
||
<div class="ai-spin" id="aiSpin"></div>
|
||
<div>Gemini يحلل الوثائق ويطابق الوجه...</div>
|
||
</div>
|
||
<div id="aiSteps" style="display:flex;flex-direction:column;gap:6px;font-size:11px;"></div>
|
||
<div class="div"></div>
|
||
<div id="aiResult" style="display:none;background:rgba(34,197,94,.1);border:1px solid var(--green);
|
||
border-radius:8px;padding:10px 12px;font-size:11px;line-height:1.8;"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- SCREEN 7: Pending Review -->
|
||
<div class="screen" id="sc7">
|
||
<div class="app-bar" style="background:linear-gradient(135deg,#92400e,#b45309);">
|
||
<h2>قيد المراجعة ⏳</h2>
|
||
<p>خدمة العملاء تراجع وثائقك</p>
|
||
</div>
|
||
<div class="screen-body">
|
||
<div class="status-card">
|
||
<div class="status-icon">⏳</div>
|
||
<div class="status-info">
|
||
<h3>الحساب قيد المراجعة</h3>
|
||
<p>status = "yet" في جدول driver</p>
|
||
</div>
|
||
</div>
|
||
<div style="font-size:11px;color:var(--muted);line-height:1.8;margin-bottom:14px;">
|
||
✅ تم رفع وثائقك بنجاح<br/>
|
||
📬 تم إشعار فريق خدمة العملاء<br/>
|
||
🔔 FCM إلى موضوع "service"<br/>
|
||
⏱️ المراجعة تستغرق 24-48 ساعة
|
||
</div>
|
||
<div style="background:var(--s2);border:1px solid var(--bdr);border-radius:8px;
|
||
padding:10px 12px;font-size:11px;line-height:1.8;">
|
||
<div>📊 <b>driver.status:</b> <span style="color:var(--amber)">yet</span></div>
|
||
<div>📊 <b>CarRegistration.status:</b> <span style="color:var(--amber)">yet</span></div>
|
||
<div>📊 <b>driver_documents:</b> <span style="color:var(--green)">8 records ✓</span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- SCREEN 8: Activated -->
|
||
<div class="screen" id="sc8">
|
||
<div class="app-bar" style="background:linear-gradient(135deg,#064e3b,#065f46);">
|
||
<h2>🎉 تم التفعيل!</h2>
|
||
<p>خدمة العملاء فعّلت حسابك</p>
|
||
</div>
|
||
<div class="screen-body">
|
||
<div style="text-align:center;padding:20px 0;">
|
||
<div style="font-size:52px;margin-bottom:12px;">✅</div>
|
||
<div style="font-size:15px;font-weight:700;margin-bottom:6px;color:var(--green);">
|
||
مرحباً محمد الأحمد!
|
||
</div>
|
||
<div style="font-size:12px;color:var(--muted);">حسابك مفعّل وجاهز للعمل</div>
|
||
</div>
|
||
<div style="background:rgba(34,197,94,.1);border:1px solid var(--green);
|
||
border-radius:8px;padding:10px 12px;font-size:11px;line-height:1.8;margin-bottom:12px;">
|
||
<div>✅ <b>driver.status:</b> <span style="color:var(--green)">active</span></div>
|
||
<div>✅ <b>CarRegistration.status:</b> <span style="color:var(--green)">active</span></div>
|
||
<div>🔔 FCM إشعار للسائق: "تم تفعيل حسابك"</div>
|
||
<div>🔑 JWT Token صادر + تخزين في driverToken</div>
|
||
</div>
|
||
<button class="btn-submit" style="background:linear-gradient(135deg,var(--green),#16a34a)">
|
||
🚗 ابدأ العمل الآن!
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
</div><!-- /phone-screen -->
|
||
</div><!-- /phone -->
|
||
</div><!-- /phone-wrap -->
|
||
|
||
<!-- Controls -->
|
||
<div class="ctrl-row">
|
||
<button class="btn btn-pri" id="btnNext" onclick="nextStep()">▶ التالي</button>
|
||
<button class="btn btn-amb" id="btnAuto" onclick="toggleAuto()">⚡ تلقائي</button>
|
||
<button class="btn btn-ghost" onclick="resetSim()">↺ إعادة</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── RIGHT PANEL : Logs & DB ── -->
|
||
<div class="rpanel">
|
||
<div class="panel-title">تحليل تقني</div>
|
||
|
||
<div class="stats">
|
||
<div class="stat"><div class="stat-lbl">المرحلة</div><div class="stat-val" id="stPhase">0/8</div></div>
|
||
<div class="stat"><div class="stat-lbl">OTP Status</div><div class="stat-val" id="stOtp" style="color:var(--muted)">—</div></div>
|
||
<div class="stat"><div class="stat-lbl">وثائق مرفوعة</div><div class="stat-val" id="stDocs">0/8</div></div>
|
||
<div class="stat"><div class="stat-lbl">AI Gemini</div><div class="stat-val" id="stAI" style="color:var(--muted)">—</div></div>
|
||
</div>
|
||
|
||
<div class="div"></div>
|
||
<div class="panel-title">تدفق API</div>
|
||
<div class="flow" id="flowDiagram"></div>
|
||
|
||
<div class="div"></div>
|
||
<div class="panel-title">سجل الطلبات</div>
|
||
<div class="api-log" id="apiLog"></div>
|
||
|
||
<div class="div"></div>
|
||
<div class="panel-title">قاعدة البيانات — schema_primary</div>
|
||
<div id="dbPreviews"></div>
|
||
</div>
|
||
|
||
</div><!-- /main -->
|
||
|
||
<div class="toast-wrap" id="toastWrap"></div>
|
||
|
||
<script>
|
||
// ═══════════════════════════════════════════════════
|
||
// DATA
|
||
// ═══════════════════════════════════════════════════
|
||
let simSpeed = 1.5;
|
||
let currentStep = -1;
|
||
let isAuto = false;
|
||
let autoTimer = null;
|
||
let otpTimerRef = null;
|
||
let docsUploaded = 0;
|
||
|
||
const STEPS = [
|
||
{ name:'شاشة الترحيب', sub:'onboarding_controller', color:'#64748b', icon:'🏠', badge:'START' },
|
||
{ name:'إدخال رقم الهاتف', sub:'RegisterCaptainController', color:'#06b6d4', icon:'📱', badge:'PHONE' },
|
||
{ name:'التحقق بـ OTP', sub:'OtpVerificationController', color:'#a855f7', icon:'🔐', badge:'OTP' },
|
||
{ name:'المعلومات الشخصية', sub:'RegistrationController — Page 1', color:'#5b8dff', icon:'👤', badge:'STEP1' },
|
||
{ name:'معلومات المركبة', sub:'RegistrationController — Page 2', color:'#5b8dff', icon:'🚗', badge:'STEP2' },
|
||
{ name:'رفع الوثائق', sub:'uploadToSyria() × 8', color:'#f59e0b', icon:'📂', badge:'DOCS' },
|
||
{ name:'AI Gemini تحليل', sub:'Gemini Flash — مطابقة وجه + OCR', color:'#a855f7', icon:'🤖', badge:'AI' },
|
||
{ name:'قيد المراجعة', sub:'خدمة العملاء — status: yet', color:'#f59e0b', icon:'⏳', badge:'PENDING' },
|
||
{ name:'تم التفعيل', sub:'Admin Panel — status: active', color:'#22c55e', icon:'✅', badge:'ACTIVE' },
|
||
];
|
||
|
||
const FLOW_NODES = [
|
||
{ id:'otp_req', label:'POST /auth/otp/request.php', detail:'Intaleq WhatsApp' },
|
||
{ id:'otp_verify', label:'POST /auth/otp/verify.php', detail:'AES-GCM encrypted DB' },
|
||
{ id:'doc_upload', label:'POST /uploadSyrianDocs.php × 8', detail:'Signed URL — private_uploads' },
|
||
{ id:'ai_process', label:'Gemini Flash — Vision AI', detail:'Face match + OCR + JSON' },
|
||
{ id:'register', label:'POST /register_driver_and_car.php', detail:'Transaction: driver + CarRegistration' },
|
||
{ id:'notify', label:'FCM → topic:service', detail:'Admin panel notification' },
|
||
{ id:'activate', label:'Admin → status:active', detail:'JWT issued + driverToken stored' },
|
||
];
|
||
|
||
const DOCS = [
|
||
'id_front','id_back','driver_license','driver_license_back',
|
||
'profile_picture','criminal_record','car_license_front','car_license_back'
|
||
];
|
||
|
||
// ═══════════════════════════════════════════════════
|
||
// INIT
|
||
// ═══════════════════════════════════════════════════
|
||
function init(){
|
||
renderStepList();
|
||
renderFlowDiagram();
|
||
renderStepsBar();
|
||
showScreen(0);
|
||
setTimeout(nextStep, 800);
|
||
}
|
||
|
||
function renderStepList(){
|
||
const el = document.getElementById('stepList');
|
||
el.innerHTML = '';
|
||
STEPS.forEach((s,i)=>{
|
||
const d = document.createElement('div');
|
||
d.className = 'step' + (i===currentStep?' active':(i<currentStep?' done':''));
|
||
d.id = `ste_${i}`;
|
||
d.onclick = ()=>jumpTo(i);
|
||
d.innerHTML = `
|
||
<div class="step-num" style="background:${s.color}22;color:${s.color}">${s.icon}</div>
|
||
<div class="step-info">
|
||
<div class="step-name">${s.name}</div>
|
||
<div class="step-sub">${s.sub}</div>
|
||
</div>
|
||
<div class="step-badge" style="background:${s.color}22;color:${s.color}">${s.badge}</div>`;
|
||
el.appendChild(d);
|
||
});
|
||
}
|
||
|
||
function renderFlowDiagram(){
|
||
const el = document.getElementById('flowDiagram');
|
||
el.innerHTML = '';
|
||
FLOW_NODES.forEach((n,i)=>{
|
||
const d = document.createElement('div');
|
||
d.className='flow-node';d.id=`fn_${n.id}`;
|
||
d.innerHTML = `<div class="flow-dot"></div><div><div style="font-weight:600">${n.label}</div><div style="color:var(--muted);font-size:10px">${n.detail}</div></div>`;
|
||
el.appendChild(d);
|
||
if(i<FLOW_NODES.length-1){
|
||
const arr=document.createElement('div');arr.className='flow-arrow';arr.textContent='↓';
|
||
el.appendChild(arr);
|
||
}
|
||
});
|
||
}
|
||
|
||
function renderStepsBar(){
|
||
const el = document.getElementById('stepsBar');
|
||
el.innerHTML='';
|
||
STEPS.forEach((_,i)=>{
|
||
const d=document.createElement('div');
|
||
d.className='bar-seg'+(i===currentStep?' active':(i<currentStep?' done':''));
|
||
d.id=`bar_${i}`;
|
||
el.appendChild(d);
|
||
});
|
||
}
|
||
|
||
function showScreen(n){
|
||
document.querySelectorAll('.screen').forEach(s=>s.classList.remove('active'));
|
||
const sc = document.getElementById(`sc${n}`);
|
||
if(sc) sc.classList.add('active');
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════════
|
||
// STEP ACTIONS
|
||
// ═══════════════════════════════════════════════════
|
||
const stepActions = [
|
||
enterWelcome,
|
||
enterPhone,
|
||
enterOTP,
|
||
enterDriverInfo,
|
||
enterCarInfo,
|
||
enterDocUpload,
|
||
enterAIProcess,
|
||
enterPending,
|
||
enterActivated,
|
||
];
|
||
|
||
function nextStep(){
|
||
if(currentStep >= STEPS.length-1){ showToast('✅ اكتملت المحاكاة!','#22c55e'); return; }
|
||
currentStep++;
|
||
showScreen(currentStep);
|
||
renderStepList();
|
||
renderStepsBar();
|
||
document.getElementById('stPhase').textContent = `${currentStep+1}/${STEPS.length}`;
|
||
if(stepActions[currentStep]) stepActions[currentStep]();
|
||
document.getElementById('btnNext').disabled = currentStep >= STEPS.length-1;
|
||
}
|
||
|
||
function jumpTo(i){
|
||
if(isAuto) toggleAuto();
|
||
// reset docs if jumping back
|
||
if(i < 5){ docsUploaded=0; DOCS.forEach(d=>{ const el=document.getElementById('doc_'+d); if(el){el.className='doc-card';el.innerHTML=`<div class="doc-icon">${docIcon(d)}</div><div>${docLabel(d)}</div>`;}}); }
|
||
currentStep = i-1;
|
||
nextStep();
|
||
}
|
||
|
||
// ─── Step 0: Welcome ───────────────────────────────
|
||
function enterWelcome(){
|
||
apiLog('🏠 تشغيل onboarding_controller.dart — فحص الدولة: Syria','info');
|
||
apiLog('📦 getStorage → countryCode: "Syria"','info');
|
||
showToast('🏠 مرحباً — بيئة سوريا محددة','#64748b');
|
||
}
|
||
|
||
// ─── Step 1: Phone ─────────────────────────────────
|
||
function enterPhone(){
|
||
setFlow('otp_req','active');
|
||
const input = document.getElementById('phoneInput');
|
||
const rawPhone = '0944123456';
|
||
const finalPhone = '963944123456';
|
||
let i = 0;
|
||
input.value = '';
|
||
input.classList.remove('filled');
|
||
const interval = setInterval(()=>{
|
||
if(i <= rawPhone.length){ input.value = rawPhone.substring(0,i); i++; }
|
||
else{ clearInterval(interval);
|
||
setTimeout(()=>{
|
||
input.value = '→ ' + finalPhone;
|
||
input.classList.add('filled');
|
||
apiLog('📱 Phone formatting: 09xx → 963xx (Syria logic)','info');
|
||
apiLog('→ strpos("09",0)===0 → "963" + substr(phone,1)','req');
|
||
}, 300/simSpeed);
|
||
}
|
||
}, 80/simSpeed);
|
||
showToast('📱 إدخال رقم الهاتف السوري','#06b6d4');
|
||
}
|
||
|
||
// ─── Step 2: OTP ────────────────────────────────────
|
||
function enterOTP(){
|
||
setFlow('otp_verify','done');
|
||
setFlow('otp_verify','active');
|
||
document.getElementById('stOtp').textContent='إرسال...';
|
||
document.getElementById('stOtp').style.color='var(--amber)';
|
||
|
||
// Countdown
|
||
let cd = 120;
|
||
if(otpTimerRef) clearInterval(otpTimerRef);
|
||
otpTimerRef = setInterval(()=>{
|
||
cd--;
|
||
const el = document.getElementById('otpTimer');
|
||
if(el) el.textContent = cd;
|
||
if(cd <= 0){ clearInterval(otpTimerRef); }
|
||
}, 1000/simSpeed);
|
||
|
||
apiLog('🔐 POST /auth/otp/request.php','req');
|
||
apiLog('→ receiver: "963944123456", user_type: "driver"','req');
|
||
apiLog('→ country auto-detect: Syria → Intaleq WhatsApp','req');
|
||
apiLog('→ OTP: 3-digit random_int() e.g. "427"','info');
|
||
apiLog('→ DB: INSERT INTO phone_verification (encrypted_phone, encrypted_otp, expires: +5min)','req');
|
||
|
||
setTimeout(()=>{
|
||
// Simulate OTP input
|
||
const digits = ['4','2','7'];
|
||
digits.forEach((d,i)=>{
|
||
setTimeout(()=>{
|
||
const box = document.getElementById(`ob${i}`);
|
||
if(box){ box.textContent=d; box.classList.add('filled'); }
|
||
if(i===2){
|
||
setTimeout(()=>{
|
||
digits.forEach((_,j)=>{ const b=document.getElementById(`ob${j}`); if(b) b.classList.replace('filled','ok'); });
|
||
apiLog('✅ POST /auth/otp/verify.php','res');
|
||
apiLog('→ SELECT WHERE phone=encrypted & token=encrypted & expiry>NOW()','res');
|
||
apiLog('→ UPDATE is_verified=1','res');
|
||
document.getElementById('stOtp').textContent='✓ تم';
|
||
document.getElementById('stOtp').style.color='var(--green)';
|
||
showToast('✅ OTP صحيح — تم التحقق','#22c55e');
|
||
}, 600/simSpeed);
|
||
}
|
||
}, i*400/simSpeed);
|
||
});
|
||
}, 1500/simSpeed);
|
||
}
|
||
|
||
// ─── Step 3: Driver Info ────────────────────────────
|
||
function enterDriverInfo(){
|
||
setFlow('otp_verify','done');
|
||
apiLog('👤 RegistrationController — Page 0 (driverInfoFormKey)','info');
|
||
apiLog('→ validate(): firstName, lastName, nationalId, birthdate, licenseExpiry','info');
|
||
showToast('👤 معلومات السائق الشخصية','#5b8dff');
|
||
}
|
||
|
||
// ─── Step 4: Car Info ───────────────────────────────
|
||
function enterCarInfo(){
|
||
apiLog('🚗 RegistrationController — Page 1 (carInfoFormKey)','info');
|
||
apiLog('→ carPlate, make, model, year, color_hex, vehicle_category_id, fuel_type_id','info');
|
||
apiLog('→ فئة: 1 (Car) | وقود: 1 (Petrol)','info');
|
||
showToast('🚗 معلومات المركبة','#5b8dff');
|
||
}
|
||
|
||
// ─── Step 5: Doc Upload ─────────────────────────────
|
||
function enterDocUpload(){
|
||
setFlow('doc_upload','active');
|
||
docsUploaded = 0;
|
||
document.getElementById('stDocs').textContent = '0/8';
|
||
document.getElementById('docBtn').disabled = true;
|
||
document.getElementById('docBtn').textContent = 'في انتظار رفع الوثائق...';
|
||
|
||
apiLog('📂 بدء رفع الوثائق — uploadToSyria() × 8','info');
|
||
apiLog('→ 🇸🇾 Syria: driver_license_back إلزامي','info');
|
||
apiLog('→ Compress: quality=70, maxWidth=1024px (EXIF-fixed)','info');
|
||
apiLog('→ POST /uploadSyrianDocs.php — MultipartRequest','req');
|
||
apiLog('→ Headers: Authorization:Bearer JWT, X-HMAC-Auth','req');
|
||
apiLog('→ Response: { status:"success", file_url: Signed URL (2 days TTL) }','info');
|
||
|
||
DOCS.forEach((docId, idx)=>{
|
||
setTimeout(()=>{
|
||
const card = document.getElementById('doc_'+docId);
|
||
if(!card) return;
|
||
card.className = 'doc-card uploading';
|
||
card.innerHTML = `<div class="ai-spin" style="width:18px;height:18px;border-color:rgba(245,158,11,.3);border-top-color:var(--amber)"></div><div>${docLabel(docId)}</div><div style="color:var(--amber);font-size:10px">Uploading...</div>`;
|
||
|
||
setTimeout(()=>{
|
||
card.className = 'doc-card done';
|
||
card.innerHTML = `<div class="doc-icon">${docIcon(docId)}</div><div>${docLabel(docId)}</div><div style="font-size:10px">✅ Signed URL</div>`;
|
||
docsUploaded++;
|
||
document.getElementById('stDocs').textContent = `${docsUploaded}/8`;
|
||
apiLog(`✅ ${docId} → secured_url/siro/secure_image.php?driver_id=...&sig=...`,'res');
|
||
|
||
if(docsUploaded === 8){
|
||
setFlow('doc_upload','done');
|
||
document.getElementById('docBtn').disabled = false;
|
||
document.getElementById('docBtn').textContent = '✅ جميع الوثائق مرفوعة — إرسال للمراجعة ▶';
|
||
showToast('📂 تم رفع كل الوثائق بنجاح!','#22c55e');
|
||
}
|
||
}, 900/simSpeed);
|
||
}, idx * 700/simSpeed);
|
||
});
|
||
|
||
showToast('📂 جاري رفع الوثائق...','#f59e0b');
|
||
}
|
||
|
||
// ─── Step 6: AI Process ─────────────────────────────
|
||
function enterAIProcess(){
|
||
setFlow('ai_process','active');
|
||
document.getElementById('stAI').textContent='⚙️ يعمل';
|
||
document.getElementById('stAI').style.color='var(--purple)';
|
||
|
||
const aiSteps = document.getElementById('aiSteps');
|
||
aiSteps.innerHTML = '';
|
||
const aiResult = document.getElementById('aiResult');
|
||
aiResult.style.display = 'none';
|
||
|
||
const logs = [
|
||
['🔗 SSRF Check: validate doc URLs against allowedHosts','info'],
|
||
['📥 تحميل الصور كـ base64 → Gemini Flash','ai'],
|
||
['👁️ Gemini: فحص الهوية الوطنية (وجه + خلف)','ai'],
|
||
['🪪 OCR: استخراج الاسم + الرقم الوطني + DOB','ai'],
|
||
['📋 OCR: رخصة القيادة — فئة، انتهاء، رقم الرخصة','ai'],
|
||
['🚗 OCR: ترخيص المركبة — VIN، لوحة، اللون','ai'],
|
||
['🤳 FACE MATCHING: صورة شخصية ↔ هوية ↔ رخصة','ai'],
|
||
['📄 فحص وثيقة "لا حكم عليه" — التحقق من الاسم','ai'],
|
||
['✅ AI status: "success", face_match_confidence: "high"','res'],
|
||
];
|
||
|
||
logs.forEach((log, i)=>{
|
||
setTimeout(()=>{
|
||
const d = document.createElement('div');
|
||
d.style.cssText='display:flex;gap:6px;align-items:flex-start;padding:4px 0;border-bottom:1px solid rgba(255,255,255,.04);';
|
||
d.innerHTML = `<span style="flex-shrink:0;font-size:9px;color:var(--muted);margin-top:2px">${String(i+1).padStart(2,'0')}</span><span>${log[0]}</span>`;
|
||
aiSteps.appendChild(d);
|
||
apiLog(log[0], log[1]);
|
||
|
||
if(i === logs.length-1){
|
||
setTimeout(()=>{
|
||
document.getElementById('aiSpin').style.display='none';
|
||
aiResult.style.display = 'block';
|
||
aiResult.innerHTML = `
|
||
<b style="color:var(--green)">✅ AI Response JSON:</b><br/>
|
||
<code style="color:var(--cyan);">{</code><br/>
|
||
"status": <span style="color:var(--green)">"success"</span>,<br/>
|
||
"face_match_confidence": <span style="color:var(--green)">"high"</span>,<br/>
|
||
"driver": { full_name: <span style="color:var(--amber)">"محمد الأحمد"</span>, dob: <span style="color:var(--amber)">"1990-01-01"</span> },<br/>
|
||
"car": { car_plate: <span style="color:var(--amber)">"155186 درعا"</span>, vin: <span style="color:var(--amber)">"KMH..."</span> }<br/>
|
||
<code style="color:var(--cyan);">}</code>`;
|
||
document.getElementById('stAI').textContent='✓ نجح';
|
||
document.getElementById('stAI').style.color='var(--green)';
|
||
showToast('🤖 AI: مطابقة ناجحة — face_match: high','#a855f7');
|
||
}, 500/simSpeed);
|
||
}
|
||
}, i * 500/simSpeed);
|
||
});
|
||
}
|
||
|
||
// ─── Step 7: Pending ────────────────────────────────
|
||
function enterPending(){
|
||
setFlow('ai_process','done');
|
||
setFlow('register','done');
|
||
setFlow('notify','done');
|
||
|
||
apiLog('📤 POST /register_driver_and_car.php — Transaction','req');
|
||
apiLog('→ 1) Phone format: 09xx → 963xx (Syria)','req');
|
||
apiLog('→ 2) birthdate: "1990" → "1990-01-01"','req');
|
||
apiLog('→ 3) AES-GCM Encrypt: phone, email, first_name, last_name, national_number...','req');
|
||
apiLog('→ 4) Password: HMAC-SHA256(id|phone|nationalNum) → bcrypt','req');
|
||
apiLog('→ 5) Check duplicate: SELECT WHERE phone=enc OR email=enc','req');
|
||
apiLog('→ 6) INSERT driver (status="yet")','req');
|
||
apiLog('→ 7) INSERT CarRegistration (vehicle_category_id=1, fuel_type_id=1, status="yet")','req');
|
||
apiLog('→ 8) INSERT driver_documents × 8 + imageProfileCaptain','req');
|
||
apiLog('→ 9) COMMIT transaction','req');
|
||
apiLog('→ 10) FCM: topic:service "سائق جديد" notification','req');
|
||
apiLog('✅ { status:"success", driverID:"DRV2024...", carRegID:1796 }','res');
|
||
|
||
renderDBPreview();
|
||
showToast('⏳ تم التسجيل — قيد مراجعة خدمة العملاء','#f59e0b');
|
||
}
|
||
|
||
// ─── Step 8: Activated ──────────────────────────────
|
||
function enterActivated(){
|
||
setFlow('notify','done');
|
||
setFlow('activate','done');
|
||
|
||
apiLog('🔑 Admin Panel → UPDATE driver SET status="active"','res');
|
||
apiLog('🔑 Admin Panel → UPDATE CarRegistration SET status="active"','res');
|
||
apiLog('🔔 FCM Push → driverToken: "تم تفعيل حسابك!"','res');
|
||
apiLog('🔑 JWT issued → INSERT driverToken (captain_id, fingerPrint)','res');
|
||
apiLog('✅ السائق جاهز للعمل','res');
|
||
showToast('🎉 تم تفعيل حساب السائق!','#22c55e');
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════════
|
||
// HELPERS
|
||
// ═══════════════════════════════════════════════════
|
||
function setFlow(id, state){
|
||
const n = document.getElementById(`fn_${id}`);
|
||
if(!n) return;
|
||
n.className = 'flow-node ' + state;
|
||
}
|
||
|
||
function apiLog(msg, type='info'){
|
||
const el = document.getElementById('apiLog');
|
||
const now = new Date();
|
||
const t = `${String(now.getHours()).padStart(2,'0')}:${String(now.getMinutes()).padStart(2,'0')}:${String(now.getSeconds()).padStart(2,'0')}`;
|
||
const d = document.createElement('div');
|
||
d.className = `api-entry ${type}`;
|
||
d.innerHTML = `<div class="api-time">${t}</div>${msg}`;
|
||
el.insertBefore(d, el.firstChild);
|
||
while(el.children.length > 50) el.removeChild(el.lastChild);
|
||
}
|
||
|
||
function renderDBPreview(){
|
||
const el = document.getElementById('dbPreviews');
|
||
el.innerHTML = `
|
||
<div class="db-preview">
|
||
<div class="db-header">⚡ driver</div>
|
||
<div class="db-row"><span class="db-key">id</span><span class="db-val">DRV20240619xxx</span></div>
|
||
<div class="db-row"><span class="db-key">phone</span><span class="db-val enc">AES{963944...}</span></div>
|
||
<div class="db-row"><span class="db-key">first_name</span><span class="db-val enc">AES{محمد}</span></div>
|
||
<div class="db-row"><span class="db-key">national_number</span><span class="db-val enc">AES{012345678}</span></div>
|
||
<div class="db-row"><span class="db-key">password</span><span class="db-val enc">bcrypt(HMAC(...))</span></div>
|
||
<div class="db-row"><span class="db-key">status</span><span class="db-val pending">yet</span></div>
|
||
</div>
|
||
<div class="db-preview" style="margin-top:8px">
|
||
<div class="db-header">⚡ CarRegistration</div>
|
||
<div class="db-row"><span class="db-key">driverID</span><span class="db-val">DRV20240619xxx</span></div>
|
||
<div class="db-row"><span class="db-key">car_plate</span><span class="db-val enc">AES{155186 درعا}</span></div>
|
||
<div class="db-row"><span class="db-key">vin</span><span class="db-val enc">AES{KMH...}</span></div>
|
||
<div class="db-row"><span class="db-key">vehicle_category_id</span><span class="db-val">1 (Car)</span></div>
|
||
<div class="db-row"><span class="db-key">fuel_type_id</span><span class="db-val">1 (Petrol)</span></div>
|
||
<div class="db-row"><span class="db-key">status</span><span class="db-val pending">yet</span></div>
|
||
</div>
|
||
<div class="db-preview" style="margin-top:8px">
|
||
<div class="db-header">⚡ driver_documents (8 records)</div>
|
||
${DOCS.map(d=>`<div class="db-row"><span class="db-key">${d}</span><span class="db-val">Signed URL ✓</span></div>`).join('')}
|
||
</div>`;
|
||
}
|
||
|
||
function docLabel(id){
|
||
const m = {
|
||
id_front:'هوية — وجه',id_back:'هوية — خلف',
|
||
driver_license:'رخصة — وجه',driver_license_back:'رخصة — خلف 🇸🇾',
|
||
profile_picture:'صورة شخصية',criminal_record:'لا حكم عليه',
|
||
car_license_front:'ترخيص سيارة — وجه',car_license_back:'ترخيص سيارة — خلف'
|
||
};
|
||
return m[id]||id;
|
||
}
|
||
function docIcon(id){
|
||
const m = {id_front:'🪪',id_back:'🪪',driver_license:'📋',driver_license_back:'📋',
|
||
profile_picture:'🤳',criminal_record:'📄',car_license_front:'🚗',car_license_back:'🚗'};
|
||
return m[id]||'📎';
|
||
}
|
||
|
||
function showToast(msg, color='#5b8dff'){
|
||
const wrap = document.getElementById('toastWrap');
|
||
const t = document.createElement('div');
|
||
t.className='toast';t.style.borderColor=color;t.style.color=color;
|
||
t.textContent=msg;wrap.appendChild(t);
|
||
setTimeout(()=>{ t.style.opacity='0';t.style.transition='opacity .4s';
|
||
setTimeout(()=>t.remove(),400); },2800);
|
||
}
|
||
|
||
function toggleAuto(){
|
||
if(isAuto){
|
||
clearTimeout(autoTimer);isAuto=false;
|
||
document.getElementById('btnAuto').textContent='⚡ تلقائي';
|
||
document.getElementById('btnAuto').className='btn btn-amb';
|
||
} else {
|
||
isAuto=true;
|
||
document.getElementById('btnAuto').textContent='⏸ إيقاف';
|
||
document.getElementById('btnAuto').className='btn btn-red';
|
||
const delays = [2000,2500,3000,2500,2500,9000,6000,3000,3000];
|
||
const go = ()=>{
|
||
if(currentStep >= STEPS.length-1){ toggleAuto(); return; }
|
||
nextStep();
|
||
const d = (delays[currentStep]||2500)/simSpeed;
|
||
autoTimer = setTimeout(go, d);
|
||
};
|
||
autoTimer = setTimeout(go, 1500/simSpeed);
|
||
}
|
||
}
|
||
|
||
function resetSim(){
|
||
if(isAuto) toggleAuto();
|
||
if(otpTimerRef) clearInterval(otpTimerRef);
|
||
currentStep = -1;docsUploaded=0;
|
||
document.getElementById('stPhase').textContent='0/8';
|
||
document.getElementById('stOtp').textContent='—';document.getElementById('stOtp').style.color='var(--muted)';
|
||
document.getElementById('stDocs').textContent='0/8';
|
||
document.getElementById('stAI').textContent='—';document.getElementById('stAI').style.color='var(--muted)';
|
||
document.getElementById('apiLog').innerHTML='';
|
||
document.getElementById('dbPreviews').innerHTML='';
|
||
document.getElementById('btnNext').disabled=false;
|
||
document.getElementById('aiSpin').style.display='';
|
||
document.getElementById('aiSteps').innerHTML='';
|
||
document.getElementById('aiResult').style.display='none';
|
||
DOCS.forEach(d=>{ const el=document.getElementById('doc_'+d); if(el){el.className='doc-card';el.innerHTML=`<div class="doc-icon">${docIcon(d)}</div><div>${docLabel(d)}</div>`;}});
|
||
FLOW_NODES.forEach(n=>{ const el=document.getElementById(`fn_${n.id}`); if(el) el.className='flow-node'; });
|
||
renderStepList();renderStepsBar();showScreen(0);
|
||
setTimeout(nextStep,600);
|
||
}
|
||
|
||
function setSpeed(v){ simSpeed=parseFloat(v); document.getElementById('spdLbl').textContent=`${v}×`; }
|
||
|
||
window.onload = init;
|
||
</script>
|
||
</body>
|
||
</html>
|