diff --git a/eslint.config.mjs b/eslint.config.mjs index 05e726d..1b9b9cf 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -12,6 +12,8 @@ "out/**", "build/**", "next-env.d.ts", + // Reference-only UI mock, not production code. + "src/mocks/**", ]), ]); diff --git a/src/mocks/d-watch-mock.jsx b/src/mocks/d-watch-mock.jsx new file mode 100644 index 0000000..3efaaf5 --- /dev/null +++ b/src/mocks/d-watch-mock.jsx @@ -0,0 +1,2339 @@ +import React, { useState } from "react"; + +const DARK = { + bg: "#0f1419", sf: "#1a2332", sfH: "#1e2a3a", bd: "#2a3a4e", + pr: "#3b82f6", prD: "#1e3a5f", ok: "#22c55e", okD: "#0a3d1f", + wn: "#f59e0b", wnD: "#3d2a06", ng: "#ef4444", ngD: "#3d1111", + ngB: "#ff6b6b", tx: "#e2e8f0", txD: "#94a3b8", txM: "#64748b", ac: "#06b6d4" +}; +const LIGHT = { + bg: "#f1f5f9", sf: "#ffffff", sfH: "#f8fafc", bd: "#e2e8f0", + pr: "#2563eb", prD: "#dbeafe", ok: "#16a34a", okD: "#dcfce7", + wn: "#d97706", wnD: "#fef3c7", ng: "#dc2626", ngD: "#fee2e2", + ngB: "#dc2626", tx: "#1e293b", txD: "#475569", txM: "#94a3b8", ac: "#0891b2" +}; + +const LOG = [ + { f: "MEGAドンキ渋谷本店", m: "PUDU CC1", s: "8111R5628060011", d: "3/27", t: "02:00", h1: "充電中", h30: "待機中", dp: true, cp: 92, g: "◎" }, + { f: "ドンキ新宿歌舞伎町店", m: "PUDU CC1", s: "8111R5628060022", d: "3/27", t: "02:00", h1: "ハンドルアップ", h30: "ハンドルアップ", dp: false, cp: 0, g: "×" }, + { f: "MEGAドンキ池袋駅北口店", m: "PUDU CC1", s: "8111R5628060033", d: "3/27", t: "01:30", h1: "待機中", h30: "待機中", dp: true, cp: 45, g: "△" }, + { f: "ドンキ中目黒本店", m: "PUDU CC1", s: "8111R5628060044", d: "3/27", t: "03:00", h1: "待機中", h30: "待機中", dp: false, cp: 0, g: "×" }, + { f: "MEGAドンキ港山下総本店", m: "PUDU CC1", s: "8111R5628060055", d: "3/27", t: "02:00", h1: "待機中", h30: "待機中", dp: true, cp: 35, g: "△" }, + { f: "ドンキ八王子駅前店", m: "PUDU CC1", s: "8111R5628060077", d: "3/27", t: "02:30", h1: "オフライン", h30: "オフライン", dp: false, cp: 0, g: "×" }, + { f: "ドンキ町田店", m: "PUDU CC1", s: "8111R5628060088", d: "3/27", t: "03:00", h1: "待機中", h30: "待機中", dp: false, cp: 0, g: "×" }, + { f: "MEGAドンキ立川店", m: "PUDU CC1", s: "8111R5628060099", d: "3/27", t: "01:30", h1: "緊急停止", h30: "待機中", dp: true, cp: 62, g: "△" }, + { f: "MEGAドンキ吉祥寺店", m: "PUDU CC1", s: "8111R5628060122", t: "02:00", h1: "待機中", h30: "待機中", dp: true, cp: 96, g: "◎" }, + { f: "ドンキ三軒茶屋店", m: "PUDU CC1", s: "8111R5628060133", t: "02:30", h1: "待機中", h30: "待機中", dp: true, cp: 94, g: "◎" }, +]; + +const PRECHK = [ + { id: 1, sev: "1h前異常", serial: "8111R5628060011", fac: "MEGAドンキ渋谷本店", sch: "02:00", st: "充電中", ok: true, at: "01:45", dt: "3/27 01:00", note: "30分前までに充電完了、出発成功" }, + { id: 2, sev: "1h前異常", serial: "8111R5628060022", fac: "ドンキ新宿歌舞伎町店", sch: "02:00", st: "ハンドルアップ", ok: false, at: "-", dt: "3/27 01:00", note: "30分前も未解消 → 出発失敗" }, + { id: 3, sev: "1h前異常", serial: "8111R5628060099", fac: "MEGAドンキ立川店", sch: "01:30", st: "緊急停止", ok: true, at: "01:10", dt: "3/27 00:30", note: "30分前に復旧、出発成功(完了率62%)" }, + { id: 4, sev: "1h前異常", serial: "8111R5628060077", fac: "ドンキ八王子駅前店", sch: "02:30", st: "オフライン", ok: false, at: "-", dt: "3/27 01:30", note: "30分前もオフライン → 出発失敗" }, + { id: 5, sev: "出発失敗", serial: "8111R5628060044", fac: "ドンキ中目黒本店", sch: "03:00", st: "セキュリティ有効", ok: false, at: "-", dt: "3/27 03:00", note: "セキュリティ解除されず出発不可" }, + { id: 6, sev: "出発失敗", serial: "8111R5628060088", fac: "ドンキ町田店", sch: "03:00", st: "セキュリティ有効", ok: false, at: "-", dt: "3/27 03:00", note: "セキュリティ解除されず出発不可" }, +]; + +const DSA_INIT = [ + { id: 1, type: "セキュリティ無効", dev: "DS-001", fac: "MEGAドンキ渋谷本店", tm: "3/27 03:42", det: "清掃終了後もセキュリティが復帰していない", st: "未対応", mm: "" }, + { id: 2, type: "通信断", dev: "DS-005", fac: "MEGAドンキ大森山王店", tm: "3/27 02:55", det: "DS-005 が6時間以上通信なし", st: "未対応", mm: "" }, + { id: 3, type: "トークン期限", dev: "DS-004", fac: "MEGAドンキ港山下総本店", tm: "3/20", det: "DS-004 残り9日", st: "対応中", mm: "更新手続き中" }, + { id: 4, type: "セキュリティ無効", dev: "DS-003", fac: "ドンキ中目黒本店", tm: "3/26 03:55", det: "復帰遅延(10分後に復帰)", st: "対応済み", mm: "正常復帰を確認" }, +]; + +const RANK = [ + { f: "ドンキ蒲田駅前店", ar: 68, arP: 75, dp: 72, dpP: 80, cv: 55, cvP: 62, er: 12.5, erP: 9.0, s: "要改善", cont: 5 }, + { f: "ドンキ八王子駅前店", ar: 72, arP: 70, dp: 65, dpP: 68, cv: 63, cvP: 60, er: 9.8, erP: 10.2, s: "要改善", cont: 3 }, + { f: "ドンキ新宿歌舞伎町店", ar: 78, arP: 82, dp: 82, dpP: 85, cv: 71, cvP: 78, er: 7.2, erP: 5.5, s: "注意", cont: 0 }, + { f: "MEGAドンキ立川店", ar: 85, arP: 83, dp: 90, dpP: 88, cv: 80, cvP: 78, er: 5.1, erP: 5.8, s: "注意", cont: 0 }, + { f: "MEGAドンキ池袋駅北口店", ar: 92, arP: 90, dp: 95, dpP: 93, cv: 88, cvP: 85, er: 3.2, erP: 4.0, s: "良好", cont: 0 }, + { f: "MEGAドンキ渋谷本店", ar: 96, arP: 95, dp: 98, dpP: 97, cv: 94, cvP: 92, er: 1.8, erP: 2.1, s: "良好", cont: 0 }, +]; + +const ROBOTS = [ + { f: "MEGAドンキ渋谷本店", pref: "東京都", addr: "サンプル区サンプル町1-1", tel: "03-0000-0001", mgr: "店長A", staff: "担当者A", email: "store01@example.com", mon: true, dsec: true, bots: [{ mk: "PUDU", md: "CC1", sn: "8111R5628060011" }] }, + { f: "ドンキ新宿歌舞伎町店", pref: "東京都", addr: "サンプル区サンプル町1-2", tel: "03-0000-0002", mgr: "店長B", staff: "担当者B", email: "store02@example.com", mon: true, dsec: false, bots: [{ mk: "PUDU", md: "CC1", sn: "8111R5628060022" }] }, + { f: "MEGAドンキ池袋駅北口店", pref: "東京都", addr: "サンプル区サンプル町1-3", tel: "03-0000-0003", mgr: "店長C", staff: "担当者C", email: "store03@example.com", mon: true, dsec: true, bots: [{ mk: "PUDU", md: "CC1", sn: "8111R5628060033" }] }, + { f: "ドンキ中目黒本店", pref: "東京都", addr: "サンプル区サンプル町1-4", tel: "03-0000-0004", mgr: "店長D", staff: "担当者D", email: "store04@example.com", mon: true, dsec: true, bots: [{ mk: "PUDU", md: "CC1", sn: "8111R5628060044" }] }, + { f: "MEGAドンキ港山下総本店", pref: "神奈川県", addr: "サンプル市サンプル町1-5", tel: "045-000-0005", mgr: "店長E", staff: "担当者E", email: "store05@example.com", mon: true, dsec: true, bots: [{ mk: "PUDU", md: "CC1", sn: "8111R5628060055" }, { mk: "PUDU", md: "Phantas", sn: "8222P4527080066" }] }, + { f: "ドンキ八王子駅前店", pref: "東京都", addr: "サンプル市サンプル町1-6", tel: "042-000-0006", mgr: "店長F", staff: "担当者F", email: "store06@example.com", mon: true, dsec: false, bots: [{ mk: "PUDU", md: "CC1", sn: "8111R5628060077" }] }, + { f: "MEGAドンキ立川店", pref: "東京都", addr: "サンプル市サンプル町1-7", tel: "042-000-0007", mgr: "店長G", staff: "担当者G", email: "store07@example.com", mon: true, dsec: true, bots: [{ mk: "PUDU", md: "CC1", sn: "8111R5628060099" }, { mk: "PUDU", md: "CC1", sn: "8111R5628060100" }, { mk: "Keenon", md: "T5", sn: "9333K7412050111" }] }, + { f: "MEGAドンキ吉祥寺店", pref: "東京都", addr: "サンプル市サンプル町1-8", tel: "0422-00-0008", mgr: "店長H", staff: "担当者H", email: "store08@example.com", mon: true, dsec: false, bots: [{ mk: "PUDU", md: "CC1", sn: "8111R5628060122" }] }, + { f: "ドンキ三軒茶屋店", pref: "東京都", addr: "サンプル区サンプル町1-9", tel: "03-0000-0009", mgr: "店長I", staff: "担当者I", email: "store09@example.com", mon: true, dsec: false, bots: [{ mk: "PUDU", md: "CC1", sn: "8111R5628060133" }] }, +]; + +const SCHED = [ + { s: "8111R5628060011", f: "MEGAドンキ渋谷本店", d: "毎日", st: "02:00", en: "03:00", on: true }, + { s: "8111R5628060022", f: "ドンキ新宿歌舞伎町店", d: "毎日", st: "02:00", en: "02:45", on: true }, + { s: "8111R5628060044", f: "ドンキ中目黒本店", d: "月水金", st: "03:00", en: "03:45", on: true }, + { s: "8111R5628060077", f: "ドンキ八王子駅前店", d: "毎日", st: "02:30", en: "03:15", on: false }, +]; + +const DSDEV = [ + { id: "DS-001", f: "MEGAドンキ渋谷本店", st: "異常", lc: "3/27 03:42", te: "2026-06-15", n: "セキュリティ復帰失敗" }, + { id: "DS-002", f: "MEGAドンキ池袋駅北口店", st: "正常", lc: "3/27 09:01", te: "2026-08-20", n: "" }, + { id: "DS-003", f: "ドンキ中目黒本店", st: "正常", lc: "3/27 09:00", te: "2026-07-10", n: "" }, + { id: "DS-004", f: "MEGAドンキ港山下総本店", st: "正常", lc: "3/27 08:55", te: "2026-04-05", n: "" }, + { id: "DS-005", f: "MEGAドンキ大森山王店", st: "通信断", lc: "3/27 02:55", te: "2026-07-20", n: "6時間以上通信なし" }, +]; +var INSP_ITEMS = ["ブラシ", "ダストボックス", "フィルター", "スクイジー", "吸引ホース", "汚水タンク", "ブラシ裏面"]; +const INSPECTIONS = [ + { id: 1, date: "2026/03/11", inspector: "検査担当A", company: "DFA Robotics", brand: "ドン・キホーテ", facility: "サンプル店舗1", robot: "8111R5628060011", notes: "" }, + { id: 2, date: "2026/03/11", inspector: "検査担当A", company: "DFA Robotics", brand: "ドン・キホーテ", facility: "サンプル店舗2", robot: "8111R5628060022", notes: "" }, + { id: 3, date: "2026/03/11", inspector: "検査担当A", company: "DFA Robotics", brand: "ドン・キホーテ", facility: "サンプル店舗3", robot: "8111R5628060033", notes: "サンプルメモ: 過去の対応履歴あり、現状異常なし" }, + { id: 4, date: "2026/03/11", inspector: "検査担当A", company: "DFA Robotics", brand: "ドン・キホーテ", facility: "サンプル店舗4", robot: "8111R5628060044", notes: "" }, + { id: 5, date: "2026/03/16", inspector: "検査担当A", company: "DFA Robotics", brand: "ドン・キホーテ", facility: "サンプル店舗5", robot: "8111R5628060055", notes: "" }, + { id: 6, date: "2026/03/16", inspector: "検査担当A", company: "DFA Robotics", brand: "ユニー", facility: "サンプル店舗6", robot: "8111R5628060066", notes: "" }, + { id: 7, date: "2026/03/16", inspector: "検査担当A", company: "DFA Robotics", brand: "ドン・キホーテ", facility: "サンプル店舗7", robot: "8111R5628060077", notes: "" }, +]; + +function Pill({ text, color }) { + return ( + + {text} + + ); +} + +function Num({ value, label, sub, color, onClick, c }) { + return ( +
+
{label}
+
{value}
+ {sub &&
{sub}
} +
+ ); +} + +function stColor(c, v) { + if (v === "待機中") return c.ok; + if (v === "充電中") return c.pr; + if (v === "ハンドルアップ" || v === "緊急停止" || v === "オフライン") return c.ng; + if (v === "セキュリティ有効") return c.ac; + return c.txD; +} + +function gradeStyle(c, g) { + if (g === "◎" || g === "○") return { bg: c.okD, tx: c.ok }; + if (g === "△") return { bg: c.wnD, tx: c.wn }; + return { bg: c.ngD, tx: c.ng }; +} + +export default function App() { + const [page, setPage] = useState("dashboard"); + const [dark, setDark] = useState(true); + const [lang, setLang] = useState("ja"); + var T = { + "エラー率": "Error Rate", "変化": "Change", "期間": "Period", + "最終ログイン": "Last Login", "上位10件に戻す": "Show top 10", + "シリアル:": "Serial:", "シリアルNo.": "Serial No.", "ブランド:": "Brand:", + "所属会社:": "Org.:", "記入者:": "Inspector:", + "ロボット *": "Robot *", "店舗 *": "Store *", "所属会社 *": "Org. *", + "記入者氏名 *": "Inspector name *", "訪問日 *": "Visit date *", + "選択してください": "Please select", "店舗を選択すると表示": "Select a store to show", + "📷 アップロード": "📷 Upload", "📷 写真": "📷 Photo", + "姓 名": "First Last", "例: PPIH": "e.g. PPIH", "例: 株式会社○○": "e.g. ○○ Inc.", + "PPIH(グループ)": "PPIH (Group)", + "施設名 / シリアル": "Facility / Serial", "施設名 / シリアル / 住所": "Facility / Serial / Address", + "施設名 / デバイスID": "Facility / Device ID", "施設名 / 記入者": "Facility / Inspector", + "問題エリアはありません(全エリア◎○)": "No problem areas (all ◎○)", + "申し送り事項があればこちらに記載ください": "Add any handover notes here", + "DFAロボット監視システム": "DFA Robot Monitoring System", "DFA運用担当": "DFA Operator", + "📊 ダッシュボード": "📊 Dashboard", "📋 清掃詳細ログ": "📋 Cleaning Logs", "🔔 アラート": "🔔 Alerts", "📈 レポート": "📈 Reports", "🔧 点検記録": "🔧 Inspections", + "🔒 D-Secログ": "🔒 D-Sec Logs", "⚠ D-Secアラート": "⚠ D-Sec Alerts", + "🏢 施設ロボットマスタ": "🏢 Facility Master", "📅 スケジュール": "📅 Schedules", "⚙ システム管理": "⚙ System Admin", + "モニタリング": "MONITORING", "設定": "SETTINGS", + "施設名": "Facility", "機種": "Model", "シリアル": "Serial", "日付": "Date", "予定": "Scheduled", "出発": "Dep.", "完了率": "Completion", "判定": "Grade", + "接続状態": "Status", "最終通信": "Last Comm.", "トークン期限": "Token Expiry", "残日数": "Days Left", "備考": "Notes", "操作": "Actions", + "会社名": "Company", "グループ名": "Group", "連絡先": "Contact", "契約プラン": "Plan", "契約モジュール": "Modules", "状態": "Status", "ステータス": "Status", + "名前": "Name", "メール": "Email", "ロール": "Role", "会社": "Company", "施設": "Facility", "訪問日": "Visit Date", "記入者": "Inspector", "所属会社": "Org.", "店舗名": "Store", "ブランド": "Brand", "申し送り": "Notes", + "ダッシュボード": "Dashboard", "清掃詳細ログ": "Cleaning Detail Log", "アラート(事前チェック)": "Alerts (Pre-check)", "レポート": "Reports", "D-Sec ログ": "D-Sec Logs", "D-Security アラート": "D-Security Alerts", "施設ロボットマスタ": "Facility Robot Master", "スケジュール管理": "Schedule Mgmt", "システム管理": "System Admin", "点検記録": "Inspection Records", + "CSVエクスポート": "CSV Export", "CSV": "CSV", "検索": "Search", "閉じる": "Close", "キャンセル": "Cancel", "保存": "Save", "登録": "Register", "編集": "Edit", "削除": "Delete", "詳細": "Detail", + "全グループ": "All Groups", "全会社": "All Companies", "全件": "All", "全ステータス": "All Status", "全種別": "All Types", "全て": "All", + "未解消のみ": "Unresolved", "解消済み": "Resolved", "未対応・対応中": "Open / In Progress", "対応済み": "Done", "△×のみ": "△× Only", "×のみ": "× Only", + "問題エリアのみ": "Problems Only", + "未解消": "Unresolved", "要対応": "Action Needed", "合計": "Total", "全アラート": "All Alerts", "対応完了": "Completed", "未対応": "Open", "即時対応": "Urgent", "対応中": "In Progress", "進行中": "Ongoing", "完了": "Done", + "正常稼働": "Normal", "異常・通信断": "Error / Offline", "D-Sデバイス総数": "Total Devices", + "週次": "Weekly", "月次": "Monthly", "稼働率": "Uptime", "出発成功率": "Dep. Rate", "清掃完了率": "Completion", "エラー発生率": "Error Rate", + "連続問題施設": "Recurring Issues", "エラー種別の内訳": "Error Breakdown", "施設別パフォーマンス(稼働率ワースト順)": "Facility Performance (Worst Uptime)", + "表示設定": "Display", "通知設定": "Notifications", "ユーザー管理": "Users", "権限管理": "Permissions", "顧客管理": "Customers", "グループ管理": "Groups", "運用設定": "Operations", "監査ログ": "Audit Log", + "要改善": "Needs Fix", "注意": "Caution", "良好": "Good", "有効": "Active", "停止": "Inactive", + "事前チェック設定": "Pre-check Timing", "完了率の判定閾値": "Completion Thresholds", "アドバイス エスカレーション閾値": "Escalation Thresholds", "分前": "min before", "日以上連続": "consecutive days+", + "+ 新規登録": "+ New Record", "+ 顧客追加": "+ Add Customer", "+ グループ追加": "+ Add Group", "📥 CSVインポート": "📥 CSV Import", + "エリア別詳細を見る →": "View Area Details →", "エリア別清掃完了率": "Area Completion Rates", + "点検記録 詳細": "Inspection Detail", "点検記録 新規登録": "New Inspection", "総点検数": "Total", "期間内": "In Period", "点検施設数": "Facilities", "ユニーク": "Unique", "申し送りあり": "Has Notes", "要確認": "Review", "あり": "Yes", + "スケジュール開始前のエラー検知と出発失敗": "Pre-schedule error detection and departure failures", + "セキュリティ無効・通信断・トークン期限のアラート管理": "Security / connection / token alerts", + "定期点検の実施記録・写真・申し送り事項を管理": "Manage inspection records, photos and notes", + "申し送り事項": "Notes", "Before(点検前)": "Before", "After(点検後)": "After", + "待機中": "Idle", + "充電中": "Charging", + "ハンドルアップ": "Handle Up", + "緊急停止": "E-Stop", + "オフライン": "Offline", + "セキュリティ有効": "Security On", + "正常": "Normal", + "異常": "Error", + "通信断": "No Comms", + "出発失敗": "Departure Failed", + "1h前異常": "1h Pre-alert", + "セキュリティ無効": "Security Off", + "トークン期限": "Token Expiry", + "DFAシステム管理者": "DFA System Admin", + "DFA運用担当": "DFA Operator", + "顧客管理者": "Customer Admin", + "施設運用者": "Facility Operator", + "施設運用者・清掃業者": "Facility Operator / Cleaner", + "会社": "Company", + "ー": "—", + "ー(会社全体)": "— (Company-wide)", + "ー(会社/グループ全体)": "— (Company/Group)", + "ー(グループなし)": "— (No group)", + "全顧客・全施設": "All customers & facilities", + "自社施設のみ": "Own facilities only", + "自施設のみ": "Own facility only", + "全権限。権限管理の編集はこのロールのみ": "Full access. Only this role can edit permissions.", + "全メニュー閲覧+施設ロボットマスタ編集。権限管理は閲覧のみ": "Read all menus + edit Facility Master. Permissions read-only.", + "自社の閲覧全般。施設ロボットマスタの編集不可": "Read access to own company. Cannot edit Facility Master.", + "設定メニュー以外の自施設データを閲覧": "Read own-facility data except Settings menu.", + "閲覧": "View", + "閲覧/編集": "View/Edit", + "閲覧/編集/登録": "View/Edit/Create", + "システム設定": "System Settings", + "フル権限": "Full access", + "新規登録不可": "no create", + "表示のみ": "view only", + "アクセス不可": "no access", + "1h前ST": "1h ST", + "30分前ST": "30m ST", + "都道府県": "Prefecture", + "住所": "Address", + "電話番号": "Phone", + "責任者名": "Manager", + "担当者名": "Contact", + "監視": "Monitor", + "ロボット(メーカー / 機種 / シリアル)": "Robots (Maker / Model / Serial)", + "曜日": "Days", + "時間": "Time", + "会社/グループ": "Company/Group", + "ロール": "Role", + "実装": "Phase", + "紐づき": "Binding", + "データスコープ": "Data Scope", + "説明": "Description", + "メニュー": "Menu", + "DFA管理者": "DFA Admin", + "DFA運用": "DFA Ops", + "チャネル": "Channel", + "送信先": "Destination", + "イベント": "Event", + "スコープ": "Scope", + "グループ": "Group", + "傘下会社": "Member Cos.", + "会社数": "Companies", + "総施設数": "Facilities", + "総ロボット数": "Robots", + "契約モジュール": "Modules", + "監視施設": "Monitored", + "D-Sec施設": "D-Sec", + "全施設": "All Fac.", + "ロボット": "Robots", + "日時": "Timestamp", + "操作者": "User", + "アクション": "Action", + "対象": "Target", + "成功": "Success", + "失敗": "Failed", + "未実施": "Not Run", + "成功率": "Success Rate", + "/ 300施設": "/ 300 facilities", + "ロボット7 / セキュリティ2 / 制御盤1": "Robot 7 / Security 2 / Panel 1", + "予定時刻経過・実績なし": "Past scheduled time, no run", + "目標: 95%": "Target: 95%", + "D-Sデバイス総数": "Total D-Sec Devices", + "正常稼働": "Normal", + "異常・通信断": "Error / No Comms", + "昨夜の清掃結果サマリー — 2026-03-27": "Last night's cleaning summary — 2026-03-27", + "⚠ セキュリティ無効: MEGAドンキ渋谷本店 1F売場(03:42以降)": "⚠ Security off: MEGAドンキ渋谷本店 1F floor (after 03:42)", + "清掃詳細ログ": "Cleaning Detail Log", + "スケジュール進捗": "Schedule Progress", + " / 312 完了": " / 312 done", + "失敗の内訳": "Failure Breakdown", + "ロボット異常": "Robot Error", + "制御盤異常": "Control Panel Error", + "1行=1ロボット×1スケジュール実行": "1 row = 1 robot × 1 schedule run", + "判定基準(タスク完了率):": "Grade criteria (task completion):", + "◎ 90%超": "◎ >90%", + "○ 70%超": "○ >70%", + "× 10%未満": "× <10%", + "(閾値はシステム設定で変更可能)": "(thresholds editable in settings)", + "🔴 レベル3 — DFA介入を検討": "🔴 Level 3 — Consider DFA intervention", + "🔶 レベル2 — DFA運用担当がアクション": "🔶 Level 2 — DFA operator to act", + "⚠ レベル1 — 現場確認が必要": "⚠ Level 1 — On-site check needed", + "出発失敗(R1)": "Departure failed (R1)", + "事前チェックのアラート内容を確認してください。セキュリティ未解除/ロボット異常/充電不足のいずれかが原因です。": "Check the pre-check alert. Cause is likely security not released, robot error, or low charge.", + "完了率が極端に低い(R2)": "Completion extremely low (R2)", + "現場の通路状況+ロボット状態を確認してください。出発直後に停止した可能性があります。": "Check aisle conditions and robot state on site. It may have stopped right after departure.", + "マップ更新の確認": "Check map update", + "マップがレイアウト変更に追従していない可能性があります。直近の棚替え・売場変更がなかったか現場に確認してください。": "The map may not reflect a layout change. Confirm any recent shelf/floor changes with the site.", + "2日連続で完了率低下(R3)": "Completion down 2 days running (R3)", + "恒常的な環境変化の可能性があります。現場責任者に状況をヒアリングしてください。": "A persistent environmental change is likely. Ask the site manager about the situation.", + "複数日連続で清掃未実施(R4)": "No cleaning for several days (R4)", + "現場対応では解決しない構造的問題の可能性があります。DFAオンサイト訪問(マップ再作成・環境調査)の手配を検討してください。": "Likely a structural issue not fixable on site. Consider arranging a DFA on-site visit (re-map / survey).", + "ハンドルアップ検知": "Handle-up detected", + "ハンドルが上がった状態です。現場にハンドルを下ろすよう連絡してください。": "The handle is raised. Ask the site to lower it.", + "オフライン検知": "Offline detected", + "ネットワーク接続が切れています。Wi-Fiルーターの再起動を現場に依頼してください。": "Network connection is down. Ask the site to restart the Wi-Fi router.", + "← 戻る": "← Back", + "全項目 →": "All columns →", + "2日以上連続で△以下または出発失敗の施設": "Facilities at △ or below, or departure-failed, 2+ days running", + "該当施設なし": "No matching facilities", + "今週のエラー発生件数(種別ごと)": "This week's error count by type", + "ブラシエラー": "Brush Error", + "充電不足": "Low Charge", + "その他": "Other", + "前週": "Prev. wk", + "前月": "Prev. mo", + "稼働率": "Uptime", + "出発成功率": "Dep. Rate", + "要改善": "Needs Fix", + "注意": "Caution", + "良好": "Good", + "残日数": "Days Left", + "+ 新規追加": "+ Add New", + "📥 インポート": "📥 Import", + "あり": "Yes", + "ダッシュボード表示項目": "Dashboard items", + "各項目のON/OFFでダッシュボードの表示内容をカスタマイズ": "Toggle items to customize the dashboard", + "D-Securityアラートバナー": "D-Security alert banner", + "セキュリティ無効施設の赤色警告バナー": "Red warning banner for security-off facilities", + "清掃詳細ログ(コンパクト版)": "Cleaning detail log (compact)", + "△×の清掃詳細ログテーブル": "Detail log table for △×", + "完了数/予定数のプログレスバー": "Progress bar of done/scheduled", + "ロボット異常/未実施/セキュリティ有効/制御盤異常の件数": "Counts of robot error / not run / security on / panel error", + "清掃開始予定時刻の何分前にロボット状態を自動チェックするかを設定": "Set how many minutes before the scheduled start to auto-check robot state", + "清掃開始予定時刻の何分前にロボット状態をチェックするか": "How many minutes before scheduled start to check robot state", + "第1回チェック(分前)": "1st check (min before)", + "第2回チェック(分前)": "2nd check (min before)", + "第1回チェック": "1st check", + "第2回チェック": "2nd check", + "デフォルト: 60分": "Default: 60 min", + "デフォルト: 30分": "Default: 30 min", + "清掃詳細ログの◎○△×判定に使用する閾値を設定": "Set thresholds used for ◎○△× grading in the detail log", + "清掃詳細ログの◎○△×判定に使用する閾値": "Thresholds used for ◎○△× grading in the detail log", + "◎ 基準": "◎ threshold", + "○ 基準": "○ threshold", + "△ 基準": "△ threshold", + "以上で◎": "≥ for ◎", + "以上で○(◎未満)": "≥ for ○ (below ◎)", + "以上で△(○未満)": "≥ for △ (below ○)", + "以上": "≥", + "未満": "<", + "上記未満": "below the above", + "※ △基準未満は自動的に×判定。現在はシステム全体の設定。P2以降で顧客別設定に拡張予定": "* Below △ is auto ×. Currently system-wide; per-customer settings planned for P2+.", + "対応アドバイスのレベルが上がる連続日数の閾値を設定": "Set the consecutive-day thresholds at which advice escalates", + "同一施設で問題が連続した場合のレベル引き上げ基準(スケジュール存在日の連続日数)": "Escalation criteria for repeated issues at the same facility (consecutive scheduled days)", + "レベル2(DFA運用がアクション)": "Level 2 (DFA operator acts)", + "レベル3(DFA介入・オンサイト訪問検討)": "Level 3 (DFA intervention / on-site visit)", + "※「連続」はスケジュール存在日の連続。実績データに基づき調整を推奨": "* “Consecutive” counts scheduled days only. Tune against actual data.", + "清掃監視の運用パラメータ。変更はDFAシステム管理者のみ": "Cleaning-monitoring parameters. Editable by DFA System Admin only.", + "顧客デフォルト+施設上書きの継承モデル": "Customer default + facility override model", + "+ 通知追加": "+ Add notification", + "メール": "Email", + "全イベント": "All events", + "全施設(デフォルト)": "All facilities (default)", + "渋谷本店(上書き)": "Shibuya HQ (override)", + "ユーザーマスタ": "User Master", + "顧客管理者は会社/グループに紐づく。施設運用者は施設に紐づく": "Customer admins bind to a company/group; facility operators bind to a facility.", + "ロール定義(4層)": "Role definitions (4 tiers)", + "各ロールのスコープと紐づき": "Scope and binding per role", + "メニュー×ロール アクセスマトリクス": "Menu × Role access matrix", + "施設運用者のスコープは自施設のみに限定": "Facility operators are scoped to their own facility only", + "会社単位の契約・モジュール管理": "Per-company contract & module management", + "○ 監視": "✓ Monitor", + "ー 監視": "— Monitor", + "○ D-Sec": "✓ D-Sec", + "ー D-Sec": "— D-Sec", + "グループ会社の親子関係を管理。顧客管理者をグループに紐づけると傘下の全会社を横断閲覧可能": "Manage group parent-child relations. A customer admin bound to a group can view all member companies.", + "グループサマリー": "Group summary", + "チェックした会社がこのグループの傘下として紐づきます。グループ管理者は傘下全社のデータを横断閲覧可能になります。": "Checked companies are bound under this group. Group admins can view data across all member companies.", + "傘下会社の選択": "Select member companies", + "所属中": "Member", + "操作ログ(監査ログ)": "Operation log (audit)", + "全ての手動操作を記録。誰が・いつ・何をしたかを追跡": "Records all manual operations; tracks who did what and when.", + "清掃開始(手動)": "Start cleaning (manual)", + "スケジュール変更": "Schedule change", + "ロボット同期": "Robot sync", + "顧客モジュール変更": "Customer module change", + "施設追加": "Facility added", + "MEGAドンキ渋谷本店 / 1F売場": "MEGAドンキ渋谷本店 / 1F floor", + "ドンキ八王子駅前店 — 無効化": "ドンキ八王子駅前店 — disabled", + "8111R5628060011 v3.2に更新": "8111R5628060011 updated to v3.2", + "ドン・キホーテ — D-Security有効化": "ドン・キホーテ — D-Security enabled", + "30分前までに充電完了、出発成功": "Charged before 30-min mark; departed OK", + "30分前も未解消 → 出発失敗": "Unresolved at 30-min mark → departure failed", + "30分前に復旧、出発成功(完了率62%)": "Recovered at 30-min mark; departed OK (62% complete)", + "30分前もオフライン → 出発失敗": "Still offline at 30-min mark → departure failed", + "セキュリティ解除されず出発不可": "Security not released; cannot depart", + "清掃終了後もセキュリティが復帰していない": "Security not restored after cleaning", + "DS-005 が6時間以上通信なし": "DS-005 no comms for 6+ hours", + "DS-004 残り9日": "DS-004 9 days left", + "復帰遅延(10分後に復帰)": "Restore delayed (recovered after 10 min)", + "更新手続き中": "Renewal in progress", + "正常復帰を確認": "Confirmed normal restore", + "セキュリティ復帰失敗": "Security restore failed", + "6時間以上通信なし": "No comms for 6+ hours", + "副店長様より、以前水漏れがしていて何度かメンテナンスをしてもらったとのこと。現状は改善されているとのことで、今回メンテナンス時も異常なし": "Per the assistant manager, there had been a past leak serviced several times. It is reported resolved; no issues found at this maintenance.", + "会社名 *": "Company *", + "連絡先メール *": "Contact email *", + "契約プラン": "Plan", + "連絡先メール": "Contact email", + "○ 監視(モニタリング)": "✓ Monitoring", + "名前 *": "Name *", + "メールアドレス *": "Email *", + "ロール *": "Role *", + "会社 / グループ *": "Company / Group *", + "施設(施設運用者のみ)": "Facility (operators only)", + "グループ名 *": "Group name *", + "最終ログイン: ": "Last login: ", + "顧客追加": "Add Customer", + "ユーザー新規登録": "New User", + "グループ追加": "Add Group", + "点検記録 新規登録": "New Inspection Record", + "問題エリアのみ": "Problems only", + "全件": "All", + "判定": "Grade", + "比": "vs", + }; + function t(s) { return lang === "en" && T[s] ? T[s] : s; } + const [dFilt, setDFilt] = useState("problem"); + const [lFilt, setLFilt] = useState("problem"); + const [lSF, setLSF] = useState(""); + const [lSM, setLSM] = useState(""); + const [lSS, setLSS] = useState(""); + const [aFilt, setAFilt] = useState("unresolved"); + const [daFilt, setDaFilt] = useState("unresolved"); + const [dsaState, setDsaState] = useState(DSA_INIT); + const [rSrch, setRSrch] = useState(""); + const [sSrch, setSSrch] = useState(""); + const [showDS, setShowDS] = useState(true); + const [sysTab, setSysTab] = useState("display"); + const [dashItems, setDashItems] = useState({ secBanner: true, detailLog: true, scheduleBar: true, failBreakdown: true }); + const [showGradeInfo, setShowGradeInfo] = useState(false); + const [custEdit, setCustEdit] = useState(null); + const [userEdit, setUserEdit] = useState(null); + const [grpEdit, setGrpEdit] = useState(null); + const [expandedRow, setExpandedRow] = useState(null); + const [areaModal, setAreaModal] = useState(null); + const [areaFilt, setAreaFilt] = useState("problem"); + const [rptPeriod, setRptPeriod] = useState("weekly"); + const [showAllRank, setShowAllRank] = useState(false); + const [showAllCont, setShowAllCont] = useState(false); + const [dsLogFilt, setDsLogFilt] = useState("all"); + const [dsaTypeFilt, setDsaTypeFilt] = useState("all"); + const [inspDetail, setInspDetail] = useState(null); + const [inspNew, setInspNew] = useState(false); + + const c = dark ? DARK : LIGHT; + + const unresPre = PRECHK.filter(function (a) { return !a.ok; }).length; + const unresDsa = dsaState.filter(function (a) { return a.st === "未対応" || a.st === "対応中"; }).length; + + function cycleDsa(id) { + setDsaState(function (prev) { + return prev.map(function (a) { + if (a.id !== id) return a; + var nx = "未対応"; + if (a.st === "未対応") nx = "対応中"; + if (a.st === "対応中") nx = "対応済み"; + if (a.st === "対応済み") nx = "未対応"; + return Object.assign({}, a, { st: nx }); + }); + }); + } + + function filt(data, grade, sf, sm, ss) { + var d = data; + if (grade === "problem") d = d.filter(function (r) { return r.g === "△" || r.g === "×"; }); + else if (grade !== "all") d = d.filter(function (r) { return r.g === grade; }); + if (sf) d = d.filter(function (r) { return r.f.toLowerCase().indexOf(sf.toLowerCase()) >= 0; }); + if (sm) d = d.filter(function (r) { return r.m.toLowerCase().indexOf(sm.toLowerCase()) >= 0; }); + if (ss) d = d.filter(function (r) { return r.s.toLowerCase().indexOf(ss.toLowerCase()) >= 0; }); + return d; + } + + var th = { padding: "10px 12px", fontSize: 11, color: c.txM, textTransform: "uppercase", letterSpacing: 0.6, borderBottom: "1px solid " + c.bd, whiteSpace: "nowrap" }; + var td = { padding: "12px", fontSize: 13, color: c.tx }; + var tdm = { padding: "12px", fontSize: 13, color: c.txD }; + + function BtnF(props) { + var active = props.active; + var color = props.color; + return ( + + ); + } + + function getAdvice(row) { + var advices = []; + if (!row.dp && row.cp === 0) { + advices.push({ level: 1, icon: "⚠", title: "出発失敗(R1)", text: "事前チェックのアラート内容を確認してください。セキュリティ未解除/ロボット異常/充電不足のいずれかが原因です。" }); + } + if (row.cp > 0 && row.cp < 10) { + advices.push({ level: 1, icon: "⚠", title: "完了率が極端に低い(R2)", text: "現場の通路状況+ロボット状態を確認してください。出発直後に停止した可能性があります。" }); + } + if (row.cp >= 10 && row.cp < 70) { + advices.push({ level: 1, icon: "⚠", title: "マップ更新の確認", text: "マップがレイアウト変更に追従していない可能性があります。直近の棚替え・売場変更がなかったか現場に確認してください。" }); + } + if (row.g === "△") { + advices.push({ level: 2, icon: "🔶", title: "2日連続で完了率低下(R3)", text: "恒常的な環境変化の可能性があります。現場責任者に状況をヒアリングしてください。" }); + } + if (row.g === "×" && !row.dp) { + advices.push({ level: 3, icon: "🔴", title: "複数日連続で清掃未実施(R4)", text: "現場対応では解決しない構造的問題の可能性があります。DFAオンサイト訪問(マップ再作成・環境調査)の手配を検討してください。" }); + } + if (row.h1 === "ハンドルアップ" || row.h30 === "ハンドルアップ") { + advices.push({ level: 1, icon: "🔧", title: "ハンドルアップ検知", text: "ハンドルが上がった状態です。現場にハンドルを下ろすよう連絡してください。" }); + } + if (row.h1 === "オフライン" || row.h30 === "オフライン") { + advices.push({ level: 1, icon: "📡", title: "オフライン検知", text: "ネットワーク接続が切れています。Wi-Fiルーターの再起動を現場に依頼してください。" }); + } + return advices; + } + + var AREA_DATA = { + "MEGAドンキ渋谷本店": [ + { name: "1F入口", cp: 100 }, { name: "1F売場", cp: 98 }, { name: "1Fレジ前", cp: 95 }, { name: "1F通路A", cp: 100 }, + { name: "2F家電", cp: 92 }, { name: "2F文具", cp: 88 }, { name: "2F玩具", cp: 96 }, { name: "2F通路B", cp: 100 }, + { name: "3Fアパレル", cp: 94 }, { name: "3F靴売場", cp: 90 }, { name: "B1食品", cp: 97 }, { name: "B1飲料", cp: 100 }, + ], + "MEGAドンキ立川店": [ + { name: "1F入口", cp: 100 }, { name: "1F売場", cp: 98 }, { name: "1Fレジ前", cp: 95 }, { name: "1F通路A", cp: 100 }, + { name: "1Fコスメ", cp: 92 }, { name: "1Fお菓子", cp: 88 }, + { name: "2F家電入口", cp: 45 }, { name: "2F家電奥", cp: 78 }, { name: "2F文具", cp: 82 }, { name: "2F玩具", cp: 96 }, + { name: "3F倉庫前通路", cp: 38 }, { name: "3Fアパレル", cp: 72 }, { name: "3F靴売場", cp: 90 }, + { name: "B1食品", cp: 12 }, { name: "B1飲料", cp: 55 }, { name: "B1日配", cp: 68 }, + ], + "MEGAドンキ池袋駅北口店": [ + { name: "1", cp: 98 }, { name: "2", cp: 95 }, { name: "3", cp: 42 }, { name: "4", cp: 88 }, + { name: "5", cp: 35 }, { name: "6", cp: 100 }, { name: "7", cp: 52 }, { name: "8", cp: 90 }, + ], + "MEGAドンキ港山下総本店": [ + { name: "A棟1F", cp: 100 }, { name: "A棟2F", cp: 85 }, { name: "A棟3F", cp: 22 }, + { name: "B棟1F", cp: 95 }, { name: "B棟2F", cp: 18 }, { name: "B棟3F", cp: 42 }, + ], + }; + function getAreaData(facility) { + return AREA_DATA[facility] || [ + { name: "1", cp: 88 }, { name: "2", cp: 45 }, { name: "3", cp: 12 }, + { name: "4", cp: 92 }, { name: "5", cp: 78 }, { name: "6", cp: 100 }, + ]; + } + function areaGrade(cp) { return cp >= 90 ? "◎" : cp >= 70 ? "○" : cp >= 10 ? "△" : "×"; } + function tokenDays(dateStr) { var d = new Date(dateStr); var now = new Date("2026-03-27"); return Math.ceil((d - now) / 86400000); } + + function LogTable(props) { + var data = props.data; + var compact = props.compact; + return ( + + + + + {!compact && } + {!compact && } + + + + + + + + + + {showGradeInfo && ( + + )} + + {data.map(function (r, i) { + var gs = gradeStyle(c, r.g); + var hasAdvice = r.g === "△" || r.g === "×"; + var isExpanded = expandedRow === (compact ? "d" + i : "l" + i); + var rowKey = compact ? "d" + i : "l" + i; + var advices = hasAdvice ? getAdvice(r) : []; + var maxLevel = advices.reduce(function (m, a) { return Math.max(m, a.level); }, 0); + return ( + + + + {!compact && } + {!compact && } + + + + + + + + + {isExpanded && advices.length > 0 && ( + + )} + + ); + })} + +
{t("施設名")}{t("機種")}{t("シリアル")}{t("日付")}{t("予定")}{t("1h前ST")}{t("30分前ST")}{t("出発")}{t("完了率")}{t("判定")} ?
+
+ {t("判定基準(タスク完了率):")} + {t("◎ 90%超")} + {t("○ 70%超")} + △ 10-69% + {t("× 10%未満")} + {t("(閾値はシステム設定で変更可能)")} + +
+
{r.f}{r.m}{r.s}{r.d}{r.t}{r.dp ? "✓" : "✗"}{r.cp}% +
+ {r.g} + {hasAdvice && {isExpanded ? "▲" : "▼"}} +
+
+
= 3 ? c.ng + "08" : maxLevel >= 2 ? c.wn + "08" : c.pr + "06" }}> +
= 3 ? c.ng : maxLevel >= 2 ? c.wn : c.pr, marginBottom: 10 }}> + {maxLevel >= 3 ? t("🔴 レベル3 — DFA介入を検討") : maxLevel >= 2 ? t("🔶 レベル2 — DFA運用担当がアクション") : t("⚠ レベル1 — 現場確認が必要")} +
+ {advices.map(function (a, j) { + return ( +
+
{a.icon} {t(a.title)}
+
{t(a.text)}
+
+ ); + })} + +
+
+ ); + } + + var menuMon = [ + { id: "detailLog", label: t("📋 清掃詳細ログ") }, + { id: "alert", label: t("🔔 アラート"), badge: unresPre }, + { id: "report", label: t("📈 レポート") }, + { id: "inspection", label: t("🔧 点検記録") }, + ]; + var menuDS = [ + { id: "dsecurity", label: t("🔒 D-Secログ") }, + { id: "dsalert", label: t("⚠ D-Secアラート"), badge: unresDsa }, + ]; + var menuSet = [ + { id: "robot", label: t("🏢 施設ロボットマスタ") }, + { id: "schedule", label: t("📅 スケジュール") }, + { id: "system", label: t("⚙ システム管理") }, + ]; + + function NavItem(props) { + var active = page === props.id; + return ( +
+ {props.label} + {props.badge > 0 && {props.badge}} +
+ ); + } + + function Section(props) { + return
{props.title}
; + } + + var inputSt = { fontSize: 13, padding: "8px 12px", borderRadius: 6, border: "1px solid " + c.bd, background: c.sfH, color: c.tx, width: "100%" }; + var selectSt = { fontSize: 13, padding: "8px 12px", borderRadius: 6, border: "1px solid " + c.bd, background: c.sfH, color: c.tx, width: "100%" }; + var labelSt = { fontSize: 12, color: c.txM, marginBottom: 4, display: "block" }; + var fieldSt = { marginBottom: 14 }; + + function Modal(props) { + return ( +
+
+
+
{props.title}
+ +
+
+ {props.children} +
+
+ + +
+
+
+ ); + } + + return ( +
+ + {/* Sidebar */} +
+
+
+
+ + + + + + + + + +
+
D-Watch
+
{lang === "en" ? "\"DFA Robot Monitoring System\"" : "\"DFAロボット監視システム\""}
+
+
+
+ + +
+
+ +
+ {menuMon.map(function (m) { return ; })} +
+ {menuDS.map(function (m) { return ; })} +
+ {menuSet.map(function (m) { return ; })} +
+
{lang === "en" ? "\"DFA Operator\"" : "\"DFA運用担当\""}
+
+ + {/* Main */} +
+ + {/* DASHBOARD */} + {page === "dashboard" && ( +
+
+
+

{t("ダッシュボード")}

+
{t("昨夜の清掃結果サマリー — 2026-03-27")}
+
+
+ {unresPre > 0 && } + {dashItems.secBanner && unresDsa > 0 && } +
+
+ + {dashItems.secBanner && ( +
+
{t("⚠ セキュリティ無効: MEGAドンキ渋谷本店 1F売場(03:42以降)")}
+
+ )} + +
+ + + + +
+ + {dashItems.detailLog && ( +
+
+ {t("清掃詳細ログ")} +
+ {t("△×のみ")} + {t("全件")} + +
+
+
+ +
+
+ )} + +
+ {dashItems.scheduleBar && ( +
+
{t("スケジュール進捗")}
+ 300 + {t(" / 312 完了")} +
+
+
+
+ )} + {dashItems.failBreakdown && ( +
+
{t("失敗の内訳")}
+
{t("ロボット異常")}7
+
{t("未実施")}5
+
{t("セキュリティ有効")}2
+
{t("制御盤異常")}1
+
+ )} +
+
+ )} + + {/* DETAIL LOG */} + {page === "detailLog" && ( +
+
+
+

{t("清掃詳細ログ")}

+
{t("1行=1ロボット×1スケジュール実行")}
+
+
+ + + + +
+
+
+
+
+ + + {t("△×のみ")} + {t("全件")} + {t("×のみ")} +
+
+ + + + +
+
+
+ +
+
{filt(LOG, lFilt, lSF, lSM, lSS).length}{lang === "en" ? " shown / " : "件 / 全"}{LOG.length}{lang === "en" ? " total" : "件"}
+
+
+ )} + + {/* ALERT - Pre-check */} + {page === "alert" && ( +
+

{t("アラート(事前チェック)")}

+
{t("スケジュール開始前のエラー検知と出発失敗")}
+ + {/* #2 サマリーカウント */} +
+
+
{PRECHK.filter(function (a) { return !a.ok; }).length}
+
{t("未解消")}
{t("要対応")}
+
+
+
{PRECHK.filter(function (a) { return a.ok; }).length}
+
{t("解消済み")}
{t("対応完了")}
+
+
+
{PRECHK.length}
+
{t("合計")}
{t("全アラート")}
+
+
+ + {/* #1 日付フィルタ + #3 グループ/会社フィルタ + ステータスフィルタ */} +
+
+ + + +
+ + +
+ {t("未解消のみ")} + {t("全件")} + {t("解消済み")} +
+
+ + {PRECHK.filter(function (a) { + if (aFilt === "unresolved") return !a.ok; + if (aFilt === "resolved") return a.ok; + return true; + }).map(function (a) { + var sevCol = a.sev === "出発失敗" ? c.ng : c.wn; + return ( +
+
+
+
+ + + {a.serial} +
+
{a.fac}
+
予定: {a.sch} — {t(a.note)}
+
検知: {a.dt}
+
+
+ {a.ok + ? 解消 ({a.at}) + : {t("未解消")} + } +
+
+
+ ); + })} +
+ )} + + {/* REPORT */} + {page === "report" && ( +
+
+

{t("レポート")}

+ +
+
+
+ {t("期間")} + + + +
+ + + + + + +
+
+
+ + +
+
+
+ {[["稼働率", 92, 89, c.ok], ["出発成功率", 88, 90, c.pr], ["清掃完了率", 87, 85, c.ac], ["エラー発生率", 4.2, 4.7, c.ng]].map(function (item, i) { + var diff = Math.round((item[1] - item[2]) * 10) / 10; + var better = i < 3 ? diff > 0 : diff < 0; + var worse = i < 3 ? diff < 0 : diff > 0; + var prevLabel = rptPeriod === "weekly" ? t("前週") : t("前月"); + return ( +
+
{t(item[0])}
+
{item[1]}%
+
+ {diff > 0 ? "▲" : diff < 0 ? "▼" : "→"}{Math.abs(diff)}% + {prevLabel}{t("比")} +
+
{prevLabel}: {item[2]}%
+
+ ); + })} +
+ + {/* 連続問題施設 + エラー種別 */} +
+ {/* #4 連続問題施設 */} +
+
+
{t("連続問題施設")}
+
{t("2日以上連続で△以下または出発失敗の施設")}
+
+ {RANK.filter(function (f) { return f.cont >= 2; }).length === 0 + ?
{t("該当施設なし")}
+ : (function () { + var contFacs = RANK.filter(function (f) { return f.cont >= 2; }).sort(function (a, b) { return b.cont - a.cont; }); + var displayFacs = showAllCont ? contFacs : contFacs.slice(0, 10); + return ( +
+ {displayFacs.map(function (f, i) { + var lv = f.cont >= 3 ? 3 : 2; + var lvCol = lv >= 3 ? c.ng : c.wn; + return ( +
+ Lv{lv} / {f.cont}{lang === "en" ? "d" : "日"} + {f.f} + {t("完了率")} {f.cv}% +
+ ); + })} + {!showAllCont && contFacs.length > 10 && ( +
+ +
+ )} +
+ ); + })() + } +
+ + {/* #5 エラー種別の内訳 */} +
+
+
{t("エラー種別の内訳")}
+
{t("今週のエラー発生件数(種別ごと)")}
+
+ {[ + { name: "ハンドルアップ", count: 8, pct: 32, color: c.wn }, + { name: "LostLocation", count: 5, pct: 20, color: c.ng }, + { name: "オフライン", count: 4, pct: 16, color: c.ng }, + { name: "ブラシエラー", count: 3, pct: 12, color: c.wn }, + { name: "緊急停止", count: 2, pct: 8, color: c.ng }, + { name: "充電不足", count: 2, pct: 8, color: c.txD }, + { name: "その他", count: 1, pct: 4, color: c.txM }, + ].map(function (e, i) { + return ( +
+
{t(e.name)}
+
+
+
+
{e.count}
+
{e.pct}%
+
+ ); + })} +
+
+ + {/* 施設別パフォーマンス */} +
+
+
{t("施設別パフォーマンス(稼働率ワースト順)")}
+ {RANK.length}{lang === "en" ? " facilities" : "施設"} +
+
+ + + + + + + + + + + + + + {(showAllRank ? RANK : RANK.slice(0, 10)).map(function (f, i) { + var sc = f.s === "要改善" ? c.ng : f.s === "注意" ? c.wn : c.ok; + var arDiff = f.ar - f.arP; + var cvDiff = f.cv - f.cvP; + var overallDiff = Math.round((arDiff + cvDiff) / 2); + var diffCol = overallDiff > 0 ? c.ok : overallDiff < 0 ? c.ng : c.txM; + return ( + + + + + + + + + + + + ); + })} + +
{t("施設名")}{t("稼働率")}{rptPeriod === "weekly" ? t("前週") : t("前月")}{t("出発成功率")}{t("完了率")}{rptPeriod === "weekly" ? t("前週") : t("前月")}{t("エラー率")}{t("変化")}{t("ステータス")}
{f.f}{f.ar}%{f.arP}%{f.dp}%{f.cv}%{f.cvP}% 8 ? c.ng : c.txD }}>{f.er}%{overallDiff > 0 ? "▲" + overallDiff : overallDiff < 0 ? "▼" + Math.abs(overallDiff) : "→"}
+
+ {!showAllRank && RANK.length > 10 && ( +
+ +
+ )} + {showAllRank && RANK.length > 10 && ( +
+ +
+ )} +
+
+ )} + + {/* D-SECURITY DEVICE */} + {page === "dsecurity" && ( +
+

{t("D-Sec ログ")}

+
+ + + +
+ {t("全て")} + {t("異常・通信断")} + 正常 +
+
+
+ + + +
+
+ + + + + + + + + + + + {DSDEV.filter(function (d) { + if (dsLogFilt === "abnormal") return d.st !== "正常"; + if (dsLogFilt === "normal") return d.st === "正常"; + return true; + }).sort(function (a, b) { + var order = { "異常": 0, "通信断": 1, "正常": 2 }; + return (order[a.st] || 9) - (order[b.st] || 9); + }).map(function (d, i) { + var days = tokenDays(d.te); + var dayCol = days <= 7 ? c.ng : days <= 30 ? c.wn : c.ok; + return ( + + + + + + + + + + ); + })} + +
ID{t("施設名")}{t("接続状態")}{t("最終通信")}{t("トークン期限")}{t("残日数")}{t("備考")}
{d.id}{d.f}{d.lc}{d.te}{days}{lang === "en" ? "d" : "日"}{t(d.n) || "-"}
+
+
+ )} + + {/* D-SECURITY ALERT */} + {page === "dsalert" && ( +
+

{t("D-Security アラート")}

+
{t("セキュリティ無効・通信断・トークン期限のアラート管理")}
+ + {/* サマリーカウント */} +
+
+
{dsaState.filter(function (a) { return a.st === "未対応"; }).length}
+
{t("未対応")}
{t("即時対応")}
+
+
+
{dsaState.filter(function (a) { return a.st === "対応中"; }).length}
+
{t("対応中")}
{t("進行中")}
+
+
+
{dsaState.filter(function (a) { return a.st === "対応済み"; }).length}
+
{t("対応済み")}
{t("完了")}
+
+
+ + {/* フィルタ */} +
+
+ + + +
+ + + +
+ {t("未対応・対応中")} + {t("全件")} + {t("対応済み")} +
+
+ + {dsaState.filter(function (a) { + if (daFilt === "unresolved") return a.st === "未対応" || a.st === "対応中"; + if (daFilt === "done") return a.st === "対応済み"; + return true; + }).filter(function (a) { + if (dsaTypeFilt === "all") return true; + return a.type === dsaTypeFilt; + }).map(function (a) { + var tpCol = a.type === "トークン期限" ? c.wn : c.ng; + var ss = a.st === "未対応" ? { b: c.ngD, t: c.ng } : a.st === "対応中" ? { b: c.wnD, t: c.wn } : { b: c.okD, t: c.ok }; + return ( +
+
+
+
+ + {a.dev} +
+
{a.fac}
+
{t(a.det)}
+
検知: {a.tm}
+ {a.mm &&
{t(a.mm)}
} +
+ +
+
+ ); + })} +
+ )} + + {/* FACILITY ROBOT MASTER */} + {page === "robot" && ( +
+
+

{t("施設ロボットマスタ")}

+
+ + + +
+
+
+
+ + + + + + + + + + + + + + + {ROBOTS.filter(function (r) { + if (!rSrch) return true; + var q = rSrch.toLowerCase(); + var botMatch = r.bots.some(function (b) { return b.sn.toLowerCase().indexOf(q) >= 0 || b.mk.toLowerCase().indexOf(q) >= 0 || b.md.toLowerCase().indexOf(q) >= 0; }); + return r.f.toLowerCase().indexOf(q) >= 0 || r.pref.toLowerCase().indexOf(q) >= 0 || r.addr.toLowerCase().indexOf(q) >= 0 || botMatch; + }).map(function (r, i) { + return ( + + + + + + + + + + + + + ); + })} + +
{t("施設名")}{t("都道府県")}{t("住所")}{t("電話番号")}{t("責任者名")}{t("担当者名")}{t("メール")}{t("監視")}D-Sec{t("ロボット(メーカー / 機種 / シリアル)")}
{r.f}{r.pref}{r.addr}{r.tel}{r.mgr}{r.staff}{r.email}{r.mon ? : }{r.dsec ? : } + {r.bots.map(function (b, j) { + return ( +
+ {b.mk} + {b.md} + {b.sn} +
+ ); + })} +
+
+
{lang === "en" ? "All " : "全"}{ROBOTS.length}{lang === "en" ? " facilities" : "施設"}
+
+
+ )} + + {/* SCHEDULE */} + {page === "schedule" && ( +
+
+

{t("スケジュール管理")}

+ +
+
+ + + + + + + + + + {SCHED.filter(function (s) { return !sSrch || s.s.toLowerCase().indexOf(sSrch.toLowerCase()) >= 0 || s.f.toLowerCase().indexOf(sSrch.toLowerCase()) >= 0; }).map(function (s, i) { + return ( + + + + + + + + ); + })} + +
{t("シリアル")}{t("施設名")}{t("曜日")}{t("時間")}{t("有効")}
{s.s}{s.f}{s.d}{s.st} - {s.en}{s.on ? "●" : "○"}
+
+
+ )} + + {/* INSPECTION RECORDS */} + {page === "inspection" && ( +
+
+
+

{t("点検記録")}

+
{t("定期点検の実施記録・写真・申し送り事項を管理")}
+
+
+ + +
+
+
+
+ + + +
+ + + +
+
+
+
{INSPECTIONS.length}
+
{t("総点検数")}
{t("期間内")}
+
+
+
{new Set(INSPECTIONS.map(function (x) { return x.facility; })).size}
+
{t("点検施設数")}
{t("ユニーク")}
+
+
+
{INSPECTIONS.filter(function (x) { return x.notes; }).length}
+
{t("申し送りあり")}
{t("要確認")}
+
+
+
+
+ + + + + + + + + + + + + {INSPECTIONS.map(function (ins, i) { + return ( + + + + + + + + + + + ); + })} + +
{t("訪問日")}{t("記入者")}{t("所属会社")}{t("店舗名")}{t("ブランド")}{t("シリアル")}{t("申し送り")}{t("操作")}
{ins.date}{ins.inspector}{ins.company}{ins.facility}{ins.robot}{ins.notes ? {t("あり")} : } + +
+
+
+
+ )} + + {/* SYSTEM */} + {page === "system" && ( +
+

{t("システム管理")}

+ + {/* Sub-menu tabs */} +
+ {[ + { id: "display", label: t("表示設定") }, + { id: "ops", label: t("運用設定") }, + { id: "notify", label: t("通知設定") }, + { id: "users", label: t("ユーザー管理") }, + { id: "roles", label: t("権限管理") }, + { id: "customer", label: t("顧客管理") }, + { id: "group", label: t("グループ管理") }, + { id: "ops", label: t("運用設定") }, + { id: "auditlog", label: t("監査ログ") }, + ].map(function (tab) { + var active = sysTab === tab.id; + return ( + + ); + })} +
+ + {/* 表示設定 */} + {sysTab === "display" && ( +
+
+
+
{t("ダッシュボード表示項目")}
+
{t("各項目のON/OFFでダッシュボードの表示内容をカスタマイズ")}
+
+ {[ + { key: "secBanner", label: "D-Securityアラートバナー", desc: "セキュリティ無効施設の赤色警告バナー" }, + { key: "detailLog", label: "清掃詳細ログ(コンパクト版)", desc: "△×の清掃詳細ログテーブル" }, + { key: "scheduleBar", label: "スケジュール進捗", desc: "完了数/予定数のプログレスバー" }, + { key: "failBreakdown", label: "失敗の内訳", desc: "ロボット異常/未実施/セキュリティ有効/制御盤異常の件数" }, + ].map(function (item, i) { + var isOn = dashItems[item.key]; + return ( +
+
+
{t(item.label)}
+
{t(item.desc)}
+
+
+ + {isOn ? "ON" : "OFF"} +
+
+ ); + })} +
+
+ )} + + {/* 運用設定 */} + {sysTab === "ops" && ( +
+
+
+
{t("事前チェック設定")}
+
{t("清掃開始予定時刻の何分前にロボット状態を自動チェックするかを設定")}
+
+
+
+
+ +
+ + {t("分前")} + {t("デフォルト: 60分")} +
+
+
+ +
+ + {t("分前")} + {t("デフォルト: 30分")} +
+
+
+
+
+ +
+
+
{t("完了率の判定閾値")}
+
{t("清掃詳細ログの◎○△×判定に使用する閾値を設定")}
+
+
+
+ {[["◎ 基準", 90, c.ok, "以上で◎"], ["○ 基準", 70, c.ok, "以上で○(◎未満)"], ["△ 基準", 10, c.wn, "以上で△(○未満)"]].map(function (item, i) { + return ( +
+ +
+ + % +
+
{t(item[3])}
+
+ ); + })} +
+
{t("※ △基準未満は自動的に×判定。現在はシステム全体の設定。P2以降で顧客別設定に拡張予定")}
+
+
+ +
+
+
{t("アドバイス エスカレーション閾値")}
+
{t("対応アドバイスのレベルが上がる連続日数の閾値を設定")}
+
+
+
+
+ +
+ + {t("日以上連続")} +
+
+
+ +
+ + {t("日以上連続")} +
+
+
+
{t("※「連続」はスケジュール存在日の連続。実績データに基づき調整を推奨")}
+
+
+
+ )} + + {/* 通知設定 */} + {sysTab === "notify" && ( +
+
+
+
+
{t("通知設定")}
+
{t("顧客デフォルト+施設上書きの継承モデル")}
+
+ +
+ + + + + + + + + {[ + { ch: "Slack", dest: "#donki-robot-alert", ev: "Critical, Warning", sc: "全施設(デフォルト)" }, + { ch: "メール", dest: "ops@example.com", ev: "Critical", sc: "全施設(デフォルト)" }, + { ch: "Slack", dest: "#shibuya-ops", ev: "全イベント", sc: "渋谷本店(上書き)" }, + ].map(function (n, i) { + return ( + + + + + + + ); + })} + +
{t("チャネル")}{t("送信先")}{t("イベント")}{t("スコープ")}
{n.dest}{t(n.ev)}= 0 ? c.ac : c.txD }}>{t(n.sc)}
+
+
+ )} + + {/* ユーザー管理 */} + {sysTab === "users" && ( +
+
+
+
+
{t("ユーザーマスタ")}
+
{t("顧客管理者は会社/グループに紐づく。施設運用者は施設に紐づく")}
+
+ +
+
+ + + + + + + + + + + + {[ + { nm: "DFA担当者A", em: "dfa-admin1@example.com", rl: "DFAシステム管理者", co: "DFA Robotics", fac: "ー", ll: "2026-03-27 09:02" }, + { nm: "DFA担当者B", em: "dfa-admin2@example.com", rl: "DFA運用担当", co: "DFA Robotics", fac: "ー", ll: "2026-03-27 09:05" }, + { nm: "DFA担当者C", em: "dfa-admin3@example.com", rl: "DFA運用担当", co: "DFA Robotics", fac: "ー", ll: "2026-03-26 09:10" }, + { nm: "顧客担当者A", em: "customer-admin1@example.com", rl: "顧客管理者", co: "ドン・キホーテ", fac: "ー(会社全体)", ll: "2026-03-25 14:00" }, + { nm: "担当者A", em: "store01@example.com", rl: "施設運用者", co: "ドン・キホーテ", fac: "MEGAドンキ渋谷本店", ll: "2026-03-27 06:30" }, + { nm: "担当者B", em: "store02@example.com", rl: "施設運用者", co: "ドン・キホーテ", fac: "ドンキ新宿歌舞伎町店", ll: "2026-03-27 06:45" }, + ].map(function (u, i) { + var rlCol = u.rl.indexOf("システム管理") >= 0 ? c.pr : u.rl.indexOf("運用担当") >= 0 ? c.ac : u.rl.indexOf("顧客") >= 0 ? c.wn : c.txD; + return ( + + + + + + + + + + ); + })} + +
{t("名前")}{t("メール")}{t("ロール")}{t("会社/グループ")}{t("施設")}{t("最終ログイン")}{t("操作")}
{u.nm}{u.em}{u.co}= 0 ? c.txM : c.tx }}>{t(u.fac)}{u.ll} +
+ + +
+
+
+
+
+ )} + + {/* 権限管理 */} + {sysTab === "roles" && ( +
+
+
+
{t("ロール定義(4層)")}
+
{t("各ロールのスコープと紐づき")}
+
+ + + + + + + + + + {[ + { rl: "DFAシステム管理者", ph: "P1", bind: "ー", sc: "全顧客・全施設", desc: "全権限。権限管理の編集はこのロールのみ" }, + { rl: "DFA運用担当", ph: "P1", bind: "ー", sc: "全顧客・全施設", desc: "全メニュー閲覧+施設ロボットマスタ編集。権限管理は閲覧のみ" }, + { rl: "顧客管理者", ph: "P2", bind: "会社", sc: "自社施設のみ", desc: "自社の閲覧全般。施設ロボットマスタの編集不可" }, + { rl: "施設運用者・清掃業者", ph: "P2", bind: "施設", sc: "自施設のみ", desc: "設定メニュー以外の自施設データを閲覧" }, + ].map(function (r, i) { + var bindCol = r.bind === "会社" ? c.wn : r.bind === "施設" ? c.ac : c.txM; + return ( + + + + + + + + ); + })} + +
{t("ロール")}{t("実装")}{t("紐づき")}{t("データスコープ")}{t("説明")}
{t(r.rl)}{r.bind !== "ー" ? : }{t(r.sc)}{t(r.desc)}
+
+
+
+
{t("メニュー×ロール アクセスマトリクス")}
+
{t("施設運用者のスコープは自施設のみに限定")}
+
+
+ + + + + + + + + + {[ + { menu: "ダッシュボード", v: ["閲覧", "閲覧", "閲覧", "閲覧"] }, + { menu: "清掃詳細ログ", v: ["閲覧", "閲覧", "閲覧", "閲覧"] }, + { menu: "アラート", v: ["閲覧", "閲覧", "閲覧", "閲覧"] }, + { menu: "レポート", v: ["閲覧", "閲覧", "閲覧", "閲覧"] }, + { menu: "D-Secログ", v: ["閲覧", "閲覧", "閲覧", "閲覧"] }, + { menu: "D-Secアラート", v: ["閲覧", "閲覧", "閲覧", "閲覧"] }, + { menu: "施設ロボットマスタ", v: ["閲覧/編集/登録", "閲覧/編集/登録", "閲覧", "×"] }, + { menu: "スケジュール", v: ["閲覧", "閲覧", "閲覧", "閲覧"] }, + { menu: "システム設定", v: ["", "", "", ""], sec: true }, + { menu: "表示設定", v: ["閲覧/編集", "閲覧/編集", "閲覧/編集", "閲覧/編集"], sub: true }, + { menu: "運用設定", v: ["閲覧/編集", "閲覧", "×", "×"], sub: true }, + { menu: "通知設定", v: ["閲覧/編集/登録", "閲覧", "閲覧", "×"], sub: true }, + { menu: "ユーザー管理", v: ["閲覧/編集/登録", "閲覧", "×", "×"], sub: true }, + { menu: "権限管理", v: ["閲覧/編集/登録", "閲覧", "×", "×"], sub: true }, + { menu: "顧客管理", v: ["閲覧/編集/登録", "閲覧", "×", "×"], sub: true }, + { menu: "グループ管理", v: ["閲覧/編集/登録", "閲覧", "×", "×"], sub: true }, + { menu: "運用設定", v: ["閲覧/編集", "閲覧", "×", "×"], sub: true }, + { menu: "監査ログ", v: ["閲覧", "閲覧", "×", "×"], sub: true }, + ].map(function (r, i) { + function acStyle(v) { + if (!v) return { padding: "8px 12px", textAlign: "center" }; + var col = c.txM; + if (v.indexOf("登録") >= 0) col = c.pr; + else if (v.indexOf("編集") >= 0) col = c.ok; + else if (v === "閲覧") col = c.txD; + else if (v === "×") col = c.ng; + return { padding: "8px 12px", textAlign: "center", fontSize: 11, color: col, fontWeight: v.indexOf("登録") >= 0 ? 600 : 400 }; + } + if (r.sec) { + return ( + + + + ); + } + return ( + + + + + + + + ); + })} + +
{t("メニュー")}{t("DFA管理者")}{t("DFA運用")}{t("顧客管理者")}{t("施設運用者")}
{t(r.menu)}
{r.sub ? "└ " : ""}{t(r.menu)}{t(r.v[0])}{t(r.v[1])}{t(r.v[2])}{t(r.v[3])}
+
+
+ {t("閲覧/編集/登録")} = {t("フル権限")} / {t("閲覧/編集")} = {t("新規登録不可")} / {t("閲覧")} = {t("表示のみ")} / × = アクセス不可 +
+
+
+ )} + + {/* 顧客管理 */} + {sysTab === "customer" && ( +
+
+
+
+
{t("顧客管理")}
+
{t("会社単位の契約・モジュール管理")}
+
+ +
+
+ + + + + + + + + + + + + + + + {[ + { grp: "PPIH", nm: "株式会社ドン・キホーテ", ct: "customer-admin1@example.com", plan: "Insight Assist", mon: true, dsec: true, monF: 9, dsecF: 5, fac: 9, rob: 11, st: "有効" }, + { grp: "PPIH", nm: "株式会社ユニー", ct: "uny-admin@example.com", plan: "Monitor Assist", mon: true, dsec: false, monF: 2, dsecF: 0, fac: 2, rob: 2, st: "有効" }, + { grp: "ー", nm: "株式会社サンプル商事C", ct: "sample-c@example.com", plan: "Monitor Assist", mon: true, dsec: false, monF: 3, dsecF: 0, fac: 3, rob: 3, st: "有効" }, + { grp: "ー", nm: "株式会社サンプル商事D", ct: "sample-d@example.com", plan: "D-Security", mon: false, dsec: true, monF: 0, dsecF: 2, fac: 2, rob: 0, st: "有効" }, + ].map(function (cu, i) { + return ( + + + + + + + + + + + + + + ); + })} + +
{t("グループ")}{t("会社名")}{t("連絡先")}{t("契約プラン")}{t("契約モジュール")}{t("監視施設")}{t("D-Sec施設")}{t("全施設")}{t("ロボット")}{t("状態")}{t("操作")}
{cu.grp}{cu.nm}{cu.ct} +
+ {cu.mon ? t("○ 監視") : t("ー 監視")} + {cu.dsec ? t("○ D-Sec") : t("ー D-Sec")} +
+
0 ? c.ok : c.txM }}>{cu.monF > 0 ? cu.monF + " / " + cu.fac : "ー"} 0 ? c.ac : c.txM }}>{cu.dsecF > 0 ? cu.dsecF + " / " + cu.fac : "ー"}{cu.fac}{cu.rob} +
+ + +
+
+
+
+
+ )} + + {/* グループ管理 */} + {sysTab === "group" && ( +
+
+
+
+
{t("グループ管理")}
+
{t("グループ会社の親子関係を管理。顧客管理者をグループに紐づけると傘下の全会社を横断閲覧可能")}
+
+ +
+
+ + + + + + + + + + + + + {[ + { nm: "PPIH", ct: "ppih-admin@example.com", tel: "03-0000-1111", companies: ["株式会社ドン・キホーテ", "株式会社ユニー"], cnt: 2, fac: 11, rob: 13, st: "有効" }, + ].map(function (g, i) { + return ( + + + + + + + + + + + ); + })} + +
{t("グループ名")}{t("連絡先")}{t("傘下会社")}{t("会社数")}{t("総施設数")}{t("総ロボット数")}{t("状態")}{t("操作")}
{g.nm}{g.ct} + {g.companies.map(function (co, j) { + return {co}; + })} + {g.cnt}{g.fac}{g.rob} +
+ + +
+
+
+
+
+ )} + + {/* 運用設定 */} + {sysTab === "ops" && ( +
+
+
+
{t("運用設定")}
+
{t("清掃監視の運用パラメータ。変更はDFAシステム管理者のみ")}
+
+ + {/* 事前チェック設定 */} +
+
{t("事前チェック設定")}
+
{t("清掃開始予定時刻の何分前にロボット状態をチェックするか")}
+
+
+ {t("第1回チェック")} + + {t("分前")} +
+
+ {t("第2回チェック")} + + {t("分前")} +
+
+
+ + {/* 判定閾値設定 */} +
+
{t("完了率の判定閾値")}
+
{t("清掃詳細ログの◎○△×判定に使用する閾値")}
+
+ {[ + { label: "◎", desc: "以上", def: 90, color: c.ok }, + { label: "○", desc: "以上", def: 70, color: c.ok }, + { label: "△", desc: "以上", def: 10, color: c.wn }, + { label: "×", desc: "未満", def: 10, color: c.ng }, + ].map(function (g, i) { + return ( +
+ {g.label} + {i < 3 && } + {i < 3 && %{t(g.desc)}} + {i === 3 && {t("上記未満")}} +
+ ); + })} +
+
+ + {/* エスカレーション閾値 */} +
+
{t("アドバイス エスカレーション閾値")}
+
{t("同一施設で問題が連続した場合のレベル引き上げ基準(スケジュール存在日の連続日数)")}
+
+
+ Lv2 + + {t("日以上連続")} +
+
+ Lv3 + + {t("日以上連続")} +
+
+
+
+
+ )} + + {/* 監査ログ */} + {sysTab === "auditlog" && ( +
+
+
+
{t("操作ログ(監査ログ)")}
+
{t("全ての手動操作を記録。誰が・いつ・何をしたかを追跡")}
+
+ + + + + + + + + {[ + { t: "2026-03-27 09:15", u: "田中 太郎", a: "清掃開始(手動)", tg: "MEGAドンキ渋谷本店 / 1F売場" }, + { t: "2026-03-26 22:30", u: "田中 太郎", a: "スケジュール変更", tg: "ドンキ八王子駅前店 — 無効化" }, + { t: "2026-03-26 15:00", u: "佐藤 花子", a: "ロボット同期", tg: "8111R5628060011 v3.2に更新" }, + { t: "2026-03-25 11:00", u: "田中 太郎", a: "顧客モジュール変更", tg: "ドン・キホーテ — D-Security有効化" }, + { t: "2026-03-25 10:30", u: "田中 太郎", a: "施設追加", tg: "MEGAドンキ大森山王店" }, + ].map(function (l, i) { + return ( + + + + + + + ); + })} + +
{t("日時")}{t("操作者")}{t("アクション")}{t("対象")}
{l.t}{l.u}= 0 ? c.wn : c.pr} />{t(l.tg)}
+
+
+ )} + +
+ )} + +
+ + {/* Customer Edit Modal */} + {custEdit && ( + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+ + +
+
+
+
+ + +
+
+ + +
+
+
+ )} + + {/* User Edit Modal */} + {userEdit && ( + +
+
+ + +
+
+ + +
+
+
+ +
+ {["DFAシステム管理者", "DFA運用担当", "顧客管理者", "施設運用者"].map(function (rl) { + var active = (userEdit.rl || "") === rl; + var col = rl.indexOf("システム管理") >= 0 ? c.pr : rl.indexOf("運用担当") >= 0 ? c.ac : rl.indexOf("顧客") >= 0 ? c.wn : c.txD; + return ( +
+ {active ? "● " : "○ "}{rl} +
+ ); + })} +
+
+
+
+ + +
+
+ + +
+
+ {userEdit.ll && ( +
+
{t("最終ログイン: ")}{userEdit.ll}
+
+ )} +
+ )} + + {/* Group Edit Modal */} + {grpEdit && ( + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+ {["株式会社ドン・キホーテ", "株式会社ユニー", "株式会社セブン-イレブン", "株式会社ローソン"].map(function (co, j) { + var checked = grpEdit.companies && grpEdit.companies.indexOf(co) >= 0; + return ( + + ); + })} +
+
{t("チェックした会社がこのグループの傘下として紐づきます。グループ管理者は傘下全社のデータを横断閲覧可能になります。")}
+
+ {grpEdit.cnt && ( +
+
{t("グループサマリー")}
+
+
+
{grpEdit.cnt}
+
{t("傘下会社")}
+
+
+
{grpEdit.fac}
+
{t("総施設数")}
+
+
+
{grpEdit.rob}
+
{t("総ロボット数")}
+
+
+
+ )} +
+ )} + + {/* Area Detail Modal */} + {areaModal && ( +
+
+
+
+
+
{t("エリア別清掃完了率")}
+
{areaModal.facility} | {areaModal.date} | {lang === "en" ? "Overall" : "全体"} {areaModal.cp}% {areaModal.g}
+
+ +
+
+ {["problem", "all"].map(function (f) { + var active = areaFilt === f; + var label = f === "problem" ? t("問題エリアのみ") : t("全件"); + return ; + })} +
+
+
+ {(function () { + var areas = getAreaData(areaModal.facility).map(function (a) { return Object.assign({}, a, { g: areaGrade(a.cp) }); }); + areas.sort(function (a, b) { return a.cp - b.cp; }); + var filtered = areaFilt === "problem" ? areas.filter(function (a) { return a.g === "△" || a.g === "×"; }) : areas; + var problemCount = areas.filter(function (a) { return a.g === "△" || a.g === "×"; }).length; + return ( +
+
+ {areaFilt === "problem" ? (lang === "en" ? ("Problem areas (" + problemCount + " / " + areas.length + ")") : ("問題エリア(" + problemCount + " / " + areas.length + "エリア)")) : (lang === "en" ? ("All " + areas.length + " areas (by completion)") : ("全" + areas.length + "エリア(完了率昇順)"))} +
+ {filtered.length === 0 && ( +
{t("問題エリアはありません(全エリア◎○)")}
+ )} + {filtered.map(function (a, i) { + var gs = gradeStyle(c, a.g); + var barColor = a.g === "◎" || a.g === "○" ? c.ok : a.g === "△" ? c.wn : c.ng; + return ( +
+
{a.name}
+
+
+
+
{a.cp}%
+ {a.g} +
+ ); + })} +
+ ); + })()} +
+
+ +
+
+
+ )} + + {/* Inspection Detail Modal */} + {inspDetail && ( +
+
+
+
+
{t("点検記録 詳細")}
+
{inspDetail.facility} | {inspDetail.date}
+
+ +
+
+
+
{t("記入者:")} {inspDetail.inspector}
+
{t("所属会社:")} {inspDetail.company}
+
{t("ブランド:")} {inspDetail.brand}
+
{t("シリアル:")} {inspDetail.robot}
+
+ +
+ {["Before(点検前)", "After(点検後)"].map(function (phase, pi) { + return ( +
+
{phase}
+ {INSP_ITEMS.map(function (item, j) { + return ( +
+ {item} + {t("📷 写真")} +
+ ); + })} +
+ ); + })} +
+ + {inspDetail.notes && ( +
+
{t("申し送り事項")}
+
{t(inspDetail.notes)}
+
+ )} +
+
+ +
+
+
+ )} + + {/* Inspection New Modal */} + {inspNew && ( + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ {["Before(点検前)", "After(点検後)"].map(function (phase, pi) { + return ( +
+
{phase}
+ {INSP_ITEMS.map(function (item, j) { + return ( +
+ {item} + +
+ ); + })} +
+ ); + })} +
+
+ +
+ +