Files
App/www/index.html
2026-06-13 13:35:00 +07:00

928 lines
44 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="vi">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>LiDAR Manager</title>
<link rel="stylesheet" href="/style.css" />
</head>
<body>
<div class="shell">
<aside class="sidebar">
<div class="brand">
<div class="brandIcon">R</div>
<div class="brandText">
<div class="brandTitle">PhenikaaX</div>
<div class="brandSub">RobotApp</div>
</div>
</div>
<div class="navTitle">WORKSPACE</div>
<nav class="nav">
<a class="navItem active" href="#" data-page="dashboard" aria-current="page">
<span class="navDot"></span>
Dashboard
</a>
<a class="navItem" href="#" data-page="config">
<span class="navDot"></span>
Cấu hình
</a>
</nav>
<div class="navTitle">CÀI ĐẶT</div>
<nav class="nav">
<a class="navItem" href="#" data-page="missions">
<span class="navDot"></span>
Missions
</a>
<a class="navItem" href="#" data-page="integrations">
<span class="navDot"></span>
Tích hợp
</a>
</nav>
<div class="sidebarFooter">
<div class="statusBadge">
<span class="statusLed"></span>
<span id="status" class="statusText"></span>
</div>
</div>
</aside>
<div class="body">
<header class="topbar">
<div class="topbarTitle">
<div class="kicker">PhenikaaX Robotics</div>
<div class="pageTitle">Cấu Hình</div>
</div>
<div class="topbarActions">
<button id="refreshBtn" type="button" class="btn subtle">Tải lại</button>
<button id="saveLayoutBtn" class="btn primary" type="button">Lưu layout</button>
</div>
</header>
<main class="content">
<div class="page" id="pageOverview" data-page-content="dashboard" hidden>
<div class="dashboardPage">
<section class="card">
<div class="cardHeader">
<div>
<div class="cardTitle">Dashboard</div>
<div class="cardSub">Widget mission — chạy, xếp hàng và tạm dừng giống MiR Fleet.</div>
</div>
<div class="dashboardToolbar">
<button id="dashboardAddWidgetBtn" type="button" class="btn subtle">Thêm widget</button>
<button id="dashboardEditBtn" type="button" class="btn subtle">Sửa layout</button>
</div>
</div>
<div class="cardBody">
<div id="dashboardGrid" class="dashboardGrid"></div>
<p id="dashboardEmpty" class="mutedNote dashboardEmpty" hidden>Chưa có widget. Bấm «Thêm widget» để bắt đầu.</p>
</div>
</section>
<section class="card dashboardInfoCard">
<div class="cardHeader">
<div>
<div class="cardTitle">Hệ thống</div>
<div class="cardSub">Trạng thái backend và layout đang active.</div>
</div>
</div>
<div class="cardBody dashboardInfoGrid">
<div class="row rowWide">
<label>Backend</label>
<div id="overviewBackend" class="mutedNote"></div>
</div>
<div class="row rowWide">
<label>Layout</label>
<div id="overviewActiveLayout" class="mutedNote"></div>
</div>
<div class="row rowWide">
<label>Model robot</label>
<div id="overviewActiveModel" class="mutedNote"></div>
</div>
<div class="row rowWide">
<label>LiDAR / IMU</label>
<div id="overviewActiveSensors" class="mutedNote"></div>
</div>
</div>
</section>
</div>
</div>
<div class="page" id="pageConfig" data-page-content="config">
<div class="contentLeft">
<section class="card" id="layoutManagerCard">
<div class="cardHeader">
<div>
<div class="cardTitle">Quản lý layout</div>
<div class="cardSub">Nhiều cấu hình robot — mỗi layout có LiDAR và model riêng.</div>
</div>
</div>
<div class="cardBody">
<div class="row rowWide">
<label>Layout hiện tại</label>
<select id="layoutSelect"></select>
</div>
<div class="row rowWide">
<label>Tên layout mới</label>
<input id="layoutNewName" type="text" placeholder="VD: AGV kho A" />
</div>
<div class="checkRow">
<label>
<input id="layoutCloneCurrent" type="checkbox" />
Sao chép từ layout đang mở
</label>
</div>
<div class="layoutManagerActions">
<button id="layoutCreateBtn" type="button" class="btn subtle">Tạo layout</button>
<button id="layoutDeleteBtn" type="button" class="btn subtle danger">Xóa</button>
</div>
<p id="layoutActiveHint" class="mutedNote"></p>
</div>
</section>
<section class="card collapsible" id="lidarListCard">
<div
class="cardHeader cardHeaderToggle"
id="lidarListCardToggle"
role="button"
tabindex="0"
aria-expanded="true"
aria-controls="lidarListCardBody"
>
<div>
<div class="cardTitle">LiDARs</div>
<div class="cardSub">Đăng ký tên, IP, port và chỉnh pose theo robot frame.</div>
</div>
<span class="cardChevron" aria-hidden="true"></span>
</div>
<div class="cardBody" id="lidarListCardBody">
<form id="lidarForm" class="form">
<div class="row">
<label>Tên</label>
<input id="name" placeholder="Lidar trước" required />
</div>
<div class="row">
<label>IP</label>
<input id="ip" placeholder="192.168.0.10" required />
</div>
<div class="row">
<label>Port</label>
<input id="port" type="number" min="1" max="65535" value="2112" required />
</div>
<div class="actions">
<button id="addLidarBtn" class="btn primary" type="button">Thêm</button>
</div>
<p id="lidarFormHint" class="formHint" hidden></p>
</form>
<div id="lidarList" class="list"></div>
</div>
</section>
<section class="card collapsible" id="imuListCard">
<div
class="cardHeader cardHeaderToggle"
id="imuListCardToggle"
role="button"
tabindex="0"
aria-expanded="true"
aria-controls="imuListCardBody"
>
<div>
<div class="cardTitle">IMU</div>
<div class="cardSub">Cảm biến quán tính — frame, topic và pose trên robot.</div>
</div>
<span class="cardChevron" aria-hidden="true"></span>
</div>
<div class="cardBody" id="imuListCardBody">
<form id="imuForm" class="form">
<div class="row">
<label>Tên</label>
<input id="imuName" placeholder="IMU chính" required />
</div>
<div class="row">
<label>Frame ID</label>
<input id="imuFrameId" placeholder="imu_link" required />
</div>
<div class="row">
<label>Topic</label>
<input id="imuTopic" placeholder="imu/data" value="imu/data" required />
</div>
<div class="row rowWide">
<label>Nguồn</label>
<select id="imuSource">
<option value="external">Ngoài (ROS topic)</option>
<option value="lidar_builtin">Tích hợp LiDAR</option>
<option value="onboard">Onboard robot</option>
</select>
</div>
<div class="row">
<label>Tần số (Hz)</label>
<input id="imuRateHz" type="number" min="1" max="1000" step="1" value="100" />
</div>
<div class="checkRow">
<label>
<input id="imuEnabled" type="checkbox" checked />
Bật IMU
</label>
</div>
<div class="actions">
<button id="addImuBtn" class="btn primary" type="button">Thêm IMU</button>
</div>
<p id="imuFormHint" class="formHint" hidden></p>
</form>
<div id="imuList" class="list"></div>
</div>
</section>
<section class="card collapsible" id="robotModelCard">
<div
class="cardHeader cardHeaderToggle"
id="robotModelCardToggle"
role="button"
tabindex="0"
aria-expanded="true"
aria-controls="robotModelCardBody"
>
<div>
<div class="cardTitle">Model robot</div>
<div class="cardSub">Kinematic differential — bánh, động cơ và giới hạn vận tốc.</div>
</div>
<span class="cardChevron" aria-hidden="true"></span>
</div>
<div class="cardBody" id="robotModelCardBody">
<div class="modelForm">
<div class="row rowWide">
<label>Model</label>
<select id="robotModel">
<option value="diff">Differential (2 bánh)</option>
<option value="bicycle">Bicycle</option>
</select>
</div>
<div id="diffParams" class="modelParams">
<details class="acc" open>
<summary>Hình học bánh</summary>
<div class="accBody">
<div class="row rowWide">
<label>Khoảng cách 2 bánh</label>
<div class="inputUnit">
<input id="wheelSeparationM" type="number" min="0.05" max="5" step="0.01" value="1.0" />
<span class="unit">m</span>
</div>
</div>
<div class="row rowWide">
<label>Bán kính bánh</label>
<div class="inputUnit">
<input id="wheelRadiusM" type="number" min="0.02" max="1" step="0.01" value="0.3" />
<span class="unit">m</span>
</div>
</div>
<div class="row rowWide">
<label>Tỷ lệ hiển thị</label>
<div class="inputUnit">
<input id="scaleMPerPx" type="number" min="0.001" max="0.1" step="0.001" value="0.005" />
<span class="unit">m/px</span>
</div>
</div>
<details class="acc accNested">
<summary>Hiệu chỉnh (nâng cao)</summary>
<div class="accBody">
<div class="row rowWide">
<label>b multiplier</label>
<input id="wheelSeparationMult" type="number" min="0.5" max="2" step="0.01" value="1.0" />
</div>
<div class="row rowWide">
<label>r multiplier</label>
<input id="wheelRadiusMult" type="number" min="0.5" max="2" step="0.01" value="1.0" />
</div>
</div>
</details>
</div>
</details>
<details class="acc">
<summary>Động cơ</summary>
<div class="accBody">
<p class="mutedNote">Mỗi bánh gán một động cơ — chọn hãng và model.</p>
<div id="motorWheels" class="motorWheels"></div>
</div>
</details>
<details class="acc">
<summary>Giới hạn vận tốc</summary>
<div class="accBody">
<div class="row rowWide">
<label>cmd_vel timeout</label>
<div class="inputUnit">
<input id="cmdVelTimeout" type="number" min="0.05" max="5" step="0.05" value="0.25" />
<span class="unit">s</span>
</div>
</div>
<div class="row rowWide">
<label>Linear max</label>
<div class="inputUnit">
<input id="linearMaxVel" type="number" min="0.01" max="5" step="0.05" value="1.0" />
<span class="unit">m/s</span>
</div>
</div>
<div class="row rowWide">
<label>Linear min</label>
<div class="inputUnit">
<input id="linearMinVel" type="number" min="-5" max="0" step="0.05" value="-0.5" />
<span class="unit">m/s</span>
</div>
</div>
<div class="row rowWide">
<label>Linear accel max</label>
<div class="inputUnit">
<input id="linearMaxAccel" type="number" min="0.01" max="10" step="0.05" value="0.8" />
<span class="unit">m/s²</span>
</div>
</div>
<div class="row rowWide">
<label>Angular max</label>
<div class="inputUnit">
<input id="angularMaxVel" type="number" min="0.01" max="10" step="0.05" value="1.7" />
<span class="unit">rad/s</span>
</div>
</div>
<div class="row rowWide">
<label>Angular accel max</label>
<div class="inputUnit">
<input id="angularMaxAccel" type="number" min="0.01" max="10" step="0.05" value="1.5" />
<span class="unit">rad/s²</span>
</div>
</div>
</div>
</details>
<p id="diffValidation" class="validation" hidden></p>
</div>
<div id="bicycleParams" class="modelParams" hidden>
<p class="mutedNote">
Kinematic bicycle — tham chiếu trục sau, quan hệ
<span class="mono">ω = v·tan(δ)/L</span>
(<a href="https://thomasfermi.github.io/Algorithms-for-Automated-Driving/Control/BicycleModel.html" target="_blank" rel="noopener">mô hình</a>).
</p>
<details class="acc" open>
<summary>Hình học (wheelbase)</summary>
<div class="accBody">
<div class="row rowWide">
<label>Wheelbase L</label>
<div class="inputUnit">
<input id="bicycleWheelbaseM" type="number" min="0.2" max="5" step="0.05" value="1.2" />
<span class="unit">m</span>
</div>
</div>
<div class="row rowWide">
<label>Bán kính bánh</label>
<div class="inputUnit">
<input id="bicycleWheelRadiusM" type="number" min="0.02" max="1" step="0.01" value="0.15" />
<span class="unit">m</span>
</div>
</div>
<div class="row rowWide">
<label>Tỷ lệ hiển thị</label>
<div class="inputUnit">
<input id="bicycleScaleMPerPx" type="number" min="0.001" max="0.1" step="0.001" value="0.005" />
<span class="unit">m/px</span>
</div>
</div>
<div class="row rowWide">
<label>Góc lái xem trước</label>
<div class="inputUnit">
<input id="bicycleSteerPreviewDeg" type="number" min="-45" max="45" step="1" value="15" />
<span class="unit">°</span>
</div>
</div>
</div>
</details>
<details class="acc">
<summary>Động cơ</summary>
<div class="accBody">
<p class="mutedNote">Bánh sau (drive) và bánh trước (steer).</p>
<div id="bicycleMotorWheels" class="motorWheels"></div>
</div>
</details>
<details class="acc">
<summary>Giới hạn</summary>
<div class="accBody">
<div class="row rowWide">
<label>δ max (lái)</label>
<div class="inputUnit">
<input id="bicycleSteerMaxDeg" type="number" min="5" max="60" step="1" value="35" />
<span class="unit">°</span>
</div>
</div>
<div class="row rowWide">
<label>cmd_vel timeout</label>
<div class="inputUnit">
<input id="bicycleCmdVelTimeout" type="number" min="0.05" max="5" step="0.05" value="0.25" />
<span class="unit">s</span>
</div>
</div>
<div class="row rowWide">
<label>Linear max</label>
<div class="inputUnit">
<input id="bicycleLinearMaxVel" type="number" min="0.01" max="5" step="0.05" value="1.0" />
<span class="unit">m/s</span>
</div>
</div>
<div class="row rowWide">
<label>Linear accel max</label>
<div class="inputUnit">
<input id="bicycleLinearMaxAccel" type="number" min="0.01" max="10" step="0.05" value="0.8" />
<span class="unit">m/s²</span>
</div>
</div>
</div>
</details>
<p id="bicycleValidation" class="validation" hidden></p>
</div>
<details class="acc">
<summary>Footprint</summary>
<div class="accBody">
<p class="mutedNote">Hình dạng robot (ROS polygon) — tọa độ theo robot frame.</p>
<div class="row rowWide">
<label>Hình dạng</label>
<select id="footprintShape">
<option value="rectangle">Hình chữ nhật</option>
<option value="circle">Hình tròn</option>
<option value="regular_polygon">Đa giác đều</option>
<option value="custom">Tùy chỉnh (đa giác)</option>
</select>
</div>
<div id="footprintPresetPanel" class="footprintPresetPanel">
<div id="fpRectParams" class="fpShapeParams">
<div class="row rowWide">
<label>Chiều dài</label>
<div class="inputUnit">
<input id="fpLengthM" type="number" min="0.1" max="10" step="0.05" value="1.4" />
<span class="unit">m</span>
</div>
</div>
<div class="row rowWide">
<label>Chiều rộng</label>
<div class="inputUnit">
<input id="fpWidthM" type="number" min="0.1" max="10" step="0.05" value="1.1" />
<span class="unit">m</span>
</div>
</div>
</div>
<div id="fpCircleParams" class="fpShapeParams" hidden>
<div class="row rowWide">
<label>Bán kính</label>
<div class="inputUnit">
<input id="fpRadiusM" type="number" min="0.1" max="5" step="0.05" value="0.55" />
<span class="unit">m</span>
</div>
</div>
<div class="row rowWide">
<label>Độ mịn (cạnh)</label>
<input id="fpCircleSegments" type="number" min="8" max="64" step="1" value="32" />
</div>
</div>
<div id="fpPolyParams" class="fpShapeParams" hidden>
<div class="row rowWide">
<label>Bán kính</label>
<div class="inputUnit">
<input id="fpPolyRadiusM" type="number" min="0.1" max="5" step="0.05" value="0.6" />
<span class="unit">m</span>
</div>
</div>
<div class="row rowWide">
<label>Số cạnh</label>
<input id="fpPolySides" type="number" min="3" max="32" step="1" value="6" />
</div>
</div>
<button id="applyFootprintPresetBtn" type="button" class="btn subtle btnBlock">Áp dụng hình dạng</button>
</div>
<div id="footprintCustomPanel" class="footprintCustomPanel hidden">
<div class="fpVertexRow">
<span class="fpVertexLabel">Số đỉnh: <strong id="fpVertexCount">0</strong></span>
<span id="fpSelectedVertexText" class="fpSelectedVertex mutedNote"></span>
</div>
<div class="fpVertexActions">
<button id="fpAddVertexBtn" type="button" class="btn subtle">Thêm đỉnh</button>
<button id="fpRemoveVertexBtn" type="button" class="btn subtle" disabled>Xóa đỉnh</button>
</div>
</div>
<button id="editFootprintBtn" type="button" class="btn subtle btnBlock">Sửa footprint</button>
<div id="footprintEditHint" class="fpHint" hidden></div>
</div>
</details>
</div>
</div>
</section>
</div>
</div>
<div class="page" id="pageMissions" data-page-content="missions" hidden>
<div id="missionsListView" class="missionsPage">
<section class="card">
<div class="cardHeader">
<div>
<div class="cardTitle">Missions</div>
<div class="cardSub">Setup → Missions — danh sách nhiệm vụ robot.</div>
</div>
<button id="missionCreateOpenBtn" type="button" class="btn primary">Create mission</button>
</div>
<div class="cardBody">
<div id="missionListEmpty" class="mutedNote" hidden>Chưa có mission. Bấm Create mission để bắt đầu.</div>
<div id="missionList" class="missionList"></div>
</div>
</section>
<section class="card" id="missionQueueCard">
<div class="cardHeader">
<div>
<div class="cardTitle">Mission queue</div>
<div class="cardSub">Thêm mission bằng biểu tượng queue — robot chạy theo thứ tự từ trên xuống.</div>
</div>
<button id="missionQueueClearBtn" type="button" class="btn subtle danger">Xóa queue</button>
</div>
<div class="cardBody">
<div id="missionQueueRunner" class="missionQueueRunner mutedNote"></div>
<div id="missionQueueEmpty" class="mutedNote">Queue trống. Bấm <span class="mono"></span> trên mission để thêm.</div>
<div id="missionQueueList" class="missionQueueList"></div>
</div>
</section>
</div>
<div id="missionEditorView" class="missionsPage" hidden>
<section class="card missionEditorCard">
<div class="missionEditorTop">
<div class="missionEditorTitleWrap">
<button id="missionEditorBackBtn" type="button" class="btn subtle missionBackBtn" aria-label="Quay lại danh sách"></button>
<div>
<div class="missionEditorKicker">Mission editor</div>
<div class="missionEditorTitleRow">
<h2 id="missionEditorTitle" class="missionEditorTitle"></h2>
<button id="missionSettingsBtn" type="button" class="iconBtn" title="Cài đặt mission" aria-label="Cài đặt mission"></button>
</div>
<div id="missionEditorMeta" class="missionEditorMeta"></div>
</div>
</div>
<div class="missionEditorTopActions">
<span id="missionEditorDirty" class="missionDirtyBadge" hidden>Chưa lưu</span>
<button id="missionSaveAsBtn" type="button" class="btn subtle">Save as</button>
<button id="missionSaveBtn" type="button" class="btn primary">Save</button>
</div>
</div>
<div class="missionActionBar" id="missionActionBar" role="toolbar" aria-label="Thêm action">
<div class="missionGroupTabs" id="missionGroupTabs"></div>
</div>
<div class="missionEditorBody">
<p class="missionFlowHint">Thực thi từ trên xuống dưới. Kéo biểu tượng ↔ để đổi thứ tự. Với Loop: kéo action vào vùng bên trong.</p>
<div id="missionActionList" class="missionActionList"></div>
<div id="missionActionListEmpty" class="missionActionListEmpty mutedNote">Chọn action từ menu phía trên để bắt đầu.</div>
</div>
</section>
</div>
</div>
<div class="page" id="pageIntegrations" data-page-content="integrations" hidden>
<div class="integrationsPage">
<section class="card">
<div class="cardHeader">
<div>
<div class="cardTitle">Modbus trigger</div>
<div class="cardSub">System → Triggers — coil 10012000 gắn mission_id. Thiết bị remote bật coil (Modbus TCP :5502) → mission vào queue.</div>
</div>
<button id="integrationAddTriggerBtn" type="button" class="btn primary">Thêm trigger</button>
</div>
<div class="cardBody">
<div id="integrationTriggerEmpty" class="mutedNote">Chưa có trigger Modbus.</div>
<div id="integrationTriggerList" class="missionList"></div>
<div class="integrationCoilSection">
<div class="integrationSectionLabel">Coil đã gán (bấm để mô phỏng rising edge)</div>
<div id="integrationCoilGrid" class="integrationCoilGrid"></div>
</div>
</div>
</section>
<section class="card">
<div class="cardHeader">
<div>
<div class="cardTitle">REST API — MiR v2.0.0</div>
<div class="cardSub">Hệ thống bên ngoài POST mission vào queue qua REST.</div>
</div>
</div>
<div class="cardBody integrationApiBody">
<div class="row rowWide">
<label>Base URL</label>
<div id="integrationApiBaseUrl" class="mono integrationCode"></div>
</div>
<div class="integrationApiBlock">
<div class="integrationSectionLabel">POST /mission_queue</div>
<pre class="integrationPre">curl -X POST "<span class="integrationCurlHost">http://localhost:8080</span>/api/v2.0.0/mission_queue" \
-H "Content-Type: application/json" \
-d '{"mission_id":"&lt;mission_id&gt;","priority":0,"robot_id":"default"}'</pre>
</div>
<div class="integrationApiBlock">
<div class="integrationSectionLabel">GET /mission_queue · GET /missions · GET /status</div>
<pre class="integrationPre">GET /api/v2.0.0/mission_queue
GET /api/v2.0.0/missions
GET /api/v2.0.0/status</pre>
</div>
<div class="row rowWide integrationTestRow">
<label for="integrationRestMission">Thử nhanh</label>
<div class="integrationTestActions">
<select id="integrationRestMission"></select>
<button id="integrationRestTestBtn" type="button" class="btn subtle">POST queue</button>
</div>
</div>
</div>
</section>
<section class="card">
<div class="cardHeader">
<div>
<div class="cardTitle">MiRFleet — Lên lịch mission</div>
<div class="cardSub">Ưu tiên, gán robot, chạy ASAP hoặc theo thời gian.</div>
</div>
<div class="integrationHeaderActions">
<button id="integrationRefreshBtn" type="button" class="btn subtle">Tải lại</button>
<button id="integrationAddScheduleBtn" type="button" class="btn primary">Thêm lịch</button>
</div>
</div>
<div class="cardBody">
<div id="integrationScheduleEmpty" class="mutedNote">Chưa có lịch fleet.</div>
<div id="integrationScheduleList" class="missionList"></div>
</div>
</section>
</div>
</div>
<div id="configSplitter" class="splitter" role="separator" aria-orientation="vertical" tabindex="0"></div>
<div class="contentRight" id="contentRight">
<section class="card">
<div class="cardHeader">
<div>
<div class="cardTitle">Bố trí trên robot</div>
</div>
</div>
<div class="cardBody">
<div class="canvasWrap" id="canvasWrap">
<canvas id="canvas"></canvas>
</div>
<div class="metaBar">
<div class="viewHint">Cuộn chuột: zoom • Shift + kéo: di chuyển vùng nhìn</div>
<div id="robotDiffSummary" class="robotDiffSummary"></div>
<div>Robot center: <span id="robotCenterText"></span></div>
<div>Selected: <span id="selectedText">none</span></div>
<div>Pose: <span id="selectedRelText"></span></div>
</div>
</div>
</section>
</div>
</main>
</div>
</div>
<dialog id="missionCreateDialog" class="missionDialog">
<form id="missionCreateForm" method="dialog" class="missionDialogForm">
<div class="missionDialogHeader">
<h3>Create mission</h3>
<button type="button" class="iconBtn missionDialogClose" data-close-dialog="missionCreateDialog" aria-label="Đóng">×</button>
</div>
<div class="missionDialogBody">
<div class="row rowWide">
<label for="missionCreateName">Tên mission</label>
<input id="missionCreateName" type="text" required placeholder="VD: Go to charging station" />
</div>
<div class="row rowWide">
<label for="missionCreateGroup">Nhóm mission</label>
<select id="missionCreateGroup"></select>
</div>
<div class="row rowWide">
<label for="missionCreateGroupNew">Hoặc nhóm mới</label>
<input id="missionCreateGroupNew" type="text" placeholder="Tùy chọn" />
</div>
<div class="row rowWide">
<label for="missionCreateDesc">Mô tả</label>
<textarea id="missionCreateDesc" rows="2" placeholder="Tùy chọn"></textarea>
</div>
</div>
<div class="missionDialogFooter">
<button type="button" class="btn subtle" data-close-dialog="missionCreateDialog">Hủy</button>
<button type="submit" class="btn primary">Create mission</button>
</div>
</form>
</dialog>
<dialog id="missionSettingsDialog" class="missionDialog">
<form id="missionSettingsForm" method="dialog" class="missionDialogForm">
<div class="missionDialogHeader">
<h3>Cài đặt mission</h3>
<button type="button" class="iconBtn missionDialogClose" data-close-dialog="missionSettingsDialog" aria-label="Đóng">×</button>
</div>
<div class="missionDialogBody">
<div class="row rowWide">
<label for="missionSettingsName">Tên</label>
<input id="missionSettingsName" type="text" required />
</div>
<div class="row rowWide">
<label for="missionSettingsGroup">Nhóm</label>
<select id="missionSettingsGroup"></select>
</div>
<div class="row rowWide">
<label for="missionSettingsDesc">Mô tả</label>
<textarea id="missionSettingsDesc" rows="2"></textarea>
</div>
</div>
<div class="missionDialogFooter">
<button type="button" class="btn subtle" data-close-dialog="missionSettingsDialog">Hủy</button>
<button type="submit" class="btn primary">Áp dụng</button>
</div>
</form>
</dialog>
<dialog id="missionSaveAsDialog" class="missionDialog">
<form id="missionSaveAsForm" method="dialog" class="missionDialogForm">
<div class="missionDialogHeader">
<h3>Save mission as</h3>
<button type="button" class="iconBtn missionDialogClose" data-close-dialog="missionSaveAsDialog" aria-label="Đóng">×</button>
</div>
<div class="missionDialogBody">
<div class="row rowWide">
<label for="missionSaveAsName">Tên mission mới</label>
<input id="missionSaveAsName" type="text" required />
</div>
</div>
<div class="missionDialogFooter">
<button type="button" class="btn subtle" data-close-dialog="missionSaveAsDialog">Hủy</button>
<button type="submit" class="btn primary">Lưu bản sao</button>
</div>
</form>
</dialog>
<dialog id="missionActionConfigDialog" class="missionDialog missionDialogWide">
<form id="missionActionConfigForm" method="dialog" class="missionDialogForm">
<div class="missionDialogHeader">
<h3 id="missionActionConfigTitle">Cấu hình action</h3>
<button type="button" class="iconBtn missionDialogClose" data-close-dialog="missionActionConfigDialog" aria-label="Đóng">×</button>
</div>
<div class="missionDialogBody" id="missionActionConfigBody"></div>
<div class="missionDialogFooter">
<button type="button" class="btn subtle" data-close-dialog="missionActionConfigDialog">Hủy</button>
<button type="submit" class="btn primary">Áp dụng</button>
</div>
</form>
</dialog>
<dialog id="missionQueueDialog" class="missionDialog">
<form id="missionQueueForm" method="dialog" class="missionDialogForm">
<div class="missionDialogHeader">
<h3>Thêm vào mission queue</h3>
<button type="button" class="iconBtn missionDialogClose" data-close-dialog="missionQueueDialog" aria-label="Đóng">×</button>
</div>
<div class="missionDialogBody">
<p id="missionQueueDialogMission" class="mutedNote"></p>
<div id="missionQueueVarFields" class="missionConfigGrid"></div>
<p id="missionQueueVarHint" class="mutedNote" hidden>Tham số đã chọn sẽ hiển thị màu xanh trong queue.</p>
</div>
<div class="missionDialogFooter">
<button type="button" class="btn subtle" data-close-dialog="missionQueueDialog">Hủy</button>
<button type="submit" class="btn primary">Thêm vào queue</button>
</div>
</form>
</dialog>
</dialog>
<dialog id="dashboardAddWidgetDialog" class="missionDialog">
<form id="dashboardAddWidgetForm" method="dialog" class="missionDialogForm">
<div class="missionDialogHeader">
<h3>Thêm widget</h3>
<button type="button" class="iconBtn missionDialogClose" data-close-dialog="dashboardAddWidgetDialog" aria-label="Đóng">×</button>
</div>
<div class="missionDialogBody">
<div class="row rowWide">
<label for="dashboardWidgetType">Loại widget</label>
<select id="dashboardWidgetType" required>
<option value="mission_button">Mission button</option>
<option value="mission_group">Mission group</option>
<option value="mission_queue">Mission queue</option>
<option value="pause_continue">Pause / Continue</option>
</select>
</div>
<div id="dashboardAddWidgetFields" class="missionConfigGrid"></div>
</div>
<div class="missionDialogFooter">
<button type="button" class="btn subtle" data-close-dialog="dashboardAddWidgetDialog">Hủy</button>
<button type="submit" class="btn primary">Thêm</button>
</div>
</form>
</dialog>
<dialog id="dashboardEditWidgetDialog" class="missionDialog">
<form id="dashboardEditWidgetForm" method="dialog" class="missionDialogForm">
<div class="missionDialogHeader">
<h3>Cấu hình widget</h3>
<button type="button" class="iconBtn missionDialogClose" data-close-dialog="dashboardEditWidgetDialog" aria-label="Đóng">×</button>
</div>
<div class="missionDialogBody">
<input type="hidden" id="dashboardEditWidgetId" />
<div class="row rowWide">
<label for="dashboardEditWidgetType">Loại</label>
<input id="dashboardEditWidgetType" type="text" readonly />
</div>
<div id="dashboardEditWidgetFields" class="missionConfigGrid"></div>
</div>
<div class="missionDialogFooter">
<button type="button" class="btn subtle danger" id="dashboardDeleteWidgetBtn">Xóa widget</button>
<button type="button" class="btn subtle" data-close-dialog="dashboardEditWidgetDialog">Hủy</button>
<button type="submit" class="btn primary">Lưu</button>
</div>
</form>
</dialog>
<dialog id="integrationAddTriggerDialog" class="missionDialog">
<form id="integrationAddTriggerForm" method="dialog" class="missionDialogForm">
<div class="missionDialogHeader">
<h3>Modbus trigger</h3>
<button type="button" class="iconBtn missionDialogClose" data-close-dialog="integrationAddTriggerDialog" aria-label="Đóng">×</button>
</div>
<div class="missionDialogBody">
<div class="row rowWide">
<label for="integrationTriggerName">Tên trigger</label>
<input id="integrationTriggerName" type="text" required placeholder="VD: PLC line 1 start" />
</div>
<div class="row rowWide">
<label for="integrationTriggerCoil">Coil ID</label>
<input id="integrationTriggerCoil" type="number" min="1001" max="2000" value="1001" required />
</div>
<div class="row rowWide">
<label for="integrationTriggerMission">Mission</label>
<select id="integrationTriggerMission" required></select>
</div>
</div>
<div class="missionDialogFooter">
<button type="button" class="btn subtle" data-close-dialog="integrationAddTriggerDialog">Hủy</button>
<button type="submit" class="btn primary">Thêm</button>
</div>
</form>
</dialog>
<dialog id="integrationAddScheduleDialog" class="missionDialog">
<form id="integrationAddScheduleForm" method="dialog" class="missionDialogForm">
<div class="missionDialogHeader">
<h3>Lịch MiRFleet</h3>
<button type="button" class="iconBtn missionDialogClose" data-close-dialog="integrationAddScheduleDialog" aria-label="Đóng">×</button>
</div>
<div class="missionDialogBody">
<div class="row rowWide">
<label for="integrationScheduleName">Tên lịch</label>
<input id="integrationScheduleName" type="text" required placeholder="VD: Ca sáng — đi dock" />
</div>
<div class="row rowWide">
<label for="integrationScheduleMission">Mission</label>
<select id="integrationScheduleMission" required></select>
</div>
<div class="row rowWide">
<label for="integrationScheduleRobot">Robot</label>
<select id="integrationScheduleRobot"></select>
</div>
<div class="row rowWide">
<label for="integrationSchedulePriority">Ưu tiên</label>
<input id="integrationSchedulePriority" type="number" value="0" />
</div>
<div class="row rowWide">
<label for="integrationScheduleMode">Chế độ</label>
<select id="integrationScheduleMode">
<option value="asap">ASAP — vào queue ngay</option>
<option value="scheduled">Scheduled — theo thời gian</option>
</select>
</div>
<div class="row rowWide" id="integrationScheduleStartAtRow" hidden>
<label for="integrationScheduleStartAt">Thời gian bắt đầu</label>
<input id="integrationScheduleStartAt" type="datetime-local" />
</div>
</div>
<div class="missionDialogFooter">
<button type="button" class="btn subtle" data-close-dialog="integrationAddScheduleDialog">Hủy</button>
<button type="submit" class="btn primary">Thêm</button>
</div>
</form>
</dialog>
<script src="/missions.js"></script>
<script src="/dashboard.js"></script>
<script src="/integrations.js"></script>
<script src="/app.js"></script>
</body>
</html>