/* ============================================================ data.jsx — REAL data fetched from /api endpoints Same shape as the mock; populates window.STATUS_DATA, then dispatches 'status-data-ready' once everything is loaded. ============================================================ */ /* ============================================================ i18n (English / العربية) — shared desktop core. data.jsx loads BEFORE every other desktop module (shell, app, pages) AND before mobile.jsx, so exposing the helpers here makes one language preference + one translation surface for the whole app. The mobile surface keeps its own dictionary; this is the desktop/admin one. English string IS the key — any unwrapped or untranslated text gracefully stays English, so the UI can never break mid-translation. The preference lives in the SAME localStorage key as mobile ('m_lang'), so switching language in the admin Tweaks panel and switching it on the phone are one and the same setting. Switching reloads (cheap + rare), so the lang read at load is always correct. ============================================================ */ // NOTE: names here are deliberately collision-proof. mobile.jsx declares a // top-level `const I18N_AR` / `i18n` / `t`, and Babel-standalone leaks those // onto the global object; since mobile.jsx loads AFTER data.jsx it would clobber // any `window.I18N_AR`/`window.i18n` we set. So the desktop dict + helper use // unique names (APP_I18N / appT / appLang) that mobile never touches. appT also // falls back to the leaked mobile dict, so shared keys translate either way. window.APP_I18N = window.APP_I18N || { // nav groups "Activity": "النشاط", "Insight": "التحليلات", "Configure": "الإعدادات", // nav items "Today": "اليوم", "Employees": "الموظفون", "Requests": "الطلبات", "Summary": "الملخص", "Reports": "التقارير", "Tasks": "المهام", "Support": "الدعم", "On-call": "المناوبة", "Messages": "الرسائل", "Audit log": "سجل التدقيق", "Teams": "الفِرق", "Rooms": "الغرف", "Bots": "الروبوتات", "Bot Behavior": "سلوك الروبوت", "Integrations": "التكاملات", "Server": "الخادم", "Help / API": "المساعدة / API", // tweaks / appearance / settings panel "Tweaks": "التخصيص", "Theme": "السمة", "Appearance": "المظهر", "Light": "فاتح", "Dark": "داكن", "Language": "اللغة", "Display": "العرض", "Notifications on": "الإشعارات مفعّلة", "Enable notifications": "تفعيل الإشعارات", "Install / QR code": "التثبيت / رمز QR", "Status colors": "ألوان الحالة", "Editorial Calm · light + dark. Status colors stay constant.": "تصميم هادئ · فاتح + داكن. ألوان الحالة ثابتة.", // login page "Sign in to your team.": "سجّل الدخول إلى فريقك.", "We'll DM a 6-digit code to your K9 Chat ID. No passwords, no setup.": "سنرسل لك رمزًا من 6 أرقام إلى معرّف K9 Chat. بدون كلمات مرور، بدون إعداد.", "K9 Chat ID": "معرّف K9 Chat", "Send code": "إرسال الرمز", "Sending code…": "جارٍ إرسال الرمز…", "Trusted devices stay signed in for 90 days": "تبقى الأجهزة الموثوقة مسجّلة الدخول لمدة 90 يومًا", "Sign in with password": "تسجيل الدخول بكلمة المرور", "Password": "كلمة المرور", "Signing in…": "جارٍ تسجيل الدخول…", "Sign in": "تسجيل الدخول", "Verifying…": "جارٍ التحقق…", "Use a one-time code instead": "استخدم رمزًا لمرة واحدة بدلاً من ذلك", "Verification code": "رمز التحقق", "Back": "رجوع", "welcome back…": "مرحبًا بعودتك…", "signing you in": "جارٍ تسجيل دخولك", "expires in 5 min": "تنتهي صلاحيته خلال 5 دقائق", "Code sent to": "أُرسل الرمز إلى", "Tip: enter": "تلميح: أدخل", "to demo": "للتجربة", "One K9 room. One dashboard. Adaptive late-detection, AI roll-ups, payroll-ready exports — and a bot that's nicer than your old time-clock.": "غرفة K9 واحدة. لوحة تحكم واحدة. كشف تلقائي للتأخير، وملخصات بالذكاء الاصطناعي، وتصدير جاهز للرواتب — وروبوت ألطف من ساعة الدوام القديمة.", // common chrome / actions "Refresh": "تحديث", "Refreshing…": "جارٍ التحديث…", "Search": "بحث", "Settings": "الإعدادات", "Edit profile": "تعديل الملف", "Sign out": "تسجيل الخروج", "Save": "حفظ", "Cancel": "إلغاء", "Close": "إغلاق", "Confirm": "تأكيد", "Export": "تصدير", "Ask Pulse": "اسأل Pulse", "Search pages, people, or run a command…": "ابحث عن صفحات أو أشخاص أو نفّذ أمرًا…", "Switch to dark theme": "التبديل إلى السمة الداكنة", "Switch to light theme": "التبديل إلى السمة الفاتحة", // page-today + primitives (auto-merged) "Reported a blocker (no details given).": "أبلغ عن عائق (لم تُذكر تفاصيل).", "Hours must be a number 0-24, or empty to clear": "يجب أن تكون الساعات رقماً بين 0 و24، أو فارغة للمسح", "Daily sign-in": "تسجيل الدخول اليومي", "Update sign-in": "تحديث تسجيل الدخول", "Yesterday": "الأمس", "What you did yesterday": "ما الذي أنجزته أمس", "What you're working on today": "ما الذي تعمل عليه اليوم", "Issues / blockers": "المشكلات / العوائق", "'none' if unblocked": "اكتب 'none' إذا لم يكن هناك عائق", "Posting…": "جارٍ النشر…", "Post sign-in": "نشر تسجيل الدخول", "DAY COMPLETE": "اكتمل اليوم", "SIGNED IN": "مُسجّل الدخول", "Signed off · see you tomorrow": "مُسجّل الخروج · إلى اللقاء غداً", "Update": "تحديث", "Sign off": "تسجيل الخروج", "Edit entry": "تعديل الإدخال", "Sign in for today": "سجّل الدخول لليوم", "Signed off — nice work today.": "تم تسجيل الخروج — عمل رائع اليوم.", "Not scored yet": "لم يُقيّم بعد", "Made progress": "أحرز تقدماً", "No progress": "لا تقدم", "What they worked on today": "ما عمل عليه اليوم", "No \"Today\" items recorded.": "لا توجد عناصر \"اليوم\" مُسجّلة.", "Their last sign-in (for comparison)": "آخر تسجيل دخول له (للمقارنة)", "No previous tasks on record.": "لا توجد مهام سابقة مُسجّلة.", "Why this verdict": "سبب هذا الحكم", "Team": "الفريق", "Good morning": "صباح الخير", "Good afternoon": "مساء الخير", "Good evening": "مساء الخير", "Overview": "نظرة عامة", "top of page": "أعلى الصفحة", "Violations": "المخالفات", "late / no sign-in / format issues": "التأخير / عدم تسجيل الدخول / مشكلات التنسيق", "Top 10": "أفضل 10", "progress / blockers / absent": "التقدم / العوائق / الغياب", "Live": "مباشر", "online / away / offline now": "متصل / غائب / غير متصل الآن", "Attendance": "الحضور", "per-user breakdown": "تفصيل لكل مستخدم", "Scoreboard": "لوحة النتائج", "on-time vs late vs absent": "الالتزام بالموعد مقابل التأخير مقابل الغياب", "recent audit-log events": "أحدث أحداث سجل التدقيق", "Infra": "البنية التحتية", "bots / rooms / teams": "البوتات / الغرف / الفرق", "Blockers": "العوائق", "people stuck right now": "أشخاص متعثرون الآن", "My Servers": "خوادمي", "your VPSie nodes": "عُقد VPSie الخاصة بك", "Jump to a section on this page": "الانتقال إلى قسم في هذه الصفحة", "Search or jump to a section…": "ابحث أو انتقل إلى قسم…", "Type to filter sections… (Enter to jump, Esc to close)": "اكتب لتصفية الأقسام… (Enter للانتقال، Esc للإغلاق)", "Export options": "خيارات التصدير", "Export CSV": "تصدير CSV", "Export PDF": "تصدير PDF", "Send report…": "إرسال تقرير…", "Use the Reports tab to send bulk reports via DM.": "استخدم تبويب التقارير لإرسال تقارير جماعية عبر الرسائل الخاصة.", "Refreshed": "تم التحديث", "Not yet scored — bot updates after sign-in": "لم يُقيّم بعد — يحدّث البوت بعد تسجيل الدخول", "unscored": "غير مُقيّم", "Bot detected progress vs yesterday": "رصد البوت تقدماً مقارنة بالأمس", "Yes": "نعم", "Bot detected no progress vs yesterday": "رصد البوت عدم وجود تقدم مقارنة بالأمس", "No": "لا", "Today's progress": "تقدم اليوم", "No sign-ins scored yet": "لم يُقيّم أي تسجيل دخول بعد", "The progress bot scores each sign-in against yesterday's tasks. Results appear here once at least one person is scored.": "يقيّم بوت التقدم كل تسجيل دخول مقارنة بمهام الأمس. تظهر النتائج هنا بمجرد تقييم شخص واحد على الأقل.", "Live — bot scores each sign-in for progress vs yesterday": "مباشر — يقيّم البوت كل تسجيل دخول للتقدم مقارنة بالأمس", "No users have been scored as making progress yet today.": "لم يُقيّم أي مستخدم على أنه أحرز تقدماً اليوم بعد.", "Bot:": "البوت:", "Nobody flagged as no-progress today.": "لم يُعلّم أحد بعدم التقدم اليوم.", "Why:": "السبب:", "Today's scoreboard": "لوحة نتائج اليوم", "Live — updates as people post in #attendance": "مباشر — يُحدّث مع نشر الأشخاص في #attendance", "All": "الكل", "Present": "حاضر", "Late": "متأخر", "Signed off": "مُسجّل الخروج", "Absent": "غائب", "Person": "الشخص", "Status": "الحالة", "Sign-in": "تسجيل الدخول", "Today / Progress": "اليوم / التقدم", "Hours": "الساعات", "Strikes": "المخالفات", "Unscored — bot updates after sign-in": "غير مُقيّم — يحدّث البوت بعد تسجيل الدخول", "Bot detected progress": "رصد البوت تقدماً", "Bot detected no progress": "رصد البوت عدم وجود تقدم", "employees": "موظفين", "present": "حاضرين", "late": "متأخرين", "signed-off": "مُسجّلي الخروج", "absent": "غائبين", "Sign-in times": "أوقات تسجيل الدخول", "Sign-off": "تسجيل الخروج", "Active blockers": "العوائق النشطة", "Tap a card to open that person and DM them": "اضغط على بطاقة لفتح ذلك الشخص ومراسلته", "Open profile": "فتح الملف الشخصي", "Signed in": "مُسجّل الدخول", "today": "اليوم", "Signed out": "مُسجّل الخروج", "done today": "أُنجز اليوم", "confirmed": "مؤكَّد", "no-show": "لم يحضر", "Click to see who's in this category": "انقر لمعرفة من في هذه الفئة", "Signed out (done today)": "مُسجّل الخروج (أُنجز اليوم)", "Late today": "متأخر اليوم", "Absent today": "غائب اليوم", "Blockers reported": "عوائق مُبلَّغ عنها", "Nobody has signed in yet.": "لم يسجّل أحد الدخول بعد.", "Nobody has signed off yet.": "لم يسجّل أحد الخروج بعد.", "Nobody is late today. 👍": "لا أحد متأخر اليوم. 👍", "No confirmed absences. 🎉": "لا غيابات مؤكَّدة. 🎉", "No blockers reported.": "لا عوائق مُبلَّغ عنها.", "No progress flags yet today.": "لا أعلام تقدم بعد اليوم.", "DM to": "رسالة خاصة إلى", "Send timed out — check audit log for delivery status.": "انتهت مهلة الإرسال — تحقق من سجل التدقيق لمعرفة حالة التسليم.", "Late >3 times this week": "تأخر أكثر من 3 مرات هذا الأسبوع", "Strike": "مخالفة", "DM": "رسالة خاصة", "Newest first": "الأحدث أولاً", "Oldest first": "الأقدم أولاً", "By person": "حسب الشخص", "Toggle grouping by user": "تبديل التجميع حسب المستخدم", "Flat": "مسطّح", "Group": "تجميع", "No activity matches": "لا نشاط مطابق", "Adjust the type filter above.": "عدّل مرشّح النوع أعلاه.", "No activity yet": "لا نشاط بعد", "Events appear here as they happen.": "تظهر الأحداث هنا فور حدوثها.", "Loading recent sign-ins…": "جارٍ تحميل أحدث تسجيلات الدخول…", "No sign-ins recorded in the last 7 days": "لا توجد تسجيلات دخول مُسجّلة في آخر 7 أيام", "Loading…": "جارٍ التحميل…", "VPSie virtual machines": "الأجهزة الافتراضية VPSie", "No VPSie account connected.": "لا يوجد حساب VPSie متصل.", "DM the bot:": "راسل البوت:", "No VMs on your account yet.": "لا توجد أجهزة افتراضية على حسابك بعد.", "Open VPSie dashboard ↗": "فتح لوحة تحكم VPSie ↗", "Unreachable": "غير قابل للوصول", "Infrastructure": "البنية التحتية", "Bots · Rooms · Teams": "البوتات · الغرف · الفرق", "Click to open Bots tab": "انقر لفتح تبويب البوتات", "No active bots": "لا بوتات نشطة", "Click to open Rooms tab": "انقر لفتح تبويب الغرف", "No active rooms": "لا غرف نشطة", "With bot": "مع بوت", "Click to open Teams tab": "انقر لفتح تبويب الفرق", "No teams configured": "لا فرق مُهيّأة", "(unnamed)": "(بلا اسم)", "Total members": "إجمالي الأعضاء", "Last 30 days · click any metric to re-rank": "آخر 30 يوماً · انقر على أي مقياس لإعادة الترتيب", "Days with a reported blocker.": "الأيام التي بها عائق مُبلَّغ عنه.", "Working days with no sign-in.": "أيام العمل دون تسجيل دخول.", "Days the user signed in.": "الأيام التي سجّل فيها المستخدم الدخول.", "Total hours worked in the window (highest first).": "إجمالي ساعات العمل في النطاق (الأعلى أولاً).", "Total hours worked in the window (lowest first).": "إجمالي ساعات العمل في النطاق (الأدنى أولاً).", "Period — affects missed-hours math + window": "الفترة — تؤثر على حساب الساعات المفقودة + النطاق", "Day": "يوم", "Week": "أسبوع", "Month": "شهر", "Quarter": "ربع سنة", "6 months": "6 أشهر", "Year": "سنة", "No data": "لا توجد بيانات", "User": "المستخدم", "Worked": "المُنجَز", "Hours actually worked in window": "الساعات المُنجَزة فعلياً في النطاق", "Missed": "المفقود", "Expected": "المتوقع", "By team — live": "حسب الفريق — مباشر", "PTO": "إجازة", "Sick": "مريض", "In": "حاضر", "off": "خرج", "in": "حاضر", "Live K9 presence — per user": "تواجد K9 المباشر — لكل مستخدم", "Refresh now": "تحديث الآن", "Online": "متصل", "Away": "غائب", "Offline": "غير متصل", "On time": "في الموعد", "PTO/Leave": "إجازة", "Future": "مستقبل", "Weekend": "عطلة نهاية الأسبوع", "Attendance report — per user": "تقرير الحضور — لكل مستخدم", "6 mo": "6 أشهر", "On-time": "في الموعد", "Half-day": "نصف يوم", "Vacation": "عطلة", "Holiday": "عطلة رسمية", "Test account — no stats tracked": "حساب تجريبي — لا تُتتبّع إحصاءات", "Add details": "إضافة تفاصيل", "Delete": "حذف", "Change saved.": "تم حفظ التغيير.", "Undo": "تراجع", "Failed": "فشل", "Click to edit": "انقر للتعديل", "late · signed off": "متأخر · سجّل الخروج", "pto": "إجازة", "pending": "قيد الانتظار", "approved": "موافق عليه", "denied": "مرفوض", "active": "نشط", "inactive": "غير نشط", "Help": "مساعدة", "min": "دقيقة", "Search…": "بحث…", "No heatmap data": "لا توجد بيانات للخريطة الحرارية", "Session expired — please log in again.": "انتهت الجلسة — يرجى تسجيل الدخول مرة أخرى.", "Select a server…": "اختر خادمًا…", "Could not reach the server": "تعذّر الوصول إلى الخادم", "Disconnect your VPSie account? Your stored credentials will be deleted.": "هل تريد فصل حساب VPSie الخاص بك؟ سيتم حذف بيانات الاعتماد المخزّنة.", "Loading VPSie servers…": "جارٍ تحميل خوادم VPSie…", "Connect VPSie": "ربط VPSie", "Provide a VPSie API client ID and secret — stored encrypted, scoped to you.": "أدخل معرّف العميل والسرّ لواجهة VPSie البرمجية — يُخزَّن مشفّرًا وخاصًّا بك.", "Retry": "إعادة المحاولة", "No VPSie servers found on your account.": "لم يتم العثور على خوادم VPSie في حسابك.", "VPSie connected": "تم ربط VPSie", "disconnect": "فصل", "signed off": "سجّل الخروج", // batch-2: employees/monthly/reports/tasks/support/oncall/bots/bot-behavior/misc/ops/help/hud " Counts match — bot state machine is consistent with audit history.": " الأعداد متطابقة — آلة حالة البوت متسقة مع سجل التدقيق.", " your ": " الخاص بك ", " ⚠️ Mismatch — likely an old `force_clear_strikes` event before the audit log was added, or a bot restart lost a transient counter. Use the \"Forgive all\" or \"Downgrade\" buttons on the employee card to reconcile.": " ⚠️ عدم تطابق — على الأرجح حدث `force_clear_strikes` قديم قبل إضافة سجل التدقيق، أو أن إعادة تشغيل البوت أفقدت عدّادًا مؤقتًا. استخدم زرّي \"العفو عن الكل\" أو \"تخفيض\" في بطاقة الموظف للمطابقة.", "(current)": "(الحالي)", "(currently set — leave blank to keep)": "(مُعيَّن حاليًا — اتركه فارغًا للإبقاء عليه)", "(default · always present)": "(افتراضي · حاضر دائمًا)", "(default)": "(افتراضي)", "(from K9 Chat ID)": "(من معرّف K9 Chat)", "(no team)": "(بلا فريق)", "(on_time_days + 0.5 × half_days) / working_days × 100. Green ≥ 90%, orange 70-89%, red < 70%.": "(أيام الحضور في الموعد + 0.5 × أنصاف الأيام) / أيام العمل × 100. أخضر ≥ 90%، برتقالي 70-89%، أحمر < 70%.", "(optional)": "(اختياري)", "+ New trigger": "+ مُحفِّز جديد", "+ Save current state as preset": "+ حفظ الحالة الحالية كإعداد مسبق", "+items": "+عناصر", "1 month": "شهر واحد", "1 year": "سنة واحدة", "1/3 forgiveness": "1/3 عفو", "2/3 last warning": "2/3 الإنذار الأخير", "3 months": "3 أشهر", "3-strike rule: 1 = forgiveness DM · 2 = last warning · 3 = admin report": "قاعدة الإنذارات الثلاثة: 1 = رسالة عفو خاصة · 2 = الإنذار الأخير · 3 = تقرير للمشرف", "3/3 admin reported": "3/3 تم إبلاغ المشرف", "30 days": "30 يومًا", "30-d attendance": "حضور 30 يومًا", "30-day moving line for each employee — hours / attendance % / progress score. Click a legend item to isolate one user.": "خط متحرك على مدى 30 يومًا لكل موظف — الساعات / نسبة الحضور % / درجة التقدّم. انقر على عنصر في وسيلة الإيضاح لعزل مستخدم واحد.", "7 days": "7 أيام", "90 days": "90 يومًا", "= sign-ins/sign-offs here will NOT be recorded for that user. Use for people who are in the room for visibility but report to a manager elsewhere.": "= لن يتم تسجيل تسجيلات الدخول/الخروج هنا لذلك المستخدم. استخدمه للأشخاص الموجودين في الغرفة لغرض الاطلاع لكنهم يتبعون مديرًا في مكان آخر.", "? This cannot be undone. Any rooms assigned to this bot will lose their bot assignment.": "؟ لا يمكن التراجع عن هذا. أي غرف مُسنَدة إلى هذا البوت ستفقد تعيين البوت الخاص بها.", "AAD Tenant ID, App ID, and App Password are required": "معرّف مستأجر AAD ومعرّف التطبيق وكلمة مرور التطبيق مطلوبة", "AI": "الذكاء الاصطناعي", "AI Bot": "بوت الذكاء الاصطناعي", "AI accomplishments summary": "ملخص الإنجازات بالذكاء الاصطناعي", "AI bot is always here. Add specialist bots (DevOps, Tech…)": "بوت الذكاء الاصطناعي حاضر دائمًا. أضف بوتات متخصصة (DevOps، تقنية…)", "AI generate": "توليد بالذكاء الاصطناعي", "AI generate failed: ": "فشل التوليد بالذكاء الاصطناعي: ", "AI provider": "مزوّد الذكاء الاصطناعي", "AI providers": "مزوّدو الذكاء الاصطناعي", "AI summary (multi-day)": "ملخص بالذكاء الاصطناعي (متعدد الأيام)", "AI summary failed: ": "فشل ملخص الذكاء الاصطناعي: ", "AI-generate a draft based on the textarea contents": "توليد مسودة بالذكاء الاصطناعي بناءً على محتوى مربع النص", "AI-summarized 'what got done' per day per user, derived from yesterday/today fields of every sign-in in the month.": "ملخص بالذكاء الاصطناعي لِـ\"ما تم إنجازه\" لكل يوم لكل مستخدم، مستمدّ من حقول الأمس/اليوم في كل تسجيل دخول خلال الشهر.", "AI-written narrative summarizing this employee's signed-in days. Click Generate to re-render; Send to user DMs the summary.": "سرد مكتوب بالذكاء الاصطناعي يلخّص أيام تسجيل دخول هذا الموظف. انقر على توليد لإعادة العرض؛ انقر على إرسال للمستخدم لإرسال الملخص عبر رسالة خاصة.", "API Key": "مفتاح API", "API key is required": "مفتاح API مطلوب", "Accent color": "اللون المميِّز", "Access Token": "رمز الوصول", "Access token": "رمز الوصول", "Access token is required": "رمز الوصول مطلوب", "Accomplishments fetch failed: ": "فشل جلب الإنجازات: ", "Ack to sender": "إقرار للمُرسِل", "Acknowledge": "إقرار", "Acknowledged": "تم الإقرار", "Action": "إجراء", "Actions": "الإجراءات", "Activate bot": "تفعيل البوت", "Active": "نشط", "Active employee": "موظف نشط", "Active scheduler jobs": "مهام المُجدوِل النشطة", "Activity from the last 7 days will appear here.": "سيظهر نشاط آخر 7 أيام هنا.", "Activity will appear as users sign in and admins make changes.": "سيظهر النشاط مع تسجيل المستخدمين الدخول وإجراء المشرفين للتغييرات.", "Actor (admin or bot)": "الفاعل (مشرف أو بوت)", "Actor · Subject": "الفاعل · الموضوع", "Adaptive (learns each person’s usual time)": "تكيُّفي (يتعلّم الوقت المعتاد لكل شخص)", "Add": "إضافة", "Add API key": "إضافة مفتاح API", "Add Attachments": "إضافة مرفقات", "Add PTO": "إضافة إجازة", "Add PTO failed: ": "فشل إضافة الإجازة: ", "Add PTO for a user…": "إضافة إجازة لمستخدم…", "Add a comment…": "إضافة تعليق…", "Add a rotation below to assign someone.": "أضف مناوبة أدناه لتعيين شخص.", "Add company holiday": "إضافة عطلة رسمية للشركة", "Add holiday": "إضافة عطلة", "Add holiday failed: ": "فشل إضافة العطلة: ", "Add item...": "إضافة عنصر...", "Add member": "إضافة عضو", "Add or remove team members": "إضافة أعضاء الفريق أو إزالتهم", "Add provider to failover chain…": "إضافة مزوّد إلى سلسلة التجاوز عند الفشل…", "Add rotation": "إضافة مناوبة", "Add strike failed: ": "فشل إضافة الإنذار: ", "Add team member": "إضافة عضو إلى الفريق", "Add tenant": "إضافة مستأجر", "Adding…": "جارٍ الإضافة…", "Additional bots": "بوتات إضافية", "Additional bots (optional)": "بوتات إضافية (اختياري)", "Additional bots in this room": "بوتات إضافية في هذه الغرفة", "Admin": "مشرف", "Admin access required": "صلاحية المشرف مطلوبة", "Admin notified": "تم إبلاغ المشرف", "Admin or superadmin required to change role": "تغيير الدور يتطلّب مشرفًا أو مشرفًا أعلى", "Admin weekly digest": "الموجز الأسبوعي للمشرف", "Admin-managed scheduled report dispatch. Toggle each cadence on/off, pick recipients (employees / admins / specific users / room), pick channel, and see last-run + next-run.": "إرسال تقارير مجدولة يديره المشرف. فعّل/عطّل كل وتيرة، واختر المستلمين (الموظفين / المشرفين / مستخدمين محددين / الغرفة)، واختر القناة، وشاهد آخر تشغيل + التشغيل التالي.", "Admins cannot see them. Generate creds at": "لا يستطيع المشرفون رؤيتها. أنشئ بيانات الاعتماد من", "Admins only": "للمشرفين فقط", "Admins only. Ask an admin to configure scheduled reports.": "للمشرفين فقط. اطلب من مشرف إعداد التقارير المجدولة.", "After 5 minutes of no K9 acknowledgement following a critical-keyword escalation, the bot will WhatsApp all on-call users who have a cell phone on file.": "بعد 5 دقائق من عدم وجود إقرار في K9 عقب تصعيد بكلمة مفتاحية حرجة، سيرسل البوت رسالة WhatsApp إلى كل مستخدمي المناوبة الذين لديهم رقم جوّال مسجّل.", "Alert recipients (comma-separated)": "مستلمو التنبيهات (مفصولون بفواصل)", "All 7": "كل الأيام السبعة", "All 7 days — flexible / always-on employees with no fixed day off (the bot won't send any off-day DMs)": "كل الأيام السبعة — موظفون مرنون / دائمو العمل بلا يوم عطلة ثابت (لن يرسل البوت أي رسائل خاصة في أيام العطل)", "All actions": "كل الإجراءات", "All categories": "كل الفئات", "All clear!": "كل شيء على ما يرام!", "All defaults": "كل الإعدادات الافتراضية", "All employees": "كل الموظفين", "All employees have made progress in this period.": "أحرز كل الموظفين تقدّمًا في هذه الفترة.", "All keywords": "كل الكلمات المفتاحية", "All kinds": "كل الأنواع", "All locations": "كل المواقع", "All tasks regardless of status": "كل المهام بغض النظر عن الحالة", "All teams": "كل الفرق", "All users": "كل المستخدمين", "Allow password login": "السماح بتسجيل الدخول بكلمة المرور", "Already saved — paste here only to overwrite": "محفوظ مسبقًا — الصق هنا فقط للكتابة فوقه", "Already saved — paste to overwrite": "محفوظ مسبقًا — الصق للكتابة فوقه", "Already selected as primary persona": "محدَّد بالفعل كشخصية أساسية", "Alt phone": "هاتف بديل", "Annual": "سنوي", "Annual PTO quota per user vs days taken. Default 20 vacation + 10 sick (per team_settings). Surplus/deficit highlighted.": "حصة الإجازة السنوية لكل مستخدم مقابل الأيام المستخدمة. الافتراضي 20 إجازة + 10 مرضية (وفق team_settings). يتم إبراز الفائض/العجز.", "Annual quota updated to": "تم تحديث الحصة السنوية إلى", "Any keyword": "أي كلمة مفتاحية", "App ID (Application Client ID)": "معرّف التطبيق (معرّف عميل التطبيق)", "App Password (Client Secret)": "كلمة مرور التطبيق (السر السري للعميل)", "App name": "اسم التطبيق", "App name, accent color, login tagline, and favicon": "اسم التطبيق واللون المميِّز وشعار تسجيل الدخول وأيقونة الموقع", "Applied": "تم التطبيق", "Apply": "تطبيق", "Applying...": "جارٍ التطبيق...", "Approve": "موافقة", "Approved": "موافق عليه", "Approved + pending PTO/sick/vacation blocks on a calendar grid. Click a block to approve/deny.": "كتل الإجازات/المرضية/الإجازة الموافق عليها والمعلّقة على شبكة تقويم. انقر على كتلة للموافقة/الرفض.", "Approved blocks only": "الكتل الموافق عليها فقط", "Article not yet documented": "المقال غير موثّق بعد", "Ask the AI": "اسأل الذكاء الاصطناعي", "Assign": "تعيين", "Assign PTO": "تعيين إجازة", "Assign PTO (auto-DMs the user)": "تعيين إجازة (يرسل رسالة خاصة للمستخدم تلقائيًا)", "Assign bot (optional)": "تعيين بوت (اختياري)", "Assign members to this team (multi-select)": "تعيين أعضاء لهذا الفريق (اختيار متعدد)", "Assign rooms": "تعيين الغرف", "Assign rooms (multi-select)": "تعيين الغرف (اختيار متعدد)", "Assign rooms (optional)": "تعيين الغرف (اختياري)", "Assigned bot": "البوت المُعيَّن", "Assigned bot (optional)": "البوت المُعيَّن (اختياري)", "Assigned members": "الأعضاء المُعيَّنون", "Assigned rooms": "الغرف المُعيَّنة", "Assigned to": "مُعيَّن إلى", "Assigned to me": "مُعيَّن إليّ", "Assigning…": "جارٍ التعيين…", "Atlassian app credentials": "بيانات اعتماد تطبيق Atlassian", "Att %": "الحضور %", "Attendance % over the selected period": "نسبة الحضور % خلال الفترة المحددة", "Attendance Bot": "بوت الحضور", "Attendance Breakdown": "تفصيل الحضور", "Attendance breakdown": "تفصيل الحضور", "Attendance breakdown, hours trend, and status distribution": "تفصيل الحضور واتجاه الساعات وتوزيع الحالة", "Attendance calendar — per user": "تقويم الحضور — لكل مستخدم", "Attendance heatmap, scoreboard, progress, and violations": "خريطة حرارية للحضور، ولوحة النتائج، والتقدّم، والمخالفات", "Attendance messages the bot has parsed and classified": "رسائل الحضور التي حلّلها البوت وصنّفها", "Attendance risk": "مخاطر الحضور", "Attendance tracking": "تتبّع الحضور", "Attendance — 30 days": "الحضور — 30 يومًا", "Authenticated login events and actions": "أحداث تسجيل الدخول المُوثَّقة والإجراءات", "Auto-invite team members to selected rooms (via assigned bot)": "دعوة أعضاء الفريق تلقائيًا إلى الغرف المحددة (عبر البوت المُعيَّن)", "Auto-open is muted — click to unmute": "الفتح التلقائي مكتوم — انقر لإلغاء الكتم", "Auto: ": "تلقائي: ", "Automation": "أتمتة", "Automations": "الأتمتة", "Average sign-off time, user's local TZ.": "متوسط وقت تسجيل الخروج، حسب المنطقة الزمنية المحلية للمستخدم.", "Average wall-clock sign-in time in the user's local timezone.": "متوسط وقت تسجيل الدخول الفعلي حسب المنطقة الزمنية المحلية للمستخدم.", "Avg progress score across the last 7 working days — recent trajectory.": "متوسط درجة التقدّم خلال آخر 7 أيام عمل — المسار الأخير.", "Avg sign-in": "متوسط تسجيل الدخول", "Avg sign-off": "متوسط تسجيل الخروج", "Awaiting dates": "بانتظار التواريخ", "Away:": "بعيد:", "Azure AD App (Bot) ID — paste here to bake into the manifest, or leave blank to download a template you can edit in Teams' Developer Portal.": "معرّف تطبيق (بوت) Azure AD — الصقه هنا لتضمينه في البيان، أو اتركه فارغًا لتنزيل قالب يمكنك تعديله في بوابة المطوّرين لـ Microsoft Teams.", "Azure AD Tenant ID": "معرّف مستأجر Azure AD", "Back to list": "العودة إلى القائمة", "Backup": "احتياطي", "Balances": "الأرصدة", "Balances fetch failed: ": "فشل جلب الأرصدة: ", "BarChart not loaded.": "لم يتم تحميل المخطط الشريطي.", "Blocked": "محظور", "Blocker / no sign-off": "عائق / لا يوجد تسجيل خروج", "Blocker reported": "تم الإبلاغ عن عائق", "Blocker:": "عائق:", "Blocker: ": "عائق: ", "Blockers resolved": "تم حل العوائق", "Bot": "بوت", "Bot Behavior overrides": "تجاوزات سلوك البوت", "Bot DMs": "الرسائل الخاصة للبوت", "Bot DMs and admin replies will appear here.": "ستظهر الرسائل الخاصة للبوت وردود المشرفين هنا.", "Bot User OAuth Token": "رمز OAuth لمستخدم البوت", "Bot ack behaviour": "سلوك إقرار البوت", "Bot assignment": "تعيين البوت", "Bot display name": "الاسم المعروض للبوت", "Bot health alerts are sent via": "تُرسَل تنبيهات صحة البوت عبر", "Bot ignores this user completely — no DMs, no attendance counting, no reports.": "يتجاهل البوت هذا المستخدم تمامًا — لا رسائل خاصة، ولا احتساب للحضور، ولا تقارير.", "Bot line-up updated for": "تم تحديث تشكيلة البوتات لـ", "Bot logic": "منطق البوت", "Bot name": "اسم البوت", "Bot reasoning:": "تعليل البوت:", "Bot silently skips the sign-off room ack at or after this local hour. Set 24 to disable. Default 21 (9 PM).": "يتخطى البوت بصمت إقرار غرفة تسجيل الخروج عند هذه الساعة المحلية أو بعدها. اضبطه على 24 للتعطيل. الافتراضي 21 (9 مساءً).", "Bot — not tracked": "بوت — غير متتبَّع", "Bot's verdict per signed-in day": "حكم البوت لكل يوم تم تسجيل الدخول فيه", "Bots in": "البوتات في", "Brand name": "اسم العلامة التجارية", "Brand name, color, slogan, support email, custom domain": "اسم العلامة التجارية واللون والشعار وبريد الدعم والنطاق المخصص", "Branding": "الهوية البصرية", "Breakdown by kind": "التفصيل حسب النوع", "Brief description...": "وصف موجز...", "Briefly describe the issue": "صِف المشكلة باختصار", "Broadcast a direct message to one or more users": "بثّ رسالة مباشرة إلى مستخدم واحد أو أكثر", "Browser": "المتصفح", "Builds a PDF report scoped to the selected employee + range. The PDF includes: hours-per-day line chart, attendance donut, PTO breakdown, top contributors bar, per-employee table. 'Send via DM' delivers the PDF to the employee's persistent DM with the bot.": "ينشئ تقرير PDF محصورًا بالموظف المحدد + النطاق. يتضمّن الـ PDF: مخطط خطي للساعات في اليوم، ومخطط دائري للحضور، وتفصيل الإجازات، وشريط أبرز المساهمين، وجدول لكل موظف. خيار 'إرسال عبر رسالة خاصة' يسلّم الـ PDF إلى الرسالة الخاصة الدائمة للموظف مع البوت.", "Built-in presets": "إعدادات مسبقة مدمجة", "Bulk DM": "رسائل خاصة جماعية", "Burnout cap (hours/day — bot flags above this)": "حد الإرهاق (ساعات/يوم — يضع البوت علامة عند تجاوزه)", "Burnout risk": "مخاطر الإرهاق", "By team": "حسب الفريق", "By type": "حسب النوع", "By user": "حسب المستخدم", "CPU": "المعالج", "Cadence": "الوتيرة", "Cairo": "القاهرة", "Calendar fetch failed: ": "فشل جلب التقويم: ", "Case Subject": "موضوع الحالة", "Case-sensitive": "حساس لحالة الأحرف", "Categories": "الفئات", "Category": "الفئة", "Cell phone": "الهاتف الجوّال", "Change in attendance % vs the equivalent prior window.": "التغيّر في نسبة الحضور % مقابل النافذة السابقة المكافئة.", "Change in total hours vs the equivalent prior window.": "التغيّر في إجمالي الساعات مقابل النافذة السابقة المكافئة.", "Change reverted": "تم التراجع عن التغيير", "Changed:": "تم التغيير:", "Changes apply within ~60s (config cache TTL). Restart the bot container for instant pickup.": "تُطبَّق التغييرات خلال ~60 ثانية (مدة بقاء ذاكرة التخزين المؤقت للإعدادات). أعد تشغيل حاوية البوت للتطبيق الفوري.", "Changes save automatically when switched.": "تُحفَظ التغييرات تلقائيًا عند التبديل.", "Channel": "القناة", "Channel ID (the standup channel)": "معرّف القناة (قناة الاجتماع اليومي)", "Charts": "المخططات", "Chat ping": "نبضة المحادثة", "Check config": "فحص الإعدادات", "Choose an export format": "اختر صيغة التصدير", "Choose the model that powers Ask Pulse and the morning roll-up": "اختر النموذج الذي يشغّل Ask Pulse والموجز الصباحي", "Choose which metrics show on this card": "اختر المقاييس التي تظهر على هذه البطاقة", "Clear": "مسح", "Clear all filters": "مسح كل عوامل التصفية", "Clear search": "مسح البحث", "Click": "انقر", "Click 'Generate AI summary' to compile sign-in messages into a narrative.": "انقر على 'توليد ملخص بالذكاء الاصطناعي' لتجميع رسائل تسجيل الدخول في سرد.", "Click any name to add/remove from this team. Members are also editable via the dedicated Members drawer at the top.": "انقر على أي اسم لإضافته/إزالته من هذا الفريق. يمكن تعديل الأعضاء أيضًا عبر لوحة الأعضاء المخصصة في الأعلى.", "Click to add/remove from this room": "انقر للإضافة/الإزالة من هذه الغرفة", "Click to change the annual vacation quota for everyone": "انقر لتغيير حصة الإجازة السنوية للجميع", "Click to filter": "انقر للتصفية", "Click to see every strike event for this user + validate count against the audit log": "انقر لرؤية كل أحداث الإنذارات لهذا المستخدم + التحقق من العدد مقابل سجل التدقيق", "Client ID": "معرّف العميل", "Client Secret": "السر السري للعميل", "Close (Esc)": "إغلاق (Esc)", "Codebase Search": "البحث في قاعدة الشيفرة", "Coming soon — endpoint not yet available": "قريبًا — نقطة النهاية غير متوفّرة بعد", "Comma or space separated. Useful for invitees who haven't onboarded yet.": "مفصولة بفواصل أو مسافات. مفيدة للمدعوّين الذين لم ينضمّوا بعد.", "Command": "أمر", "Comments": "التعليقات", "Compare to global": "مقارنة بالعام", "Compliance": "الامتثال", "Compliance — strikes per user": "الامتثال — الإنذارات لكل مستخدم", "Compose a manual DM to one user as the bot. Optional template dropdown for prebuilt messages.": "أنشئ رسالة خاصة يدوية لمستخدم واحد بصفة البوت. قائمة منسدلة اختيارية بالقوالب للرسائل الجاهزة.", "Computed from the same period as the rest of the drawer. Click the gear to pick which metrics to show — your selection is remembered per admin.": "محسوب من الفترة نفسها لبقية اللوحة. انقر على الترس لاختيار المقاييس المعروضة — يتم تذكّر اختيارك لكل مشرف.", "Computing report…": "جارٍ حساب التقرير…", "Configure Teams integration": "تكوين تكامل Microsoft Teams", "Configure credentials": "تكوين بيانات الاعتماد", "Configure how the bot interacts with your team. Changes apply within 60 seconds.": "كوّن كيفية تفاعل البوت مع فريقك. تُطبَّق التغييرات خلال 60 ثانية.", "Confirm deactivate": "تأكيد إلغاء التفعيل", "Connect": "اتصال", "Connect Jira site": "ربط موقع Jira", "Connect Slack workspace": "ربط مساحة عمل Slack", "Connect VPSie support": "ربط دعم VPSie", "Connect another site": "ربط موقع آخر", "Connect support room failed: ": "فشل ربط غرفة الدعم: ", "Connect the bot to #VPSie-Support:k9.ms? New messages there will become tickets, auto-assigned via the on-call calendar.": "هل تريد ربط البوت بـ #VPSie-Support:k9.ms؟ ستتحوّل الرسائل الجديدة هناك إلى تذاكر، تُسنَد تلقائيًا عبر تقويم المناوبة.", "Connect to #VPSie-Support:k9.ms — ingest tickets and auto-assign via the on-call calendar": "الاتصال بـ #VPSie-Support:k9.ms — استيعاب التذاكر والإسناد التلقائي عبر تقويم المناوبة", "Connect your VPSie account": "اربط حساب VPSie الخاص بك", "Connected to #VPSie-Support — ingestion armed": "متصل بـ #VPSie-Support — الاستيعاب مُفعَّل", "Connecting…": "جارٍ الاتصال…", "Connectivity": "الاتصالية", "Consecutive days signed in AND on-time, walking back from today.": "أيام متتالية تم فيها تسجيل الدخول وفي الموعد، بالرجوع من اليوم.", "Contract": "عقد", "Copied": "تم النسخ", "Copy": "نسخ", "Copy failed. Select the password manually and copy with Cmd/Ctrl+C.": "فشل النسخ. حدّد كلمة المرور يدويًا وانسخها بـ Cmd/Ctrl+C.", "Copy this password now — it will never be shown again": "انسخ كلمة المرور هذه الآن — لن تُعرَض مرة أخرى أبدًا", "Could not load article": "تعذّر تحميل المقال", "Count of today_text lines with a ship-verb across the period.": "عدد أسطر today_text التي تحتوي على فعل إنجاز خلال الفترة.", "Counts of standup messages grouped by parsed kind (signin / signoff / sick / pto / unknown). Last 30 days.": "أعداد رسائل الاجتماع اليومي مجمّعة حسب النوع المُحلَّل (دخول / خروج / مرضية / إجازة / غير معروف). آخر 30 يومًا.", "Coverage": "التغطية", "Create Support Case": "إنشاء حالة دعم", "Create a room": "إنشاء غرفة", "Create admin room": "إنشاء غرفة مشرف", "Create an Azure Bot at portal.azure.com, copy the credentials below, then side-load the manifest zip into your Teams tenant.": "أنشئ بوت Azure من portal.azure.com، وانسخ بيانات الاعتماد أدناه، ثم حمّل ملف البيان المضغوط جانبيًا إلى مستأجر Microsoft Teams الخاص بك.", "Create an OAuth 2.0 (3LO) app at developer.atlassian.com/console/myapps · Redirect URL:": "أنشئ تطبيق OAuth 2.0 (3LO) من developer.atlassian.com/console/myapps · عنوان إعادة التوجيه:", "Create new room": "إنشاء غرفة جديدة", "Create or register a K9 room to get started.": "أنشئ غرفة K9 أو سجّلها للبدء.", "Create room": "إنشاء غرفة", "Create team": "إنشاء فريق", "Create your first team to configure attendance rules.": "أنشئ فريقك الأول لتكوين قواعد الحضور.", "Created": "تم الإنشاء", "Creating Support Case…": "جارٍ إنشاء حالة الدعم…", "Creating...": "جارٍ الإنشاء...", "Creating…": "جارٍ الإنشاء…", "Cron": "Cron", "Cron / trigger": "Cron / مُحفِّز", "Current:": "الحالي:", "Currently NOT tracked here — click to start tracking": "غير متتبَّع هنا حاليًا — انقر لبدء التتبّع", "Currently on-call": "في المناوبة حاليًا", "Currently tracked — click to stop tracking sign-ins for this user in this room": "متتبَّع حاليًا — انقر لإيقاف تتبّع تسجيلات دخول هذا المستخدم في هذه الغرفة", "Custom domain": "نطاق مخصص", "Custom presets": "إعدادات مسبقة مخصصة", "Custom system prompt...": "موجّه نظام مخصص...", "Customer Success": "نجاح العملاء", "Customize app_name, accent color, login background. Settings persist per-tenant.": "خصّص app_name واللون المميِّز وخلفية تسجيل الدخول. تُحفَظ الإعدادات لكل مستأجر.", "Customized": "مخصَّص", "C… (right-click channel in Slack → View channel details → bottom)": "C… (انقر بزر الفأرة الأيمن على القناة في Slack ← عرض تفاصيل القناة ← الأسفل)", "DB": "قاعدة البيانات", "DB Schema Browser": "مستعرض مخطط قاعدة البيانات", "DB roundtrip": "رحلة ذهاب وإياب لقاعدة البيانات", "DM + room": "رسالة خاصة + غرفة", "DM admins": "رسالة خاصة للمشرفين", "DM digest to admins": "موجز برسالة خاصة للمشرفين", "DM only": "رسالة خاصة فقط", "DM this user": "إرسال رسالة خاصة لهذا المستخدم", "Daily": "يومي", "Daily accomplishments": "الإنجازات اليومية", "Daily attendance breakdown": "تفصيل الحضور اليومي", "Daily sign-in details": "تفاصيل تسجيل الدخول اليومي", "Dashboard role": "دور لوحة التحكم", "Database": "قاعدة البيانات", "Dates": "التواريخ", "Dates TBD": "التواريخ ستُحدَّد لاحقًا", "Days": "أيام", "Days absent in the selected period": "أيام الغياب في الفترة المحددة", "Days back:": "أيام للوراء:", "Days late": "أيام التأخّر", "Days late in the selected period": "أيام التأخّر في الفترة المحددة", "Days present": "أيام الحضور", "Days the employee signed in before (expected_start + late_buffer). Default 9:00 + 30 min.": "الأيام التي سجّل فيها الموظف الدخول قبل (وقت البدء المتوقع + مهلة التأخّر). الافتراضي 9:00 + 30 دقيقة.", "Days the employee signed in but past the late buffer. A 3rd late strike escalates to admin.": "الأيام التي سجّل فيها الموظف الدخول لكن بعد مهلة التأخّر. الإنذار الثالث للتأخّر يُصعَّد إلى المشرف.", "Days where the bot's progress score was 0 (today text was vague or copy-pasted from yesterday). Manager attention candidates.": "الأيام التي كانت فيها درجة تقدّم البوت 0 (كان نص اليوم غامضًا أو منسوخًا من الأمس). مرشّحة لانتباه المدير.", "Days worked below the expected hours": "أيام العمل بأقل من الساعات المتوقعة", "Days:": "أيام:", "Deactivate": "إلغاء التفعيل", "Deactivate bot": "إلغاء تفعيل البوت", "Deactivate failed": "فشل إلغاء التفعيل", "Deep-linked": "رابط عميق", "Default": "افتراضي", "Default — env bot (always added as creator)": "افتراضي — بوت البيئة (يُضاف دائمًا كمنشئ)", "Delete bot": "حذف البوت", "Delete failed:": "فشل الحذف:", "Delete failed: ": "فشل الحذف: ", "Delete permanently": "حذف نهائي", "Delete room": "حذف الغرفة", "Delete team": "حذف الفريق", "Delete this on-call rotation?": "هل تريد حذف مناوبة الطوارئ هذه؟", "Delete this team": "حذف هذا الفريق", "Delete this trigger?": "هل تريد حذف هذا المُحفِّز؟", "Deleted": "تم الحذف", "Deleting...": "جارٍ الحذف...", "Delivered items": "العناصر المُسلَّمة", "Delivery %": "نسبة التسليم %", "Denied": "مرفوض", "Deny": "رفض", "Describe the issue in detail…": "صِف المشكلة بالتفصيل…", "Describe the issue you have. The more details you add, the faster we'll be able to resolve it.": "صِف المشكلة التي لديك. كلما أضفت تفاصيل أكثر، أسرعنا في حلّها.", "Description": "الوصف", "Description (optional)": "الوصف (اختياري)", "Detail": "تفصيل", "DevOps / Infrastructure": "DevOps / البنية التحتية", "Digest generates automatically each week.": "يُولَّد الموجز تلقائيًا كل أسبوع.", "Disable": "تعطيل", "Disabled": "معطّل", "Discard": "تجاهل", "Discard team draft?": "هل تريد تجاهل مسودة الفريق؟", "Discard unsaved change": "تجاهل التغيير غير المحفوظ", "Disconnect": "قطع الاتصال", "Disconnect this Jira installation?": "هل تريد قطع اتصال تثبيت Jira هذا؟", "Disk": "القرص", "Dismiss": "تجاهل", "Display name": "الاسم المعروض", "Display name (optional)": "الاسم المعروض (اختياري)", "Display name is required": "الاسم المعروض مطلوب", "Docs will be fetched on first load.": "ستُجلَب الوثائق عند التحميل الأول.", "Doctor note": "تقرير طبي", "Doctor notes, contracts, ID copies, tax forms, performance reviews. Visible to admins/managers/HR and the employee themselves. Stored encrypted-at-rest.": "التقارير الطبية والعقود ونسخ الهوية والنماذج الضريبية وتقييمات الأداء. مرئية للمشرفين/المديرين/الموارد البشرية والموظف نفسه. مخزّنة مشفّرة أثناء السكون.", "Done": "تم", "Down": "متوقف", "Downgrade failed: ": "فشل التخفيض: ", "Downgrade strikes by 1": "تخفيض الإنذارات بمقدار 1", "Download": "تنزيل", "Download CSV": "تنزيل CSV", "Download PDF": "تنزيل PDF", "Download XLSX": "تنزيل XLSX", "Download a sample CSV (primary/backup/escalation + dates)": "تنزيل نموذج CSV (أساسي/احتياطي/تصعيد + التواريخ)", "Download failed: ": "فشل التنزيل: ", "Download manifest": "تنزيل البيان", "Download or send via DM": "تنزيل أو إرسال عبر رسالة خاصة", "Download report": "تنزيل التقرير", "Downloaded": "تم التنزيل", "Downloading…": "جارٍ التنزيل…", "Dry-run only — surfaces parse drift without mutating data": "تشغيل تجريبي فقط — يُظهِر انحراف التحليل دون تعديل البيانات", "Dry-run re-parse of the last N days' raw messages with the current parser. Shows which Checkin rows would change. Does not apply changes.": "إعادة تحليل تجريبية لرسائل آخر N يومًا الخام باستخدام المحلّل الحالي. يُظهِر صفوف Checkin التي ستتغيّر. لا يطبّق التغييرات.", "Each cadence runs on its cron; PATCH updates take effect on the next firing.": "تعمل كل وتيرة وفق جدول cron الخاص بها؛ تسري تحديثات PATCH عند التشغيل التالي.", "Each employee can have a different schedule": "يمكن أن يكون لكل موظف جدول مختلف", "Edit": "تعديل", "Edit bot": "تعديل البوت", "Edit calendar": "تعديل التقويم", "Edit grace (minutes — edits within this window still ack)": "مهلة التعديل (دقائق — التعديلات ضمن هذه النافذة لا تزال تُقَرّ)", "Edit prompt": "تعديل الموجّه", "Edit role": "تعديل الدور", "Edit room": "تعديل الغرفة", "Edit working hours schedule": "تعديل جدول ساعات العمل", "Editing is permission-gated: superadmin can edit any room; managers & admins can only edit rooms for their own team.": "التعديل مقيَّد بالصلاحيات: يمكن للمشرف الأعلى تعديل أي غرفة؛ ويمكن للمديرين والمشرفين تعديل غرف فريقهم فقط.", "Editing your own profile": "تعديل ملفك الشخصي", "Edits arriving within this many minutes of the original sign-in/off STILL get a room ack (typo fix). Later edits stay silent. Default 0 = always silent on edits.": "التعديلات الواردة خلال هذا العدد من الدقائق من تسجيل الدخول/الخروج الأصلي لا تزال تتلقّى إقرارًا في الغرفة (تصحيح خطأ مطبعي). التعديلات اللاحقة تبقى صامتة. الافتراضي 0 = صامت دائمًا عند التعديلات.", "Email": "البريد الإلكتروني", "Email (optional)": "البريد الإلكتروني (اختياري)", "Email alert rules. When an enabled rule fires, the bot emails alert_email_recipients via SendGrid.": "قواعد التنبيه بالبريد الإلكتروني. عند تفعيل قاعدة مُمكَّنة، يرسل البوت بريدًا إلى alert_email_recipients عبر SendGrid.", "Email alerts": "تنبيهات البريد الإلكتروني", "Employee": "موظف", "Employees can post standups directly from Slack. Sign-ins, sign-offs, and status declarations route through the same pipeline as K9 Chat.": "يمكن للموظفين نشر الاجتماعات اليومية مباشرة من Slack. تمرّ تسجيلات الدخول والخروج وإعلانات الحالة عبر المسار نفسه لـ K9 Chat.", "Employees whose today-line repeated from yesterday": "الموظفون الذين تكرّر سطر اليوم لديهم من الأمس", "Enable": "تمكين", "Enable Edit mode (top-right) to inline-edit per-team thresholds. Read-only summary:": "فعّل وضع التعديل (أعلى اليمين) لتعديل عتبات كل فريق مباشرة. ملخص للقراءة فقط:", "Enable WhatsApp escalation": "تمكين التصعيد عبر WhatsApp", "Enable alerts (when off, the scheduler still logs but does not send)": "تمكين التنبيهات (عند الإيقاف، لا يزال المُجدوِل يسجّل لكنه لا يرسل)", "Enabled": "مُمكَّن", "End": "النهاية", "End date": "تاريخ الانتهاء", "End date must be ≥ start": "يجب أن يكون تاريخ الانتهاء ≥ البداية", "Engineer": "مهندس", "Enter a preset name": "أدخل اسم إعداد مسبق", "Error": "خطأ", "Error:": "خطأ:", "Escalation": "تصعيد", "Escalations": "التصعيدات", "Event": "حدث", "Every Matrix message is parsed by the bot and routed through these rules. Numbers below control": "يحلّل البوت كل رسالة Matrix ويوجّهها عبر هذه القواعد. الأرقام أدناه تتحكّم في", "Every action the bot took — 90-day retention": "كل إجراء قام به البوت — استبقاء لمدة 90 يومًا", "Every approved PTO/sick/vacation/half-day block covering this range. Pending requests live on the Ops tab.": "كل كتل الإجازات/المرضية/الإجازة/نصف اليوم الموافق عليها التي تغطي هذا النطاق. الطلبات المعلّقة موجودة في علامة تبويب العمليات.", "Every background automation the bot runs.": "كل الأتمتة الخلفية التي يشغّلها البوت.", "Every employee": "كل موظف", "Every format-strike event in the month with the original message body. Strikes ≥3 trigger the admin report.": "كل أحداث إنذارات التنسيق في الشهر مع نص الرسالة الأصلي. الإنذارات ≥3 تُطلِق تقرير المشرف.", "Every sign-in posted in the selected window. Yesterday/today/blocker text are extracted from the raw Matrix message body.": "كل تسجيل دخول نُشِر في النافذة المحددة. تُستخرَج نصوص الأمس/اليوم/العائق من نص رسالة Matrix الخام.", "Every signed-in standup in the selected window scored progress ≥ 2.": "كل اجتماع يومي تم تسجيل الدخول فيه ضمن النافذة المحددة وسجّل تقدّمًا ≥ 2.", "Everyone": "الجميع", "Everyone is already in this room.": "الجميع موجودون بالفعل في هذه الغرفة.", "Exact match": "تطابق تام", "Existing team": "فريق موجود", "Export failed:": "فشل التصدير:", "Export failed: ": "فشل التصدير: ", "Exporting…": "جارٍ التصدير…", "Export…": "تصدير…", "External docs": "وثائق خارجية", "External source": "مصدر خارجي", "Failed to apply preset:": "فشل تطبيق الإعداد المسبق:", "Failed to create ticket. Please try again.": "فشل إنشاء التذكرة. يرجى المحاولة مرة أخرى.", "Failed to load schedule": "فشل تحميل الجدول", "Failed to load settings:": "فشل تحميل الإعدادات:", "Failed to post comment": "فشل نشر التعليق", "Failed to save": "فشل الحفظ", "Failed to update status": "فشل تحديث الحالة", "Failed:": "فشل:", "Failed: ": "فشل: ", "Failover order": "ترتيب التجاوز عند الفشل", "Favicon / logo (PNG, JPG, ICO — max 1 MB)": "أيقونة الموقع / الشعار (PNG، JPG، ICO — بحد أقصى 1 ميغابايت)", "Fill all required fields": "املأ كل الحقول المطلوبة", "Filter VPSie topics...": "تصفية مواضيع VPSie...", "Filter by actor — admin display name or bot label (case-insensitive substring)": "تصفية حسب الفاعل — الاسم المعروض للمشرف أو تسمية البوت (سلسلة فرعية غير حساسة لحالة الأحرف)", "Filter by user + period + violation kind (presence / late / absent / strike / missed_signoff / all). Shows counts and the event timeline so admins can drill into specific compliance issues per person.": "تصفية حسب المستخدم + الفترة + نوع المخالفة (حضور / متأخر / غائب / إنذار / تسجيل خروج فائت / الكل). تُظهِر الأعداد والخط الزمني للأحداث ليتمكّن المشرفون من التعمّق في مشكلات امتثال محددة لكل شخص.", "Filter by where they're signing in from (latest IP classification)": "تصفية حسب مكان تسجيل الدخول (أحدث تصنيف لعنوان IP)", "Filter employees…": "تصفية الموظفين…", "Filter team…": "تصفية الفريق…", "Finance": "المالية", "Fires": "يُطلِق", "Flexible": "مرن", "Flexible / superadmin (no bot nagging)": "مرن / مشرف أعلى (لا إزعاج من البوت)", "Flexible sign-in (skip deadline, track hours only)": "تسجيل دخول مرن (تخطّي الموعد النهائي، تتبّع الساعات فقط)", "Flexible: no nag DMs, no strikes, still counted on dashboard.": "مرن: لا رسائل إزعاج خاصة، ولا إنذارات، ولا يزال محتسبًا في لوحة التحكم.", "For standup rooms, leave this empty — the primary @ai.bot owns sign-in parsing & format checks. The picker is for Q&A rooms where you want a specific specialist to own replies.": "لغرف الاجتماع اليومي، اترك هذا فارغًا — يملك @ai.bot الأساسي تحليل تسجيل الدخول وفحوص التنسيق. المُحدِّد مخصص لغرف الأسئلة والأجوبة حيث تريد متخصصًا معيّنًا أن يملك الردود.", "Forgive ALL strikes": "العفو عن كل الإنذارات", "Forgive failed: ": "فشل العفو: ", "Format": "التنسيق", "Format strike": "إنذار تنسيق", "Format violations (attendance report)": "مخالفات التنسيق (تقرير الحضور)", "Forward-looking attendance risk from recent on-time rate + direction. Check in early on medium/high.": "مخاطر حضور استشرافية من معدل الالتزام بالموعد الأخير + الاتجاه. تابع مبكرًا عند المستوى المتوسط/المرتفع.", "Fr": "ج", "Fraction of today_text items containing a ship-verb (shipped/done/completed/merged/deployed/finished/closed/released).": "نسبة عناصر today_text التي تحتوي على فعل إنجاز (تم الشحن/منجز/مكتمل/مدموج/منشور/منتهٍ/مغلق/مُصدَر).", "Fri": "الجمعة", "From": "من", "From date": "من تاريخ", "From email (verified sender)": "من البريد الإلكتروني (مُرسِل مُوثَّق)", "Full K9 chat ID including the @user:server part": "معرّف K9 chat الكامل بما في ذلك جزء @user:server", "Full report": "تقرير كامل", "Full row-by-row dump (no row cap) for compliance / Excel": "تفريغ كامل صفًا بصف (بلا حد للصفوف) للامتثال / Excel", "Fully flexible (not tracked)": "مرن تمامًا (غير متتبَّع)", "Fully flexible — don't track sign-in times at all (exec / async teams)": "مرن تمامًا — لا تتبّع أوقات تسجيل الدخول إطلاقًا (الفرق التنفيذية / غير المتزامنة)", "General": "عام", "General Support": "الدعم العام", "Generate": "توليد", "Generate AI summary": "توليد ملخص بالذكاء الاصطناعي", "Generate report": "توليد التقرير", "Generate, download, or send reports via DM": "توليد التقارير أو تنزيلها أو إرسالها عبر رسالة خاصة", "Generating…": "جارٍ التوليد…", "Get a free API key at": "احصل على مفتاح API مجاني من", "Global value": "القيمة العامة", "Global:": "عام:", "Guest": "ضيف", "Guides, quick-start cards, and API reference": "أدلّة وبطاقات بدء سريع ومرجع API", "HR": "الموارد البشرية", "HR documents": "مستندات الموارد البشرية", "HUD": "شاشة العرض العلوية", "Half day": "نصف يوم", "Health of the bot's outbound connections — Matrix homeserver, DB, AI providers, mail relay. Auto-refreshes every 3s.": "صحة اتصالات البوت الصادرة — خادم Matrix الرئيسي، وقاعدة البيانات، ومزوّدي الذكاء الاصطناعي، ومُرحِّل البريد. يتحدّث تلقائيًا كل 3 ثوانٍ.", "Heatmap fetch failed: ": "فشل جلب الخريطة الحرارية: ", "Heatmap month:": "شهر الخريطة الحرارية:", "Heatmap of every employee × every day over the selected window (1/3/6/12 months). Color encodes status: green on-time, yellow late, red absent, blue PTO, hatched holiday, grey off-day.": "خريطة حرارية لكل موظف × كل يوم خلال النافذة المحددة (1/3/6/12 شهرًا). يرمز اللون للحالة: أخضر في الموعد، أصفر متأخر، أحمر غائب، أزرق إجازة، مخطّط عطلة، رمادي يوم عطلة.", "Heatmap window": "نافذة الخريطة الحرارية", "Help articles grouped by topic. Click a category to filter.": "مقالات المساعدة مجمّعة حسب الموضوع. انقر على فئة للتصفية.", "Help error": "خطأ في المساعدة", "Hide diff": "إخفاء الفروق", "Hide recipients": "إخفاء المستلمين", "Histogram of daily-hours bins across all employees in the range. Spot under/over-work patterns.": "رسم بياني تكراري لفئات الساعات اليومية عبر كل الموظفين في النطاق. اكتشف أنماط نقص/زيادة العمل.", "Holiday name required": "اسم العطلة مطلوب", "Holiday removed": "تمت إزالة العطلة", "Home": "المنزل", "Home IPs": "عناوين IP المنزلية", "Host probe every 60s — email on threshold breach": "فحص المضيف كل 60 ثانية — بريد إلكتروني عند تجاوز العتبة", "Host resource metrics are visible to admins only.": "مقاييس موارد المضيف مرئية للمشرفين فقط.", "Host resources": "موارد المضيف", "Host resources, connectivity, scheduler, branding, and request logs": "موارد المضيف والاتصالية والمُجدوِل والهوية البصرية وسجلات الطلبات", "Hour expectations & detection": "توقعات الساعات والكشف", "Hours Worked Today": "الساعات المعمولة اليوم", "Hours distribution": "توزيع الساعات", "Hours must be 0-24, or empty to clear": "يجب أن تكون الساعات 0-24، أو فارغة للمسح", "Hours today": "ساعات اليوم", "Hours — week trend": "الساعات — اتجاه الأسبوع", "Hours-per-day, attendance %, top contributors. Auto-refresh on filter change. PDF export via the Generate Report card above.": "الساعات في اليوم، ونسبة الحضور %، وأبرز المساهمين. تحديث تلقائي عند تغيير التصفية. تصدير PDF عبر بطاقة توليد التقرير أعلاه.", "How it works": "كيف يعمل", "How the bot decides things": "كيف يتخذ البوت القرارات", "Human-readable name": "اسم مقروء للبشر", "Human-readable summary (first 500 rows)": "ملخص مقروء للبشر (أول 500 صف)", "I have saved this password securely": "لقد حفظت كلمة المرور هذه بأمان", "ID document": "وثيقة هوية", "IP": "عنوان IP", "IP patterns": "أنماط عناوين IP", "IP red-flag detection": "كشف الإشارات الحمراء لعناوين IP", "IP red-flag enabled": "كشف الإشارات الحمراء لعناوين IP مُمكَّن", "Identity": "الهوية", "Importing…": "جارٍ الاستيراد…", "In:": "دخول:", "Inactivity reminder (hours)": "تذكير عدم النشاط (ساعات)", "Inline settings (admin edit mode)": "إعدادات مباشرة (وضع تعديل المشرف)", "Interval (minutes between checks per user)": "الفاصل (دقائق بين الفحوص لكل مستخدم)", "Invalid": "غير صالح", "Invalid matrix ID:": "معرّف Matrix غير صالح:", "Invite": "دعوة", "Invite a new employee": "دعوة موظف جديد", "Invite failed:": "فشلت الدعوة:", "Invite people": "دعوة أشخاص", "Invite to ALL rooms": "دعوة إلى كل الغرف", "Invite to rooms": "دعوة إلى الغرف", "Invited": "تمت الدعوة", "Issue": "مشكلة", "Issue Details": "تفاصيل المشكلة", "Issue details are required.": "تفاصيل المشكلة مطلوبة.", "Issues": "المشكلات", "Issues:": "المشكلات:", "Jira (Atlassian OAuth)": "Jira (Atlassian OAuth)", "Job name": "اسم المهمة", "K9 Chat ID must match @user:server": "يجب أن يطابق معرّف K9 Chat الصيغة @user:server", "K9 Room ID *": "معرّف غرفة K9 *", "K9 sync": "مزامنة K9", "K9 sync lag": "تأخّر مزامنة K9", "Keyword lists": "قوائم الكلمات المفتاحية", "Keywords": "الكلمات المفتاحية", "Kick": "طرد", "Kick failed:": "فشل الطرد:", "Kick from room": "طرد من الغرفة", "Kicked": "تم الطرد", "Kind": "النوع", "Kinds tracked": "الأنواع المتتبَّعة", "Last": "الأخير", "Last 1000 HTTP requests to the dashboard API. Filter by IP, status, path, method, user. Used to debug 4xx/5xx spikes.": "آخر 1000 طلب HTTP إلى API لوحة التحكم. صفِّ حسب عنوان IP والحالة والمسار والطريقة والمستخدم. يُستخدَم لتصحيح ارتفاعات 4xx/5xx.", "Last 14 days": "آخر 14 يومًا", "Last 3 days": "آخر 3 أيام", "Last 30 days": "آخر 30 يومًا", "Last 30 days: % of reported blocker-days that aren't still open today.": "آخر 30 يومًا: نسبة % لأيام العوائق المُبلَّغ عنها التي لم تعد مفتوحة اليوم.", "Last 50 audit-log events involving this user — DMs sent, strikes, PTO submissions, admin actions. Labels are humanized (e.g. format_strike → 'Format strike').": "آخر 50 حدثًا في سجل التدقيق تخص هذا المستخدم — الرسائل الخاصة المُرسَلة، والإنذارات، وطلبات الإجازة، وإجراءات المشرف. التسميات مبسّطة للقراءة (مثلًا format_strike ← 'إنذار تنسيق').", "Last 7 days": "آخر 7 أيام", "Last probe:": "آخر فحص:", "Last run": "آخر تشغيل", "Last test: FAILED": "آخر اختبار: فشل", "Last test: OK": "آخر اختبار: ناجح", "Last updated": "آخر تحديث", "Last violation": "آخر مخالفة", "Last warning": "الإنذار الأخير", "Last-30-day pie: on-time / late / absent slices. Click slices for details.": "مخطط دائري لآخر 30 يومًا: شرائح في الموعد / متأخر / غائب. انقر على الشرائح للتفاصيل.", "Late buffer (minutes)": "مهلة التأخّر (دقائق)", "Late lookback (days)": "نطاق مراجعة التأخّر (أيام)", "Late min samples": "الحد الأدنى لعيّنات التأخّر", "Late sign-in": "تسجيل دخول متأخر", "Latest summary": "أحدث ملخص", "Latest:": "الأحدث:", "Leaderboards": "لوحات الصدارة", "Learned from this person's own sign-in/out history (their timezone)": "مُتعلَّم من سجل تسجيل الدخول/الخروج الخاص بهذا الشخص (منطقته الزمنية)", "Leave blank to keep existing": "اتركه فارغًا للإبقاء على الموجود", "Leave left": "إجازة متبقية", "Let employees post standups directly in Microsoft Teams. Free-text Y/T/B and Adaptive Card form both supported. Multi-tenant; credentials stored encrypted.": "اسمح للموظفين بنشر الاجتماعات اليومية مباشرة في Microsoft Teams. يُدعَم كل من النص الحر للأمس/اليوم/العائق ونموذج البطاقة التكيّفية. متعدد المستأجرين؛ بيانات الاعتماد مخزّنة مشفّرة.", "Lifetime strikes left out of strike cap (default 3)": "الإنذارات المتبقية مدى الحياة من حد الإنذارات (الافتراضي 3)", "Line trace": "تتبّع الخط", "Live CPU / memory / disk on the VPS hosting the bot. Sampled every 3s; donuts smoothly animate between samples.": "المعالج / الذاكرة / القرص المباشر على الخادم الافتراضي المستضيف للبوت. تُؤخَذ العيّنات كل 3 ثوانٍ؛ تتحرّك المخططات الدائرية بسلاسة بين العيّنات.", "Live K9 presence": "تواجد K9 المباشر", "Live service health · polled every 3s": "صحة الخدمة المباشرة · يُستطلَع كل 3 ثوانٍ", "Live · polled every 3s — requires admin": "مباشر · يُستطلَع كل 3 ثوانٍ — يتطلّب مشرفًا", "Load failed": "فشل التحميل", "Load failed: ": "فشل التحميل: ", "Loading PTO…": "جارٍ تحميل الإجازات…", "Loading audit…": "جارٍ تحميل التدقيق…", "Loading balances...": "جارٍ تحميل الأرصدة...", "Loading bot logic…": "جارٍ تحميل منطق البوت…", "Loading calendar...": "جارٍ تحميل التقويم...", "Loading digest…": "جارٍ تحميل الموجز…", "Loading directory…": "جارٍ تحميل الدليل…", "Loading heatmap…": "جارٍ تحميل الخريطة الحرارية…", "Loading hours history…": "جارٍ تحميل سجل الساعات…", "Loading hours…": "جارٍ تحميل الساعات…", "Loading parser trace…": "جارٍ تحميل تتبّع المحلّل…", "Loading presence…": "جارٍ تحميل التواجد…", "Loading profile…": "جارٍ تحميل الملف الشخصي…", "Loading recipients…": "جارٍ تحميل المستلمين…", "Loading roster…": "جارٍ تحميل القائمة…", "Loading schema…": "جارٍ تحميل المخطط…", "Loading scoreboard…": "جارٍ تحميل لوحة النتائج…", "Loading settings...": "جارٍ تحميل الإعدادات...", "Loading strikes…": "جارٍ تحميل الإنذارات…", "Loading tasks…": "جارٍ تحميل المهام…", "Loading tickets…": "جارٍ تحميل التذاكر…", "Loading violations…": "جارٍ تحميل المخالفات…", "Loading...": "جارٍ التحميل...", "Location check": "فحص الموقع", "Login IPs the user has signed in from + a red-flag indicator when a new IP looks suspicious (per IP-learning thresholds in team settings).": "عناوين IP التي سجّل المستخدم الدخول منها + مؤشر إشارة حمراء عندما يبدو عنوان IP جديد مريبًا (وفق عتبات تعلّم IP في إعدادات الفريق).", "Login tagline": "شعار تسجيل الدخول", "Longest streak": "أطول سلسلة متتالية", "Made": "أُنجِز", "Make active": "جعله نشطًا", "Make this dashboard your own. Brand name, slogan and primary color are applied immediately to the header and login screen. Domain is for your records — your ops team still owns the actual nginx + DNS configuration.": "اجعل لوحة التحكم هذه خاصة بك. يُطبَّق اسم العلامة التجارية والشعار واللون الأساسي فورًا على الترويسة وشاشة تسجيل الدخول. النطاق لسجلّاتك — لا يزال فريق العمليات لديك يملك التكوين الفعلي لـ nginx + DNS.", "Manage VMs": "إدارة الأجهزة الافتراضية", "Manage members": "إدارة الأعضاء", "Manage teams": "إدارة الفرق", "Manager": "مدير", "Manager DM fired Mondays 09:00 local. Per-team summary: attendance, blockers, outliers. Triggered by _job_manager_weekly_digest.": "رسالة خاصة للمدير تُرسَل أيام الاثنين الساعة 09:00 محليًا. ملخص لكل فريق: الحضور والعوائق والقيم الشاذة. يُطلِقها _job_manager_weekly_digest.", "Manager or admin required": "يتطلّب مديرًا أو مشرفًا", "Manager+ only": "للمدير فما فوق فقط", "Manual K9 IDs": "معرّفات K9 يدوية", "Manually add a strike (90s undo)": "إضافة إنذار يدويًا (تراجع خلال 90 ثانية)", "Mark this IP red-flag as reviewed": "وضع علامة على هذه الإشارة الحمراء لعنوان IP كمُراجَعة", "Match": "تطابق", "Match %": "نسبة التطابق %", "Match mode": "وضع المطابقة", "Matched:": "تطابق:", "Matrix room": "غرفة Matrix", "Matrix room ID (e.g. !abc:k9.ms):": "معرّف غرفة Matrix (مثال !abc:k9.ms):", "Matrix topic": "موضوع Matrix", "Max consecutive working days signed in on time.": "أقصى عدد أيام عمل متتالية تم فيها تسجيل الدخول في الموعد.", "Members": "الأعضاء", "Members updated —": "تم تحديث الأعضاء —", "Memory": "الذاكرة", "Message": "رسالة", "Message sent!": "تم إرسال الرسالة!", "Messaging integrations": "تكاملات المراسلة", "Method": "الطريقة", "Min hours / day": "أدنى عدد ساعات / يوم", "Min match % (60 = 60% agreement)": "أدنى نسبة تطابق % (60 = 60% توافق)", "Min samples": "أدنى عدد عيّنات", "Min samples before classification": "أدنى عدد عيّنات قبل التصنيف", "Mismatch": "عدم تطابق", "Missed sign-off": "تسجيل خروج فائت", "Missing-signoff poll cadence (minutes)": "وتيرة استطلاع تسجيل الخروج الفائت (دقائق)", "Mo": "ن", "Moderator": "مُشرِف محتوى", "Moderators can only edit their own profile": "يمكن لمشرفي المحتوى تعديل ملفهم الشخصي فقط", "Mon": "الاثنين", "Mon-Fri workweek (Americas / Europe / UTC default; Sat+Sun off)": "أسبوع عمل من الاثنين إلى الجمعة (الأمريكتان / أوروبا / الافتراضي UTC؛ السبت+الأحد عطلة)", "Monitoring & Alerts": "المراقبة والتنبيهات", "Monthly": "شهري", "Mon–Fri": "الاثنين–الجمعة", "More report options": "خيارات تقارير إضافية", "Most recent attendance": "أحدث حضور", "Move down": "تحريك لأسفل", "Move up": "تحريك لأعلى", "Msgs changed": "الرسائل المتغيّرة", "Must be a Meta-approved template. Default:": "يجب أن يكون قالبًا معتمدًا من Meta. الافتراضي:", "Must match @user:server": "يجب أن يطابق @user:server", "Must match format !xxx:server": "يجب أن يطابق الصيغة !xxx:server", "Mute": "كتم", "Mute auto-open (proactive HUD)": "كتم الفتح التلقائي (شاشة العرض الاستباقية)", "Name": "الاسم", "Name & timezone": "الاسم والمنطقة الزمنية", "Network (total)": "الشبكة (الإجمالي)", "New": "جديد", "New IPs first observed in last 30 days": "عناوين IP جديدة رُصِدت أول مرة في آخر 30 يومًا", "New Tab": "علامة تبويب جديدة", "New team": "فريق جديد", "New team name…": "اسم فريق جديد…", "Next": "التالي", "Next 30 days of on-call shifts. Edit any cell to override (admin only).": "مناوبات الطوارئ لـ 30 يومًا القادمة. عدّل أي خلية للتجاوز (للمشرف فقط).", "Next run": "التشغيل التالي", "Next:": "التالي:", "No AI summary yet": "لا يوجد ملخص بالذكاء الاصطناعي بعد", "No PTO in this range.": "لا توجد إجازات في هذا النطاق.", "No VPSie topics": "لا توجد مواضيع VPSie", "No activity in this period.": "لا يوجد نشاط في هذه الفترة.", "No additional bots configured.": "لم تُكوَّن بوتات إضافية.", "No alerts fired.": "لم تُطلَق أي تنبيهات.", "No articles": "لا توجد مقالات", "No attendance data": "لا توجد بيانات حضور", "No attendance data for this month.": "لا توجد بيانات حضور لهذا الشهر.", "No attendance data in the last 30 days.": "لا توجد بيانات حضور في آخر 30 يومًا.", "No attendance data.": "لا توجد بيانات حضور.", "No attendance hours logged for this period.": "لم تُسجَّل ساعات حضور لهذه الفترة.", "No audit events match": "لا توجد أحداث تدقيق مطابقة", "No audit events recorded": "لم تُسجَّل أحداث تدقيق", "No audit events.": "لا توجد أحداث تدقيق.", "No automations.": "لا توجد أتمتة.", "No balance data": "لا توجد بيانات أرصدة", "No bot": "لا يوجد بوت", "No bot assigned": "لم يُعيَّن بوت", "No bots registered. Add some in the Bots tab first.": "لا توجد بوتات مسجّلة. أضف بعضها في علامة تبويب البوتات أولًا.", "No bots yet": "لا توجد بوتات بعد", "No changes": "لا توجد تغييرات", "No comments yet · be the first to add one": "لا توجد تعليقات بعد · كن أول من يضيف تعليقًا", "No custom chain — using the built-in default order.": "لا توجد سلسلة مخصصة — باستخدام الترتيب الافتراضي المدمج.", "No data for this period.": "لا توجد بيانات لهذه الفترة.", "No data.": "لا توجد بيانات.", "No digest available": "لا يوجد موجز متاح", "No distribution data": "لا توجد بيانات توزيع", "No documents.": "لا توجد مستندات.", "No drift detected — parser output matches existing checkins.": "لم يُكتشَف انحراف — مخرجات المحلّل تطابق تسجيلات الدخول الموجودة.", "No employee data in this digest.": "لا توجد بيانات موظفين في هذا الموجز.", "No employees available.": "لا يوجد موظفون متاحون.", "No employees have signed in for this date yet.": "لم يسجّل أي موظف الدخول لهذا التاريخ بعد.", "No employees loaded": "لم يُحمَّل أي موظف", "No employees match": "لا يوجد موظفون مطابقون", "No employees.": "لا يوجد موظفون.", "No events for this filter.": "لا توجد أحداث لهذا الفلتر.", "No fixed deadline. The bot learns each member's usual sign-in & sign-off time (in their own timezone, after ~4 days) and flags only when someone is later than their own average in — or earlier out. Signing in early is fine.": "لا يوجد موعد نهائي ثابت. يتعلّم البوت الوقت المعتاد لتسجيل الدخول والخروج لكل عضو (في منطقته الزمنية، بعد ~4 أيام) ويضع علامة فقط عندما يتأخّر أحدهم عن متوسطه في الدخول — أو يخرج أبكر. تسجيل الدخول مبكرًا أمر جيّد.", "No format violations in this period.": "لا توجد مخالفات تنسيق في هذه الفترة.", "No hours data": "لا توجد بيانات ساعات", "No hours logged yet today.": "لم تُسجَّل ساعات بعد اليوم.", "No items — add below": "لا توجد عناصر — أضف أدناه", "No log entries": "لا توجد إدخالات سجل", "No matches.": "لا توجد تطابقات.", "No matching employees.": "لا يوجد موظفون مطابقون.", "No members found": "لم يُعثَر على أعضاء", "No members match": "لا يوجد أعضاء مطابقون", "No messages yet": "لا توجد رسائل بعد", "No metrics selected — click": "لم تُحدَّد مقاييس — انقر", "No no-progress flags": "لا توجد إشارات عدم تقدّم", "No no-progress flags for this period": "لا توجد إشارات عدم تقدّم لهذه الفترة", "No off-days set": "لم تُحدَّد أيام عطلة", "No on-call data available.": "لا توجد بيانات مناوبة متاحة.", "No overrides set — all settings inherit from global.": "لم تُحدَّد تجاوزات — كل الإعدادات موروثة من العام.", "No pending requests": "لا توجد طلبات معلّقة", "No people match": "لا يوجد أشخاص مطابقون", "No presence telemetry today.": "لا توجد قياسات تواجد اليوم.", "No probe data yet": "لا توجد بيانات فحص بعد", "No recent events": "لا توجد أحداث حديثة", "No recipients selected": "لم يُحدَّد مستلمون", "No records for this period.": "لا توجد سجلات لهذه الفترة.", "No records found for this metric and period.": "لم يُعثَر على سجلات لهذا المقياس وهذه الفترة.", "No requests yet": "لا توجد طلبات بعد", "No rooms available": "لا توجد غرف متاحة", "No rooms available. Create rooms first.": "لا توجد غرف متاحة. أنشئ غرفًا أولًا.", "No rooms configured.": "لم تُكوَّن غرف.", "No rooms yet": "لا توجد غرف بعد", "No scheduler data": "لا توجد بيانات مُجدوِل", "No scoreboard data": "لا توجد بيانات لوحة نتائج", "No settings match": "لا توجد إعدادات مطابقة", "No sign-in": "لا يوجد تسجيل دخول", "No sign-in activity to send": "لا يوجد نشاط تسجيل دخول لإرساله", "No sign-in messages recorded.": "لم تُسجَّل رسائل تسجيل دخول.", "No sign-in recorded today": "لم يُسجَّل دخول اليوم", "No sign-ins yet today.": "لا توجد تسجيلات دخول بعد اليوم.", "No sign-off yet — running hours are still accruing": "لا يوجد تسجيل خروج بعد — لا تزال الساعات الجارية تتراكم", "No strike events on record.": "لا توجد أحداث إنذارات مسجّلة.", "No tables found.": "لم يُعثَر على جداول.", "No team": "بلا فريق", "No teams found.": "لم يُعثَر على فرق.", "No teams yet": "لا توجد فرق بعد", "No triggers matched.": "لم تتطابق أي مُحفِّزات.", "No upcoming rotations": "لا توجد مناوبات قادمة", "No violations": "لا توجد مخالفات", "No violations in this window 🎉": "لا توجد مخالفات في هذه النافذة 🎉", "No violations recorded in this range.": "لم تُسجَّل مخالفات في هذا النطاق.", "No-progress flags": "إشارات عدم التقدّم", "Nobody on-call right now": "لا أحد في المناوبة الآن", "None": "لا شيء", "None available": "لا شيء متاح", "Not an employee": "ليس موظفًا", "Not connected": "غير متصل", "Note: The bot will still be in the K9 room. Leave manually in Element/Synapse if desired.": "ملاحظة: سيبقى البوت في غرفة K9. غادر يدويًا في Element/Synapse إذا رغبت.", "Note: rule firing is configured here but evaluated by the scheduler's event hooks — not from inbound text.": "ملاحظة: يُكوَّن إطلاق القاعدة هنا لكن يُقيَّم بواسطة خطافات أحداث المُجدوِل — وليس من النص الوارد.", "Notes": "ملاحظات", "Notify admin": "إبلاغ المشرف", "Notify failed: ": "فشل الإبلاغ: ", "Nullable": "قابل لأن يكون فارغًا", "OFF": "إيقاف", "OK": "موافق", "ON": "تشغيل", "OVERRIDE": "تجاوز", "Off": "إيقاف", "Off:": "عطلة:", "Office": "المكتب", "Office IPs": "عناوين IP للمكتب", "Offline:": "غير متصل:", "Oldest:": "الأقدم:", "On": "تشغيل", "On-call calendar": "تقويم المناوبة", "On-call rotation created": "تم إنشاء مناوبة الطوارئ", "On-call rotations": "مناوبات الطوارئ", "On-call standings": "ترتيب المناوبة", "On-time / late / absent / sick / vacation / half-day / holiday days over the selected period. Half-days count 0.5 toward on-time.": "أيام في الموعد / متأخر / غائب / مرضية / إجازة / نصف يوم / عطلة خلال الفترة المحددة. تُحتسَب أنصاف الأيام بـ 0.5 ضمن أيام الالتزام بالموعد.", "On-time rate this week vs last week. ↑ improving, ↓ slipping.": "معدل الالتزام بالموعد هذا الأسبوع مقابل الأسبوع الماضي. ↑ تحسّن، ↓ تراجع.", "On-time streak": "سلسلة الالتزام بالموعد", "On-time trend": "اتجاه الالتزام بالموعد", "Onboarding": "الانضمام", "One item per line": "عنصر واحد في كل سطر", "One or more files exceed the 25 MB per-file limit.": "ملف واحد أو أكثر يتجاوز حد 25 ميغابايت لكل ملف.", "Online:": "متصل:", "Only admins can add company holidays.": "يمكن للمشرفين فقط إضافة عطل الشركة.", "Only admins can remove company holidays.": "يمكن للمشرفين فقط إزالة عطل الشركة.", "Only lowercase a-z, 0-9, dots, underscores, hyphens": "أحرف صغيرة a-z و0-9 ونقاط وشرطات سفلية وشرطات فقط", "Open": "فتح", "Open scoreboard (pies + tables + AI summary + send-to-user)": "فتح لوحة النتائج (مخططات دائرية + جداول + ملخص بالذكاء الاصطناعي + إرسال للمستخدم)", "Open scoreboard (view only)": "فتح لوحة النتائج (عرض فقط)", "Open tasks >= 2 days old without a finished_at — candidates for nudge.": "المهام المفتوحة منذ يومين أو أكثر بلا finished_at — مرشّحة للتنبيه.", "Open the create-room modal pre-set to type=admin (description + members + bots)": "فتح نافذة إنشاء الغرفة مُعدّة مسبقًا على type=admin (الوصف + الأعضاء + البوتات)", "Open this HUD in a new browser tab": "فتح شاشة العرض هذه في علامة تبويب متصفح جديدة", "Optional !room:server": "!room:server اختياري", "Optional notes": "ملاحظات اختيارية", "Optional reason for declining (visible to the user). Leave blank to skip:": "سبب اختياري للرفض (مرئي للمستخدم). اتركه فارغًا للتخطّي:", "Optional reply back to the original sender": "رد اختياري إلى المُرسِل الأصلي", "Or add by matrix ID": "أو أضف بمعرّف Matrix", "Org name, primary color, favicon, logo. Changes auto-reload across all open dashboards (#37) — no hard refresh needed.": "اسم المؤسسة واللون الأساسي وأيقونة الموقع والشعار. تتحدّث التغييرات تلقائيًا عبر كل لوحات التحكم المفتوحة (#37) — لا حاجة لتحديث كامل.", "Org role": "دور المؤسسة", "Other": "أخرى", "Out:": "خروج:", "Override the default sign-in room ack. Placeholders: {first} = first name, {name} = full name, {tail} = welcome back / early start / late.": "تجاوز إقرار غرفة تسجيل الدخول الافتراضي. العناصر النائبة: {first} = الاسم الأول، {name} = الاسم الكامل، {tail} = مرحبًا بعودتك / بداية مبكرة / متأخر.", "Override the default sign-off room ack. Use {name} for the user's full display name.": "تجاوز إقرار غرفة تسجيل الخروج الافتراضي. استخدم {name} للاسم المعروض الكامل للمستخدم.", "PK": "مفتاح أساسي", "PTO balances will appear once quota is configured.": "ستظهر أرصدة الإجازات بمجرد تكوين الحصة.", "PTO blocks": "كتل الإجازات", "PTO calendar": "تقويم الإجازات", "PTO calendar — this month": "تقويم الإجازات — هذا الشهر", "PTO history": "سجل الإجازات", "PTO request created": "تم إنشاء طلب الإجازة", "PTO requests will show up here for review.": "ستظهر طلبات الإجازة هنا للمراجعة.", "PTOs": "الإجازات", "Partial": "جزئي", "Path": "المسار", "Path:": "المسار:", "Pending": "قيد الانتظار", "Pending — dates TBD": "قيد الانتظار — التواريخ ستُحدَّد لاحقًا", "Per employee — stacked by day · use the Range dropdown to scope": "لكل موظف — مكدّس حسب اليوم · استخدم قائمة النطاق المنسدلة لتحديد النطاق", "Per-day progress score (0/1/2). Bot scores each sign-in vs yesterday: 2 = made progress, 1 = partial, 0 = none. Card shows daily breakdown.": "درجة التقدّم اليومية (0/1/2). يقيّم البوت كل تسجيل دخول مقابل الأمس: 2 = أحرز تقدّمًا، 1 = جزئي، 0 = لا شيء. تعرض البطاقة التفصيل اليومي.", "Per-day progress score (0–2) across the selected period. Light vertical bands = approved PTO (color-coded by kind). Orange dots on the score line = days with a reported blocker. Red ▲ triangles at the top = strike events. Hover any day for details.": "درجة التقدّم اليومية (0–2) خلال الفترة المحددة. الأشرطة العمودية الفاتحة = إجازة موافق عليها (مرمَّزة بالألوان حسب النوع). النقاط البرتقالية على خط الدرجة = أيام فيها عائق مُبلَّغ عنه. المثلثات الحمراء ▲ في الأعلى = أحداث إنذارات. مرّر فوق أي يوم للتفاصيل.", "Per-employee violations": "المخالفات لكل موظف", "Per-person quota / used / remaining": "الحصة / المستخدم / المتبقي لكل شخص", "Per-team thresholds for the suspicious-IP detector. Edit mode required.": "عتبات كل فريق لكاشف عناوين IP المريبة. وضع التعديل مطلوب.", "Per-tenant Atlassian Cloud integration via OAuth 2.0 (3LO). Each tenant connects their own Atlassian site and picks a target project + status mapping.": "تكامل Atlassian Cloud لكل مستأجر عبر OAuth 2.0 (3LO). يربط كل مستأجر موقع Atlassian الخاص به ويختار مشروعًا هدفًا + تعيين الحالة.", "Per-user IP-fingerprint detection. When a sign-in comes from a far-from-baseline IP, flag the audit row + DM admin. Tune samples / match_pct / throttle_hours here.": "كشف بصمة IP لكل مستخدم. عندما يأتي تسجيل دخول من عنوان IP بعيد عن الأساس، ضع علامة على صف التدقيق + أرسل رسالة خاصة للمشرف. اضبط العيّنات / match_pct / throttle_hours هنا.", "Per-user message counts in the 30-day window. Click a row to filter the message list.": "أعداد رسائل كل مستخدم في نافذة الـ 30 يومًا. انقر على صف لتصفية قائمة الرسائل.", "Per-user month summary: hours, days present, late, attendance %. Drives the per-employee monthly DM.": "ملخص شهري لكل مستخدم: الساعات وأيام الحضور والتأخّر ونسبة الحضور %. يشغّل الرسالة الخاصة الشهرية لكل موظف.", "Per-user on-call coverage stats — shifts, hours covered, response time avg. Defensive metric for fair-rotation review.": "إحصائيات تغطية المناوبة لكل مستخدم — المناوبات والساعات المغطّاة ومتوسط زمن الاستجابة. مقياس دفاعي لمراجعة عدالة المناوبة.", "Per-user sign-in mute (opt specific users out of tracking)": "كتم تسجيل دخول لكل مستخدم (استثناء مستخدمين محددين من التتبّع)", "Per-user summary parsed from sign-in messages, with progress flags + blockers": "ملخص لكل مستخدم مُحلَّل من رسائل تسجيل الدخول، مع إشارات التقدّم + العوائق", "Performance review": "تقييم الأداء", "Period": "الفترة", "Period metrics": "مقاييس الفترة", "Period total": "إجمالي الفترة", "Period:": "الفترة:", "Permanently delete": "حذف نهائي", "Permanently delete team": "حذف الفريق نهائيًا", "Permanently delete this bot": "حذف هذا البوت نهائيًا", "Permanently remove": "إزالة نهائية", "Persona": "الشخصية", "Personal": "شخصي", "Personal day": "يوم شخصي", "Phone Number ID": "معرّف رقم الهاتف", "Phrases the bot treats as triggers. Comma-separated. Fuzzy-matched (close spellings + word variants count too).": "العبارات التي يعاملها البوت كمُحفِّزات. مفصولة بفواصل. مطابقة تقريبية (الأخطاء الإملائية القريبة + صيغ الكلمات تُحتسَب أيضًا).", "Pick": "اختر", "Pick a file first": "اختر ملفًا أولًا", "Pick a person…": "اختر شخصًا…", "Pick a team…": "اختر فريقًا…", "Pick a template…": "اختر قالبًا…", "Pick a user": "اختر مستخدمًا", "Pick a user to see violations.": "اختر مستخدمًا لرؤية المخالفات.", "Pick a user, then filter by type. Last update reflects the current period selection.": "اختر مستخدمًا، ثم صفِّ حسب النوع. يعكس آخر تحديث اختيار الفترة الحالي.", "Pick employees": "اختر موظفين", "Pie of online/away/offline/in-call minutes during today's signed-in window. Sampled every minute from Synapse + cross-referenced with call sessions.": "مخطط دائري لدقائق المتصل/البعيد/غير المتصل/في مكالمة خلال نافذة تسجيل الدخول اليوم. تُؤخَذ العيّنات كل دقيقة من Synapse + تُقابَل مع جلسات المكالمات.", "Pingpong (random availability checks)": "Pingpong (فحوص توفّر عشوائية)", "Pingpong enabled": "Pingpong مُمكَّن", "Post comment": "نشر تعليق", "Post to room": "نشر في الغرفة", "Presence": "التواجد", "Preset": "إعداد مسبق", "Preset name": "اسم الإعداد المسبق", "Presets": "الإعدادات المسبقة", "Previous attendance": "الحضور السابق", "Primary": "أساسي", "Primary + secondary on-call right now (per the active rotation). Auto-resolves on every ticket landing.": "المناوبة الأساسية + الثانوية الآن (وفق المناوبة النشطة). تُحسَم تلقائيًا عند وصول كل تذكرة.", "Primary AI bot": "بوت الذكاء الاصطناعي الأساسي", "Primary bot persona (Q&A replies)": "شخصية البوت الأساسية (ردود الأسئلة والأجوبة)", "Primary color": "اللون الأساسي", "Prio": "الأولوية", "Priority": "الأولوية", "Private email": "بريد إلكتروني خاص", "Proactive": "استباقي", "Proactive update": "تحديث استباقي", "Probe runs every 60s.": "يعمل الفحص كل 60 ثانية.", "Processing…": "جارٍ المعالجة…", "Progress fetch failed: ": "فشل جلب التقدّم: ", "Progress flags": "إشارات التقدّم", "Progress timeline": "الخط الزمني للتقدّم", "Project / PM": "المشروع / مدير المشروع", "Project:": "المشروع:", "Provision bot": "تجهيز بوت", "Provision new bot": "تجهيز بوت جديد", "Provision or register a bot to get started.": "جهّز بوتًا أو سجّله للبدء.", "Provisioning...": "جارٍ التجهيز...", "Quarterly": "ربع سنوي", "Quota must be 0–365 days": "يجب أن تكون الحصة 0–365 يومًا", "Ran now —": "تم التشغيل الآن —", "Range:": "النطاق:", "Re-parse historical messages": "إعادة تحليل الرسائل التاريخية", "Reason": "السبب", "Reason (optional)": "السبب (اختياري)", "Reason required": "السبب مطلوب", "Recent Alerts": "التنبيهات الأخيرة", "Recent activity": "النشاط الأخير", "Recent audit events": "أحداث التدقيق الأخيرة", "Recipients": "المستلمون", "Recipients (comma-separated)": "المستلمون (مفصولون بفواصل)", "Record strike": "تسجيل إنذار", "Recording…": "جارٍ التسجيل…", "Recv": "مُستقبَل", "Red-flag IP mismatches in last 30 days": "حالات عدم تطابق عناوين IP ذات الإشارة الحمراء في آخر 30 يومًا", "Refetch latest": "إعادة جلب الأحدث", "Refresh complete": "اكتمل التحديث", "Refresh failed: ": "فشل التحديث: ", "Regenerate": "إعادة التوليد", "Register": "تسجيل", "Register existing": "تسجيل موجود", "Register existing bot": "تسجيل بوت موجود", "Register existing room": "تسجيل غرفة موجودة", "Register room": "تسجيل غرفة", "Registering...": "جارٍ التسجيل...", "Related Server": "الخادم المرتبط", "Remove": "إزالة", "Remove ALL bot behavior overrides for team": "إزالة كل تجاوزات سلوك البوت للفريق", "Remove holiday": "إزالة العطلة", "Remove holiday failed: ": "فشل إزالة العطلة: ", "Remove override": "إزالة التجاوز", "Remove team override (inherit from global)": "إزالة تجاوز الفريق (الوراثة من العام)", "Removed": "تمت الإزالة", "Removing…": "جارٍ الإزالة…", "Rename failed": "فشلت إعادة التسمية", "Renames the room everywhere — dashboard + the live chat room.": "يعيد تسمية الغرفة في كل مكان — لوحة التحكم + غرفة الدردشة المباشرة.", "Render error": "خطأ في العرض", "Repeated wins": "إنجازات متكررة", "Replace all future on-call shifts with this file?\n\nOK = replace future shifts · Cancel = append to existing": "هل تريد استبدال كل مناوبات الطوارئ المستقبلية بهذا الملف؟\n\nموافق = استبدال المناوبات المستقبلية · إلغاء = الإلحاق بالموجود", "Reply text": "نص الرد", "Report fired": "تم إطلاق التقرير", "Request": "طلب", "Request PTO": "طلب إجازة", "Request logs": "سجلات الطلبات", "Request time off": "طلب إجازة", "Require @location tag": "اشتراط وسم @location", "Required for \"Connect Slack workspace\" OAuth flow. Get these from your Slack app at": "مطلوب لتدفّق OAuth لـ \"ربط مساحة عمل Slack\". احصل عليها من تطبيق Slack الخاص بك من", "Reset": "إعادة تعيين", "Reset all team overrides": "إعادة تعيين كل تجاوزات الفريق", "Reset to all users": "إعادة التعيين إلى كل المستخدمين", "Reset to default": "إعادة التعيين إلى الافتراضي", "Resolution notes": "ملاحظات الحل", "Response window (minutes before miss)": "نافذة الاستجابة (الدقائق قبل اعتبارها فائتة)", "Result": "النتيجة", "Results for": "النتائج لـ", "Reverted": "تم التراجع", "Role": "الدور", "Role / title": "الدور / المسمى الوظيفي", "Role:": "الدور:", "Rolling 7-d score": "درجة متحركة لـ 7 أيام", "Room ID must be in format !xxx:server": "يجب أن يكون معرّف الغرفة بالصيغة !xxx:server", "Room assignment": "تعيين الغرفة", "Room created —": "تم إنشاء الغرفة —", "Room invites": "دعوات الغرفة", "Room may be empty or inaccessible.": "قد تكون الغرفة فارغة أو غير قابلة للوصول.", "Room name": "اسم الغرفة", "Room name is required": "اسم الغرفة مطلوب", "Room only": "الغرفة فقط", "Room post": "نشر في الغرفة", "Room registered:": "تم تسجيل الغرفة:", "Room removed from dashboard": "تمت إزالة الغرفة من لوحة التحكم", "Room settings — general, bots, members, attendance tracking": "إعدادات الغرفة — عام، بوتات، أعضاء، تتبّع الحضور", "Room type": "نوع الغرفة", "Room type, team, primary bot persona": "نوع الغرفة والفريق وشخصية البوت الأساسية", "Room updated": "تم تحديث الغرفة", "Rooms · New": "الغرف · جديد", "Route to": "التوجيه إلى", "Run": "تشغيل", "Run daily_recompute now": "تشغيل daily_recompute الآن", "Run dry-run": "تشغيل تجريبي", "Run failed:": "فشل التشغيل:", "Run now": "تشغيل الآن", "Running hours so far today — recompute as the user works": "الساعات الجارية حتى الآن اليوم — يُعاد الحساب أثناء عمل المستخدم", "Running: ": "قيد التشغيل: ", "Running…": "قيد التشغيل…", "Runs the daily attendance recompute for today and returns a fresh executive report. Choose your download format.": "يشغّل إعادة حساب الحضور اليومي لهذا اليوم ويعيد تقريرًا تنفيذيًا جديدًا. اختر صيغة التنزيل.", "SMTP/SendGrid config. Test button verifies credentials end-to-end before saving.": "تكوين SMTP/SendGrid. زر الاختبار يتحقّق من بيانات الاعتماد من البداية إلى النهاية قبل الحفظ.", "Sa": "س", "Sales": "المبيعات", "Same accomplishment listed in yesterday_text 3+ days in a row — potential padding.": "نفس الإنجاز مدرَج في yesterday_text لـ 3 أيام متتالية أو أكثر — حشو محتمل.", "Same data as Today's Top 10 widget but with selectable period (week / month / quarter / 6 months / year / annual). Switch metric to compare attendance, hours, blockers, absences, or progress score across employees.": "نفس بيانات أداة أفضل 10 لليوم لكن بفترة قابلة للاختيار (أسبوع / شهر / ربع سنة / 6 أشهر / سنة / سنوي). بدّل المقياس لمقارنة الحضور أو الساعات أو العوائق أو الغيابات أو درجة التقدّم عبر الموظفين.", "Same task on 2+ days, not completed": "نفس المهمة في يومين أو أكثر، غير مكتملة", "Same task on 3+ days in the window": "نفس المهمة في 3 أيام أو أكثر ضمن النافذة", "Sample": "عيّنة", "Sample CSV": "نموذج CSV", "Sat": "السبت", "Save alert config": "حفظ تكوين التنبيه", "Save branding": "حفظ الهوية البصرية", "Save changes": "حفظ التغييرات", "Save credentials": "حفظ بيانات الاعتماد", "Save current state as preset": "حفظ الحالة الحالية كإعداد مسبق", "Save failed:": "فشل الحفظ:", "Save key": "حفظ المفتاح", "Save order": "حفظ الترتيب", "Save preset": "حفظ الإعداد المسبق", "Save profile": "حفظ الملف الشخصي", "Save prompt": "حفظ الموجّه", "Save recipients": "حفظ المستلمين", "Save schedule": "حفظ الجدول", "Save settings": "حفظ الإعدادات", "Save white-label": "حفظ العلامة البيضاء", "Saved": "تم الحفظ", "Saved automatically.": "تم الحفظ تلقائيًا.", "Saved to localStorage per admin — your peers see their own set.": "محفوظ في localStorage لكل مشرف — يرى زملاؤك مجموعتهم الخاصة.", "Saving role…": "جارٍ حفظ الدور…", "Saving...": "جارٍ الحفظ...", "Saving…": "جارٍ الحفظ…", "Scanned:": "تم الفحص:", "Schedule": "جدول", "Schedule rotation": "جدولة مناوبة", "Schedule saved": "تم حفظ الجدول", "Schedule the next rotation below.": "جدول المناوبة التالية أدناه.", "Scheduled reports": "التقارير المجدولة", "Scheduler": "المُجدوِل", "Scheduler info loads from /api/server-health.": "تُحمَّل معلومات المُجدوِل من /api/server-health.", "Scope": "النطاق", "Scopes:": "النطاقات:", "Scoreboard fetch failed: ": "فشل جلب لوحة النتائج: ", "Scraped from apidocs.vpsie.com and vpsie.com/knowledge-base — refreshed weekly": "مُستخرَج من apidocs.vpsie.com وvpsie.com/knowledge-base — يُحدَّث أسبوعيًا", "Search IP, path, user agent...": "ابحث عن عنوان IP أو المسار أو وكيل المستخدم...", "Search articles, topics, API endpoints...": "ابحث في المقالات والمواضيع ونقاط نهاية API...", "Search body or trigger...": "ابحث في النص أو المُحفِّز...", "Search by name or K9 ID…": "ابحث بالاسم أو معرّف K9…", "Search by name or matrix ID…": "ابحث بالاسم أو معرّف Matrix…", "Search by name…": "ابحث بالاسم…", "Search employees to add…": "ابحث عن موظفين للإضافة…", "Search message body...": "ابحث في نص الرسالة...", "Search settings by name, key, or description...": "ابحث في الإعدادات بالاسم أو المفتاح أو الوصف...", "Searchable knowledge base scraped from the VPSie docs site. Re-scraped weekly by the _job_refresh_vpsie_docs cron.": "قاعدة معرفة قابلة للبحث مُستخرَجة من موقع وثائق VPSie. يُعاد استخراجها أسبوعيًا بواسطة مهمة cron المسماة _job_refresh_vpsie_docs.", "Searching…": "جارٍ البحث…", "Security": "الأمان", "Select a category or search above.": "اختر فئة أو ابحث في الأعلى.", "Select a team…": "اختر فريقًا…", "Select a timezone…": "اختر منطقة زمنية…", "Select all": "تحديد الكل", "Select category…": "اختر فئة…", "Select persona...": "اختر شخصية...", "Selected users will have their team set to": "سيتم ضبط فريق المستخدمين المحددين إلى", "Selected:": "محدَّد:", "Semi": "نصف", "Semi-annual": "نصف سنوي", "Semi-annual (6 months)": "نصف سنوي (6 أشهر)", "Send DM": "إرسال رسالة خاصة", "Send DM to": "إرسال رسالة خاصة إلى", "Send attendance report via DM (pick period)": "إرسال تقرير الحضور عبر رسالة خاصة (اختر الفترة)", "Send failed: ": "فشل الإرسال: ", "Send format/attendance violation": "إرسال مخالفة تنسيق/حضور", "Send monthly report via DM": "إرسال التقرير الشهري عبر رسالة خاصة", "Send report": "إرسال التقرير", "Send test email": "إرسال بريد اختباري", "Send to": "إرسال إلى", "Send to user": "إرسال للمستخدم", "Send via DM": "إرسال عبر رسالة خاصة", "Send via K9 DM instead of download": "إرسال عبر رسالة K9 خاصة بدلًا من التنزيل", "Send violation": "إرسال مخالفة", "Send welcome DM via bot": "إرسال رسالة ترحيب خاصة عبر البوت", "SendGrid API key": "مفتاح API لـ SendGrid", "SendGrid-powered notifications when the bot logs an error": "إشعارات مدعومة بـ SendGrid عندما يسجّل البوت خطأً", "Sending...": "جارٍ الإرسال...", "Sending…": "جارٍ الإرسال…", "Sent": "تم الإرسال", "Service URL (optional)": "عنوان URL للخدمة (اختياري)", "Set team": "تعيين الفريق", "Setting": "إعداد", "Setup checklist:": "قائمة التحقق للإعداد:", "Severity": "الخطورة", "Shift counts, hours on call, and escalations by period": "أعداد المناوبات وساعات المناوبة والتصعيدات حسب الفترة", "Short": "قصير", "Showing max 500 rows. Apply filters or date range to narrow results.": "يتم عرض 500 صف كحد أقصى. طبّق عوامل التصفية أو نطاق التاريخ لتضييق النتائج.", "Shown inside Element as the room's topic": "يُعرَض داخل Element كموضوع الغرفة", "Sick leave": "إجازة مرضية", "Sign-in ack template ({first}/{name}/{tail})": "قالب إقرار تسجيل الدخول ({first}/{name}/{tail})", "Sign-in deadline": "الموعد النهائي لتسجيل الدخول", "Sign-in deadline (HH:MM)": "الموعد النهائي لتسجيل الدخول (HH:MM)", "Sign-in range": "نطاق تسجيل الدخول", "Sign-in time": "وقت تسجيل الدخول", "Sign-in time distribution": "توزيع وقت تسجيل الدخول", "Sign-in time heatmap": "خريطة حرارية لوقت تسجيل الدخول", "Sign-in time in user's local timezone": "وقت تسجيل الدخول حسب المنطقة الزمنية المحلية للمستخدم", "Sign-off ack template ({name})": "قالب إقرار تسجيل الخروج ({name})", "Sign-off keyword": "كلمة مفتاحية لتسجيل الخروج", "Sign-off time": "وقت تسجيل الخروج", "Signed in after team's expected start + late buffer": "سجّل الدخول بعد وقت بدء الفريق المتوقع + مهلة التأخّر", "Signing Secret": "السر السري للتوقيع", "Single-occurrence tasks in progress": "مهام أحادية الحدوث قيد التنفيذ", "Single-shift add form. For bulk uploads use 'Import CSV' (#31). 'Export CSV' is available on the same page for round-tripping.": "نموذج إضافة مناوبة واحدة. للرفع الجماعي استخدم 'استيراد CSV' (#31). 'تصدير CSV' متاح في الصفحة نفسها للتنقّل ذهابًا وإيابًا.", "Single-workspace MVP. Use this if you don't want OAuth. Paste the 3 values from your Slack app.": "منتج أولي لمساحة عمل واحدة. استخدم هذا إذا كنت لا تريد OAuth. الصق القيم الثلاث من تطبيق Slack الخاص بك.", "Size": "الحجم", "Skip every nag DM for this person: no off-day messages, no format strikes, no no-progress nudges, no missed-signoff reminders. Use for founders, on-call leads, 24/7 admins, anyone who shouldn't be bugged about missed goals or attendance rules. They still get OTP login codes, welcome DMs, and admin-initiated messages.": "تخطّي كل رسائل الإزعاج الخاصة لهذا الشخص: لا رسائل في أيام العطل، ولا إنذارات تنسيق، ولا تنبيهات عدم تقدّم، ولا تذكيرات بتسجيل خروج فائت. استخدمه للمؤسسين وقادة المناوبة والمشرفين على مدار الساعة، وأي شخص لا ينبغي إزعاجه بشأن الأهداف الفائتة أو قواعد الحضور. لا يزالون يتلقّون رموز تسجيل الدخول لمرة واحدة ورسائل الترحيب والرسائل التي يبدأها المشرف.", "Slack OAuth app credentials": "بيانات اعتماد تطبيق Slack OAuth", "Slack — direct config": "Slack — تكوين مباشر", "Slice breakdown: green = on-time, orange = late, grey = absent (last 30 working days)": "تفصيل الشرائح: أخضر = في الموعد، برتقالي = متأخر، رمادي = غائب (آخر 30 يوم عمل)", "Slogan / tagline": "الشعار / العبارة التعريفية", "Snapshot of this employee's sign-in state for today. Late/early follows team's expected_start + buffer (suppressed on a flexible-signin team).": "لقطة لحالة تسجيل دخول هذا الموظف لهذا اليوم. التأخّر/التبكير يتبع وقت بدء الفريق المتوقع + المهلة (مُعطَّل في فريق ذي تسجيل دخول مرن).", "Snooze 7d": "تأجيل 7 أيام", "Snooze suppresses repeat red-flag DMs for the affected user.": "التأجيل يكبت تكرار الرسائل الخاصة ذات الإشارة الحمراء للمستخدم المتأثر.", "Snoozed 7d": "تم التأجيل 7 أيام", "Something went wrong loading this profile editor": "حدث خطأ ما أثناء تحميل محرّر الملف الشخصي هذا", "Source": "المصدر", "Specific users": "مستخدمون محددون", "Square images work best. Shown in browser tab and sidebar.": "الصور المربعة تعمل بشكل أفضل. تُعرَض في علامة تبويب المتصفح والشريط الجانبي.", "Stale tasks": "مهام راكدة", "Standard deviation of sign-in minute over the period — lower = more consistent.": "الانحراف المعياري لدقيقة تسجيل الدخول خلال الفترة — الأقل = أكثر اتساقًا.", "Start": "البداية", "Start date": "تاريخ البداية", "Status distribution": "توزيع الحالة", "Status of every APScheduler cron job — next run, last run, missed runs. Useful for verifying a job actually fires.": "حالة كل مهمة cron في APScheduler — التشغيل التالي، آخر تشغيل، التشغيلات الفائتة. مفيدة للتحقّق من أن المهمة تُطلَق فعلًا.", "Stays-late threshold (local hour 0–24)": "عتبة البقاء متأخرًا (الساعة المحلية 0–24)", "Strike cap, thresholds, keyword lists — change without redeploying": "حد الإنذارات والعتبات وقوائم الكلمات المفتاحية — تغيير دون إعادة نشر", "Strike history": "سجل الإنذارات", "Strikes left": "الإنذارات المتبقية", "Strikes left (lifetime)": "الإنذارات المتبقية (مدى الحياة)", "Su": "ح", "Submit": "إرسال", "Submit a request": "إرسال طلب", "Submitted by": "مُقدَّم بواسطة", "Submitting...": "جارٍ الإرسال...", "Summary recipients saved": "تم حفظ مستلمي الملخص", "Sun": "الأحد", "Sun–Thu (Cairo / Middle East workweek; Fri+Sat off)": "الأحد–الخميس (أسبوع عمل القاهرة / الشرق الأوسط؛ الجمعة+السبت عطلة)", "Superadmin": "مشرف أعلى", "Superadmin — never tracked": "مشرف أعلى — لا يُتتبَّع أبدًا", "Support Cases": "حالات الدعم", "Support Ticket": "تذكرة دعم", "Support email": "بريد الدعم", "Suppress further red-flag DMs for this user for 7 days": "كبت المزيد من الرسائل الخاصة ذات الإشارة الحمراء لهذا المستخدم لمدة 7 أيام", "Synapse": "Synapse", "System prompt": "موجّه النظام", "System prompt (auto-filled from persona, editable)": "موجّه النظام (مُعبّأ تلقائيًا من الشخصية، قابل للتعديل)", "System prompt for this bot...": "موجّه النظام لهذا البوت...", "System prompt...": "موجّه النظام...", "Tactical Overview": "نظرة عامة تكتيكية", "Target:": "الهدف:", "Tasks marked blocked by the user": "المهام التي وضع المستخدم علامة عليها كمحظورة", "Tasks marked complete": "المهام التي وُضِعت عليها علامة مكتمل", "Tasks parsed from standup check-ins — blocked, stalled, repeated, and completed items.": "المهام المُحلَّلة من تسجيلات الاجتماع اليومي — العناصر المحظورة والمتعثّرة والمتكررة والمكتملة.", "Tax form": "نموذج ضريبي", "Team & Rooms": "الفريق والغرف", "Team lead": "قائد الفريق", "Team name": "اسم الفريق", "Team name *": "اسم الفريق *", "Team override": "تجاوز الفريق", "Team renamed to": "تمت إعادة تسمية الفريق إلى", "Team saved": "تم حفظ الفريق", "Team:": "الفريق:", "Team…": "الفريق…", "Technical": "تقني", "Template name": "اسم القالب", "Test": "اختبار", "Test against text": "اختبار مقابل النص", "Test send": "إرسال اختباري", "Th": "خ", "The error didn't affect anything else — close this dialog and try again. If it keeps happening, take a screenshot and report the bug.": "لم يؤثّر الخطأ على أي شيء آخر — أغلق هذا الحوار وحاول مرة أخرى. إذا استمر حدوثه، التقط لقطة شاشة وأبلغ عن الخلل.", "The primary @ai.bot is always present. Add specialist bots here (e.g. DevOps Bot in Devops-Standup, Tech Bot in Tech-Room) — they join as additional members and can answer @-mentions in their domain without taking over the standup flow.": "بوت @ai.bot الأساسي حاضر دائمًا. أضف بوتات متخصصة هنا (مثل بوت DevOps في Devops-Standup، وبوت التقنية في Tech-Room) — ينضمون كأعضاء إضافيين ويمكنهم الرد على الإشارات بـ @ في مجالهم دون السيطرة على تدفّق الاجتماع اليومي.", "The room is created in Matrix with end-to-end encryption. The primary AI bot is added automatically — invite teammates and add specialist bots below.": "تُنشَأ الغرفة في Matrix بتشفير من الطرف إلى الطرف. يُضاف بوت الذكاء الاصطناعي الأساسي تلقائيًا — ادعُ زملاء الفريق وأضف بوتات متخصصة أدناه.", "These settings control how the AI bot speaks, when it interrupts, and how strict it is. Changes are saved per-category and apply within ~60 seconds. Use": "تتحكّم هذه الإعدادات في كيفية حديث بوت الذكاء الاصطناعي ومتى يقاطع ومدى صرامته. تُحفَظ التغييرات لكل فئة وتُطبَّق خلال ~60 ثانية. استخدم", "This cannot be undone.": "لا يمكن التراجع عن هذا.", "This period": "هذه الفترة", "This week": "هذا الأسبوع", "This week's effective hours minus last week's. + = trending up.": "الساعات الفعّالة لهذا الأسبوع ناقص الأسبوع الماضي. + = اتجاه صعودي.", "Threshold": "العتبة", "Throttle (h)": "التقييد (ساعات)", "Throttle hours between red-flag DMs": "ساعات التقييد بين الرسائل الخاصة ذات الإشارة الحمراء", "Thu": "الخميس", "Ticket": "تذكرة", "Ticket #": "تذكرة رقم", "Time today": "وقت اليوم", "Timeline": "الخط الزمني", "Timezone": "المنطقة الزمنية", "To": "إلى", "To date": "إلى تاريخ", "To:": "إلى:", "Today — Roster": "اليوم — القائمة", "Today's attendance": "حضور اليوم", "Today's presence": "تواجد اليوم", "Today's sign-ins by hour (12-hour)": "تسجيلات دخول اليوم حسب الساعة (نظام 12 ساعة)", "Today:": "اليوم:", "Today: ": "اليوم: ", "Toggle ON": "تبديل إلى تشغيل", "Toggle failed": "فشل التبديل", "Toggle failed:": "فشل التبديل:", "Tone": "النبرة", "Top 10 by metric and period": "أفضل 10 حسب المقياس والفترة", "Top 5 tasks (by today_text frequency)": "أفضل 5 مهام (حسب تكرار today_text)", "Top teams by shifts": "أفضل الفرق حسب المناوبات", "Top users by hours on call": "أفضل المستخدمين حسب ساعات المناوبة", "Total": "الإجمالي", "Total hours": "إجمالي الساعات", "Total hours on call": "إجمالي ساعات المناوبة", "Total hours per day": "إجمالي الساعات في اليوم", "Total hours worked (signed_off_at − signed_in_at)": "إجمالي الساعات المعمولة (signed_off_at − signed_in_at)", "Total shifts": "إجمالي المناوبات", "Total tasks": "إجمالي المهام", "Total today_text items across the period.": "إجمالي عناصر today_text خلال الفترة.", "Total violations": "إجمالي المخالفات", "Total workforce hours/day": "إجمالي ساعات القوى العاملة/يوم", "Total: ": "الإجمالي: ", "Track and resolve escalated support tickets": "تتبّع تذاكر الدعم المُصعَّدة وحلّها", "Tracked normally: off-day DMs, strikes, and reminders apply.": "متتبَّع بشكل طبيعي: تُطبَّق رسائل أيام العطل والإنذارات والتذكيرات.", "Trend": "الاتجاه", "Trend (week)": "الاتجاه (أسبوع)", "Trigger": "مُحفِّز", "Trigger created": "تم إنشاء المُحفِّز", "Trigger saved": "تم حفظ المُحفِّز", "Triggers": "المُحفِّزات", "Try a different filter or search term.": "جرّب فلترًا أو مصطلح بحث مختلفًا.", "Try a different search.": "جرّب بحثًا مختلفًا.", "Try widening filters or refreshing.": "جرّب توسيع عوامل التصفية أو التحديث.", "Try wider filters or a broader date range.": "جرّب عوامل تصفية أوسع أو نطاق تاريخ أعرض.", "Tu": "ث", "Tue": "الثلاثاء", "Tunable thresholds — late_buffer_minutes, inactivity_hours, strike_cap, dm_cooldown, burnout_hours_per_day. Restart-free; reads BotState on every check.": "عتبات قابلة للضبط — late_buffer_minutes، inactivity_hours، strike_cap، dm_cooldown، burnout_hours_per_day. دون إعادة تشغيل؛ تقرأ BotState في كل فحص.", "Twilio escalation channel": "قناة تصعيد Twilio", "Type": "النوع", "Type a message and see which triggers fire…": "اكتب رسالة وشاهد أي المُحفِّزات تُطلَق…", "Type a search term to grep app/*.py": "اكتب مصطلح بحث للبحث في app/*.py", "Type a search term…": "اكتب مصطلح بحث…", "Type your message…": "اكتب رسالتك…", "UA:": "وكيل المستخدم:", "Unassigned": "غير مُعيَّن", "Undo failed": "فشل التراجع", "Undo failed:": "فشل التراجع:", "Undo last change": "التراجع عن آخر تغيير", "Undo?": "تراجع؟", "Unified violations card (#1, #28). Donut + grid + trend + table read from the same counts+events arrays — so the donut and the count next to it never disagree.": "بطاقة مخالفات موحّدة (#1، #28). المخطط الدائري + الشبكة + الاتجاه + الجدول تقرأ من مصفوفات الأعداد+الأحداث نفسها — لذا لا يختلف المخطط الدائري والعدد المجاور له أبدًا.", "Unknown": "غير معروف", "Unmute": "إلغاء الكتم", "Unsaved": "غير محفوظ", "Unscored": "غير مُقيَّم", "Unverified": "غير مُوثَّق", "Upcoming (next 30 days)": "القادم (30 يومًا القادمة)", "Upload": "رفع", "Upload a filled rotation CSV to update the calendar": "ارفع ملف CSV لمناوبة مكتمل لتحديث التقويم", "Upload failed: ": "فشل الرفع: ", "Upload schedule": "رفع الجدول", "Uploaded": "تم الرفع", "Uploading…": "جارٍ الرفع…", "Use the form above to create your first ticket.": "استخدم النموذج أعلاه لإنشاء تذكرتك الأولى.", "Use {display_name} to personalize per recipient…": "استخدم {display_name} للتخصيص لكل مستلم…", "User has signed in but not yet signed off — hours are still accruing": "سجّل المستخدم الدخول لكنه لم يسجّل الخروج بعد — لا تزال الساعات تتراكم", "Usual:": "المعتاد:", "VPSie Documentation": "وثائق VPSie", "VPSie connected. Server list refreshed.": "تم الاتصال بـ VPSie. تم تحديث قائمة الخوادم.", "VPSie docs error": "خطأ في وثائق VPSie", "VPSie docs refreshed": "تم تحديث وثائق VPSie", "Vacation / PTO": "إجازة / إجازة", "Validated:": "تم التحقّق:", "Validating…": "جارٍ التحقّق…", "Value": "القيمة", "Verify your sender address first, then paste the key below. Alerts fire when the scheduler detects a recent": "تحقّق من عنوان المُرسِل الخاص بك أولًا، ثم الصق المفتاح أدناه. تُطلَق التنبيهات عندما يكتشف المُجدوِل حدوث", "View room members + their roles": "عرض أعضاء الغرفة + أدوارهم", "Violations fetch failed: ": "فشل جلب المخالفات: ", "Violations in the selected period": "المخالفات في الفترة المحددة", "Visual month view of primary + secondary on-call assignments. Click a shift to jump to its details. Use ← → to change months.": "عرض شهري مرئي لتعيينات المناوبة الأساسية + الثانوية. انقر على مناوبة للانتقال إلى تفاصيلها. استخدم ← → لتغيير الأشهر.", "Wall-clock sign-in / sign-off / hours worked so far today. Times are in the EMPLOYEE'S local timezone (shown next to each time). 'Late' badge fires if In was past team's expected start + buffer (default 09:00 + 30 min). 'Still in' marker means the user has signed in but not yet signed off; Hours then keeps running.": "وقت تسجيل الدخول / الخروج الفعلي / الساعات المعمولة حتى الآن اليوم. الأوقات بالمنطقة الزمنية المحلية للموظف (تُعرَض بجوار كل وقت). تظهر شارة 'متأخر' إذا كان وقت الدخول بعد وقت بدء الفريق المتوقع + المهلة (الافتراضي 09:00 + 30 دقيقة). علامة 'لا يزال بالداخل' تعني أن المستخدم سجّل الدخول لكنه لم يسجّل الخروج بعد؛ عندها تستمر الساعات في العد.", "We": "ر", "Wed": "الأربعاء", "Week hours": "ساعات الأسبوع", "Weekly": "أسبوعي", "Welcome DM": "رسالة ترحيب خاصة", "What this room is for. Shown on the room card.": "الغرض من هذه الغرفة. يُعرَض على بطاقة الغرفة.", "WhatsApp Business ID": "معرّف WhatsApp Business", "WhatsApp settings now live in this tab. If you don't see the form yet, the Server tab's WhatsApp panel needs to be moved here in the next deploy — bump the page-misc cache-bust and re-check.": "إعدادات WhatsApp موجودة الآن في علامة التبويب هذه. إذا لم تظهر لك النموذج بعد، فإن لوحة WhatsApp في علامة تبويب الخادم بحاجة إلى النقل هنا في النشر التالي — زِد قيمة كسر ذاكرة التخزين المؤقت لـ page-misc وأعد الفحص.", "When": "متى", "When ON, bot skips fixed sign-in deadline enforcement and only tracks total hours worked per day. Useful for consulting / async-first remote teams.": "عند التشغيل، يتخطى البوت فرض الموعد النهائي الثابت لتسجيل الدخول ويتتبّع فقط إجمالي الساعات المعمولة في اليوم. مفيد للفرق الاستشارية / الفرق البعيدة التي تعمل بشكل غير متزامن أولًا.", "When on, this person is tracked: sign-ins/offs are acked, they show on the roster, Today, and reports. Uncheck to deactivate (hides from counts but keeps all history) — or check to (re)activate someone who was added to a room but never turned on.": "عند التشغيل، يتم تتبّع هذا الشخص: تُقَرّ تسجيلات الدخول/الخروج، ويظهر في القائمة واليوم والتقارير. ألغِ التحديد لإلغاء التفعيل (يُخفي من الأعداد لكن يحتفظ بكل السجل) — أو حدّد لإعادة تفعيل شخص أُضيف إلى غرفة لكن لم يُفعَّل قط.", "When the active provider errors or hits a rate limit, the bot retries down this list in order. Keyless providers can't be added until you set their API key.": "عندما يخطئ المزوّد النشط أو يبلغ حد المعدل، يعيد البوت المحاولة عبر هذه القائمة بالترتيب. لا يمكن إضافة المزوّدين بلا مفتاح حتى تضبط مفتاح API الخاص بهم.", "When:": "متى:", "Whether the bot flags claimed-vs-classified IP mismatches (e.g. user claims office from a known-home IP).": "ما إذا كان البوت يضع علامة على عدم تطابق عنوان IP المُدَّعى مقابل المُصنَّف (مثلًا يدّعي المستخدم أنه في المكتب من عنوان IP معروف أنه منزلي).", "White-label": "علامة بيضاء", "Who": "من", "Who receives the per-user summary report?": "من يستلم تقرير الملخص لكل مستخدم؟", "Window (days)": "النافذة (أيام)", "WoW hours": "ساعات أسبوع مقابل أسبوع", "Work hours & schedule": "ساعات العمل والجدول", "Work hours target (per day)": "هدف ساعات العمل (في اليوم)", "Work hours target / day": "هدف ساعات العمل / يوم", "Work hours/day": "ساعات العمل/يوم", "Working": "يعمل", "Working days": "أيام العمل", "Working days (deselect off-days)": "أيام العمل (ألغِ تحديد أيام العطل)", "Working days auto-default to": "أيام العمل تُضبَط تلقائيًا افتراضيًا إلى", "Working days with no sign-in and no approved PTO. Counts against attendance %.": "أيام العمل بلا تسجيل دخول وبلا إجازة موافق عليها. تُحتسَب ضد نسبة الحضور %.", "Yesterday:": "الأمس:", "Yesterday: ": "الأمس: ", "You currently don't have any support tickets.": "ليس لديك أي تذاكر دعم حاليًا.", "Your client ID + secret are stored encrypted and only used to fetch": "معرّف العميل + السر السري مخزّنان مشفّرين ويُستخدَمان فقط لجلب", "Your slogan here": "شعارك هنا", "Your team attendance dashboard": "لوحة حضور فريقك", "_No accomplishments data for this user._": "_لا توجد بيانات إنجازات لهذا المستخدم._", "above to choose what to show.": "أعلاه لاختيار ما يُعرَض.", "activated": "مُفعَّل", "added": "مُضاف", "admin": "مشرف", "admin — flexible": "مشرف — مرن", "and it runs on its own schedule continuously;": "ويعمل وفق جدوله الخاص باستمرار؛", "and it stops. “Run now” triggers a one-off immediately (useful for testing).": "ويتوقّف. \"تشغيل الآن\" يُطلِق تشغيلًا واحدًا فورًا (مفيد للاختبار).", "and saved": "وتم الحفظ", "article": "مقال", "articles": "مقالات", "based on this timezone. Edit below if needed.": "بناءً على هذه المنطقة الزمنية. عدّل أدناه إذا لزم الأمر.", "blocker": "عائق", "blocker day": "يوم عائق", "bot": "بوت", "bot(s) invited": "تمت دعوة البوت/البوتات", "by": "بواسطة", "cap:": "الحد:", "case": "حالة", "cases": "حالات", "categories": "فئات", "change": "تغيير", "changes": "تغييرات", "clear": "مسح", "cleared": "تم المسح", "click a card to edit": "انقر على بطاقة للتعديل", "click to browse · 25 MB max each": "انقر للتصفّح · 25 ميغابايت كحد أقصى لكل ملف", "comma,separated,keywords": "كلمات,مفتاحية,مفصولة,بفواصل", "configured": "مُكوَّن", "connected": "متصل", "container": "حاوية", "created": "تم الإنشاء", "created and posted to the support room.": "تم إنشاؤها ونشرها في غرفة الدعم.", "customized": "مخصَّص", "daily score": "الدرجة اليومية", "day": "يوم", "day-slots": "خانات الأيام", "days": "أيام", "deactivated": "مُلغى التفعيل", "default prompt": "الموجّه الافتراضي", "deleted": "تم الحذف", "disabled": "معطّل", "done": "تم", "e.g. Attendance Coach": "مثال: مدرّب الحضور", "e.g. Customer escalation week — quarterly release": "مثال: أسبوع تصعيد العملاء — الإصدار الربع سنوي", "e.g. Engineering": "مثال: الهندسة", "e.g. Engineering Attendance": "مثال: حضور الهندسة", "e.g. Labor Day": "مثال: عيد العمال", "e.g. Missed sign-in 3 days in a row": "مثال: تسجيل دخول فائت 3 أيام متتالية", "e.g. attendance.bot": "مثال: attendance.bot", "e.g. team retreat": "مثال: خلوة الفريق", "employee": "موظف", "employee/team_lead/manager → tracked normally. moderator/admin → flexible (no nagging). superadmin → bot-god (not tracked at all).": "موظف/قائد فريق/مدير ← يُتتبَّع بشكل طبيعي. مشرف محتوى/مشرف ← مرن (بلا إزعاج). مشرف أعلى ← إله البوت (لا يُتتبَّع إطلاقًا).", "enabled": "مُمكَّن", "entry (debounced to 1/hour per kind).": "إدخال (مُقيَّد إلى 1/ساعة لكل نوع).", "failed": "فشل", "failed:": "فشل:", "for": "لـ", "from": "من", "from the dashboard?": "من لوحة التحكم؟", "in room": "في الغرفة", "invalid": "غير صالح", "is always here. Toggle extras on/off below.": "حاضر دائمًا. فعّل/عطّل الإضافات أدناه.", "it recognises as sick-day, PTO, or emergency language. Saved changes go live within ~60s — no rebuild required.": "ما يتعرّف عليه كلغة يوم مرضي أو إجازة أو طوارئ. تصبح التغييرات المحفوظة فعّالة خلال ~60 ثانية — لا حاجة لإعادة بناء.", "items": "عناصر", "left": "متبقٍّ", "localpart must be lowercase alphanumeric, dots, underscores, hyphens only": "يجب أن يكون الجزء المحلي أحرفًا وأرقامًا صغيرة ونقاطًا وشرطات سفلية وشرطات فقط", "locked": "مقفل", "made progress": "أحرز تقدّمًا", "manager": "مدير", "member": "عضو", "member(s) unassigned": "تم إلغاء تعيين العضو/الأعضاء", "member(s) will become unassigned from this team.": "سيصبح العضو/الأعضاء غير مُعيَّنين في هذا الفريق.", "member(s),": "عضو/أعضاء،", "members": "أعضاء", "members already on another team will be moved here": "الأعضاء الموجودون بالفعل في فريق آخر سيُنقَلون هنا", "members:": "أعضاء:", "messages": "رسائل", "messaging": "مراسلة", "missing": "مفقود", "moderator — flexible, manages others": "مشرف محتوى — مرن، يدير الآخرين", "ms": "مللي ثانية", "muted here": "مكتوم هنا", "no": "لا", "no key": "لا يوجد مفتاح", "no progress": "لا تقدّم", "no sign-in": "لا يوجد تسجيل دخول", "none": "لا شيء", "not set": "غير مُعيَّن", "of": "من", "off-day": "يوم عطلة", "on": "تشغيل", "on PTO": "في إجازة", "on the right for one-click switches between strict, friendly, or silent profiles — or hit": "على اليمين للتبديل بنقرة واحدة بين الملفات الصارمة أو الودودة أو الصامتة — أو اضغط", "on-time": "في الموعد", "optional backup #": "احتياطي اختياري رقم", "optional — select a VPSie server": "اختياري — اختر خادم VPSie", "overridden": "متجاوَز", "override": "تجاوز", "overrides": "تجاوزات", "partial": "جزئي", "persona:": "الشخصية:", "preview": "معاينة", "profile: ready": "الملف الشخصي: جاهز", "prompt set": "تم ضبط الموجّه", "pts": "نقاط", "quota": "حصة", "reset": "إعادة تعيين", "room(s)": "غرفة/غرف", "running…": "قيد التشغيل…", "saved": "تم الحفظ", "saved for": "تم الحفظ لـ", "scoreboard": "لوحة النتائج", "secret value from Azure portal": "القيمة السرية من بوابة Azure", "selected": "محدَّد", "sent": "تم الإرسال", "servers.": "خوادم.", "settings updated": "تم تحديث الإعدادات", "sick": "مرضية", "signing off": "جارٍ تسجيل الخروج", "skipped": "تم التخطّي", "still in": "لا يزال بالداخل", "strike": "إنذار", "superadmin — bot-god, never tracked": "مشرف أعلى — إله البوت، لا يُتتبَّع أبدًا", "team": "فريق", "team override": "تجاوز الفريق", "team overrides": "تجاوزات الفريق", "team:": "الفريق:", "team_lead": "قائد الفريق", "teams": "فرق", "the bot pings, warns, or strikes; keyword lists control": "ما إذا كان البوت ينبّه أو يحذّر أو يصدر إنذارًا؛ قوائم الكلمات المفتاحية تتحكّم في", "this team": "هذا الفريق", "to": "إلى", "to add": "للإضافة", "to bottle your own mix.": "لتعبئة مزيجك الخاص.", "to remove": "للإزالة", "toggle OFF": "تبديل إلى إيقاف", "total": "الإجمالي", "total settings": "إجمالي الإعدادات", "trackable member": "عضو قابل للتتبّع", "trackable members": "أعضاء قابلون للتتبّع", "trigger": "مُحفِّز", "triggers yet. Click": "مُحفِّزات بعد. انقر", "type:": "النوع:", "unsaved": "غير محفوظ", "used": "مُستخدَم", "user(s)": "مستخدم/مستخدمون", "vpsie client ID": "معرّف عميل vpsie", "vpsie client secret": "السر السري لعميل vpsie", "what": "ماذا", "when": "متى", "will be replaced per recipient.": "سيُستبدَل لكل مستلم.", "yes": "نعم", "Δ attendance% vs prior": "Δ الحضور% مقابل السابق", "Δ hours vs prior": "Δ الساعات مقابل السابق", "σ sign-in": "σ تسجيل الدخول", "— (still in)": "— (لا يزال بالداخل)", "— None (use default @ai.bot) —": "— لا شيء (استخدم @ai.bot الافتراضي) —", "— currently INACTIVE": "— غير نشط حاليًا", "— even in the same country. Pick exactly the days this person works. Empty means treat every day as working (no off-day DMs). Presets: Mon-Fri (Americas), Cairo (Sun-Thu), All 7 (flexible / always-on).": "— حتى في البلد نفسه. اختر بالضبط الأيام التي يعمل فيها هذا الشخص. الفارغ يعني معاملة كل يوم كيوم عمل (بلا رسائل في أيام العطل). الإعدادات المسبقة: الاثنين-الجمعة (الأمريكتان)، القاهرة (الأحد-الخميس)، كل الأيام السبعة (مرن / دائم العمل).", "— even in the same timezone. Toggle chips to match what this person actually works. The bot will count only those days toward their attendance and DM them on the rest.": "— حتى في المنطقة الزمنية نفسها. فعّل الرقائق لتطابق ما يعمله هذا الشخص فعلًا. سيحتسب البوت تلك الأيام فقط ضمن حضورهم ويرسل لهم رسائل خاصة في البقية.", "— not signed in —": "— لم يُسجَّل الدخول —", "— pick —": "— اختر —", "−items": "−عناصر", "⌘↵ to send": "⌘↵ للإرسال", "⚠️ Violations": "⚠️ المخالفات", "⬇ Export…": "⬇ تصدير…", "📈 Per-user trends — 30 day": "📈 اتجاهات لكل مستخدم — 30 يومًا", "🤖 Bot logic": "🤖 منطق البوت", }; window.appLang = function appLang() { try { const saved = localStorage.getItem('m_lang'); if (saved === 'ar' || saved === 'en') return saved; return (navigator.language || 'en').toLowerCase().startsWith('ar') ? 'ar' : 'en'; } catch (_) { return 'en'; } }; // appT — the desktop translator. Prefers the desktop dict (APP_I18N), then // falls back to whatever mobile leaked (window.I18N_AR) so overlapping keys // translate either way, then to the English key itself (graceful fallback). window.appT = function appT(s) { if (window.appLang() !== 'ar') return s; return (window.APP_I18N && window.APP_I18N[s]) || (window.I18N_AR && window.I18N_AR[s]) || s; }; window.setAppLang = function setAppLang(lang) { try { localStorage.setItem('m_lang', lang); } catch (_) {} try { window.location.reload(); } catch (_) {} }; // Apply direction + lang to as early as possible (idempotent with the // mobile surface, which does the same). Runs at desktop width too, so Arabic // flips the whole admin layout to RTL. (() => { try { const rtl = window.appLang() === 'ar'; document.documentElement.setAttribute('lang', window.appLang()); document.documentElement.setAttribute('dir', rtl ? 'rtl' : 'ltr'); document.documentElement.classList.toggle('app-rtl', rtl); } catch (_) {} })(); const AVATAR_PALETTE = [ ['#3b66f5','#1d3286'], ['#5b85ff','#233fae'], ['#16a34a','#0d6b30'], ['#d97706','#7a4304'], ['#9333ea','#4d1280'], ['#0891b2','#075f72'], ['#dc2626','#7d1717'], ['#ca8a04','#6b4e02'], ]; function avatarColors(seed) { let h = 0; const s = String(seed || ''); for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) >>> 0; return AVATAR_PALETTE[h % AVATAR_PALETTE.length]; } function initials(name) { return String(name || '?').split(/\s+/).slice(0,2).map(s => s[0]?.toUpperCase() || '').join(''); } // Universal 12-hour AM/PM formatter. Accepts an optional IANA timezone // (e.g. "Africa/Cairo", "America/New_York"); falls back to the browser's // local zone. Always renders hour12 so a Cairo person's 9 AM stays "9:00 AM" // no matter where the admin views from. function fmtTime(iso, tz) { if (!iso) return null; const d = new Date(iso); if (isNaN(d)) return null; try { return new Intl.DateTimeFormat('en-US', { hour: 'numeric', minute: '2-digit', hour12: true, ...(tz ? { timeZone: tz } : {}), }).format(d); } catch (_e) { return new Intl.DateTimeFormat('en-US', { hour: 'numeric', minute: '2-digit', hour12: true, }).format(d); } } // Short IANA → display label. "America/New_York" → "NY", "Africa/Cairo" → "Cairo". function tzShort(tz) { if (!tz) return ''; const map = { 'America/New_York': 'NY', 'America/Los_Angeles': 'LA', 'America/Chicago': 'CHI', 'Europe/London': 'LON', 'Europe/Paris': 'PAR', 'Europe/Berlin': 'BER', 'Africa/Cairo': 'Cairo', 'Asia/Dubai': 'Dubai', 'Asia/Kolkata': 'IST', 'Asia/Tokyo': 'TYO', 'UTC': 'UTC', }; if (map[tz]) return map[tz]; return (tz.split('/').pop() || tz).replace(/_/g, ' '); } // Expose for other modules loaded after data.jsx. window.fmtTime = fmtTime; window.tzShort = tzShort; // Role-tier helpers. Use these everywhere instead of strict // `org_role === 'admin'` so promotions to moderator/superadmin // don't accidentally lock people out of admin-tier features. // - isAdminOrAbove: admin perms (admin, moderator, superadmin). // - isSuperadmin: bot-god only (Hany etc.) — for the small set // of routes/actions reserved for superadmins. function isAdminOrAbove(me) { if (!me) return false; if (me.is_admin) return true; const r = String(me.org_role || me.role || '').toLowerCase(); return r === 'admin' || r === 'moderator' || r === 'superadmin'; } function isSuperadmin(me) { if (!me) return false; const r = String(me.org_role || me.role || '').toLowerCase(); return r === 'superadmin'; } window.isAdminOrAbove = isAdminOrAbove; window.isSuperadmin = isSuperadmin; // ── Cross-platform "save this blob" helper ── // Strategies, in order: // 1. blob URL in window.open — iOS WKWebView native preview with Share btn // 2. navigator.share({files}) — iOS sim / Android (gesture-fragile on real // iOS devices because the await fetch above // consumes the transient activation) // 3. — desktop browsers // Each strategy returns { via: '' } so the caller's toast can show // which path was actually taken (useful for debugging on real devices). window.saveDownload = async function saveDownload(blob, filename) { const cap = (typeof window !== 'undefined') ? window.Capacitor : undefined; const isCapacitor = !!( cap && ( (typeof cap.isNativePlatform === 'function' && cap.isNativePlatform()) || (typeof cap.getPlatform === 'function' && cap.getPlatform() !== 'web') ) ); // Strategy 0 — Capacitor Filesystem + Share native plugins. This is the // bulletproof path on iOS real devices: write the blob to the app's cache // dir, then trigger the system Share Sheet with the file URI. Skips the // user-gesture transient-activation trap and the canShare({files}) // detection inconsistency that broke the JS-only path on real iPhones. // Requires the app to be built with @capacitor/filesystem + @capacitor/share // (see attendance-ios/package.json). if (isCapacitor && cap.Plugins && cap.Plugins.Filesystem && cap.Plugins.Share) { try { const Filesystem = cap.Plugins.Filesystem; const Share = cap.Plugins.Share; // blob → base64 const base64 = await new Promise((resolve, reject) => { const r = new FileReader(); r.onload = () => { const dataUrl = r.result || ''; const idx = String(dataUrl).indexOf(','); resolve(idx >= 0 ? String(dataUrl).slice(idx + 1) : ''); }; r.onerror = reject; r.readAsDataURL(blob); }); // Unique cache filename so repeated exports don't collide. const safeName = String(filename).replace(/[^a-zA-Z0-9._-]/g, '_'); const cachePath = `${Date.now()}-${safeName}`; const written = await Filesystem.writeFile({ path: cachePath, data: base64, directory: 'CACHE', }); // Share the file URI — iOS Share Sheet ("Save to Files", "Mail", etc.). try { await Share.share({ title: filename, url: written.uri, dialogTitle: `Save ${filename}`, }); return { ok: true, via: 'native-share' }; } catch (e) { // user dismissed sheet → treat as success if (e && /cancel/i.test(e.message || '')) { return { ok: true, via: 'native-cancelled' }; } // share threw — fall through to JS strategies } } catch (_) { /* fall through */ } } // Strategy 1 — open the blob in a new WKWebView window (legacy fallback if // native plugins aren't loaded for some reason). if (isCapacitor) { try { const blobUrl = URL.createObjectURL(blob); const w = window.open(blobUrl, '_blank'); setTimeout(() => { try { URL.revokeObjectURL(blobUrl); } catch(_) {} }, 60000); if (w !== null && w !== false && w !== undefined) { return { ok: true, via: 'blob-window' }; } } catch (_) { /* fall through */ } } // Strategy 2 — Web Share API with files (iOS 15+ Safari/WKWebView, Android). // GATED to touch-mobile UA: Chrome on macOS/Windows also reports // `navigator.canShare({files:[...]})===true`, so without this gate the // share sheet pops up on desktop and the user never gets a real // downloaded file. iOS/Android = share is the right answer; everything // else falls through to the plain path below. const ua = (typeof navigator !== 'undefined' && navigator.userAgent) || ''; const isTouchMobile = /iPhone|iPad|iPod|Android/i.test(ua); try { if (isTouchMobile && typeof navigator !== 'undefined' && navigator.canShare && typeof File !== 'undefined') { const file = new File( [blob], filename, { type: blob.type || 'application/octet-stream' } ); if (navigator.canShare({ files: [file] })) { try { await navigator.share({ files: [file], title: filename }); return { ok: true, via: 'share-sheet' }; } catch (e) { if (e && (e.name === 'AbortError' || /cancel/i.test(e.message || ''))) { return { ok: true, via: 'share-cancelled' }; } // Fall through to strategy 3. } } } } catch (_) { /* fall through */ } // Strategy 3 — for desktop browsers. try { const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filename; a.rel = 'noopener'; document.body.appendChild(a); a.click(); setTimeout(() => { try { URL.revokeObjectURL(url); a.remove(); } catch(_) {} }, 1500); return { ok: true, via: 'a-download' }; } catch (e) { // Last resort — open in a new tab so user can long-press to save. try { const url = URL.createObjectURL(blob); window.open(url, '_blank'); return { ok: true, via: 'window-open-fallback' }; } catch (e2) { return { ok: false, via: 'failed', error: String(e2 && e2.message || e2) }; } } }; // ── Empty defaults so React can mount before fetches resolve ── window.STATUS_DATA = { EMPLOYEES: [], ME: {display_name:'…', matrix_id:'', org_role:'employee', avatar: AVATAR_PALETTE[0], initials:'?'}, ACTIVITY: [], SIGN_IN_HISTOGRAM: [], SERIES_14: [], PTO: [], TEAMS: [], ROOMS: [], AUDIT: [], LOGIN_LOG: [], PROVIDERS: [], MESSAGES: [], MESSAGES_STATS: {total:0, this_week:0, by_kind:[]}, SERVER: {uptime_s:0, bot_version:'…', scheduler_jobs:[]}, REPORT_DISTRIBUTION: [], ROLLUP_TEXT: 'Loading…', TEAM_NAMES: [], avatarColors, initials, }; const J = (r) => r.ok ? r.json() : Promise.reject(new Error(`${r.status} ${r.url}`)); // Keep-last-good: on a failed/!ok fetch, reuse the previous good value for that // endpoint instead of an empty fallback. A single blip (bot mid-restart, a // timed-out request, a transient 5xx) must NEVER wipe the dashboard to // all-zeros / everyone-absent / 0 hours. The empty fallback only applies on the // very first load, before any good value has been cached. const _lastGood = (window.__DATA_LAST_GOOD = window.__DATA_LAST_GOOD || {}); const keep = (key, url, fallback) => fetch(url, {credentials:'include'}).then(J) .then(v => { _lastGood[key] = v; return v; }) .catch(e => { console.warn('[data] keep-last-good for', key, '-', e.message); return (key in _lastGood) ? _lastGood[key] : fallback; }); async function bootstrap() { const [me, emps, attn, audit, login, teams, rooms, ptoReqs, providers, msgs, msgsStats, server, todayText, todayKpis, dist, ts] = await Promise.all([ keep('me', '/api/auth/me', null), keep('employees', '/api/employees', []), keep('attendance', '/api/attendance', []), keep('audit', '/api/audit?limit=50', []), keep('login', '/api/login-log?limit=50', []), keep('teams', '/api/teams', []), keep('rooms', '/api/rooms', []), keep('pto', '/api/pto/requests', []), keep('providers', '/api/providers', []), keep('messages', '/api/messages?limit=50', []), keep('messages_stats','/api/messages/stats', {total:0,this_week:0,by_kind:[]}), keep('server', '/api/server-health', {uptime_s:0,bot_version:'?',scheduler_jobs:[]}), keep('today_text', '/api/today/text', {text:''}), keep('today_kpis', '/api/today/kpis', null), keep('distribution', '/api/reports/distribution', []), keep('timeseries', '/api/reports/timeseries?days=14', []), ]); // ── Index attendance by user_id for merge ── const attnByUid = {}; (Array.isArray(attn) ? attn : []).forEach(a => { attnByUid[a.user_id] = a; }); // ── EMPLOYEES: merge employees + attendance ── const employees = (Array.isArray(emps) ? emps : []).map((e, idx) => { const a = attnByUid[e.id] || {}; return { id: e.id, matrix_id: e.matrix_id, display_name: e.display_name, role: e.role || 'Employee', team: e.team || '—', org_role: e.org_role || (me && me.user_id === e.id ? me.org_role : 'employee'), // Superadmins (founders) are excluded from Today attendance counts. // Without carrying this flag the !is_superadmin filter was a no-op // (undefined) and Hany showed up in Absent + the total. is_superadmin: !!e.is_superadmin || ((e.org_role || '') === 'superadmin'), is_flexible: !!e.is_flexible, // Bot-confirmed absence (DM'd past their adaptive sign-in time). // Drives the "Absent" headline vs the "not in yet" sub-count. absence_confirmed: !!a.absence_confirmed, signed_in: !!a.signed_in, signed_off: !!a.signed_off, was_late: !!a.was_late, // On-time streak (consecutive working days signed-in & on-time). // Drives the 🔥 streak badge on Today. 0 when not applicable. on_time_streak: +(a.on_time_streak || 0), is_short_day: !!a.is_short_day, had_blocker: !!a.had_blocker, blocker_text: a.blocker_text || null, hours_worked: typeof a.hours_worked === 'number' ? +a.hours_worked.toFixed(2) : 0, // Per-user timezone (Cairo / NY / etc) — render each user's // sign-in in their local hour so flex schedules read correctly. tz: e.timezone || a.timezone || null, tz_short: window.tzShort ? window.tzShort(e.timezone || a.timezone) : '', sign_in_at: fmtTime(a.sign_in_at, e.timezone || a.timezone) || null, sign_off_at: fmtTime(a.sign_off_at, e.timezone || a.timezone) || null, // Phase 1 admin-edit: raw ISO + checkin/checkout row IDs so the // EditableCell can populate precisely // and PATCH the right row. data.jsx pre-formats sign_in_at into a // display string for the bulk of the dashboard; the *_iso fields // are the unmangled values for editors. sign_in_at_iso: a.sign_in_at_iso || a.sign_in_at || null, sign_off_at_iso: a.sign_off_at_iso || a.sign_off_at || null, checkin_id: a.checkin_id || null, checkout_id: a.checkout_id || null, // Phase 4 hours override (null = use auto-computed hours_worked). hours_override: a.hours_override ?? null, daily_attendance_id: a.daily_attendance_id ?? null, // notes may contain system diagnostics ("late-threshold not yet established", // "missing sign-off", etc.) — never surface these as today_text. today: a.today_text || '', yesterday: a.yesterday_text || '', last_seen_online_at: e.last_seen_online_at, avatar: avatarColors(e.matrix_id || e.display_name), initials: initials(e.display_name), is_test_account: !!e.is_test_account, // Per-variant avatar fields for theme-aware rendering avatar_url: e.avatar_url || null, avatar_dark_url: e.avatar_dark_url || null, avatar_light_url: e.avatar_light_url || null, avatar_preference: e.avatar_preference || 'auto', synapse_avatar_url: e.synapse_avatar_url || null, }; }); // ── ME ── const meEmp = employees.find(x => me && x.matrix_id === me.matrix_id); const ME = me ? { id: me.user_id, matrix_id: me.matrix_id, display_name: me.display_name, role: meEmp?.role || 'Admin', team: meEmp?.team || 'Admin', org_role: me.org_role || 'employee', avatar: avatarColors(me.matrix_id), initials: initials(me.display_name), // Per-variant avatar fields for theme-aware rendering avatar_url: me.avatar_url || null, avatar_dark_url: me.avatar_dark_url || null, avatar_light_url: me.avatar_light_url || null, avatar_preference: me.avatar_preference || 'auto', synapse_avatar_url: me.synapse_avatar_url || null, } : window.STATUS_DATA.ME; // ── ACTIVITY: derive from audit ── // Bot-initiated actions (dm_sent, thread_reply, public_reply, reaction, // escalation, specialist routing) have no user_id — they were done BY the // bot. Show the bot as the actor instead of a bare "?". const BOT_ACTIONS = new Set([ 'dm_sent', 'thread_reply', 'public_reply', 'reaction', 'escalation', 'specialist_routing', 'advise_general_room', 'issue_routing_suppressed_under_2d', 'format_strike', 'admin_report', 'oncall_alert', 'screenshot_requested_auto_approved', ]); const humanize = (window.humanizeActionType) || ((s) => String(s || '').replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase())); const ACTIVITY = (Array.isArray(audit) ? audit : []).slice(0, 20).map(a => { const trig = (a.trigger || '').toLowerCase(); const act = (a.action_type || '').toLowerCase(); const isBot = BOT_ACTIONS.has(act) || (!a.user_display_name && !a.display_name && !a.matrix_id); const actorName = a.user_display_name || a.display_name || a.matrix_id; return { type: trig.includes('sign_in') || trig.includes('sign-in') || trig.includes('signin') ? 'signin' : trig.includes('sign_off') || trig.includes('sign-off') || trig.includes('signoff') ? 'signoff' : trig.includes('blocker') ? 'blocker' : trig.includes('pto') ? 'pto' : isBot && !actorName ? 'bot' : 'admin', // Actor: real user name if present, else the bot for bot-actions. who: actorName || (isBot ? '🤖 K9 Bot' : 'System'), at: fmtTime(a.occurred_at) || '', // Prefer the humanized label (Q2 action_label from the API) over the // raw snake_case action_type so the feed reads in plain English. meta: a.detail || a.action_label || humanize(a.action_type) || a.trigger || '', }; }); // ── PTO ── const PTO = (Array.isArray(ptoReqs) ? ptoReqs : []).map(p => ({ id: p.id, display_name: p.display_name || p.matrix_id, matrix_id: p.matrix_id, pto_type: p.pto_type || 'Vacation', start_date: p.start_date, end_date: p.end_date, days: p.days || 1, status: p.status || 'pending', approved: p.status === 'approved', })); // ── TEAMS, ROOMS, PROVIDERS, AUDIT, LOGIN_LOG, MESSAGES — pass through (shapes match) ── const TEAMS = (Array.isArray(teams) ? teams : []).map(t => ({ team_name: t.team_name || '(default)', timezone: t.timezone, working_days: t.working_days, min_hours_per_day: t.min_hours_per_day, late_buffer_minutes: t.late_buffer_minutes, inactivity_hours: t.inactivity_hours, signoff_poll_minutes: t.signoff_poll_minutes, burnout_hours_cap: t.burnout_hours_cap, help_followup_hours: t.help_followup_hours, require_issues_line: t.require_issues_line, members: t.members || 0, updated_at: t.updated_at, })); const TEAM_NAMES = TEAMS.map(t => t.team_name).filter(Boolean); // ── REPORT_DISTRIBUTION normalize ── const REPORT_DISTRIBUTION = (Array.isArray(dist) ? dist : []).map(d => ({ bucket: d.bucket || d.label || '', count: d.count || 0, color: d.color || 'var(--brand)', })); // ── SERIES_14 normalize ── const SERIES_14 = (Array.isArray(ts) ? ts : []).slice(-14).map(d => ({ date: d.date, day: d.day || (d.date ? new Date(d.date).toLocaleDateString(undefined,{weekday:'short'}) : ''), isWeekend: d.is_weekend || false, present: d.present || 0, late: d.late || 0, blockers: d.blockers || 0, hours: d.hours || 0, })); // ── Sign-in histogram from attendance times ── const histMap = {}; (Array.isArray(attn) ? attn : []).forEach(a => { if (!a.signed_in || !a.sign_in_at) return; const h = String(new Date(a.sign_in_at).getHours()).padStart(2,'0'); histMap[h] = (histMap[h] || 0) + 1; }); const SIGN_IN_HISTOGRAM = Object.keys(histMap).sort().map(h => ({hour: h, n: histMap[h]})); // ── Normalize PROVIDERS — API returns {active:{...}, available:[...]} ── const rawProviders = providers || {}; const providersAvailable = Array.isArray(rawProviders) ? rawProviders : (rawProviders.available || []); const activeProviderName = rawProviders.active?.name || rawProviders.active?.active_provider || ''; const PROVIDERS = providersAvailable.map(p => ({ id: p.name || p.id, name: p.display_name || p.name, default_model: p.default_model || p.summary_model || '', tagline: p.notes || p.tagline || '', tier: p.tier || 'unknown', configured: !!p.configured || !!p.api_key_configured, is_active: p.active || (p.name === activeProviderName) || false, })); // ── Normalize MESSAGES_STATS — API returns by_kind as object or array ── const rawStats = msgsStats || {total:0, this_week:0, by_kind:[]}; const rawByKind = rawStats.by_kind; const byKindArr = Array.isArray(rawByKind) ? rawByKind : Object.entries(rawByKind || {}).map(([kind, count]) => ({ kind, count })); const MESSAGES_STATS = { total: rawStats.total || 0, this_week: rawStats.this_week || rawStats.period_days || 0, by_kind: byKindArr, }; // ── Normalize SERVER — map real field names to UI-expected names ── const rawServer = server || {}; const SERVER = { uptime_s: rawServer.uptime_seconds || rawServer.uptime_s || 0, bot_version: rawServer.bot_version || 'running', matrix_homeserver: rawServer.matrix_homeserver || 'chat.k9.ms', matrix_user: rawServer.bot_matrix_id || rawServer.matrix_user || '', standup_room: rawServer.standup_room || '', matrix_sync_lag_s: rawServer.matrix_sync_lag_s ?? null, db_size_mb: rawServer.db_size_mb ?? null, ai_provider: rawServer.active_provider || rawServer.ai_provider || '', ai_model: rawServer.ai_model || rawServer.summary_model || '', ai_calls_24h: rawServer.ai_calls_24h || 0, events_processed_24h: rawServer.events_processed_24h || rawServer.db_row_counts?.raw_messages || 0, ack_p50_ms: rawServer.ack_p50_ms ?? null, ack_p95_ms: rawServer.ack_p95_ms ?? null, scheduler_jobs: rawServer.scheduler_jobs || [], memory_mb: rawServer.memory_mb, cpu_percent: rawServer.cpu_percent, disk_percent: rawServer.disk_percent, bot_connected: rawServer.bot_connected, db_ok: rawServer.db_ok, db_row_counts: rawServer.db_row_counts || {}, open_errors_count: rawServer.open_errors_count || 0, }; // ── Normalize MESSAGES — API returns {total, offset, limit, items:[...]} ── const rawMsgs = msgs || {}; const msgsItems = Array.isArray(rawMsgs) ? rawMsgs : (rawMsgs.items || []); const MESSAGES = msgsItems.map(m => ({ id: m.id, from: m.display_name || m.matrix_id || '?', matrix_id: m.matrix_id || '', kind: m.parsed_kind || (m.is_valid ? 'valid' : 'invalid'), ts: m.sent_at || m.edited_at || '', body: m.body || '', })); // ── Normalize AUDIT — API returns {id, user_display_name, target, action_type, trigger, body, occurred_at} ── const auditRaw = Array.isArray(audit) ? audit : []; const AUDIT = auditRaw.map(a => ({ id: a.id, matrix_id: a.target || a.matrix_id || '', user_display_name: a.user_display_name || a.display_name || a.matrix_id || '?', action_type: a.action_type || '', trigger: a.trigger || '', detail: a.detail || a.action_type || '', body: a.body || '', occurred_at: a.occurred_at || '', })); // ── Commit to window ── Object.assign(window.STATUS_DATA, { EMPLOYEES: employees, ME, ACTIVITY, SIGN_IN_HISTOGRAM, SERIES_14, PTO, TEAMS, ROOMS: rooms || [], AUDIT, LOGIN_LOG: login || [], PROVIDERS, MESSAGES, MESSAGES_STATS, SERVER, REPORT_DISTRIBUTION, ROLLUP_TEXT: (todayText && todayText.text) || 'No roll-up yet today.', TEAM_NAMES, KPIS: todayKpis, avatarColors, initials, }); window.dispatchEvent(new CustomEvent('status-data-ready', {detail: window.STATUS_DATA})); } // Expose bootstrap so the Today tab refresh button (and elsewhere) can re-fetch // every dataset on demand. Listen for the refresh-requested event dispatched // from page-today.jsx and call bootstrap() again. window.STATUS_REFRESH = bootstrap; window.addEventListener('status-data-refresh-requested', () => { bootstrap().catch(err => console.warn('[data] refresh failed:', err)); }); bootstrap();