@openrfid/shell 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,931 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ DashboardView: () => DashboardView,
34
+ EventHistoryView: () => EventHistoryView,
35
+ EventMonitorView: () => EventMonitorView,
36
+ ProtocolsPluginsView: () => ProtocolsPluginsView,
37
+ ReadersView: () => ReadersView,
38
+ TagsView: () => TagsView
39
+ });
40
+ module.exports = __toCommonJS(index_exports);
41
+
42
+ // src/views/DashboardView.tsx
43
+ var import_react = __toESM(require("react"));
44
+ var LIMIT_PRESETS = [1e3, 5e3, 1e4, 5e4, 1e5];
45
+ var card = {
46
+ backgroundColor: "#1E293B",
47
+ padding: "20px",
48
+ borderRadius: "10px",
49
+ border: "1px solid #334155"
50
+ };
51
+ var DashboardView = ({ simulator, storage }) => {
52
+ const [readers, setReaders] = (0, import_react.useState)(simulator.getAllReaders());
53
+ const [tags, setTags] = (0, import_react.useState)(simulator.getAllTags());
54
+ const [memUsage, setMemUsage] = (0, import_react.useState)(storage?.getMemoryUsage());
55
+ const [customLimit, setCustomLimit] = (0, import_react.useState)("");
56
+ const [autoTrim, setAutoTrim] = (0, import_react.useState)(true);
57
+ const [showWarning, setShowWarning] = (0, import_react.useState)(true);
58
+ const [limitInput, setLimitInput] = (0, import_react.useState)(storage?.getMemoryUsage().limit ?? 1e4);
59
+ (0, import_react.useEffect)(() => {
60
+ const interval = setInterval(() => {
61
+ setReaders(simulator.getAllReaders());
62
+ setTags(simulator.getAllTags());
63
+ if (storage) setMemUsage(storage.getMemoryUsage());
64
+ }, 2e3);
65
+ return () => clearInterval(interval);
66
+ }, [simulator, storage]);
67
+ const onlineCount = readers.filter((r) => r.status === "ONLINE").length;
68
+ const offlineCount = readers.length - onlineCount;
69
+ const applyLimit = (limit) => {
70
+ if (!storage) return;
71
+ storage.setMemoryLimit(limit);
72
+ setLimitInput(limit);
73
+ setMemUsage(storage.getMemoryUsage());
74
+ };
75
+ const handleApplyCustom = () => {
76
+ const v = parseInt(customLimit, 10);
77
+ if (!isNaN(v) && v >= 100) applyLimit(v);
78
+ };
79
+ const handleClearBuffer = () => {
80
+ storage?.clearAllEvents();
81
+ setMemUsage(storage?.getMemoryUsage());
82
+ };
83
+ const mem = memUsage;
84
+ const memPercent = mem?.percent ?? 0;
85
+ const memBarColor = memPercent >= 80 ? "#F59E0B" : memPercent >= 95 ? "#EF4444" : "#10B981";
86
+ return /* @__PURE__ */ import_react.default.createElement("div", { style: { padding: "24px" } }, /* @__PURE__ */ import_react.default.createElement("div", { style: { display: "flex", gap: "12px", flexWrap: "wrap", marginBottom: "20px", padding: "12px 16px", backgroundColor: "#0F172A", borderRadius: "8px", border: "1px solid #334155", fontSize: "13px" } }, /* @__PURE__ */ import_react.default.createElement("span", { style: { color: "#10B981", fontWeight: "bold" } }, "\u25CF ", onlineCount, " Online"), /* @__PURE__ */ import_react.default.createElement("span", { style: { color: "#64748B" } }, "|"), /* @__PURE__ */ import_react.default.createElement("span", { style: { color: "#64748B" } }, offlineCount, " Offline"), /* @__PURE__ */ import_react.default.createElement("span", { style: { color: "#64748B" } }, "|"), /* @__PURE__ */ import_react.default.createElement("span", { style: { color: "#00E5EE" } }, "\u{1F3F7} ", tags.length.toLocaleString(), " Tags"), mem && /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("span", { style: { color: "#64748B" } }, "|"), /* @__PURE__ */ import_react.default.createElement("span", { style: { color: mem.isWarning ? "#F59E0B" : "#94A3B8" } }, mem.isWarning ? "\u26A0\uFE0F " : "", "Buffer: ", mem.current.toLocaleString(), "/", mem.limit.toLocaleString(), " (", mem.percent, "%)"))), /* @__PURE__ */ import_react.default.createElement("h2", { style: { fontSize: "20px", fontWeight: "bold", marginBottom: "20px" } }, "Dashboard Overview"), /* @__PURE__ */ import_react.default.createElement("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(200px, 1fr))", gap: "16px" } }, /* @__PURE__ */ import_react.default.createElement("div", { style: card }, /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: "13px", color: "#94A3B8" } }, "Readers Online"), /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: "28px", fontWeight: "bold", color: "#10B981", marginTop: "4px" } }, onlineCount)), /* @__PURE__ */ import_react.default.createElement("div", { style: card }, /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: "13px", color: "#94A3B8" } }, "Readers Offline"), /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: "28px", fontWeight: "bold", color: "#64748B", marginTop: "4px" } }, offlineCount)), /* @__PURE__ */ import_react.default.createElement("div", { style: card }, /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: "13px", color: "#94A3B8" } }, "Active Simulated Tags"), /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: "28px", fontWeight: "bold", color: "#00E5EE", marginTop: "4px" } }, tags.length.toLocaleString())), /* @__PURE__ */ import_react.default.createElement("div", { style: card }, /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: "13px", color: "#94A3B8" } }, "Event Buffer"), /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: "28px", fontWeight: "bold", color: mem?.isWarning ? "#F59E0B" : "#FFFFFF", marginTop: "4px" } }, mem ? `${mem.current.toLocaleString()}` : "\u2014"), mem && /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: "11px", color: "#64748B", marginTop: "2px" } }, "of ", mem.limit.toLocaleString(), " limit"))), /* @__PURE__ */ import_react.default.createElement("div", { style: { ...card, marginTop: "20px" } }, /* @__PURE__ */ import_react.default.createElement("h3", { style: { margin: "0 0 12px", fontSize: "16px" } }, "Quick Controls"), /* @__PURE__ */ import_react.default.createElement("div", { style: { display: "flex", gap: "12px" } }, /* @__PURE__ */ import_react.default.createElement(
87
+ "button",
88
+ {
89
+ onClick: () => simulator.startAll(),
90
+ style: { backgroundColor: "#10B981", color: "#FFF", border: "none", padding: "8px 16px", borderRadius: "6px", cursor: "pointer", fontWeight: "bold" }
91
+ },
92
+ "\u25B6 Start All Readers"
93
+ ), /* @__PURE__ */ import_react.default.createElement(
94
+ "button",
95
+ {
96
+ onClick: () => simulator.stopAll(),
97
+ style: { backgroundColor: "#EF4444", color: "#FFF", border: "none", padding: "8px 16px", borderRadius: "6px", cursor: "pointer", fontWeight: "bold" }
98
+ },
99
+ "\u25A0 Stop All Readers"
100
+ ))), storage && /* @__PURE__ */ import_react.default.createElement("div", { style: { ...card, marginTop: "20px" } }, /* @__PURE__ */ import_react.default.createElement("h3", { style: { margin: "0 0 16px", fontSize: "16px" } }, "\u2699\uFE0F Storage & Memory Settings"), /* @__PURE__ */ import_react.default.createElement("div", { style: { marginBottom: "14px", fontSize: "14px", color: "#94A3B8" } }, "In-Memory Event Buffer Limit"), mem && /* @__PURE__ */ import_react.default.createElement("div", { style: { marginBottom: "16px" } }, /* @__PURE__ */ import_react.default.createElement("div", { style: { display: "flex", justifyContent: "space-between", fontSize: "13px", marginBottom: "4px" } }, /* @__PURE__ */ import_react.default.createElement("span", { style: { color: mem.isWarning ? "#F59E0B" : "#94A3B8" } }, mem.isWarning ? "\u26A0\uFE0F " : "", mem.current.toLocaleString(), " events in buffer"), /* @__PURE__ */ import_react.default.createElement("span", { style: { color: "#64748B" } }, mem.percent, "% of ", mem.limit.toLocaleString())), /* @__PURE__ */ import_react.default.createElement("div", { style: { backgroundColor: "#0F172A", borderRadius: "6px", height: "10px", overflow: "hidden" } }, /* @__PURE__ */ import_react.default.createElement(
101
+ "div",
102
+ {
103
+ style: {
104
+ width: `${Math.min(100, mem.percent)}%`,
105
+ height: "100%",
106
+ backgroundColor: memBarColor,
107
+ borderRadius: "6px",
108
+ transition: "width 0.5s ease"
109
+ }
110
+ }
111
+ ))), /* @__PURE__ */ import_react.default.createElement("div", { style: { display: "flex", gap: "8px", flexWrap: "wrap", marginBottom: "14px" } }, LIMIT_PRESETS.map((preset) => /* @__PURE__ */ import_react.default.createElement(
112
+ "button",
113
+ {
114
+ key: preset,
115
+ onClick: () => applyLimit(preset),
116
+ style: {
117
+ padding: "5px 12px",
118
+ borderRadius: "6px",
119
+ border: `1px solid ${limitInput === preset ? "#00E5EE" : "#334155"}`,
120
+ backgroundColor: limitInput === preset ? "#0E3A3F" : "#0F172A",
121
+ color: limitInput === preset ? "#00E5EE" : "#94A3B8",
122
+ cursor: "pointer",
123
+ fontSize: "13px",
124
+ fontWeight: limitInput === preset ? "bold" : "normal"
125
+ }
126
+ },
127
+ preset.toLocaleString()
128
+ ))), /* @__PURE__ */ import_react.default.createElement("div", { style: { display: "flex", gap: "8px", marginBottom: "16px", alignItems: "center" } }, /* @__PURE__ */ import_react.default.createElement(
129
+ "input",
130
+ {
131
+ type: "number",
132
+ placeholder: "Custom limit (100\u2013500,000)",
133
+ value: customLimit,
134
+ onChange: (e) => setCustomLimit(e.target.value),
135
+ style: { backgroundColor: "#0F172A", border: "1px solid #334155", color: "#F8FAFC", padding: "6px 10px", borderRadius: "6px", fontSize: "13px", width: "240px" }
136
+ }
137
+ ), /* @__PURE__ */ import_react.default.createElement(
138
+ "button",
139
+ {
140
+ onClick: handleApplyCustom,
141
+ style: { backgroundColor: "#00E5EE", color: "#0F172A", border: "none", padding: "6px 14px", borderRadius: "6px", cursor: "pointer", fontWeight: "bold", fontSize: "13px" }
142
+ },
143
+ "Apply Limit"
144
+ )), /* @__PURE__ */ import_react.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: "10px", marginBottom: "16px", fontSize: "14px" } }, /* @__PURE__ */ import_react.default.createElement("label", { style: { display: "flex", alignItems: "center", gap: "8px", cursor: "pointer" } }, /* @__PURE__ */ import_react.default.createElement(
145
+ "input",
146
+ {
147
+ type: "checkbox",
148
+ checked: autoTrim,
149
+ onChange: (e) => setAutoTrim(e.target.checked),
150
+ style: { accentColor: "#00E5EE", width: "16px", height: "16px" }
151
+ }
152
+ ), /* @__PURE__ */ import_react.default.createElement("span", { style: { color: "#CBD5E1" } }, "Auto-trim oldest events when limit is reached")), /* @__PURE__ */ import_react.default.createElement("label", { style: { display: "flex", alignItems: "center", gap: "8px", cursor: "pointer" } }, /* @__PURE__ */ import_react.default.createElement(
153
+ "input",
154
+ {
155
+ type: "checkbox",
156
+ checked: showWarning,
157
+ onChange: (e) => setShowWarning(e.target.checked),
158
+ style: { accentColor: "#00E5EE", width: "16px", height: "16px" }
159
+ }
160
+ ), /* @__PURE__ */ import_react.default.createElement("span", { style: { color: "#CBD5E1" } }, "Show \u26A0\uFE0F warning when buffer is \u226580% full"))), /* @__PURE__ */ import_react.default.createElement(
161
+ "button",
162
+ {
163
+ onClick: handleClearBuffer,
164
+ style: { backgroundColor: "#334155", color: "#F8FAFC", border: "none", padding: "8px 16px", borderRadius: "6px", cursor: "pointer", fontWeight: "bold", fontSize: "13px" }
165
+ },
166
+ "\u{1F5D1} Clear Memory Buffer Now"
167
+ )));
168
+ };
169
+
170
+ // src/views/ReadersView.tsx
171
+ var import_react2 = __toESM(require("react"));
172
+ var import_readers = require("@openrfid/readers");
173
+ var import_ui = require("@openrfid/ui");
174
+ var ReadersView = ({ simulator, refresh }) => {
175
+ const [, setTick] = (0, import_react2.useState)(0);
176
+ const readers = simulator.getAllReaders();
177
+ const [showAddForm, setShowAddForm] = (0, import_react2.useState)(false);
178
+ const [readerName, setReaderName] = (0, import_react2.useState)("");
179
+ const [vendor, setVendor] = (0, import_react2.useState)("RFID Softwares");
180
+ const [readerType, setReaderType] = (0, import_react2.useState)("4-Port Reader");
181
+ const [readMode, setReadMode] = (0, import_react2.useState)("continuous");
182
+ const [readIntervalValue, setReadIntervalValue] = (0, import_react2.useState)(1);
183
+ const [readIntervalUnit, setReadIntervalUnit] = (0, import_react2.useState)("seconds");
184
+ const [readRate, setReadRate] = (0, import_react2.useState)(0);
185
+ const [epcFilterPrefix, setEpcFilterPrefix] = (0, import_react2.useState)("");
186
+ const [epcFilterStart, setEpcFilterStart] = (0, import_react2.useState)("");
187
+ const [epcFilterEnd, setEpcFilterEnd] = (0, import_react2.useState)("");
188
+ const [expandedReaderId, setExpandedReaderId] = (0, import_react2.useState)(null);
189
+ const triggerUpdate = () => {
190
+ setTick((t) => t + 1);
191
+ refresh();
192
+ };
193
+ const getAntennasCount = (type) => {
194
+ if (type === "Desktop Reader") return 1;
195
+ if (type === "Integrated Reader") return 2;
196
+ if (type === "4-Port Reader") return 4;
197
+ if (type === "8-Port Reader") return 8;
198
+ if (type === "16-Port Reader") return 16;
199
+ return 4;
200
+ };
201
+ const handleAddReader = () => {
202
+ const name = readerName.trim() || `${vendor} ${readerType}`;
203
+ const antennasCount = getAntennasCount(readerType);
204
+ const reader = new import_readers.VirtualReader({
205
+ name,
206
+ vendor,
207
+ model: readerType,
208
+ antennasCount,
209
+ readRate,
210
+ readMode,
211
+ readIntervalValue,
212
+ readIntervalUnit,
213
+ epcFilterStart,
214
+ epcFilterEnd,
215
+ epcFilterPrefix
216
+ });
217
+ simulator.addReader(reader);
218
+ setReaderName("");
219
+ setEpcFilterPrefix("");
220
+ setEpcFilterStart("");
221
+ setEpcFilterEnd("");
222
+ setReadRate(0);
223
+ setReadMode("continuous");
224
+ setReadIntervalValue(1);
225
+ setReadIntervalUnit("seconds");
226
+ setShowAddForm(false);
227
+ triggerUpdate();
228
+ };
229
+ const handleReaderSettingsChange = (reader, key, val) => {
230
+ reader[key] = val;
231
+ if (simulator.storage) {
232
+ simulator.storage.saveReader(reader.toJSON());
233
+ }
234
+ if (reader.status === "ONLINE") {
235
+ simulator.stopReader(reader.id);
236
+ simulator.startReader(reader.id);
237
+ }
238
+ triggerUpdate();
239
+ };
240
+ const handleToggleAntenna = (reader, index) => {
241
+ const antenna = reader.getAntenna(index);
242
+ if (antenna) {
243
+ antenna.enabled = !antenna.enabled;
244
+ if (simulator.storage) {
245
+ simulator.storage.saveAntenna({
246
+ id: antenna.id,
247
+ readerId: antenna.readerId,
248
+ index: antenna.index,
249
+ gain: antenna.gain,
250
+ power: antenna.power,
251
+ frequency: antenna.frequency,
252
+ rssiOffset: antenna.rssiOffset,
253
+ readZone: antenna.readZone,
254
+ enabled: antenna.enabled
255
+ });
256
+ }
257
+ triggerUpdate();
258
+ }
259
+ };
260
+ const handleAntennaPowerChange = (reader, index, power) => {
261
+ const antenna = reader.getAntenna(index);
262
+ if (antenna) {
263
+ antenna.power = power;
264
+ if (simulator.storage) {
265
+ simulator.storage.saveAntenna({
266
+ id: antenna.id,
267
+ readerId: antenna.readerId,
268
+ index: antenna.index,
269
+ gain: antenna.gain,
270
+ power: antenna.power,
271
+ frequency: antenna.frequency,
272
+ rssiOffset: antenna.rssiOffset,
273
+ readZone: antenna.readZone,
274
+ enabled: antenna.enabled
275
+ });
276
+ }
277
+ triggerUpdate();
278
+ }
279
+ };
280
+ const handleAntennaGainChange = (reader, index, gain) => {
281
+ const antenna = reader.getAntenna(index);
282
+ if (antenna) {
283
+ antenna.gain = gain;
284
+ if (simulator.storage) {
285
+ simulator.storage.saveAntenna({
286
+ id: antenna.id,
287
+ readerId: antenna.readerId,
288
+ index: antenna.index,
289
+ gain: antenna.gain,
290
+ power: antenna.power,
291
+ frequency: antenna.frequency,
292
+ rssiOffset: antenna.rssiOffset,
293
+ readZone: antenna.readZone,
294
+ enabled: antenna.enabled
295
+ });
296
+ }
297
+ triggerUpdate();
298
+ }
299
+ };
300
+ const handleAntennaRssiChange = (reader, index, rssiOffset) => {
301
+ const antenna = reader.getAntenna(index);
302
+ if (antenna) {
303
+ antenna.rssiOffset = rssiOffset;
304
+ if (simulator.storage) {
305
+ simulator.storage.saveAntenna({
306
+ id: antenna.id,
307
+ readerId: antenna.readerId,
308
+ index: antenna.index,
309
+ gain: antenna.gain,
310
+ power: antenna.power,
311
+ frequency: antenna.frequency,
312
+ rssiOffset: antenna.rssiOffset,
313
+ readZone: antenna.readZone,
314
+ enabled: antenna.enabled
315
+ });
316
+ }
317
+ triggerUpdate();
318
+ }
319
+ };
320
+ const handleAntennaZoneChange = (reader, index, zone) => {
321
+ const antenna = reader.getAntenna(index);
322
+ if (antenna) {
323
+ antenna.readZone = zone;
324
+ if (simulator.storage) {
325
+ simulator.storage.saveAntenna({
326
+ id: antenna.id,
327
+ readerId: antenna.readerId,
328
+ index: antenna.index,
329
+ gain: antenna.gain,
330
+ power: antenna.power,
331
+ frequency: antenna.frequency,
332
+ rssiOffset: antenna.rssiOffset,
333
+ readZone: antenna.readZone,
334
+ enabled: antenna.enabled
335
+ });
336
+ }
337
+ triggerUpdate();
338
+ }
339
+ };
340
+ return /* @__PURE__ */ import_react2.default.createElement("div", { style: { padding: "24px" } }, /* @__PURE__ */ import_react2.default.createElement("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "20px" } }, /* @__PURE__ */ import_react2.default.createElement("h2", { style: { fontSize: "20px", fontWeight: "bold", margin: 0 } }, "Virtual Readers (", readers.length, ")"), /* @__PURE__ */ import_react2.default.createElement(
341
+ "button",
342
+ {
343
+ onClick: () => setShowAddForm(!showAddForm),
344
+ style: { backgroundColor: "#00E5EE", color: "#0F172A", border: "none", padding: "8px 16px", borderRadius: "6px", cursor: "pointer", fontWeight: "bold" }
345
+ },
346
+ showAddForm ? "Close Form" : "\u2795 Add Reader"
347
+ )), showAddForm && /* @__PURE__ */ import_react2.default.createElement("div", { style: { backgroundColor: "#1E293B", padding: "20px", borderRadius: "8px", border: "1px solid #00E5EE", marginBottom: "20px", display: "flex", flexDirection: "column", gap: "16px" } }, /* @__PURE__ */ import_react2.default.createElement("h3", { style: { margin: 0, fontSize: "16px", color: "#00E5EE" } }, "Configure New RFID Reader"), /* @__PURE__ */ import_react2.default.createElement("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(200px, 1fr))", gap: "16px" } }, /* @__PURE__ */ import_react2.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: "4px" } }, /* @__PURE__ */ import_react2.default.createElement("span", { style: { fontSize: "13px", color: "#D1D5DB" } }, "Reader Name"), /* @__PURE__ */ import_react2.default.createElement(
348
+ "input",
349
+ {
350
+ type: "text",
351
+ placeholder: "e.g. Warehouse Gate A...",
352
+ value: readerName,
353
+ onChange: (e) => setReaderName(e.target.value),
354
+ style: { padding: "8px 12px", borderRadius: "6px", border: "1px solid #334155", backgroundColor: "#0F172A", color: "#FFF" }
355
+ }
356
+ )), /* @__PURE__ */ import_react2.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: "4px" } }, /* @__PURE__ */ import_react2.default.createElement("span", { style: { fontSize: "13px", color: "#D1D5DB" } }, "Vendor"), /* @__PURE__ */ import_react2.default.createElement(
357
+ "select",
358
+ {
359
+ value: vendor,
360
+ onChange: (e) => setVendor(e.target.value),
361
+ style: { padding: "8px 12px", borderRadius: "6px", border: "1px solid #334155", backgroundColor: "#0F172A", color: "#FFF" }
362
+ },
363
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: "RFID Softwares" }, "RFID Softwares"),
364
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: "Impinj" }, "Impinj"),
365
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: "Zebra" }, "Zebra"),
366
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: "ID Tech" }, "ID Tech"),
367
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: "Identium" }, "Identium"),
368
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: "Realtime" }, "Realtime"),
369
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: "Generic" }, "Generic")
370
+ )), /* @__PURE__ */ import_react2.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: "4px" } }, /* @__PURE__ */ import_react2.default.createElement("span", { style: { fontSize: "13px", color: "#D1D5DB" } }, "Type & Ports"), /* @__PURE__ */ import_react2.default.createElement(
371
+ "select",
372
+ {
373
+ value: readerType,
374
+ onChange: (e) => setReaderType(e.target.value),
375
+ style: { padding: "8px 12px", borderRadius: "6px", border: "1px solid #334155", backgroundColor: "#0F172A", color: "#FFF" }
376
+ },
377
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: "Desktop Reader" }, "Desktop Reader (1 Port)"),
378
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: "Integrated Reader" }, "Integrated Reader (2 Ports)"),
379
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: "4-Port Reader" }, "4-Port Reader (4 Ports)"),
380
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: "8-Port Reader" }, "8-Port Reader (8 Ports)"),
381
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: "16-Port Reader" }, "16-Port Reader (16 Ports)")
382
+ ))), /* @__PURE__ */ import_react2.default.createElement("div", { style: { borderTop: "1px dashed #334155", paddingTop: "16px" } }, /* @__PURE__ */ import_react2.default.createElement("h4", { style: { margin: "0 0 12px 0", fontSize: "14px", color: "#00E5EE" } }, "\u2699\uFE0F Default Simulation Settings"), /* @__PURE__ */ import_react2.default.createElement("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(200px, 1fr))", gap: "16px" } }, /* @__PURE__ */ import_react2.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: "4px" } }, /* @__PURE__ */ import_react2.default.createElement("span", { style: { fontSize: "13px", color: "#D1D5DB" } }, "Read Mode"), /* @__PURE__ */ import_react2.default.createElement(
383
+ "select",
384
+ {
385
+ value: readMode,
386
+ onChange: (e) => setReadMode(e.target.value),
387
+ style: { padding: "8px 12px", borderRadius: "6px", border: "1px solid #334155", backgroundColor: "#0F172A", color: "#FFF" }
388
+ },
389
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: "continuous" }, "Continuous Read"),
390
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: "periodic" }, "Periodic Read")
391
+ )), readMode === "periodic" && /* @__PURE__ */ import_react2.default.createElement("div", { style: { display: "flex", gap: "8px", alignItems: "flex-end" } }, /* @__PURE__ */ import_react2.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: "4px", flex: 1 } }, /* @__PURE__ */ import_react2.default.createElement("span", { style: { fontSize: "13px", color: "#D1D5DB" } }, "Interval"), /* @__PURE__ */ import_react2.default.createElement(
392
+ "input",
393
+ {
394
+ type: "number",
395
+ min: 1,
396
+ value: readIntervalValue,
397
+ onChange: (e) => setReadIntervalValue(parseInt(e.target.value, 10)),
398
+ style: { padding: "8px 12px", borderRadius: "6px", border: "1px solid #334155", backgroundColor: "#0F172A", color: "#FFF", width: "100%" }
399
+ }
400
+ )), /* @__PURE__ */ import_react2.default.createElement(
401
+ "select",
402
+ {
403
+ value: readIntervalUnit,
404
+ onChange: (e) => setReadIntervalUnit(e.target.value),
405
+ style: { padding: "8px 12px", borderRadius: "6px", border: "1px solid #334155", backgroundColor: "#0F172A", color: "#FFF", height: "37px" }
406
+ },
407
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: "seconds" }, "Seconds"),
408
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: "minutes" }, "Minutes"),
409
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: "hours" }, "Hours")
410
+ )), /* @__PURE__ */ import_react2.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: "4px" } }, /* @__PURE__ */ import_react2.default.createElement("span", { style: { fontSize: "13px", color: "#D1D5DB" } }, "Max Read Rate Limit"), /* @__PURE__ */ import_react2.default.createElement(
411
+ "select",
412
+ {
413
+ value: readRate,
414
+ onChange: (e) => setReadRate(parseInt(e.target.value, 10)),
415
+ style: { padding: "8px 12px", borderRadius: "6px", border: "1px solid #334155", backgroundColor: "#0F172A", color: "#FFF" }
416
+ },
417
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: 0 }, "Unlimited"),
418
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: 1 }, "1 tag/sec"),
419
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: 10 }, "10 tags/sec"),
420
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: 100 }, "100 tags/sec"),
421
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: 1e3 }, "1000 tags/sec")
422
+ )), /* @__PURE__ */ import_react2.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: "4px" } }, /* @__PURE__ */ import_react2.default.createElement("span", { style: { fontSize: "13px", color: "#D1D5DB" } }, "EPC Filter Prefix"), /* @__PURE__ */ import_react2.default.createElement(
423
+ "input",
424
+ {
425
+ type: "text",
426
+ placeholder: "e.g. E200",
427
+ value: epcFilterPrefix,
428
+ onChange: (e) => setEpcFilterPrefix(e.target.value),
429
+ style: { padding: "8px 12px", borderRadius: "6px", border: "1px solid #334155", backgroundColor: "#0F172A", color: "#FFF" }
430
+ }
431
+ )), /* @__PURE__ */ import_react2.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: "4px" } }, /* @__PURE__ */ import_react2.default.createElement("span", { style: { fontSize: "13px", color: "#D1D5DB" } }, "EPC Range Start"), /* @__PURE__ */ import_react2.default.createElement(
432
+ "input",
433
+ {
434
+ type: "text",
435
+ placeholder: "e.g. E20000000001",
436
+ value: epcFilterStart,
437
+ onChange: (e) => setEpcFilterStart(e.target.value),
438
+ style: { padding: "8px 12px", borderRadius: "6px", border: "1px solid #334155", backgroundColor: "#0F172A", color: "#FFF" }
439
+ }
440
+ )), /* @__PURE__ */ import_react2.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: "4px" } }, /* @__PURE__ */ import_react2.default.createElement("span", { style: { fontSize: "13px", color: "#D1D5DB" } }, "EPC Range End"), /* @__PURE__ */ import_react2.default.createElement(
441
+ "input",
442
+ {
443
+ type: "text",
444
+ placeholder: "e.g. E20000000010",
445
+ value: epcFilterEnd,
446
+ onChange: (e) => setEpcFilterEnd(e.target.value),
447
+ style: { padding: "8px 12px", borderRadius: "6px", border: "1px solid #334155", backgroundColor: "#0F172A", color: "#FFF" }
448
+ }
449
+ )))), /* @__PURE__ */ import_react2.default.createElement("div", { style: { display: "flex", gap: "10px", marginTop: "10px" } }, /* @__PURE__ */ import_react2.default.createElement(
450
+ "button",
451
+ {
452
+ onClick: handleAddReader,
453
+ style: { backgroundColor: "#00E5EE", color: "#0F172A", border: "none", padding: "8px 24px", borderRadius: "6px", cursor: "pointer", fontWeight: "bold" }
454
+ },
455
+ "Confirm and Add"
456
+ ), /* @__PURE__ */ import_react2.default.createElement(
457
+ "button",
458
+ {
459
+ onClick: () => setShowAddForm(false),
460
+ style: { backgroundColor: "#334155", color: "#FFF", border: "none", padding: "8px 24px", borderRadius: "6px", cursor: "pointer", fontWeight: "bold" }
461
+ },
462
+ "Cancel"
463
+ ))), /* @__PURE__ */ import_react2.default.createElement("div", { style: { display: "grid", gridTemplateColumns: "1fr", gap: "16px" } }, readers.map((r) => {
464
+ const isExpanded = expandedReaderId === r.id;
465
+ return /* @__PURE__ */ import_react2.default.createElement("div", { key: r.id, style: { backgroundColor: "#1E293B", padding: "20px", borderRadius: "8px", border: "1px solid #334155" } }, /* @__PURE__ */ import_react2.default.createElement("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" } }, /* @__PURE__ */ import_react2.default.createElement("div", null, /* @__PURE__ */ import_react2.default.createElement("span", { style: { fontWeight: "bold", fontSize: "18px", marginRight: "12px" } }, r.name), /* @__PURE__ */ import_react2.default.createElement("span", { style: { fontSize: "12px", color: "#64748B", fontFamily: "monospace" } }, "ID: ", r.id)), /* @__PURE__ */ import_react2.default.createElement("div", { style: { display: "flex", alignItems: "center", gap: "12px" } }, /* @__PURE__ */ import_react2.default.createElement(import_ui.ReaderStatusBadge, { status: r.status }), /* @__PURE__ */ import_react2.default.createElement(
466
+ "button",
467
+ {
468
+ onClick: () => setExpandedReaderId(isExpanded ? null : r.id),
469
+ style: { backgroundColor: "#334155", color: "#FFF", border: "none", padding: "6px 12px", borderRadius: "6px", cursor: "pointer", fontSize: "13px" }
470
+ },
471
+ isExpanded ? "Hide Config" : "Configure Reader"
472
+ ))), /* @__PURE__ */ import_react2.default.createElement("div", { style: { fontSize: "13px", color: "#94A3B8", marginTop: "12px", display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(200px, 1fr))", gap: "16px" } }, /* @__PURE__ */ import_react2.default.createElement("div", null, /* @__PURE__ */ import_react2.default.createElement("strong", null, "Vendor:"), " ", r.vendor, " (", r.model, ")"), /* @__PURE__ */ import_react2.default.createElement("div", null, /* @__PURE__ */ import_react2.default.createElement("strong", null, "Address:"), " ", r.ip, ":", r.port), /* @__PURE__ */ import_react2.default.createElement("div", null, /* @__PURE__ */ import_react2.default.createElement("strong", null, "Protocol:"), " ", r.protocol), /* @__PURE__ */ import_react2.default.createElement("div", null, /* @__PURE__ */ import_react2.default.createElement("strong", null, "Antenna Ports:"), " ", r.antennas.size, " ports")), /* @__PURE__ */ import_react2.default.createElement("div", { style: { display: "flex", gap: "8px", marginTop: "16px" } }, r.status === "ONLINE" ? /* @__PURE__ */ import_react2.default.createElement(
473
+ "button",
474
+ {
475
+ onClick: () => {
476
+ simulator.stopReader(r.id);
477
+ triggerUpdate();
478
+ },
479
+ style: { backgroundColor: "#EF4444", color: "#FFF", border: "none", padding: "6px 14px", borderRadius: "6px", cursor: "pointer", fontSize: "13px", fontWeight: "bold" }
480
+ },
481
+ "\u23F9 Stop Simulation"
482
+ ) : /* @__PURE__ */ import_react2.default.createElement(
483
+ "button",
484
+ {
485
+ onClick: () => {
486
+ simulator.startReader(r.id);
487
+ triggerUpdate();
488
+ },
489
+ style: { backgroundColor: "#10B981", color: "#FFF", border: "none", padding: "6px 14px", borderRadius: "6px", cursor: "pointer", fontSize: "13px", fontWeight: "bold" }
490
+ },
491
+ "\u25B6 Start Simulation"
492
+ ), /* @__PURE__ */ import_react2.default.createElement(
493
+ "button",
494
+ {
495
+ onClick: () => {
496
+ simulator.removeReader(r.id);
497
+ triggerUpdate();
498
+ },
499
+ style: { backgroundColor: "#334155", color: "#FFF", border: "none", padding: "6px 14px", borderRadius: "6px", cursor: "pointer", fontSize: "13px", fontWeight: "bold" }
500
+ },
501
+ "\u{1F5D1} Delete Reader"
502
+ )), isExpanded && /* @__PURE__ */ import_react2.default.createElement("div", { style: { marginTop: "20px", borderTop: "1px solid #334155", paddingTop: "20px" } }, /* @__PURE__ */ import_react2.default.createElement("div", { style: { marginBottom: "24px" } }, /* @__PURE__ */ import_react2.default.createElement("h4", { style: { margin: "0 0 16px", fontSize: "15px", color: "#00E5EE" } }, "\u2699\uFE0F Simulation & Filtering Settings"), /* @__PURE__ */ import_react2.default.createElement("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(220px, 1fr))", gap: "16px", backgroundColor: "#0F172A", padding: "16px", borderRadius: "8px", border: "1px solid #1E293B" } }, /* @__PURE__ */ import_react2.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: "4px" } }, /* @__PURE__ */ import_react2.default.createElement("span", { style: { fontSize: "13px", color: "#D1D5DB" } }, "Read Mode"), /* @__PURE__ */ import_react2.default.createElement(
503
+ "select",
504
+ {
505
+ value: r.readMode,
506
+ onChange: (e) => handleReaderSettingsChange(r, "readMode", e.target.value),
507
+ style: { padding: "6px", borderRadius: "6px", backgroundColor: "#1E293B", color: "#FFF", border: "1px solid #334155" }
508
+ },
509
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: "continuous" }, "Continuous Read"),
510
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: "periodic" }, "Periodic Read")
511
+ )), r.readMode === "periodic" && /* @__PURE__ */ import_react2.default.createElement("div", { style: { display: "flex", gap: "8px", alignItems: "flex-end" } }, /* @__PURE__ */ import_react2.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: "4px", flex: 1 } }, /* @__PURE__ */ import_react2.default.createElement("span", { style: { fontSize: "13px", color: "#D1D5DB" } }, "Interval"), /* @__PURE__ */ import_react2.default.createElement(
512
+ "input",
513
+ {
514
+ type: "number",
515
+ min: 1,
516
+ value: r.readIntervalValue ?? 1,
517
+ onChange: (e) => handleReaderSettingsChange(r, "readIntervalValue", parseInt(e.target.value, 10)),
518
+ style: { padding: "6px", borderRadius: "6px", backgroundColor: "#1E293B", color: "#FFF", border: "1px solid #334155", width: "100%" }
519
+ }
520
+ )), /* @__PURE__ */ import_react2.default.createElement(
521
+ "select",
522
+ {
523
+ value: r.readIntervalUnit,
524
+ onChange: (e) => handleReaderSettingsChange(r, "readIntervalUnit", e.target.value),
525
+ style: { padding: "6px", borderRadius: "6px", backgroundColor: "#1E293B", color: "#FFF", border: "1px solid #334155", height: "33px" }
526
+ },
527
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: "seconds" }, "Seconds"),
528
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: "minutes" }, "Minutes"),
529
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: "hours" }, "Hours")
530
+ )), /* @__PURE__ */ import_react2.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: "4px" } }, /* @__PURE__ */ import_react2.default.createElement("span", { style: { fontSize: "13px", color: "#D1D5DB" } }, "Read Rate Limit"), /* @__PURE__ */ import_react2.default.createElement(
531
+ "select",
532
+ {
533
+ value: r.readRate ?? 0,
534
+ onChange: (e) => handleReaderSettingsChange(r, "readRate", parseInt(e.target.value, 10)),
535
+ style: { padding: "6px", borderRadius: "6px", backgroundColor: "#1E293B", color: "#FFF", border: "1px solid #334155" }
536
+ },
537
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: 0 }, "Unlimited"),
538
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: 1 }, "1 tag/sec"),
539
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: 10 }, "10 tags/sec"),
540
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: 100 }, "100 tags/sec"),
541
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: 1e3 }, "1000 tags/sec")
542
+ )), /* @__PURE__ */ import_react2.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: "4px" } }, /* @__PURE__ */ import_react2.default.createElement("span", { style: { fontSize: "13px", color: "#D1D5DB" } }, "EPC Filter Prefix"), /* @__PURE__ */ import_react2.default.createElement(
543
+ "input",
544
+ {
545
+ type: "text",
546
+ placeholder: "e.g. E200",
547
+ value: r.epcFilterPrefix ?? "",
548
+ onChange: (e) => handleReaderSettingsChange(r, "epcFilterPrefix", e.target.value),
549
+ style: { padding: "6px", borderRadius: "6px", backgroundColor: "#1E293B", color: "#FFF", border: "1px solid #334155" }
550
+ }
551
+ )), /* @__PURE__ */ import_react2.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: "4px" } }, /* @__PURE__ */ import_react2.default.createElement("span", { style: { fontSize: "13px", color: "#D1D5DB" } }, "EPC Range Start"), /* @__PURE__ */ import_react2.default.createElement(
552
+ "input",
553
+ {
554
+ type: "text",
555
+ placeholder: "e.g. E20000000001",
556
+ value: r.epcFilterStart ?? "",
557
+ onChange: (e) => handleReaderSettingsChange(r, "epcFilterStart", e.target.value),
558
+ style: { padding: "6px", borderRadius: "6px", backgroundColor: "#1E293B", color: "#FFF", border: "1px solid #334155" }
559
+ }
560
+ )), /* @__PURE__ */ import_react2.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: "4px" } }, /* @__PURE__ */ import_react2.default.createElement("span", { style: { fontSize: "13px", color: "#D1D5DB" } }, "EPC Range End"), /* @__PURE__ */ import_react2.default.createElement(
561
+ "input",
562
+ {
563
+ type: "text",
564
+ placeholder: "e.g. E20000000010",
565
+ value: r.epcFilterEnd ?? "",
566
+ onChange: (e) => handleReaderSettingsChange(r, "epcFilterEnd", e.target.value),
567
+ style: { padding: "6px", borderRadius: "6px", backgroundColor: "#1E293B", color: "#FFF", border: "1px solid #334155" }
568
+ }
569
+ )))), /* @__PURE__ */ import_react2.default.createElement("div", null, /* @__PURE__ */ import_react2.default.createElement("h4", { style: { margin: "0 0 16px", fontSize: "15px", color: "#00E5EE" } }, "\u{1F4E1} Antenna Configuration"), /* @__PURE__ */ import_react2.default.createElement("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(240px, 1fr))", gap: "16px" } }, Array.from(r.antennas.values()).map((ant) => /* @__PURE__ */ import_react2.default.createElement("div", { key: ant.id, style: { backgroundColor: "#0F172A", padding: "16px", borderRadius: "8px", border: ant.enabled ? "1px solid #00E5EE" : "1px solid #1E293B", display: "flex", flexDirection: "column", gap: "12px" } }, /* @__PURE__ */ import_react2.default.createElement("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" } }, /* @__PURE__ */ import_react2.default.createElement("span", { style: { fontWeight: "bold", fontSize: "14px", color: ant.enabled ? "#FFF" : "#64748B" } }, "Port ", ant.index), /* @__PURE__ */ import_react2.default.createElement("label", { style: { display: "flex", alignItems: "center", gap: "6px", cursor: "pointer", fontSize: "12px" } }, /* @__PURE__ */ import_react2.default.createElement(
570
+ "input",
571
+ {
572
+ type: "checkbox",
573
+ checked: ant.enabled,
574
+ onChange: () => handleToggleAntenna(r, ant.index),
575
+ style: { accentColor: "#00E5EE" }
576
+ }
577
+ ), /* @__PURE__ */ import_react2.default.createElement("span", { style: { color: ant.enabled ? "#00E5EE" : "#64748B" } }, ant.enabled ? "Enabled" : "Disabled"))), ant.enabled ? /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement(
578
+ import_ui.AntennaPowerSlider,
579
+ {
580
+ power: ant.power,
581
+ onChange: (val) => handleAntennaPowerChange(r, ant.index, val),
582
+ min: 0,
583
+ max: 33
584
+ }
585
+ ), /* @__PURE__ */ import_react2.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: "4px" } }, /* @__PURE__ */ import_react2.default.createElement("div", { style: { display: "flex", justifyContent: "space-between", fontSize: "13px", color: "#D1D5DB" } }, /* @__PURE__ */ import_react2.default.createElement("span", null, "Antenna Gain"), /* @__PURE__ */ import_react2.default.createElement("span", { style: { fontWeight: "bold", color: "#00E5EE" } }, ant.gain.toFixed(1), " dBi")), /* @__PURE__ */ import_react2.default.createElement(
586
+ "input",
587
+ {
588
+ type: "range",
589
+ min: 0,
590
+ max: 15,
591
+ step: 0.5,
592
+ value: ant.gain,
593
+ onChange: (e) => handleAntennaGainChange(r, ant.index, parseFloat(e.target.value)),
594
+ style: { accentColor: "#00E5EE", cursor: "pointer" }
595
+ }
596
+ )), /* @__PURE__ */ import_react2.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: "4px" } }, /* @__PURE__ */ import_react2.default.createElement("div", { style: { display: "flex", justifyContent: "space-between", fontSize: "13px", color: "#D1D5DB" } }, /* @__PURE__ */ import_react2.default.createElement("span", null, "RSSI Offset"), /* @__PURE__ */ import_react2.default.createElement("span", { style: { fontWeight: "bold", color: "#00E5EE" } }, ant.rssiOffset >= 0 ? "+" : "", ant.rssiOffset.toFixed(1), " dB")), /* @__PURE__ */ import_react2.default.createElement(
597
+ "input",
598
+ {
599
+ type: "range",
600
+ min: -20,
601
+ max: 20,
602
+ step: 0.5,
603
+ value: ant.rssiOffset,
604
+ onChange: (e) => handleAntennaRssiChange(r, ant.index, parseFloat(e.target.value)),
605
+ style: { accentColor: "#00E5EE", cursor: "pointer" }
606
+ }
607
+ )), /* @__PURE__ */ import_react2.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: "4px" } }, /* @__PURE__ */ import_react2.default.createElement("span", { style: { fontSize: "13px", color: "#D1D5DB" } }, "Coverage Read Zone"), /* @__PURE__ */ import_react2.default.createElement(
608
+ "input",
609
+ {
610
+ type: "text",
611
+ placeholder: "Zone identifier...",
612
+ value: ant.readZone,
613
+ onChange: (e) => handleAntennaZoneChange(r, ant.index, e.target.value),
614
+ style: { padding: "6px 10px", fontSize: "13px", borderRadius: "6px", border: "1px solid #334155", backgroundColor: "#1E293B", color: "#FFF" }
615
+ }
616
+ ))) : /* @__PURE__ */ import_react2.default.createElement("div", { style: { display: "flex", alignItems: "center", justifyContent: "center", height: "140px", color: "#64748B", fontSize: "13px", border: "1px dashed #334155", borderRadius: "6px" } }, "Antenna port disabled")))))));
617
+ })));
618
+ };
619
+
620
+ // src/views/TagsView.tsx
621
+ var import_react3 = __toESM(require("react"));
622
+ var import_tags = require("@openrfid/tags");
623
+ var TagsView = ({ simulator, refresh }) => {
624
+ const tags = simulator.getAllTags();
625
+ const [batchCount, setBatchCount] = (0, import_react3.useState)(10);
626
+ const [genType, setGenType] = (0, import_react3.useState)("sequential");
627
+ const handleGenerate = () => {
628
+ const batch = import_tags.TagGenerator.generateBatch({ count: batchCount, type: genType });
629
+ simulator.addTagBatch(batch);
630
+ refresh();
631
+ };
632
+ const handleExportCSV = () => {
633
+ const csv = import_tags.TagSerializer.toCSV(tags);
634
+ const blob = new Blob([csv], { type: "text/csv" });
635
+ const url = URL.createObjectURL(blob);
636
+ const a = document.createElement("a");
637
+ a.href = url;
638
+ a.download = "tags_export.csv";
639
+ a.click();
640
+ URL.revokeObjectURL(url);
641
+ };
642
+ return /* @__PURE__ */ import_react3.default.createElement("div", { style: { padding: "24px" } }, /* @__PURE__ */ import_react3.default.createElement("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "20px" } }, /* @__PURE__ */ import_react3.default.createElement("h2", { style: { fontSize: "20px", fontWeight: "bold", margin: 0 } }, "Simulated RFID Tags (", tags.length, ")"), /* @__PURE__ */ import_react3.default.createElement("div", { style: { display: "flex", gap: "8px" } }, /* @__PURE__ */ import_react3.default.createElement(
643
+ "select",
644
+ {
645
+ value: genType,
646
+ onChange: (e) => setGenType(e.target.value),
647
+ style: { padding: "8px", borderRadius: "6px", backgroundColor: "#0F172A", color: "#FFF", border: "1px solid #334155" }
648
+ },
649
+ /* @__PURE__ */ import_react3.default.createElement("option", { value: "sequential" }, "Sequential EPC"),
650
+ /* @__PURE__ */ import_react3.default.createElement("option", { value: "random" }, "Random Hex"),
651
+ /* @__PURE__ */ import_react3.default.createElement("option", { value: "sgtin96" }, "SGTIN-96 GS1")
652
+ ), /* @__PURE__ */ import_react3.default.createElement(
653
+ "input",
654
+ {
655
+ type: "number",
656
+ value: batchCount,
657
+ onChange: (e) => setBatchCount(parseInt(e.target.value, 10)),
658
+ style: { width: "60px", padding: "8px", borderRadius: "6px", backgroundColor: "#0F172A", color: "#FFF", border: "1px solid #334155" }
659
+ }
660
+ ), /* @__PURE__ */ import_react3.default.createElement(
661
+ "button",
662
+ {
663
+ onClick: handleGenerate,
664
+ style: { backgroundColor: "#00E5EE", color: "#0F172A", border: "none", padding: "8px 16px", borderRadius: "6px", cursor: "pointer", fontWeight: "bold" }
665
+ },
666
+ "Generate Tags"
667
+ ), /* @__PURE__ */ import_react3.default.createElement(
668
+ "button",
669
+ {
670
+ onClick: handleExportCSV,
671
+ style: { backgroundColor: "#334155", color: "#FFF", border: "none", padding: "8px 16px", borderRadius: "6px", cursor: "pointer", fontWeight: "bold" }
672
+ },
673
+ "Export CSV"
674
+ ))), /* @__PURE__ */ import_react3.default.createElement("div", { style: { backgroundColor: "#1E293B", borderRadius: "8px", border: "1px solid #334155", overflow: "hidden" } }, /* @__PURE__ */ import_react3.default.createElement("table", { style: { width: "100%", borderCollapse: "collapse", textAlign: "left", fontSize: "13px" } }, /* @__PURE__ */ import_react3.default.createElement("thead", null, /* @__PURE__ */ import_react3.default.createElement("tr", { style: { backgroundColor: "#0F172A", borderBottom: "1px solid #334155", color: "#94A3B8" } }, /* @__PURE__ */ import_react3.default.createElement("th", { style: { padding: "12px 16px" } }, "EPC Header"), /* @__PURE__ */ import_react3.default.createElement("th", { style: { padding: "12px 16px" } }, "TID"), /* @__PURE__ */ import_react3.default.createElement("th", { style: { padding: "12px 16px" } }, "Protocol"), /* @__PURE__ */ import_react3.default.createElement("th", { style: { padding: "12px 16px" } }, "Read Count"), /* @__PURE__ */ import_react3.default.createElement("th", { style: { padding: "12px 16px" } }, "Current RSSI"), /* @__PURE__ */ import_react3.default.createElement("th", { style: { padding: "12px 16px" } }, "Action"))), /* @__PURE__ */ import_react3.default.createElement("tbody", null, tags.slice(0, 100).map((t) => /* @__PURE__ */ import_react3.default.createElement("tr", { key: t.id, style: { borderBottom: "1px solid #334155" } }, /* @__PURE__ */ import_react3.default.createElement("td", { style: { padding: "12px 16px", fontFamily: "monospace", fontWeight: "bold", color: "#00E5EE" } }, t.epc), /* @__PURE__ */ import_react3.default.createElement("td", { style: { padding: "12px 16px", fontFamily: "monospace", color: "#94A3B8" } }, t.tid), /* @__PURE__ */ import_react3.default.createElement("td", { style: { padding: "12px 16px" } }, t.protocol), /* @__PURE__ */ import_react3.default.createElement("td", { style: { padding: "12px 16px" } }, t.readCount), /* @__PURE__ */ import_react3.default.createElement("td", { style: { padding: "12px 16px", color: "#10B981" } }, t.currentRssi, " dBm"), /* @__PURE__ */ import_react3.default.createElement("td", { style: { padding: "12px 16px" } }, /* @__PURE__ */ import_react3.default.createElement(
675
+ "button",
676
+ {
677
+ onClick: () => {
678
+ simulator.removeTag(t.epc);
679
+ refresh();
680
+ },
681
+ style: { backgroundColor: "#EF4444", color: "#FFF", border: "none", padding: "4px 8px", borderRadius: "4px", cursor: "pointer" }
682
+ },
683
+ "Remove"
684
+ ))))))));
685
+ };
686
+
687
+ // src/views/EventMonitorView.tsx
688
+ var import_react4 = __toESM(require("react"));
689
+ var import_ui2 = require("@openrfid/ui");
690
+ var EventMonitorView = ({ eventBus, storage, onViewHistory }) => {
691
+ const [events, setEvents] = (0, import_react4.useState)([]);
692
+ const [paused, setPaused] = (0, import_react4.useState)(false);
693
+ const [totalStored, setTotalStored] = (0, import_react4.useState)(0);
694
+ (0, import_react4.useEffect)(() => {
695
+ if (!storage) return;
696
+ const loadCount = async () => {
697
+ const stats = await storage.getEventStats();
698
+ setTotalStored(stats.totalEvents);
699
+ };
700
+ loadCount();
701
+ const interval = setInterval(loadCount, 2e3);
702
+ return () => clearInterval(interval);
703
+ }, [storage]);
704
+ (0, import_react4.useEffect)(() => {
705
+ const unsub = eventBus.on("TagDetected", (payload) => {
706
+ if (paused) return;
707
+ const item = {
708
+ id: `ev_${Date.now()}_${Math.random()}`,
709
+ timestamp: (/* @__PURE__ */ new Date()).toLocaleTimeString(),
710
+ readerId: payload.readerId,
711
+ antennaId: payload.antennaId,
712
+ epc: payload.epc,
713
+ rssi: payload.rssi,
714
+ protocol: payload.protocol || "GEN2"
715
+ };
716
+ setEvents((prev) => [item, ...prev.slice(0, 199)]);
717
+ });
718
+ return () => unsub();
719
+ }, [eventBus, paused]);
720
+ return /* @__PURE__ */ import_react4.default.createElement("div", { style: { padding: "24px" } }, /* @__PURE__ */ import_react4.default.createElement("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "20px", flexWrap: "wrap", gap: "12px" } }, /* @__PURE__ */ import_react4.default.createElement("div", null, /* @__PURE__ */ import_react4.default.createElement("h2", { style: { fontSize: "20px", fontWeight: "bold", margin: 0 } }, "Real-Time Event Stream (", events.length, ")"), storage && /* @__PURE__ */ import_react4.default.createElement("div", { style: { fontSize: "13px", color: "#94A3B8", marginTop: "4px" } }, "Total persisted events: ", /* @__PURE__ */ import_react4.default.createElement("strong", { style: { color: "#00E5EE" } }, totalStored.toLocaleString()), " in database")), /* @__PURE__ */ import_react4.default.createElement("div", { style: { display: "flex", gap: "8px" } }, onViewHistory && /* @__PURE__ */ import_react4.default.createElement(
721
+ "button",
722
+ {
723
+ onClick: onViewHistory,
724
+ style: { backgroundColor: "#7C3AED", color: "#FFF", border: "none", padding: "8px 16px", borderRadius: "6px", cursor: "pointer", fontWeight: "bold" }
725
+ },
726
+ "\u{1F4CA} View Storage History"
727
+ ), /* @__PURE__ */ import_react4.default.createElement(
728
+ "button",
729
+ {
730
+ onClick: () => setPaused(!paused),
731
+ style: { backgroundColor: paused ? "#10B981" : "#F59E0B", color: "#FFF", border: "none", padding: "8px 16px", borderRadius: "6px", cursor: "pointer", fontWeight: "bold" }
732
+ },
733
+ paused ? "Resume Stream" : "Pause Stream"
734
+ ), /* @__PURE__ */ import_react4.default.createElement(
735
+ "button",
736
+ {
737
+ onClick: () => setEvents([]),
738
+ style: { backgroundColor: "#334155", color: "#FFF", border: "none", padding: "8px 16px", borderRadius: "6px", cursor: "pointer", fontWeight: "bold" }
739
+ },
740
+ "Clear Stream"
741
+ ))), /* @__PURE__ */ import_react4.default.createElement("div", { style: { backgroundColor: "#1E293B", borderRadius: "8px", border: "1px solid #334155", overflow: "hidden" } }, /* @__PURE__ */ import_react4.default.createElement("table", { style: { width: "100%", borderCollapse: "collapse", textAlign: "left", fontSize: "13px" } }, /* @__PURE__ */ import_react4.default.createElement("thead", null, /* @__PURE__ */ import_react4.default.createElement("tr", { style: { backgroundColor: "#0F172A", borderBottom: "1px solid #334155", color: "#94A3B8" } }, /* @__PURE__ */ import_react4.default.createElement("th", { style: { padding: "12px 16px" } }, "Time"), /* @__PURE__ */ import_react4.default.createElement("th", { style: { padding: "12px 16px" } }, "Reader"), /* @__PURE__ */ import_react4.default.createElement("th", { style: { padding: "12px 16px" } }, "Antenna Port"), /* @__PURE__ */ import_react4.default.createElement("th", { style: { padding: "12px 16px" } }, "Tag EPC"), /* @__PURE__ */ import_react4.default.createElement("th", { style: { padding: "12px 16px" } }, "Signal (RSSI)"), /* @__PURE__ */ import_react4.default.createElement("th", { style: { padding: "12px 16px" } }, "Protocol"))), /* @__PURE__ */ import_react4.default.createElement("tbody", null, events.length === 0 ? /* @__PURE__ */ import_react4.default.createElement("tr", null, /* @__PURE__ */ import_react4.default.createElement("td", { colSpan: 6, style: { padding: "30px", textAlign: "center", color: "#64748B" } }, "No real-time events. Start simulated readers to see tag detections live.")) : events.map((e) => /* @__PURE__ */ import_react4.default.createElement("tr", { key: e.id, style: { borderBottom: "1px solid #334155" } }, /* @__PURE__ */ import_react4.default.createElement("td", { style: { padding: "12px 16px", color: "#94A3B8" } }, e.timestamp), /* @__PURE__ */ import_react4.default.createElement("td", { style: { padding: "12px 16px", fontWeight: "bold" } }, e.readerId), /* @__PURE__ */ import_react4.default.createElement("td", { style: { padding: "12px 16px" } }, "Port #", e.antennaId), /* @__PURE__ */ import_react4.default.createElement("td", { style: { padding: "12px 16px", fontFamily: "monospace", fontWeight: "bold", color: "#00E5EE" } }, e.epc), /* @__PURE__ */ import_react4.default.createElement("td", { style: { padding: "12px 16px" } }, /* @__PURE__ */ import_react4.default.createElement(import_ui2.TagSignalMeter, { rssi: e.rssi })), /* @__PURE__ */ import_react4.default.createElement("td", { style: { padding: "12px 16px" } }, e.protocol)))))));
742
+ };
743
+
744
+ // src/views/EventHistoryView.tsx
745
+ var import_react5 = __toESM(require("react"));
746
+ var PAGE_SIZE = 1e3;
747
+ var card2 = {
748
+ backgroundColor: "#1E293B",
749
+ borderRadius: "10px",
750
+ border: "1px solid #334155",
751
+ padding: "20px"
752
+ };
753
+ var btn = (color) => ({
754
+ backgroundColor: color,
755
+ color: "#fff",
756
+ border: "none",
757
+ padding: "6px 14px",
758
+ borderRadius: "6px",
759
+ cursor: "pointer",
760
+ fontWeight: "bold",
761
+ fontSize: "13px"
762
+ });
763
+ var EventHistoryView = ({ storage }) => {
764
+ const [stats, setStats] = (0, import_react5.useState)(null);
765
+ const [expandedDate, setExpandedDate] = (0, import_react5.useState)(null);
766
+ const [events, setEvents] = (0, import_react5.useState)([]);
767
+ const [page, setPage] = (0, import_react5.useState)(0);
768
+ const [totalCount, setTotalCount] = (0, import_react5.useState)(0);
769
+ const [filterEpc, setFilterEpc] = (0, import_react5.useState)("");
770
+ const [filterReader, setFilterReader] = (0, import_react5.useState)("");
771
+ const [filterProtocol, setFilterProtocol] = (0, import_react5.useState)("");
772
+ const [confirmClear, setConfirmClear] = (0, import_react5.useState)(null);
773
+ const [loading, setLoading] = (0, import_react5.useState)(false);
774
+ const loadStats = (0, import_react5.useCallback)(async () => {
775
+ const s = await storage.getEventStats();
776
+ setStats(s);
777
+ }, [storage]);
778
+ (0, import_react5.useEffect)(() => {
779
+ loadStats();
780
+ const interval = setInterval(loadStats, 5e3);
781
+ return () => clearInterval(interval);
782
+ }, [loadStats]);
783
+ const loadDayEvents = (0, import_react5.useCallback)(async (date, pageNum = 0) => {
784
+ setLoading(true);
785
+ const [evts, count] = await Promise.all([
786
+ storage.getEvents({
787
+ date,
788
+ epc: filterEpc || void 0,
789
+ readerId: filterReader || void 0,
790
+ protocol: filterProtocol || void 0,
791
+ limit: PAGE_SIZE,
792
+ offset: pageNum * PAGE_SIZE
793
+ }),
794
+ storage.getEventCount({
795
+ date,
796
+ epc: filterEpc || void 0,
797
+ readerId: filterReader || void 0,
798
+ protocol: filterProtocol || void 0
799
+ })
800
+ ]);
801
+ setEvents(evts);
802
+ setTotalCount(count);
803
+ setPage(pageNum);
804
+ setLoading(false);
805
+ }, [storage, filterEpc, filterReader, filterProtocol]);
806
+ const handleExpandDate = (date) => {
807
+ if (expandedDate === date) {
808
+ setExpandedDate(null);
809
+ setEvents([]);
810
+ } else {
811
+ setExpandedDate(date);
812
+ loadDayEvents(date, 0);
813
+ }
814
+ };
815
+ const handleDeleteDay = async (date) => {
816
+ if (!window.confirm(`Delete ALL events for ${date}? This cannot be undone.`)) return;
817
+ await storage.deleteEventsByDate(date);
818
+ if (expandedDate === date) {
819
+ setExpandedDate(null);
820
+ setEvents([]);
821
+ }
822
+ await loadStats();
823
+ };
824
+ const handleExportCSV = async (date) => {
825
+ const evts = await storage.getEvents({ date, limit: 1e5 });
826
+ const csv = [
827
+ "id,date,timestamp,readerId,antennaId,epc,rssi,protocol",
828
+ ...evts.map((e) => `${e.id},${e.date},${e.timestamp},${e.readerId},${e.antennaId},${e.epc},${e.rssi},${e.protocol}`)
829
+ ].join("\n");
830
+ const blob = new Blob([csv], { type: "text/csv" });
831
+ const url = URL.createObjectURL(blob);
832
+ const a = document.createElement("a");
833
+ a.href = url;
834
+ a.download = `openrfid-events-${date}.csv`;
835
+ a.click();
836
+ URL.revokeObjectURL(url);
837
+ };
838
+ const handleClearEvents = async () => {
839
+ await storage.clearAllEvents();
840
+ setConfirmClear(null);
841
+ setExpandedDate(null);
842
+ setEvents([]);
843
+ await loadStats();
844
+ };
845
+ const handleClearAll = async () => {
846
+ await storage.clearAllData();
847
+ setConfirmClear(null);
848
+ setExpandedDate(null);
849
+ setEvents([]);
850
+ await loadStats();
851
+ };
852
+ const totalPages = Math.ceil(totalCount / PAGE_SIZE);
853
+ return /* @__PURE__ */ import_react5.default.createElement("div", { style: { padding: "24px" } }, /* @__PURE__ */ import_react5.default.createElement("h2", { style: { fontSize: "20px", fontWeight: "bold", marginBottom: "20px" } }, "\u{1F4CA} Event History & Storage Manager"), stats && /* @__PURE__ */ import_react5.default.createElement("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(160px, 1fr))", gap: "12px", marginBottom: "20px" } }, [
854
+ { label: "Total Events", value: stats.totalEvents.toLocaleString(), color: "#00E5EE" },
855
+ { label: "Days with Data", value: String(stats.totalDays), color: "#10B981" },
856
+ { label: "Oldest Day", value: stats.oldestEventDate ?? "\u2014", color: "#94A3B8" },
857
+ { label: "Newest Day", value: stats.newestEventDate ?? "\u2014", color: "#94A3B8" }
858
+ ].map((stat) => /* @__PURE__ */ import_react5.default.createElement("div", { key: stat.label, style: card2 }, /* @__PURE__ */ import_react5.default.createElement("div", { style: { fontSize: "12px", color: "#94A3B8" } }, stat.label), /* @__PURE__ */ import_react5.default.createElement("div", { style: { fontSize: "22px", fontWeight: "bold", color: stat.color, marginTop: "4px" } }, stat.value)))), /* @__PURE__ */ import_react5.default.createElement("div", { style: { ...card2, display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "16px" } }, /* @__PURE__ */ import_react5.default.createElement("span", { style: { fontWeight: "bold", fontSize: "15px" } }, "Day-by-Day Event Log"), /* @__PURE__ */ import_react5.default.createElement("div", { style: { display: "flex", gap: "10px" } }, /* @__PURE__ */ import_react5.default.createElement("button", { style: btn("#EF4444"), onClick: () => setConfirmClear("events") }, "\u{1F5D1} Clear All Events"), /* @__PURE__ */ import_react5.default.createElement("button", { style: btn("#7C3AED"), onClick: () => setConfirmClear("all") }, "\u2622\uFE0F Clear All Data"))), confirmClear && /* @__PURE__ */ import_react5.default.createElement("div", { style: { ...card2, border: "1px solid #EF4444", marginBottom: "16px", backgroundColor: "#1F0A0A" } }, /* @__PURE__ */ import_react5.default.createElement("p", { style: { color: "#FCA5A5", marginBottom: "12px" } }, confirmClear === "events" ? "\u26A0\uFE0F This will permanently delete ALL event history. Readers and tags are kept." : "\u2622\uFE0F This will permanently delete ALL readers, tags, and event history. Cannot be undone."), /* @__PURE__ */ import_react5.default.createElement("div", { style: { display: "flex", gap: "10px" } }, /* @__PURE__ */ import_react5.default.createElement("button", { style: btn("#EF4444"), onClick: confirmClear === "events" ? handleClearEvents : handleClearAll }, "Confirm Delete"), /* @__PURE__ */ import_react5.default.createElement("button", { style: { ...btn("#334155") }, onClick: () => setConfirmClear(null) }, "Cancel"))), stats?.eventsByDate.length === 0 && /* @__PURE__ */ import_react5.default.createElement("div", { style: { ...card2, textAlign: "center", color: "#64748B", padding: "40px" } }, "No event history yet. Start a reader and let it run to generate events."), stats?.eventsByDate.map(({ date, count }) => /* @__PURE__ */ import_react5.default.createElement("div", { key: date, style: { ...card2, marginBottom: "10px" } }, /* @__PURE__ */ import_react5.default.createElement("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" } }, /* @__PURE__ */ import_react5.default.createElement("div", { style: { display: "flex", alignItems: "center", gap: "12px" } }, /* @__PURE__ */ import_react5.default.createElement("span", { style: { fontSize: "18px", cursor: "pointer" }, onClick: () => handleExpandDate(date) }, expandedDate === date ? "\u25BC" : "\u25B6"), /* @__PURE__ */ import_react5.default.createElement("div", null, /* @__PURE__ */ import_react5.default.createElement("span", { style: { fontWeight: "bold", fontSize: "15px", cursor: "pointer" }, onClick: () => handleExpandDate(date) }, date), /* @__PURE__ */ import_react5.default.createElement("span", { style: { fontSize: "13px", color: "#94A3B8", marginLeft: "10px" } }, count.toLocaleString(), " events"))), /* @__PURE__ */ import_react5.default.createElement("div", { style: { display: "flex", gap: "8px" } }, /* @__PURE__ */ import_react5.default.createElement("button", { style: btn("#0EA5E9"), onClick: () => handleExpandDate(date) }, expandedDate === date ? "Collapse" : "View"), /* @__PURE__ */ import_react5.default.createElement("button", { style: btn("#10B981"), onClick: () => handleExportCSV(date) }, "Export CSV"), /* @__PURE__ */ import_react5.default.createElement("button", { style: btn("#EF4444"), onClick: () => handleDeleteDay(date) }, "Delete"))), expandedDate === date && /* @__PURE__ */ import_react5.default.createElement("div", { style: { marginTop: "16px" } }, /* @__PURE__ */ import_react5.default.createElement("div", { style: { display: "flex", gap: "10px", marginBottom: "12px", flexWrap: "wrap" } }, /* @__PURE__ */ import_react5.default.createElement(
859
+ "input",
860
+ {
861
+ placeholder: "Filter by EPC...",
862
+ value: filterEpc,
863
+ onChange: (e) => setFilterEpc(e.target.value),
864
+ style: { backgroundColor: "#0F172A", border: "1px solid #334155", color: "#F8FAFC", padding: "6px 10px", borderRadius: "6px", fontSize: "13px", width: "180px" }
865
+ }
866
+ ), /* @__PURE__ */ import_react5.default.createElement(
867
+ "input",
868
+ {
869
+ placeholder: "Filter by Reader...",
870
+ value: filterReader,
871
+ onChange: (e) => setFilterReader(e.target.value),
872
+ style: { backgroundColor: "#0F172A", border: "1px solid #334155", color: "#F8FAFC", padding: "6px 10px", borderRadius: "6px", fontSize: "13px", width: "180px" }
873
+ }
874
+ ), /* @__PURE__ */ import_react5.default.createElement(
875
+ "select",
876
+ {
877
+ value: filterProtocol,
878
+ onChange: (e) => setFilterProtocol(e.target.value),
879
+ style: { backgroundColor: "#0F172A", border: "1px solid #334155", color: "#F8FAFC", padding: "6px 10px", borderRadius: "6px", fontSize: "13px" }
880
+ },
881
+ /* @__PURE__ */ import_react5.default.createElement("option", { value: "" }, "All Protocols"),
882
+ /* @__PURE__ */ import_react5.default.createElement("option", { value: "GEN2" }, "GEN2"),
883
+ /* @__PURE__ */ import_react5.default.createElement("option", { value: "ISO15693" }, "ISO15693"),
884
+ /* @__PURE__ */ import_react5.default.createElement("option", { value: "ISO14443A" }, "ISO14443A")
885
+ ), /* @__PURE__ */ import_react5.default.createElement("button", { style: btn("#00E5EE"), onClick: () => loadDayEvents(date, 0) }, "Search")), /* @__PURE__ */ import_react5.default.createElement("div", { style: { overflowX: "auto" } }, /* @__PURE__ */ import_react5.default.createElement("table", { style: { width: "100%", borderCollapse: "collapse", fontSize: "13px" } }, /* @__PURE__ */ import_react5.default.createElement("thead", null, /* @__PURE__ */ import_react5.default.createElement("tr", { style: { backgroundColor: "#0F172A", color: "#94A3B8" } }, ["Time", "Reader", "Ant", "EPC", "RSSI", "Protocol"].map((h) => /* @__PURE__ */ import_react5.default.createElement("th", { key: h, style: { padding: "8px 12px", textAlign: "left", borderBottom: "1px solid #334155" } }, h)))), /* @__PURE__ */ import_react5.default.createElement("tbody", null, loading ? /* @__PURE__ */ import_react5.default.createElement("tr", null, /* @__PURE__ */ import_react5.default.createElement("td", { colSpan: 6, style: { padding: "20px", textAlign: "center", color: "#64748B" } }, "Loading...")) : events.length === 0 ? /* @__PURE__ */ import_react5.default.createElement("tr", null, /* @__PURE__ */ import_react5.default.createElement("td", { colSpan: 6, style: { padding: "20px", textAlign: "center", color: "#64748B" } }, "No events match the current filters.")) : events.map((e) => /* @__PURE__ */ import_react5.default.createElement("tr", { key: e.id, style: { borderBottom: "1px solid #1E293B" } }, /* @__PURE__ */ import_react5.default.createElement("td", { style: { padding: "7px 12px", color: "#94A3B8" } }, e.timestamp.split("T")[1]?.split(".")[0] ?? e.timestamp), /* @__PURE__ */ import_react5.default.createElement("td", { style: { padding: "7px 12px" } }, e.readerId), /* @__PURE__ */ import_react5.default.createElement("td", { style: { padding: "7px 12px", color: "#94A3B8" } }, e.antennaId), /* @__PURE__ */ import_react5.default.createElement("td", { style: { padding: "7px 12px", fontFamily: "monospace", color: "#00E5EE" } }, e.epc), /* @__PURE__ */ import_react5.default.createElement("td", { style: { padding: "7px 12px", color: e.rssi > -60 ? "#10B981" : "#F59E0B" } }, e.rssi, " dBm"), /* @__PURE__ */ import_react5.default.createElement("td", { style: { padding: "7px 12px", color: "#94A3B8" } }, e.protocol)))))), totalPages > 1 && /* @__PURE__ */ import_react5.default.createElement("div", { style: { display: "flex", alignItems: "center", gap: "12px", marginTop: "12px", justifyContent: "center" } }, /* @__PURE__ */ import_react5.default.createElement(
886
+ "button",
887
+ {
888
+ style: btn(page > 0 ? "#334155" : "#1E293B"),
889
+ disabled: page === 0,
890
+ onClick: () => loadDayEvents(date, page - 1)
891
+ },
892
+ "\u2190 Prev"
893
+ ), /* @__PURE__ */ import_react5.default.createElement("span", { style: { color: "#94A3B8", fontSize: "13px" } }, "Page ", page + 1, " of ", totalPages, " (", totalCount.toLocaleString(), " events)"), /* @__PURE__ */ import_react5.default.createElement(
894
+ "button",
895
+ {
896
+ style: btn(page < totalPages - 1 ? "#334155" : "#1E293B"),
897
+ disabled: page >= totalPages - 1,
898
+ onClick: () => loadDayEvents(date, page + 1)
899
+ },
900
+ "Next \u2192"
901
+ ))))));
902
+ };
903
+
904
+ // src/views/ProtocolsPluginsView.tsx
905
+ var import_react6 = __toESM(require("react"));
906
+ var ProtocolsPluginsView = ({ pluginManager }) => {
907
+ const plugins = pluginManager.getAllPlugins();
908
+ return /* @__PURE__ */ import_react6.default.createElement("div", { style: { padding: "24px" } }, /* @__PURE__ */ import_react6.default.createElement("h2", { style: { fontSize: "20px", fontWeight: "bold", marginBottom: "20px" } }, "Protocol Servers & Installed Plugins"), /* @__PURE__ */ import_react6.default.createElement("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))", gap: "16px" } }, plugins.map((p) => /* @__PURE__ */ import_react6.default.createElement("div", { key: p.metadata.name, style: { backgroundColor: "#1E293B", padding: "20px", borderRadius: "10px", border: "1px solid #334155" } }, /* @__PURE__ */ import_react6.default.createElement("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" } }, /* @__PURE__ */ import_react6.default.createElement("h3", { style: { margin: 0, fontSize: "16px", color: "#00E5EE" } }, p.metadata.name), /* @__PURE__ */ import_react6.default.createElement(
909
+ "span",
910
+ {
911
+ style: {
912
+ backgroundColor: p.state === "STARTED" ? "#10B981" : "#64748B",
913
+ color: "#FFF",
914
+ fontSize: "11px",
915
+ padding: "2px 8px",
916
+ borderRadius: "10px",
917
+ fontWeight: "bold"
918
+ }
919
+ },
920
+ p.state
921
+ )), /* @__PURE__ */ import_react6.default.createElement("div", { style: { fontSize: "12px", color: "#94A3B8", marginTop: "8px" } }, "v", p.metadata.version), /* @__PURE__ */ import_react6.default.createElement("p", { style: { fontSize: "13px", color: "#CBD5E1", marginTop: "10px" } }, p.metadata.description)))));
922
+ };
923
+ // Annotate the CommonJS export names for ESM import in node:
924
+ 0 && (module.exports = {
925
+ DashboardView,
926
+ EventHistoryView,
927
+ EventMonitorView,
928
+ ProtocolsPluginsView,
929
+ ReadersView,
930
+ TagsView
931
+ });