Files
Siro/siromove.com/tutorial/generate.py
T

254 lines
14 KiB
Python

import json
units = [
{
"id": 1,
"title": "التسجيل وإنشاء الحساب",
"desc": "التسجيل يحتاج رقم هاتف فقط — لا بريد إلكتروني إجباري ولا بطاقة دفع. أقل من دقيقة من التحميل حتى أول شاشة خريطة.",
"steps": [
{"title": "تحميل التطبيق وفتحه", "desc": "شاشة البداية بشعار Siro", "img": "tutorial/registration/01_splash_screen.png"},
{"title": "شاشات التعريف", "desc": "شريحة الـ Onboarding الأولى", "img": "tutorial/registration/02_onboarding.png"},
{"title": "إدخال رقم الهاتف", "desc": "حقل الهاتف مع اختيار الدولة", "img": "tutorial/registration/03_phone_number.png"},
{"title": "رمز التحقق OTP", "desc": "شاشة إدخال الرمز (يصل واتساب/SMS)", "img": "tutorial/registration/04_otp.png"},
{"title": "إكمال البيانات", "desc": "الاسم + البريد (اختياري) ثم «ابدأ»", "img": "tutorial/registration/05_complete_profile.png"}
]
},
{
"id": 2,
"title": "طلب الرحلة",
"desc": "السعر المعروض نهائي ومحسوب من الخادم — لا مفاجآت عند الدفع (ما لم تتغير الوجهة أو يزد الانتظار).",
"steps": [
{"title": "الخريطة الرئيسية", "desc": "الخريطة مع السيارات القريبة تتحرك", "img": "tutorial/booking/01_map_main.png"},
{"title": "تحديد الوجهة", "desc": "شريط البحث + النتائج", "img": "tutorial/booking/02_search_destination.png"},
{"title": "تأكيد نقطة الانطلاق", "desc": "دبوس الانطلاق قابل للسحب", "img": "tutorial/booking/03_pickup_pin.png"},
{"title": "اختيار فئة السيارة", "desc": "قائمة الفئات التسع مع السعر وشارة التوفير", "img": "tutorial/booking/04_select_car_type.png"},
{"title": "كود الخصم", "desc": "حقل البرومو مطبّق على السعر", "img": "tutorial/booking/05_promo_code.png"},
{"title": "تأكيد الطلب", "desc": "زر «اطلب الآن» مع السعر النهائي", "img": "tutorial/booking/06_confirm_order.png"}
]
},
{
"id": 3,
"title": "الانتظار والمطابقة",
"desc": "متوسط زمن المطابقة أقل من 90 ثانية. إذا لم يقبل أحد، النظام يقترح تعديلاً بسيطاً على السعر بدل إلغاء طلبك.",
"steps": [
{"title": "البحث عن كابتن", "desc": "نافذة البحث مع المؤقّت", "img": "tutorial/matching/01_searching_captain.png"},
{"title": "اقتراح المفاوض الذكي", "desc": "حوار رفع السعر بعد 90 ثانية", "img": "tutorial/matching/02_smart_negotiator.png"},
{"title": "قبول الكابتن", "desc": "بطاقة الكابتن: الاسم، الصورة، التقييم، اللوحة", "img": "tutorial/matching/03_captain_card.png"},
{"title": "الكابتن في الطريق", "desc": "الخريطة مع المسار الأصفر والوقت المتوقع", "img": "tutorial/matching/04_captain_on_way.png"}
]
},
{
"id": 4,
"title": "أثناء الرحلة والسلامة",
"desc": "تتبع حي للرحلة مع خيارات أمان متقدمة واتصال آمن بالكابتن.",
"steps": [
{"title": "وصول الكابتن", "desc": "إشعار «الكابتن وصل» ومهلة انتظار 5 دقائق", "img": "tutorial/ride/01_captain_arrived.png"},
{"title": "بدء الرحلة", "desc": "المسار الأزرق + العداد الحي", "img": "tutorial/ride/02_ride_started.png"},
{"title": "مشاركة الرحلة", "desc": "رابط متابعة لطرف ثالث (للأهل)", "img": "tutorial/ride/03_share_ride.png"},
{"title": "زر الطوارئ SOS", "desc": "زر الطوارئ (وهزّ الهاتف 5 مرات)", "img": "tutorial/ride/04_sos_button.png"},
{"title": "الاتصال بالكابتن", "desc": "شاشة المكالمة داخل التطبيق", "img": "tutorial/ride/05_in_app_call.png"}
]
},
{
"id": 5,
"title": "الإلغاء",
"desc": "الإلغاء قبل القبول مجاني، وبعد الوصول قد تُطبّق رسوم. السائق يواجه إيقافاً بعد 3 إلغاءات.",
"steps": [
{"title": "زر الإلغاء", "desc": "مكان الزر أثناء البحث/الانتظار", "img": "tutorial/cancel/01_cancel_button.png"},
{"title": "اختيار السبب", "desc": "قائمة أسباب الإلغاء", "img": "tutorial/cancel/02_cancel_reason.png"},
{"title": "تأكيد الإلغاء", "desc": "رسالة التأكيد + سياسة الرسوم", "img": "tutorial/cancel/03_cancel_confirm.png"}
]
},
{
"id": 6,
"title": "الدفع والتقييم",
"desc": "دفع مرن مع نقاط مكافأة للمحفظة، وتقييم يعزز جودة الخدمة.",
"steps": [
{"title": "نهاية الرحلة", "desc": "شاشة الفاتورة النهائية", "img": "tutorial/payment/01_ride_finished.png"},
{"title": "طرق الدفع", "desc": "كاش/محفظة/بطاقة ووسائل الدفع المحلية", "img": "tutorial/payment/02_payment_methods.png"},
{"title": "الدفع كاش", "desc": "حوار تأكيد الكاش", "img": "tutorial/payment/03_cash_confirm.png"},
{"title": "الدفع بالمحفظة", "desc": "الخصم من الرصيد وعروض الشحن", "img": "tutorial/payment/04_wallet_pay.png"},
{"title": "تقييم الكابتن", "desc": "النجوم + الإكرامية", "img": "tutorial/payment/05_rate_captain.png"},
{"title": "سجل الرحلات", "desc": "قائمة الرحلات السابقة والفواتير", "img": "tutorial/payment/06_ride_history.png"}
]
},
{
"id": 7,
"title": "مواصلاتي — للطلاب",
"desc": "خدمة تتبع باصات الجامعات مجاناً مع إمكانية تحويل الطالب لرحلة مدفوعة إذا فاته الباص.",
"steps": [
{"title": "اختيار الجامعة", "desc": "تبويب مواصلاتي واختيار الجامعة", "img": "tutorial/transit/01_select_university.png"},
{"title": "تفعيل العضوية", "desc": "إدخال الرقم الجامعي للتفعيل الفوري", "img": "tutorial/transit/02_activate_student_id.png"},
{"title": "استعراض الخطوط", "desc": "قائمة الخطوط المعتمدة وجداولها", "img": "tutorial/transit/03_transit_lines.png"},
{"title": "التتبع الحي", "desc": "الباص يتحرك على المسار والمحطات", "img": "tutorial/transit/04_live_tracking.png"},
{"title": "فاتك الباص؟", "desc": "الزر البارز لطلب سيارة بديلة", "img": "tutorial/transit/05_missed_bus.png"}
]
}
]
html_template = """<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>كيف تستخدم سيرو — التوتوريال الشامل</title>
<style>
:root{
--bg:#F6F7F5; --surface:#FFFFFF; --surface2:#EEF1EE;
--ink:#1A241F; --muted:#5B6862; --faint:#8B968F;
--line:#E1E5E1; --line2:#D2D8D3;
--brand:#0E7A5F; --brand-ink:#0A5945; --brand-soft:#E4F0EB;
--amber:#9A6B12; --amber-soft:#F6EDDA;
--red:#B3362B; --red-soft:#F7E5E2;
--good:#2E7D4F; --good-soft:#E4F1E8;
--sans:"SF Arabic","Segoe UI",system-ui,-apple-system,"Noto Sans Arabic",Tahoma,sans-serif;
--mono:ui-monospace,"SF Mono",Consolas,monospace;
}
@media (prefers-color-scheme: dark){
:root{
--bg:#0F1512; --surface:#161E19; --surface2:#1C2620;
--ink:#E8EDEA; --muted:#9BA8A1; --faint:#6E7B74;
--line:#26302B; --line2:#32403A;
--brand:#3FBF97; --brand-ink:#8FE0C5; --brand-soft:#15342A;
--amber:#E0AE55; --amber-soft:#33290F;
--red:#E07065; --red-soft:#361A16;
--good:#57C287; --good-soft:#14301F;
}
}
body { margin: 0; padding: 0; background: var(--bg); color: var(--ink); font-family: var(--sans); font-size: 16px; line-height: 1.85; }
* { box-sizing: border-box; }
.wrap { max-width: 1060px; margin: 0 auto; padding: 0 22px; }
h1, h2, h3, h4 { margin: 0; color: var(--ink); line-height: 1.35; text-wrap: balance; }
p { margin: 0; }
.hero { padding: 64px 0 36px; border-bottom: 1px solid var(--line); }
.hero .kicker {
display: inline-block; font-size: 0.78rem; font-weight: 700; letter-spacing: 0.04em;
color: var(--brand-ink); background: var(--brand-soft); border-radius: 99px; padding: 4px 14px;
margin-bottom: 18px;
}
.hero h1 { font-size: clamp(1.7rem, 4.5vw, 2.5rem); font-weight: 800; }
.hero .lede { margin-top: 14px; font-size: 1.05rem; color: var(--muted); max-width: 60ch; }
.hero .meta { margin-top: 16px; font-size: 0.8rem; color: var(--faint); }
.tabs {
display: flex; gap: 8px; overflow-x: auto; padding: 20px 0 10px;
margin-top: 10px; scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }
.tab-btn {
background: var(--surface); border: 1px solid var(--line); color: var(--muted);
font-family: var(--sans); font-size: 0.95rem; font-weight: 700; padding: 10px 20px;
border-radius: 99px; cursor: pointer; white-space: nowrap; transition: all 0.2s;
}
.tab-btn:hover { border-color: var(--brand); color: var(--ink); }
.tab-btn.active { background: var(--brand-soft); color: var(--brand-ink); border-color: var(--brand-ink); font-weight: 800; }
.tab-content { display: none; animation: fadeIn 0.3s ease; padding: 30px 0 80px; }
.tab-content.active { display: block; }
@keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
.sec-head { margin-bottom: 40px; text-align: center; }
.sec-head h2 { font-size: 1.8rem; font-weight: 800; margin-bottom: 12px; }
.sec-head .thesis { color: var(--muted); font-size: 1.1rem; max-width: 66ch; margin: 0 auto; }
.steps-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 40px; margin-top: 20px; }
.step-card {
display: flex; flex-direction: column; gap: 20px; align-items: center;
background: var(--surface); border: 1px solid var(--line); border-radius: 24px; padding: 30px 20px;
}
.step-info { text-align: center; }
.step-num {
display: inline-flex; align-items: center; justify-content: center;
width: 32px; height: 32px; background: var(--brand); color: #fff;
border-radius: 50%; font-size: 1rem; font-weight: 800; margin-bottom: 12px; font-variant-numeric: tabular-nums;
}
.step-info h4 { font-size: 1.1rem; font-weight: 800; margin-bottom: 6px; }
.step-info p { font-size: 0.9rem; color: var(--muted); }
.phone-frame {
width: 260px; height: 540px; border: 12px solid #1a1a1a; border-radius: 36px;
overflow: hidden; position: relative; box-shadow: 0 10px 25px rgba(0,0,0,0.05); background: #f1f3f2;
}
.phone-notch {
position: absolute; top: 0; left: 50%; transform: translateX(-50%);
width: 110px; height: 26px; background: #1a1a1a;
border-bottom-left-radius: 16px; border-bottom-right-radius: 16px; z-index: 2;
}
.phone-screen {
width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;
text-align: center; padding: 20px; color: var(--faint); font-family: var(--mono);
font-size: 0.75rem; word-break: break-all; background-color: var(--surface2);
}
</style>
</head>
<body>
<header class="hero"><div class="wrap">
<span class="kicker">خطة التوتوريال — 34 لقطة</span>
<h1>كيف تستخدم سيرو</h1>
<p class="lede">صفحات شرح تفاعلية تشرح للمستخدم الجديد كل عملية خطوة بخطوة، مدعومة بلقطات شاشة حقيقية من التطبيق.</p>
<p class="meta">تم بناء هذه الصفحة حسب التقرير الشامل — في انتظار إدراج اللقطات النهائية</p>
</div></header>
<div class="wrap">
<div class="tabs">
{tabs_html}
</div>
<div class="tab-contents">
{contents_html}
</div>
</div>
<script>
function showTab(id) {
document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
document.getElementById('btn-' + id).classList.add('active');
document.getElementById('tab-' + id).classList.add('active');
}
</script>
</body>
</html>
"""
tabs_html = ""
contents_html = ""
for unit in units:
active_class = " active" if unit["id"] == 1 else ""
tabs_html += f'<button id="btn-{unit["id"]}" class="tab-btn{active_class}" onclick="showTab({unit["id"]})">{unit["id"]}. {unit["title"]}</button>\n '
steps_html = ""
for i, step in enumerate(unit["steps"]):
steps_html += f"""
<div class="step-card">
<div class="step-info">
<span class="step-num">{i+1}</span>
<h4>{step['title']}</h4>
<p>{step['desc']}</p>
</div>
<div class="phone-frame">
<div class="phone-notch"></div>
<div class="phone-screen">{step['img']}</div>
</div>
</div>"""
contents_html += f"""
<div id="tab-{unit['id']}" class="tab-content{active_class}">
<div class="sec-head">
<h2>الوحدة {unit['id']}: {unit['title']}</h2>
<p class="thesis">{unit['desc']}</p>
</div>
<div class="steps-grid">
{steps_html}
</div>
</div>
"""
final_html = html_template.replace('{tabs_html}', tabs_html).replace('{contents_html}', contents_html)
with open("/Users/hamzaaleghwairyeen/development/App/Siro/siromove.com/tutorial/index.html", "w", encoding="utf-8") as f:
f.write(final_html)
print("Generated HTML successfully.")