@camstack/ui-library 1.2.297 → 1.2.299
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/composites/addon-global-settings-form.d.ts +1 -1
- package/dist/composites/index.d.ts +2 -0
- package/dist/composites/record-entries/entry-json-schema-form.d.ts +11 -0
- package/dist/composites/record-entries/index.d.ts +4 -0
- package/dist/composites/record-entries/record-entries-hub.d.ts +9 -0
- package/dist/composites/record-entries/record-entries-panel.d.ts +6 -0
- package/dist/composites/record-entries/record-entry-secrets.d.ts +9 -0
- package/dist/composites/storage-records/index.d.ts +2 -0
- package/dist/composites/storage-records/storage-records-hub.d.ts +4 -0
- package/dist/composites/storage-records/storage-records-panel.d.ts +6 -0
- package/dist/generated/system-hooks.d.ts +8 -0
- package/dist/index.cjs +808 -19
- package/dist/index.js +793 -21
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -12001,6 +12001,14 @@ var usePtzAutotrackGetSettings = trpc.ptzAutotrack.getSettings.useQuery;
|
|
|
12001
12001
|
var usePtzAutotrackSetSettings = trpc.ptzAutotrack.setSettings.useMutation;
|
|
12002
12002
|
/** Generated alias around `trpc.reboot.reboot.useMutation`. */
|
|
12003
12003
|
var useRebootReboot = trpc.reboot.reboot.useMutation;
|
|
12004
|
+
/** Generated alias around `trpc.recordEntries.listEntryKinds.useQuery`. */
|
|
12005
|
+
var useRecordEntriesListEntryKinds = trpc.recordEntries.listEntryKinds.useQuery;
|
|
12006
|
+
/** Generated alias around `trpc.recordEntries.listEntries.useQuery`. */
|
|
12007
|
+
var useRecordEntriesListEntries = trpc.recordEntries.listEntries.useQuery;
|
|
12008
|
+
/** Generated alias around `trpc.recordEntries.putEntry.useMutation`. */
|
|
12009
|
+
var useRecordEntriesPutEntry = trpc.recordEntries.putEntry.useMutation;
|
|
12010
|
+
/** Generated alias around `trpc.recordEntries.deleteEntry.useMutation`. */
|
|
12011
|
+
var useRecordEntriesDeleteEntry = trpc.recordEntries.deleteEntry.useMutation;
|
|
12004
12012
|
/** Generated alias around `trpc.recording.listSources.useQuery`. */
|
|
12005
12013
|
var useRecordingListSources = trpc.recording.listSources.useQuery;
|
|
12006
12014
|
/** Generated alias around `trpc.recording.getAvailability.useQuery`. */
|
|
@@ -14127,7 +14135,7 @@ function ConfigNotificationTargetMultiSelectFieldView({ field, value, onChange,
|
|
|
14127
14135
|
const kindsQuery = useNotificationOutputListTargetKinds({});
|
|
14128
14136
|
const targets = (0, react$1.useMemo)(() => toTargetOptions(targetsQuery.data), [targetsQuery.data]);
|
|
14129
14137
|
const kindIconUrls = (0, react$1.useMemo)(() => buildKindIconMap(kindsQuery.data), [kindsQuery.data]);
|
|
14130
|
-
const selected = Array.isArray(value) ? value.filter((v) => typeof v === "string") : typeof value === "string" && value.length > 0 ? [value] : [];
|
|
14138
|
+
const selected = Array.isArray(value) ? value.filter((v) => typeof v === "string") : typeof value === "string" && value.length > 0 ? value.includes(",") ? value.split(",").map((id) => id.trim()).filter((id) => id.length > 0) : [value] : [];
|
|
14131
14139
|
const pending = targetsQuery.isPending || kindsQuery.isPending;
|
|
14132
14140
|
const error = targetsQuery.isError ? targetsQuery.error : kindsQuery.isError ? kindsQuery.error : null;
|
|
14133
14141
|
if (pending) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FieldShell, {
|
|
@@ -19490,11 +19498,11 @@ function useDeviceSnapshotImage(deviceId, options = {}) {
|
|
|
19490
19498
|
//#region src/composites/cap-settings/camera-grid-layout.ts
|
|
19491
19499
|
/** Smallest rectangle the editor will produce — ffmpeg refuses a zero-area crop. */
|
|
19492
19500
|
var MIN_EXTENT = .02;
|
|
19493
|
-
function isRecord(value) {
|
|
19501
|
+
function isRecord$2(value) {
|
|
19494
19502
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
19495
19503
|
}
|
|
19496
19504
|
function readRect(value) {
|
|
19497
|
-
if (!isRecord(value)) return null;
|
|
19505
|
+
if (!isRecord$2(value)) return null;
|
|
19498
19506
|
const { x, y, width, height } = value;
|
|
19499
19507
|
if (typeof x !== "number" || typeof y !== "number" || typeof width !== "number" || typeof height !== "number") return null;
|
|
19500
19508
|
if (x < 0 || y < 0 || width <= 0 || height <= 0) return null;
|
|
@@ -19507,7 +19515,7 @@ function readRect(value) {
|
|
|
19507
19515
|
};
|
|
19508
19516
|
}
|
|
19509
19517
|
function readCell(value) {
|
|
19510
|
-
if (!isRecord(value)) return null;
|
|
19518
|
+
if (!isRecord$2(value)) return null;
|
|
19511
19519
|
const deviceId = value["deviceId"];
|
|
19512
19520
|
if (typeof deviceId !== "number" || !Number.isInteger(deviceId)) return null;
|
|
19513
19521
|
const source = readRect(value["source"]);
|
|
@@ -19520,7 +19528,7 @@ function readCell(value) {
|
|
|
19520
19528
|
};
|
|
19521
19529
|
}
|
|
19522
19530
|
function readProfileOffer(value) {
|
|
19523
|
-
if (!isRecord(value)) return null;
|
|
19531
|
+
if (!isRecord$2(value)) return null;
|
|
19524
19532
|
const profile = value["profile"];
|
|
19525
19533
|
const offered = value["offered"];
|
|
19526
19534
|
const missing = value["missingSources"];
|
|
@@ -19549,7 +19557,7 @@ function readProfileOffer(value) {
|
|
|
19549
19557
|
*/
|
|
19550
19558
|
function parseGridView(raw) {
|
|
19551
19559
|
if (raw === null) return null;
|
|
19552
|
-
if (!isRecord(raw)) return "unreadable";
|
|
19560
|
+
if (!isRecord$2(raw)) return "unreadable";
|
|
19553
19561
|
const { instanceId, deviceId, name, fps, cells, profiles } = raw;
|
|
19554
19562
|
if (typeof instanceId !== "string" || typeof deviceId !== "number" || typeof name !== "string" || typeof fps !== "number" || !Array.isArray(cells) || !Array.isArray(profiles)) return "unreadable";
|
|
19555
19563
|
const parsed = [];
|
|
@@ -25706,7 +25714,7 @@ function mergeAvailabilitySpans(ranges) {
|
|
|
25706
25714
|
* significant digits (binary prefixes, matching recording sizes elsewhere).
|
|
25707
25715
|
* KB and up render one decimal under 100, none above; bytes never get a decimal.
|
|
25708
25716
|
*/
|
|
25709
|
-
function formatBytes(bytes) {
|
|
25717
|
+
function formatBytes$1(bytes) {
|
|
25710
25718
|
if (!Number.isFinite(bytes) || bytes < 0) return "—";
|
|
25711
25719
|
if (bytes < 1024) return `${Math.round(bytes)} B`;
|
|
25712
25720
|
const units = [
|
|
@@ -26239,7 +26247,7 @@ function RecordingPanel({ deviceId }) {
|
|
|
26239
26247
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(WidgetMetricCard, {
|
|
26240
26248
|
label: t("uiLibrary.recordingPanel.storage", "Storage"),
|
|
26241
26249
|
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HardDrive, { className: "h-3.5 w-3.5" }),
|
|
26242
|
-
children: status ? formatBytes(status.storageBytes) : "—"
|
|
26250
|
+
children: status ? formatBytes$1(status.storageBytes) : "—"
|
|
26243
26251
|
}),
|
|
26244
26252
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(WidgetMetricCard, {
|
|
26245
26253
|
label: t("uiLibrary.recordingPanel.recorded24h", "Recorded (24h)"),
|
|
@@ -29600,17 +29608,7 @@ function applyValuesToSchema(schema, nextValues) {
|
|
|
29600
29608
|
};
|
|
29601
29609
|
}
|
|
29602
29610
|
function stripValues(schema) {
|
|
29603
|
-
return
|
|
29604
|
-
...schema,
|
|
29605
|
-
sections: schema.sections.map((s) => ({
|
|
29606
|
-
...s,
|
|
29607
|
-
fields: s.fields.map((f) => {
|
|
29608
|
-
if (!isValueBearingField(f)) return f;
|
|
29609
|
-
const { value: _drop, ...rest } = f;
|
|
29610
|
-
return rest;
|
|
29611
|
-
})
|
|
29612
|
-
}))
|
|
29613
|
-
};
|
|
29611
|
+
return (0, _camstack_types.dehydrateSchema)(schema);
|
|
29614
29612
|
}
|
|
29615
29613
|
function AddonGlobalSettingsForm({ trpc, addonId, nodeId, title, disabled, onAfterChange, refreshToken, sectionIds, bare = false, collapsible, defaultOpen, mode, onOverrideChange }) {
|
|
29616
29614
|
const t = useUiText();
|
|
@@ -58675,6 +58673,780 @@ function EntryIcon({ url }) {
|
|
|
58675
58673
|
});
|
|
58676
58674
|
}
|
|
58677
58675
|
//#endregion
|
|
58676
|
+
//#region src/composites/record-entries/record-entry-secrets.ts
|
|
58677
|
+
/** Whether a record entry field still holds the redaction token from a read. */
|
|
58678
|
+
function isRedactedSecret(value) {
|
|
58679
|
+
return value === _camstack_types.REDACTED_SECRET;
|
|
58680
|
+
}
|
|
58681
|
+
/** Text shown in a password box for a redacted stored secret (empty — placeholder carries the hint). */
|
|
58682
|
+
function secretDisplayValue(value) {
|
|
58683
|
+
if (isRedactedSecret(value)) return "";
|
|
58684
|
+
return typeof value === "string" ? value : "";
|
|
58685
|
+
}
|
|
58686
|
+
/** Merge an edited password box back into the body sent to `putEntry`. */
|
|
58687
|
+
function mergeSecretForSubmit(draft, original) {
|
|
58688
|
+
if (!isRedactedSecret(original)) return draft;
|
|
58689
|
+
if (draft.length === 0) return _camstack_types.REDACTED_SECRET;
|
|
58690
|
+
return draft;
|
|
58691
|
+
}
|
|
58692
|
+
/** Is this JSON-schema leaf (or path) a credential field? */
|
|
58693
|
+
function isSecretField(path, secretPaths, schemaNode) {
|
|
58694
|
+
if (schemaNode["secret"] === true) return true;
|
|
58695
|
+
return secretPaths.includes(path);
|
|
58696
|
+
}
|
|
58697
|
+
//#endregion
|
|
58698
|
+
//#region src/composites/record-entries/entry-json-schema-form.tsx
|
|
58699
|
+
function isRecord$1(value) {
|
|
58700
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
58701
|
+
}
|
|
58702
|
+
function labelFor(key, node) {
|
|
58703
|
+
const desc = node["description"];
|
|
58704
|
+
if (typeof desc === "string" && desc.length > 0) return desc;
|
|
58705
|
+
return key.replace(/([A-Z])/g, " $1").replace(/^./, (c) => c.toUpperCase());
|
|
58706
|
+
}
|
|
58707
|
+
function readString(value) {
|
|
58708
|
+
return typeof value === "string" ? value : "";
|
|
58709
|
+
}
|
|
58710
|
+
function readNumber(value) {
|
|
58711
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
58712
|
+
}
|
|
58713
|
+
function readBool(value) {
|
|
58714
|
+
return value === true;
|
|
58715
|
+
}
|
|
58716
|
+
function objectFromJson(text) {
|
|
58717
|
+
try {
|
|
58718
|
+
const parsed = JSON.parse(text);
|
|
58719
|
+
return isRecord$1(parsed) ? parsed : null;
|
|
58720
|
+
} catch {
|
|
58721
|
+
return null;
|
|
58722
|
+
}
|
|
58723
|
+
}
|
|
58724
|
+
function RecordEntrySecretField({ label, required, value, disabled, onPatch }) {
|
|
58725
|
+
const [shown, setShown] = (0, react$1.useState)(false);
|
|
58726
|
+
const stored = isRedactedSecret(value);
|
|
58727
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FormField$1, {
|
|
58728
|
+
label,
|
|
58729
|
+
required,
|
|
58730
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PasswordInput, {
|
|
58731
|
+
value: secretDisplayValue(value),
|
|
58732
|
+
onValueChange: (text) => onPatch(mergeSecretForSubmit(text, value)),
|
|
58733
|
+
shown,
|
|
58734
|
+
onToggleShown: () => setShown((s) => !s),
|
|
58735
|
+
disabled,
|
|
58736
|
+
placeholder: stored ? SECRET_MASK_PLACEHOLDER : `Enter ${label.toLowerCase()}`
|
|
58737
|
+
})
|
|
58738
|
+
});
|
|
58739
|
+
}
|
|
58740
|
+
function SchemaField(props) {
|
|
58741
|
+
const { path, name, node, value, secretPaths, required, disabled, onPatch } = props;
|
|
58742
|
+
const label = labelFor(name, node);
|
|
58743
|
+
if (isSecretField(path, secretPaths, node)) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RecordEntrySecretField, {
|
|
58744
|
+
label,
|
|
58745
|
+
required,
|
|
58746
|
+
value,
|
|
58747
|
+
disabled,
|
|
58748
|
+
onPatch: (next) => onPatch(path, next)
|
|
58749
|
+
});
|
|
58750
|
+
const enumValues = node["enum"];
|
|
58751
|
+
if (Array.isArray(enumValues) && enumValues.every((v) => typeof v === "string")) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FormField$1, {
|
|
58752
|
+
label,
|
|
58753
|
+
required,
|
|
58754
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Select, {
|
|
58755
|
+
value: readString(value),
|
|
58756
|
+
onChange: (e) => onPatch(path, e.target.value),
|
|
58757
|
+
disabled,
|
|
58758
|
+
options: enumValues.map((opt) => ({
|
|
58759
|
+
value: opt,
|
|
58760
|
+
label: opt
|
|
58761
|
+
}))
|
|
58762
|
+
})
|
|
58763
|
+
});
|
|
58764
|
+
const type = node["type"];
|
|
58765
|
+
if (type === "boolean") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FormField$1, {
|
|
58766
|
+
label,
|
|
58767
|
+
required,
|
|
58768
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Switch, {
|
|
58769
|
+
checked: readBool(value),
|
|
58770
|
+
onCheckedChange: (v) => onPatch(path, v),
|
|
58771
|
+
disabled
|
|
58772
|
+
})
|
|
58773
|
+
});
|
|
58774
|
+
if (type === "integer" || type === "number") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FormField$1, {
|
|
58775
|
+
label,
|
|
58776
|
+
required,
|
|
58777
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Input, {
|
|
58778
|
+
type: "number",
|
|
58779
|
+
value: readNumber(value) ?? "",
|
|
58780
|
+
onChange: (e) => {
|
|
58781
|
+
const raw = e.target.value;
|
|
58782
|
+
onPatch(path, raw === "" ? void 0 : Number(raw));
|
|
58783
|
+
},
|
|
58784
|
+
disabled
|
|
58785
|
+
})
|
|
58786
|
+
});
|
|
58787
|
+
if (type === "object") {
|
|
58788
|
+
const properties = node["properties"];
|
|
58789
|
+
if (isRecord$1(properties)) {
|
|
58790
|
+
const childValue = isRecord$1(value) ? value : {};
|
|
58791
|
+
const childRequired = Array.isArray(node["required"]) ? node["required"].filter((k) => typeof k === "string") : [];
|
|
58792
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
|
|
58793
|
+
className: "space-y-3 rounded-lg border border-border-subtle p-3",
|
|
58794
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("legend", {
|
|
58795
|
+
className: "px-1 text-xs font-medium text-foreground-muted",
|
|
58796
|
+
children: label
|
|
58797
|
+
}), Object.entries(properties).map(([key, childNode]) => isRecord$1(childNode) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SchemaField, {
|
|
58798
|
+
path: `${path}.${key}`,
|
|
58799
|
+
name: key,
|
|
58800
|
+
node: childNode,
|
|
58801
|
+
value: childValue[key],
|
|
58802
|
+
secretPaths,
|
|
58803
|
+
required: childRequired.includes(key),
|
|
58804
|
+
disabled,
|
|
58805
|
+
onPatch
|
|
58806
|
+
}, key) : null)]
|
|
58807
|
+
});
|
|
58808
|
+
}
|
|
58809
|
+
const jsonText = isRecord$1(value) ? JSON.stringify(value, null, 2) : "{}";
|
|
58810
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FormField$1, {
|
|
58811
|
+
label,
|
|
58812
|
+
required,
|
|
58813
|
+
description: "JSON object",
|
|
58814
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
58815
|
+
className: cn("min-h-[8rem] w-full resize-y rounded-md border border-border bg-surface px-2.5 py-2 font-mono text-xs text-foreground", "placeholder:text-foreground-subtle focus:outline-none focus:ring-1 focus:ring-inset focus:ring-primary", "disabled:cursor-not-allowed disabled:opacity-50"),
|
|
58816
|
+
rows: 6,
|
|
58817
|
+
defaultValue: jsonText,
|
|
58818
|
+
disabled,
|
|
58819
|
+
onBlur: (e) => {
|
|
58820
|
+
const parsed = objectFromJson(e.target.value);
|
|
58821
|
+
if (parsed !== null) onPatch(path, parsed);
|
|
58822
|
+
}
|
|
58823
|
+
})
|
|
58824
|
+
});
|
|
58825
|
+
}
|
|
58826
|
+
if (type === "string" || type === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FormField$1, {
|
|
58827
|
+
label,
|
|
58828
|
+
required,
|
|
58829
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Input, {
|
|
58830
|
+
value: readString(value),
|
|
58831
|
+
onChange: (e) => onPatch(path, e.target.value),
|
|
58832
|
+
disabled
|
|
58833
|
+
})
|
|
58834
|
+
});
|
|
58835
|
+
return null;
|
|
58836
|
+
}
|
|
58837
|
+
function setAtPath(root, path, next) {
|
|
58838
|
+
const parts = path.split(".");
|
|
58839
|
+
if (parts.length === 1) return {
|
|
58840
|
+
...root,
|
|
58841
|
+
[parts[0]]: next
|
|
58842
|
+
};
|
|
58843
|
+
const [head, ...rest] = parts;
|
|
58844
|
+
const key = head;
|
|
58845
|
+
const child = isRecord$1(root[key]) ? root[key] : {};
|
|
58846
|
+
return {
|
|
58847
|
+
...root,
|
|
58848
|
+
[key]: setAtPath(child, rest.join("."), next)
|
|
58849
|
+
};
|
|
58850
|
+
}
|
|
58851
|
+
/** Renders an entry body from the engine's JSON Schema descriptor. */
|
|
58852
|
+
function EntryJsonSchemaForm({ schema, secretPaths, value, onChange, disabled }) {
|
|
58853
|
+
const properties = schema["properties"];
|
|
58854
|
+
if (!isRecord$1(properties)) return null;
|
|
58855
|
+
const required = Array.isArray(schema["required"]) ? schema["required"].filter((k) => typeof k === "string") : [];
|
|
58856
|
+
const onPatch = (path, next) => {
|
|
58857
|
+
onChange(setAtPath(value, path, next));
|
|
58858
|
+
};
|
|
58859
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
58860
|
+
className: "space-y-3",
|
|
58861
|
+
children: Object.entries(properties).map(([key, node]) => isRecord$1(node) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SchemaField, {
|
|
58862
|
+
path: key,
|
|
58863
|
+
name: key,
|
|
58864
|
+
node,
|
|
58865
|
+
value: value[key],
|
|
58866
|
+
secretPaths,
|
|
58867
|
+
required: required.includes(key),
|
|
58868
|
+
disabled,
|
|
58869
|
+
onPatch
|
|
58870
|
+
}, key) : null)
|
|
58871
|
+
});
|
|
58872
|
+
}
|
|
58873
|
+
//#endregion
|
|
58874
|
+
//#region src/composites/record-entries/record-entries-panel.tsx
|
|
58875
|
+
function isRecord(value) {
|
|
58876
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
58877
|
+
}
|
|
58878
|
+
function defaultBody(schema) {
|
|
58879
|
+
const properties = schema["properties"];
|
|
58880
|
+
if (!isRecord(properties)) return {};
|
|
58881
|
+
const out = {};
|
|
58882
|
+
for (const [key, node] of Object.entries(properties)) {
|
|
58883
|
+
if (!isRecord(node)) continue;
|
|
58884
|
+
if (node["default"] !== void 0) {
|
|
58885
|
+
out[key] = node["default"];
|
|
58886
|
+
continue;
|
|
58887
|
+
}
|
|
58888
|
+
if (node["type"] === "boolean") out[key] = false;
|
|
58889
|
+
else if (node["type"] === "object") out[key] = {};
|
|
58890
|
+
}
|
|
58891
|
+
return out;
|
|
58892
|
+
}
|
|
58893
|
+
function RecordEntriesPanel({ kind }) {
|
|
58894
|
+
const t = useUiText();
|
|
58895
|
+
const queryClient = (0, _tanstack_react_query.useQueryClient)();
|
|
58896
|
+
const confirm = useConfirm();
|
|
58897
|
+
const formUrl = useItemFormUrl({
|
|
58898
|
+
newKey: "entryNew",
|
|
58899
|
+
editKey: "entryEdit"
|
|
58900
|
+
});
|
|
58901
|
+
const listQuery = useRecordEntriesListEntries({
|
|
58902
|
+
owner: kind.owner,
|
|
58903
|
+
kind: kind.kind
|
|
58904
|
+
}, { staleTime: 15e3 });
|
|
58905
|
+
const putMutation = useRecordEntriesPutEntry({ onSuccess: async () => {
|
|
58906
|
+
await queryClient.invalidateQueries({ queryKey: [["recordEntries"]] });
|
|
58907
|
+
formUrl.close();
|
|
58908
|
+
} });
|
|
58909
|
+
const deleteMutation = useRecordEntriesDeleteEntry({ onSuccess: async () => {
|
|
58910
|
+
await queryClient.invalidateQueries({ queryKey: [["recordEntries"]] });
|
|
58911
|
+
} });
|
|
58912
|
+
const entries = listQuery.data?.entries ?? [];
|
|
58913
|
+
const editing = (0, react$1.useMemo)(() => {
|
|
58914
|
+
const st = formUrl.state;
|
|
58915
|
+
if (st.mode !== "edit") return null;
|
|
58916
|
+
return entries.find((e) => e.id === st.id) ?? null;
|
|
58917
|
+
}, [entries, formUrl.state]);
|
|
58918
|
+
const [draft, setDraft] = (0, react$1.useState)({});
|
|
58919
|
+
const [saveError, setSaveError] = (0, react$1.useState)(null);
|
|
58920
|
+
const openNew = (0, react$1.useCallback)(() => {
|
|
58921
|
+
setDraft(defaultBody(kind.fields));
|
|
58922
|
+
setSaveError(null);
|
|
58923
|
+
formUrl.openNew();
|
|
58924
|
+
}, [formUrl, kind.fields]);
|
|
58925
|
+
const openEdit = (0, react$1.useCallback)((entry) => {
|
|
58926
|
+
setDraft({ ...entry.data });
|
|
58927
|
+
setSaveError(null);
|
|
58928
|
+
formUrl.openEdit(entry.id);
|
|
58929
|
+
}, [formUrl]);
|
|
58930
|
+
const onDelete = (0, react$1.useCallback)(async (entry) => {
|
|
58931
|
+
if (!await confirm({
|
|
58932
|
+
title: t("uiLibrary.recordEntries.deleteTitle", "Delete entry"),
|
|
58933
|
+
message: t("uiLibrary.recordEntries.deleteDescription", "Remove \"{{name}}\" permanently?", { name: entry.name }),
|
|
58934
|
+
confirmLabel: t("uiLibrary.recordEntries.deleteConfirm", "Delete"),
|
|
58935
|
+
variant: "danger"
|
|
58936
|
+
})) return;
|
|
58937
|
+
await deleteMutation.mutateAsync({
|
|
58938
|
+
owner: kind.owner,
|
|
58939
|
+
kind: kind.kind,
|
|
58940
|
+
id: entry.id
|
|
58941
|
+
});
|
|
58942
|
+
}, [
|
|
58943
|
+
confirm,
|
|
58944
|
+
deleteMutation,
|
|
58945
|
+
kind.kind,
|
|
58946
|
+
kind.owner,
|
|
58947
|
+
t
|
|
58948
|
+
]);
|
|
58949
|
+
const columns = (0, react$1.useMemo)(() => {
|
|
58950
|
+
return [
|
|
58951
|
+
{
|
|
58952
|
+
key: "name",
|
|
58953
|
+
header: t("uiLibrary.recordEntries.colName", "Name"),
|
|
58954
|
+
render: (row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
58955
|
+
type: "button",
|
|
58956
|
+
variant: "ghost",
|
|
58957
|
+
size: "sm",
|
|
58958
|
+
className: "h-auto max-w-full justify-start px-0 py-0 font-medium hover:underline",
|
|
58959
|
+
onClick: () => openEdit(row),
|
|
58960
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
58961
|
+
className: "truncate",
|
|
58962
|
+
children: row.name
|
|
58963
|
+
})
|
|
58964
|
+
})
|
|
58965
|
+
},
|
|
58966
|
+
{
|
|
58967
|
+
key: "updated",
|
|
58968
|
+
header: t("uiLibrary.recordEntries.colUpdated", "Updated"),
|
|
58969
|
+
nowrap: true,
|
|
58970
|
+
render: (row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
58971
|
+
className: "text-[11px] text-foreground-subtle",
|
|
58972
|
+
children: new Date(row.updatedAt).toLocaleString()
|
|
58973
|
+
})
|
|
58974
|
+
},
|
|
58975
|
+
{
|
|
58976
|
+
key: "actions",
|
|
58977
|
+
header: "",
|
|
58978
|
+
align: "right",
|
|
58979
|
+
nowrap: true,
|
|
58980
|
+
render: (row) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
58981
|
+
className: "flex justify-end gap-1",
|
|
58982
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
58983
|
+
size: "sm",
|
|
58984
|
+
variant: "ghost",
|
|
58985
|
+
type: "button",
|
|
58986
|
+
onClick: () => openEdit(row),
|
|
58987
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Pencil, { className: "h-3.5 w-3.5" })
|
|
58988
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
58989
|
+
size: "sm",
|
|
58990
|
+
variant: "ghost",
|
|
58991
|
+
type: "button",
|
|
58992
|
+
onClick: () => void onDelete(row),
|
|
58993
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Trash2, { className: "h-3.5 w-3.5 text-danger" })
|
|
58994
|
+
})]
|
|
58995
|
+
})
|
|
58996
|
+
}
|
|
58997
|
+
];
|
|
58998
|
+
}, [
|
|
58999
|
+
onDelete,
|
|
59000
|
+
openEdit,
|
|
59001
|
+
t
|
|
59002
|
+
]);
|
|
59003
|
+
const dialogOpen = formUrl.state.mode !== "closed";
|
|
59004
|
+
const dialogTitle = formUrl.state.mode === "new" ? t("uiLibrary.recordEntries.addTitle", "Add entry") : t("uiLibrary.recordEntries.editTitle", "Edit entry");
|
|
59005
|
+
const onSave = async () => {
|
|
59006
|
+
setSaveError(null);
|
|
59007
|
+
try {
|
|
59008
|
+
if (formUrl.state.mode === "new") await putMutation.mutateAsync({
|
|
59009
|
+
owner: kind.owner,
|
|
59010
|
+
kind: kind.kind,
|
|
59011
|
+
data: draft
|
|
59012
|
+
});
|
|
59013
|
+
else if (formUrl.state.mode === "edit" && editing !== null) await putMutation.mutateAsync({
|
|
59014
|
+
owner: kind.owner,
|
|
59015
|
+
kind: kind.kind,
|
|
59016
|
+
id: editing.id,
|
|
59017
|
+
data: draft,
|
|
59018
|
+
ifRev: editing.rev
|
|
59019
|
+
});
|
|
59020
|
+
else {
|
|
59021
|
+
setSaveError(t("uiLibrary.recordEntries.saveUnavailable", "Nothing to save"));
|
|
59022
|
+
return;
|
|
59023
|
+
}
|
|
59024
|
+
} catch (err) {
|
|
59025
|
+
setSaveError(err instanceof Error ? err.message : t("uiLibrary.recordEntries.saveFailed", "Save failed"));
|
|
59026
|
+
}
|
|
59027
|
+
};
|
|
59028
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
59029
|
+
className: "space-y-3",
|
|
59030
|
+
children: [
|
|
59031
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
59032
|
+
className: "flex justify-end",
|
|
59033
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Button, {
|
|
59034
|
+
type: "button",
|
|
59035
|
+
size: "sm",
|
|
59036
|
+
onClick: openNew,
|
|
59037
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Plus, { className: "mr-1 h-3.5 w-3.5" }), t("uiLibrary.recordEntries.add", "Add")]
|
|
59038
|
+
})
|
|
59039
|
+
}),
|
|
59040
|
+
listQuery.isError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ErrorBox, { message: listQuery.error instanceof Error ? listQuery.error.message : t("uiLibrary.recordEntries.loadFailed", "Could not load entries") }),
|
|
59041
|
+
listQuery.isPending ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
59042
|
+
className: "flex justify-center py-8 text-foreground-subtle",
|
|
59043
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LoaderCircle, { className: "h-5 w-5 animate-spin" })
|
|
59044
|
+
}) : entries.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EmptyState, {
|
|
59045
|
+
title: t("uiLibrary.recordEntries.emptyTitle", "No entries yet"),
|
|
59046
|
+
description: t("uiLibrary.recordEntries.emptyDescription", "Add the first row for this kind.")
|
|
59047
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DataTable, {
|
|
59048
|
+
columns,
|
|
59049
|
+
rows: entries,
|
|
59050
|
+
rowKey: (r) => r.id,
|
|
59051
|
+
maxBodyHeight: TABLE_SCROLL_MAX_H,
|
|
59052
|
+
emptyMessage: t("uiLibrary.recordEntries.emptyTitle", "No entries yet")
|
|
59053
|
+
}),
|
|
59054
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ItemFormDialog, {
|
|
59055
|
+
open: dialogOpen,
|
|
59056
|
+
onClose: formUrl.close,
|
|
59057
|
+
title: dialogTitle,
|
|
59058
|
+
error: saveError,
|
|
59059
|
+
footer: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
59060
|
+
type: "button",
|
|
59061
|
+
size: "sm",
|
|
59062
|
+
disabled: putMutation.isPending,
|
|
59063
|
+
onClick: () => void onSave(),
|
|
59064
|
+
children: putMutation.isPending ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LoaderCircle, { className: "h-3.5 w-3.5 animate-spin" }) : t("uiLibrary.recordEntries.save", "Save")
|
|
59065
|
+
}),
|
|
59066
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EntryJsonSchemaForm, {
|
|
59067
|
+
schema: kind.fields,
|
|
59068
|
+
secretPaths: kind.secretPaths,
|
|
59069
|
+
value: draft,
|
|
59070
|
+
onChange: setDraft,
|
|
59071
|
+
disabled: putMutation.isPending
|
|
59072
|
+
})
|
|
59073
|
+
})
|
|
59074
|
+
]
|
|
59075
|
+
});
|
|
59076
|
+
}
|
|
59077
|
+
//#endregion
|
|
59078
|
+
//#region src/composites/record-entries/record-entries-hub.tsx
|
|
59079
|
+
var RECORD_ENTRY_OWNER_PARAM = "entryOwner";
|
|
59080
|
+
var RECORD_ENTRY_KIND_PARAM = "entryKind";
|
|
59081
|
+
function kindKey$1(k) {
|
|
59082
|
+
return `${k.owner}/${k.kind}`;
|
|
59083
|
+
}
|
|
59084
|
+
function RecordEntriesHub({ filter }) {
|
|
59085
|
+
const t = useUiText();
|
|
59086
|
+
const kindsQuery = useRecordEntriesListEntryKinds({}, { staleTime: 3e4 });
|
|
59087
|
+
const [ownerParam, setOwner] = useUrlQueryParam(RECORD_ENTRY_OWNER_PARAM);
|
|
59088
|
+
const [kindParam, setKind] = useUrlQueryParam(RECORD_ENTRY_KIND_PARAM);
|
|
59089
|
+
const kinds = (0, react$1.useMemo)(() => {
|
|
59090
|
+
return (kindsQuery.data?.kinds ?? []).filter((k) => {
|
|
59091
|
+
if (filter?.owner !== void 0 && k.owner !== filter.owner) return false;
|
|
59092
|
+
if (filter?.kind !== void 0 && k.kind !== filter.kind) return false;
|
|
59093
|
+
return true;
|
|
59094
|
+
});
|
|
59095
|
+
}, [
|
|
59096
|
+
filter?.kind,
|
|
59097
|
+
filter?.owner,
|
|
59098
|
+
kindsQuery.data?.kinds
|
|
59099
|
+
]);
|
|
59100
|
+
const active = (0, react$1.useMemo)(() => {
|
|
59101
|
+
if (kinds.length === 0) return null;
|
|
59102
|
+
return kinds.find((k) => k.owner === ownerParam && k.kind === kindParam) ?? kinds[0] ?? null;
|
|
59103
|
+
}, [
|
|
59104
|
+
kindParam,
|
|
59105
|
+
kinds,
|
|
59106
|
+
ownerParam
|
|
59107
|
+
]);
|
|
59108
|
+
const selectKind = (k) => {
|
|
59109
|
+
setOwner(k.owner);
|
|
59110
|
+
setKind(k.kind);
|
|
59111
|
+
};
|
|
59112
|
+
if (kindsQuery.isError) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ErrorBox, { message: kindsQuery.error instanceof Error ? kindsQuery.error.message : t("uiLibrary.recordEntries.kindsFailed", "Could not load entry kinds") });
|
|
59113
|
+
if (kindsQuery.isPending) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
59114
|
+
className: "flex justify-center py-10 text-foreground-subtle",
|
|
59115
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LoaderCircle, { className: "h-5 w-5 animate-spin" })
|
|
59116
|
+
});
|
|
59117
|
+
if (kinds.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EmptyState, {
|
|
59118
|
+
title: t("uiLibrary.recordEntries.noKindsTitle", "No entry kinds"),
|
|
59119
|
+
description: t("uiLibrary.recordEntries.noKindsDescription", "No addon has declared an entry record kind yet.")
|
|
59120
|
+
});
|
|
59121
|
+
const activeKey = active === null ? "" : kindKey$1(active);
|
|
59122
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
59123
|
+
className: "space-y-4",
|
|
59124
|
+
children: [kinds.length > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Tabs, {
|
|
59125
|
+
appearance: "pills",
|
|
59126
|
+
value: activeKey,
|
|
59127
|
+
onValueChange: (key) => {
|
|
59128
|
+
const found = kinds.find((k) => kindKey$1(k) === key);
|
|
59129
|
+
if (found !== void 0) selectKind(found);
|
|
59130
|
+
},
|
|
59131
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TabsList, {
|
|
59132
|
+
className: "flex flex-wrap gap-1",
|
|
59133
|
+
children: kinds.map((k) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(TabsTrigger, {
|
|
59134
|
+
value: kindKey$1(k),
|
|
59135
|
+
children: [k.title, k.rows > 0 ? ` (${k.rows})` : ""]
|
|
59136
|
+
}, kindKey$1(k)))
|
|
59137
|
+
})
|
|
59138
|
+
}), active !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [kinds.length === 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
59139
|
+
className: "mb-2 text-sm font-semibold text-foreground",
|
|
59140
|
+
children: active.title
|
|
59141
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RecordEntriesPanel, { kind: active })] })]
|
|
59142
|
+
});
|
|
59143
|
+
}
|
|
59144
|
+
//#endregion
|
|
59145
|
+
//#region src/composites/storage-records/storage-records-panel.tsx
|
|
59146
|
+
function StorageRecordsPanel({ kind }) {
|
|
59147
|
+
const t = useUiText();
|
|
59148
|
+
const [inspect, setInspect] = (0, react$1.useState)(null);
|
|
59149
|
+
const recordsQuery = trpc.storageAdmin.readRecords.useQuery({
|
|
59150
|
+
owner: kind.owner,
|
|
59151
|
+
kind: kind.kind,
|
|
59152
|
+
limit: 100,
|
|
59153
|
+
desc: true
|
|
59154
|
+
}, { staleTime: 15e3 });
|
|
59155
|
+
const records = recordsQuery.data?.records ?? [];
|
|
59156
|
+
const columns = (0, react$1.useMemo)(() => {
|
|
59157
|
+
return [
|
|
59158
|
+
{
|
|
59159
|
+
key: "id",
|
|
59160
|
+
header: t("uiLibrary.storageRecords.colId", "Id"),
|
|
59161
|
+
render: (row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
59162
|
+
type: "button",
|
|
59163
|
+
variant: "ghost",
|
|
59164
|
+
size: "sm",
|
|
59165
|
+
className: "h-auto max-w-full justify-start px-0 py-0 font-mono text-xs hover:underline",
|
|
59166
|
+
onClick: () => setInspect(row),
|
|
59167
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
59168
|
+
className: "truncate",
|
|
59169
|
+
children: row.id
|
|
59170
|
+
})
|
|
59171
|
+
})
|
|
59172
|
+
},
|
|
59173
|
+
{
|
|
59174
|
+
key: "deviceId",
|
|
59175
|
+
header: t("uiLibrary.storageRecords.colDevice", "Device"),
|
|
59176
|
+
nowrap: true,
|
|
59177
|
+
render: (row) => row.deviceId === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
59178
|
+
className: "text-foreground-subtle",
|
|
59179
|
+
children: "—"
|
|
59180
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
59181
|
+
className: "font-mono text-xs",
|
|
59182
|
+
children: row.deviceId
|
|
59183
|
+
})
|
|
59184
|
+
},
|
|
59185
|
+
{
|
|
59186
|
+
key: "at",
|
|
59187
|
+
header: t("uiLibrary.storageRecords.colAt", "At"),
|
|
59188
|
+
nowrap: true,
|
|
59189
|
+
render: (row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
59190
|
+
className: "text-[11px] text-foreground-subtle",
|
|
59191
|
+
children: row.at === null ? "—" : new Date(row.at).toLocaleString()
|
|
59192
|
+
})
|
|
59193
|
+
},
|
|
59194
|
+
{
|
|
59195
|
+
key: "updatedAt",
|
|
59196
|
+
header: t("uiLibrary.storageRecords.colUpdated", "Updated"),
|
|
59197
|
+
nowrap: true,
|
|
59198
|
+
render: (row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
59199
|
+
className: "text-[11px] text-foreground-subtle",
|
|
59200
|
+
children: new Date(row.updatedAt).toLocaleString()
|
|
59201
|
+
})
|
|
59202
|
+
}
|
|
59203
|
+
];
|
|
59204
|
+
}, [t]);
|
|
59205
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
59206
|
+
className: "space-y-3",
|
|
59207
|
+
children: [
|
|
59208
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
59209
|
+
className: "flex flex-wrap items-center gap-2",
|
|
59210
|
+
children: [
|
|
59211
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
59212
|
+
className: "text-sm font-semibold text-foreground",
|
|
59213
|
+
children: `${kind.owner}/${kind.kind}`
|
|
59214
|
+
}),
|
|
59215
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Badge, {
|
|
59216
|
+
variant: "neutral",
|
|
59217
|
+
style: "outline",
|
|
59218
|
+
children: kind.database
|
|
59219
|
+
}),
|
|
59220
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Badge, {
|
|
59221
|
+
variant: "neutral",
|
|
59222
|
+
style: "outline",
|
|
59223
|
+
children: kind.class
|
|
59224
|
+
}),
|
|
59225
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Badge, {
|
|
59226
|
+
variant: kind.state === "declared" ? "default" : "neutral",
|
|
59227
|
+
children: kind.state
|
|
59228
|
+
}),
|
|
59229
|
+
kind.entry ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Badge, {
|
|
59230
|
+
variant: "info",
|
|
59231
|
+
children: t("uiLibrary.storageRecords.entryKind", "Entry")
|
|
59232
|
+
}) : null
|
|
59233
|
+
]
|
|
59234
|
+
}),
|
|
59235
|
+
recordsQuery.isError ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ErrorBox, { message: recordsQuery.error instanceof Error ? recordsQuery.error.message : t("uiLibrary.storageRecords.loadFailed", "Could not load records") }) : recordsQuery.isPending ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
59236
|
+
className: "flex justify-center py-8 text-foreground-subtle",
|
|
59237
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LoaderCircle, { className: "h-5 w-5 animate-spin" })
|
|
59238
|
+
}) : records.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EmptyState, {
|
|
59239
|
+
title: t("uiLibrary.storageRecords.emptyTitle", "No rows"),
|
|
59240
|
+
description: t("uiLibrary.storageRecords.emptyDescription", "This kind has no stored records yet.")
|
|
59241
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DataTable, {
|
|
59242
|
+
columns,
|
|
59243
|
+
rows: records,
|
|
59244
|
+
rowKey: (r) => r.id,
|
|
59245
|
+
maxBodyHeight: TABLE_SCROLL_MAX_H,
|
|
59246
|
+
emptyMessage: t("uiLibrary.storageRecords.emptyTitle", "No rows")
|
|
59247
|
+
}),
|
|
59248
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dialog, {
|
|
59249
|
+
open: inspect !== null,
|
|
59250
|
+
onOpenChange: (open) => !open && setInspect(null),
|
|
59251
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(DialogContent, {
|
|
59252
|
+
width: "lg",
|
|
59253
|
+
placement: "sheet-on-mobile",
|
|
59254
|
+
scroll: "body",
|
|
59255
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(DialogHeader, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DialogTitle, { children: t("uiLibrary.storageRecords.inspectTitle", "Record") }), inspect !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DialogDescription, {
|
|
59256
|
+
className: "font-mono",
|
|
59257
|
+
children: inspect.id
|
|
59258
|
+
}) : null] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DialogBody, { children: inspect !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CodeBlock, {
|
|
59259
|
+
maxHeight: 480,
|
|
59260
|
+
children: JSON.stringify(inspect, null, 2)
|
|
59261
|
+
}) : null })]
|
|
59262
|
+
})
|
|
59263
|
+
})
|
|
59264
|
+
]
|
|
59265
|
+
});
|
|
59266
|
+
}
|
|
59267
|
+
//#endregion
|
|
59268
|
+
//#region src/composites/storage-records/storage-records-hub.tsx
|
|
59269
|
+
var STORAGE_RECORDS_OWNER_PARAM = "recordsOwner";
|
|
59270
|
+
var STORAGE_RECORDS_KIND_PARAM = "recordsKind";
|
|
59271
|
+
function kindKey(k) {
|
|
59272
|
+
return `${k.owner}/${k.kind}`;
|
|
59273
|
+
}
|
|
59274
|
+
function formatBytes(n) {
|
|
59275
|
+
if (n === null) return "—";
|
|
59276
|
+
if (n < 1024) return `${n} B`;
|
|
59277
|
+
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KiB`;
|
|
59278
|
+
return `${(n / (1024 * 1024)).toFixed(1)} MiB`;
|
|
59279
|
+
}
|
|
59280
|
+
function StorageRecordsHub() {
|
|
59281
|
+
const t = useUiText();
|
|
59282
|
+
const kindsQuery = trpc.storageAdmin.listKinds.useQuery({}, { staleTime: 3e4 });
|
|
59283
|
+
const [ownerParam, setOwner] = useUrlQueryParam(STORAGE_RECORDS_OWNER_PARAM);
|
|
59284
|
+
const [kindParam, setKind] = useUrlQueryParam(STORAGE_RECORDS_KIND_PARAM);
|
|
59285
|
+
const kinds = kindsQuery.data?.kinds ?? [];
|
|
59286
|
+
const filter = useDataTableFilter(kinds, {
|
|
59287
|
+
searchText: (row) => [
|
|
59288
|
+
row.owner,
|
|
59289
|
+
row.kind,
|
|
59290
|
+
row.class,
|
|
59291
|
+
row.database,
|
|
59292
|
+
row.state
|
|
59293
|
+
],
|
|
59294
|
+
facetGroups: [{
|
|
59295
|
+
id: "owner",
|
|
59296
|
+
value: (row) => row.owner
|
|
59297
|
+
}, {
|
|
59298
|
+
id: "database",
|
|
59299
|
+
value: (row) => row.database
|
|
59300
|
+
}]
|
|
59301
|
+
});
|
|
59302
|
+
const active = (0, react$1.useMemo)(() => {
|
|
59303
|
+
if (kinds.length === 0) return null;
|
|
59304
|
+
return kinds.find((k) => k.owner === ownerParam && k.kind === kindParam) ?? null;
|
|
59305
|
+
}, [
|
|
59306
|
+
kindParam,
|
|
59307
|
+
kinds,
|
|
59308
|
+
ownerParam
|
|
59309
|
+
]);
|
|
59310
|
+
const selectKind = (0, react$1.useCallback)((k) => {
|
|
59311
|
+
setOwner(k.owner);
|
|
59312
|
+
setKind(k.kind);
|
|
59313
|
+
}, [setKind, setOwner]);
|
|
59314
|
+
const columns = (0, react$1.useMemo)(() => {
|
|
59315
|
+
return [
|
|
59316
|
+
{
|
|
59317
|
+
key: "kind",
|
|
59318
|
+
header: t("uiLibrary.storageRecords.colKind", "Kind"),
|
|
59319
|
+
render: (row) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
59320
|
+
className: "font-mono text-xs",
|
|
59321
|
+
children: [
|
|
59322
|
+
row.owner,
|
|
59323
|
+
"/",
|
|
59324
|
+
row.kind
|
|
59325
|
+
]
|
|
59326
|
+
}),
|
|
59327
|
+
sortValue: (row) => kindKey(row)
|
|
59328
|
+
},
|
|
59329
|
+
{
|
|
59330
|
+
key: "database",
|
|
59331
|
+
header: t("uiLibrary.storageRecords.colDatabase", "DB"),
|
|
59332
|
+
nowrap: true,
|
|
59333
|
+
render: (row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Badge, {
|
|
59334
|
+
variant: "neutral",
|
|
59335
|
+
style: "outline",
|
|
59336
|
+
children: row.database
|
|
59337
|
+
}),
|
|
59338
|
+
sortValue: (row) => row.database
|
|
59339
|
+
},
|
|
59340
|
+
{
|
|
59341
|
+
key: "class",
|
|
59342
|
+
header: t("uiLibrary.storageRecords.colClass", "Class"),
|
|
59343
|
+
nowrap: true,
|
|
59344
|
+
render: (row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
59345
|
+
className: "text-xs text-foreground-muted",
|
|
59346
|
+
children: row.class
|
|
59347
|
+
}),
|
|
59348
|
+
sortValue: (row) => row.class
|
|
59349
|
+
},
|
|
59350
|
+
{
|
|
59351
|
+
key: "state",
|
|
59352
|
+
header: t("uiLibrary.storageRecords.colState", "State"),
|
|
59353
|
+
nowrap: true,
|
|
59354
|
+
render: (row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Badge, {
|
|
59355
|
+
variant: row.state === "declared" ? "default" : "neutral",
|
|
59356
|
+
children: row.state
|
|
59357
|
+
}),
|
|
59358
|
+
sortValue: (row) => row.state
|
|
59359
|
+
},
|
|
59360
|
+
{
|
|
59361
|
+
key: "rows",
|
|
59362
|
+
header: t("uiLibrary.storageRecords.colRows", "Rows"),
|
|
59363
|
+
align: "right",
|
|
59364
|
+
nowrap: true,
|
|
59365
|
+
render: (row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
59366
|
+
className: "font-mono text-xs tabular-nums",
|
|
59367
|
+
children: row.rows === null ? "—" : row.rows
|
|
59368
|
+
}),
|
|
59369
|
+
sortValue: (row) => row.rows ?? -1
|
|
59370
|
+
},
|
|
59371
|
+
{
|
|
59372
|
+
key: "bytes",
|
|
59373
|
+
header: t("uiLibrary.storageRecords.colSize", "Est. size"),
|
|
59374
|
+
align: "right",
|
|
59375
|
+
nowrap: true,
|
|
59376
|
+
render: (row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
59377
|
+
className: "text-xs text-foreground-subtle",
|
|
59378
|
+
children: formatBytes(row.bytesEstimate)
|
|
59379
|
+
}),
|
|
59380
|
+
sortValue: (row) => row.bytesEstimate ?? -1
|
|
59381
|
+
},
|
|
59382
|
+
{
|
|
59383
|
+
key: "inspect",
|
|
59384
|
+
header: "",
|
|
59385
|
+
align: "right",
|
|
59386
|
+
nowrap: true,
|
|
59387
|
+
render: (row) => {
|
|
59388
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
59389
|
+
type: "button",
|
|
59390
|
+
size: "sm",
|
|
59391
|
+
variant: active !== null && kindKey(active) === kindKey(row) ? "primary" : "outline",
|
|
59392
|
+
onClick: () => selectKind(row),
|
|
59393
|
+
children: t("uiLibrary.storageRecords.viewRows", "View rows")
|
|
59394
|
+
});
|
|
59395
|
+
},
|
|
59396
|
+
notSortableReason: t("uiLibrary.storageRecords.actionColumn", "Actions")
|
|
59397
|
+
}
|
|
59398
|
+
];
|
|
59399
|
+
}, [
|
|
59400
|
+
active,
|
|
59401
|
+
selectKind,
|
|
59402
|
+
t
|
|
59403
|
+
]);
|
|
59404
|
+
if (kindsQuery.isError) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ErrorBox, { message: kindsQuery.error instanceof Error ? kindsQuery.error.message : t("uiLibrary.storageRecords.kindsFailed", "Could not load record kinds") });
|
|
59405
|
+
if (kindsQuery.isPending) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
59406
|
+
className: "flex justify-center py-10 text-foreground-subtle",
|
|
59407
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LoaderCircle, { className: "h-5 w-5 animate-spin" })
|
|
59408
|
+
});
|
|
59409
|
+
if (kinds.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EmptyState, {
|
|
59410
|
+
title: t("uiLibrary.storageRecords.noKindsTitle", "No record kinds"),
|
|
59411
|
+
description: t("uiLibrary.storageRecords.noKindsDescription", "No addon has declared a record kind on this hub yet.")
|
|
59412
|
+
});
|
|
59413
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
59414
|
+
className: "space-y-4",
|
|
59415
|
+
children: [
|
|
59416
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DataTableFilterBar, {
|
|
59417
|
+
state: filter.state,
|
|
59418
|
+
facets: filter.facets,
|
|
59419
|
+
groups: filter.groups.map((g) => ({
|
|
59420
|
+
...g,
|
|
59421
|
+
label: g.id === "owner" ? t("uiLibrary.storageRecords.facetOwner", "Owner") : t("uiLibrary.storageRecords.facetDatabase", "Database")
|
|
59422
|
+
})),
|
|
59423
|
+
labels: {
|
|
59424
|
+
searchPlaceholder: t("uiLibrary.storageRecords.search", "Search kinds…"),
|
|
59425
|
+
searchLabel: t("uiLibrary.storageRecords.searchLabel", "Search record kinds"),
|
|
59426
|
+
facetsLabel: t("uiLibrary.storageRecords.facetsLabel", "Filter record kinds"),
|
|
59427
|
+
clearLabel: t("uiLibrary.storageRecords.clearFilters", "Clear filters")
|
|
59428
|
+
},
|
|
59429
|
+
onSearch: filter.setSearch,
|
|
59430
|
+
onToggleFacet: filter.toggleFacet,
|
|
59431
|
+
onToggleGroupFacet: filter.toggleGroupFacet,
|
|
59432
|
+
onClear: filter.clear
|
|
59433
|
+
}),
|
|
59434
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DataTable, {
|
|
59435
|
+
columns,
|
|
59436
|
+
rows: filter.rows,
|
|
59437
|
+
rowKey: (k) => kindKey(k),
|
|
59438
|
+
maxBodyHeight: TABLE_SCROLL_MAX_H,
|
|
59439
|
+
defaultSort: {
|
|
59440
|
+
columnKey: "kind",
|
|
59441
|
+
dir: "asc"
|
|
59442
|
+
},
|
|
59443
|
+
emptyMessage: t("uiLibrary.storageRecords.noMatch", "No kinds match the filter")
|
|
59444
|
+
}),
|
|
59445
|
+
active !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StorageRecordsPanel, { kind: active }) : null
|
|
59446
|
+
]
|
|
59447
|
+
});
|
|
59448
|
+
}
|
|
59449
|
+
//#endregion
|
|
58678
59450
|
//#region src/composites/response-log.tsx
|
|
58679
59451
|
/**
|
|
58680
59452
|
* Expandable log box showing the raw JSON response from a pipeline run.
|
|
@@ -63144,6 +63916,7 @@ exports.EllipsisVertical = EllipsisVertical;
|
|
|
63144
63916
|
exports.EmptyState = EmptyState;
|
|
63145
63917
|
exports.EndpointResultsCard = EndpointResultsCard;
|
|
63146
63918
|
exports.EndpointTestCard = EndpointTestCard;
|
|
63919
|
+
exports.EntryJsonSchemaForm = EntryJsonSchemaForm;
|
|
63147
63920
|
exports.ErrorBox = ErrorBox;
|
|
63148
63921
|
exports.EventKindGlyph = EventKindGlyph;
|
|
63149
63922
|
exports.EventStream = EventStream;
|
|
@@ -63331,6 +64104,8 @@ exports.QrCode = QrCode;
|
|
|
63331
64104
|
exports.QrCode$1 = QrCode$1;
|
|
63332
64105
|
exports.RECONNECT_POLICY = RECONNECT_POLICY;
|
|
63333
64106
|
exports.RECORDED_PLAYBACK_MODES = RECORDED_PLAYBACK_MODES;
|
|
64107
|
+
exports.RECORD_ENTRY_KIND_PARAM = RECORD_ENTRY_KIND_PARAM;
|
|
64108
|
+
exports.RECORD_ENTRY_OWNER_PARAM = RECORD_ENTRY_OWNER_PARAM;
|
|
63334
64109
|
exports.RIGHT = RIGHT;
|
|
63335
64110
|
exports.ROI_AREA_WARN_FRACTION = ROI_AREA_WARN_FRACTION;
|
|
63336
64111
|
exports.ROLE_DESCRIPTOR = ROLE_DESCRIPTOR;
|
|
@@ -63339,6 +64114,8 @@ exports.Radar = Radar;
|
|
|
63339
64114
|
exports.RadialGauge = RadialGauge;
|
|
63340
64115
|
exports.Radio = Radio;
|
|
63341
64116
|
exports.RadioTower = RadioTower;
|
|
64117
|
+
exports.RecordEntriesHub = RecordEntriesHub;
|
|
64118
|
+
exports.RecordEntriesPanel = RecordEntriesPanel;
|
|
63342
64119
|
exports.RecordedPlaybackProvider = RecordedPlaybackProvider;
|
|
63343
64120
|
exports.RecordingPanel = RecordingPanel;
|
|
63344
64121
|
exports.RefreshCw = RefreshCw;
|
|
@@ -63371,6 +64148,8 @@ exports.SPLIT_PANEL_SIDE = SPLIT_PANEL_SIDE;
|
|
|
63371
64148
|
exports.STACK_GAP = STACK_GAP;
|
|
63372
64149
|
exports.STATE_COLOR = STATE_COLOR;
|
|
63373
64150
|
exports.STEP_DOCK_WIDTH_PX = STEP_DOCK_WIDTH_PX;
|
|
64151
|
+
exports.STORAGE_RECORDS_KIND_PARAM = STORAGE_RECORDS_KIND_PARAM;
|
|
64152
|
+
exports.STORAGE_RECORDS_OWNER_PARAM = STORAGE_RECORDS_OWNER_PARAM;
|
|
63374
64153
|
exports.Sailboat = Sailboat;
|
|
63375
64154
|
exports.Save = Save;
|
|
63376
64155
|
exports.Scan = Scan;
|
|
@@ -63429,6 +64208,8 @@ exports.StepInspectorPanel = StepInspectorPanel;
|
|
|
63429
64208
|
exports.StepModelRow = StepModelRow;
|
|
63430
64209
|
exports.StepTimings = StepTimings;
|
|
63431
64210
|
exports.Stepper = Stepper;
|
|
64211
|
+
exports.StorageRecordsHub = StorageRecordsHub;
|
|
64212
|
+
exports.StorageRecordsPanel = StorageRecordsPanel;
|
|
63432
64213
|
exports.StreamBrokerSelector = StreamBrokerSelector;
|
|
63433
64214
|
exports.StreamDebugOverlay = StreamDebugOverlay;
|
|
63434
64215
|
exports.StreamPanel = StreamPanel;
|
|
@@ -63621,6 +64402,8 @@ exports.isDataTableFilterActive = isDataTableFilterActive;
|
|
|
63621
64402
|
exports.isFieldVisible = isFieldVisible;
|
|
63622
64403
|
exports.isGroupExpanded = isGroupExpanded;
|
|
63623
64404
|
exports.isKeychainFieldValue = isKeychainFieldValue;
|
|
64405
|
+
exports.isRedactedSecret = isRedactedSecret;
|
|
64406
|
+
exports.isSecretField = isSecretField;
|
|
63624
64407
|
exports.isValueField = isValueField;
|
|
63625
64408
|
exports.lawnMowerActivityMeta = lawnMowerActivityMeta;
|
|
63626
64409
|
exports.lightColors = require_theme_to_root_css.lightColors;
|
|
@@ -63629,6 +64412,7 @@ exports.loadRemoteBundle = loadRemoteBundle;
|
|
|
63629
64412
|
exports.lucideExportName = lucideExportName;
|
|
63630
64413
|
exports.makeScrubBridge = makeScrubBridge;
|
|
63631
64414
|
exports.mapRootDetectorPlacements = mapRootDetectorPlacements;
|
|
64415
|
+
exports.mergeSecretForSubmit = mergeSecretForSubmit;
|
|
63632
64416
|
exports.metadataEntries = metadataEntries;
|
|
63633
64417
|
exports.metadataString = metadataString;
|
|
63634
64418
|
exports.mirror = mirror;
|
|
@@ -63701,6 +64485,7 @@ exports.routerBasename = routerBasename;
|
|
|
63701
64485
|
exports.sameLocationSuggestions = sameLocationSuggestions;
|
|
63702
64486
|
exports.scrubReducer = scrubReducer;
|
|
63703
64487
|
exports.searchSteps = searchSteps;
|
|
64488
|
+
exports.secretDisplayValue = secretDisplayValue;
|
|
63704
64489
|
exports.secretSubmitValue = secretSubmitValue;
|
|
63705
64490
|
exports.selectPickerGroup = selectPickerGroup;
|
|
63706
64491
|
exports.selectedDeviceOptions = selectedDeviceOptions;
|
|
@@ -64692,6 +65477,10 @@ exports.usePtzSetHomePreset = usePtzSetHomePreset;
|
|
|
64692
65477
|
exports.usePtzSetHomeReturn = usePtzSetHomeReturn;
|
|
64693
65478
|
exports.usePtzStop = usePtzStop;
|
|
64694
65479
|
exports.useRebootReboot = useRebootReboot;
|
|
65480
|
+
exports.useRecordEntriesDeleteEntry = useRecordEntriesDeleteEntry;
|
|
65481
|
+
exports.useRecordEntriesListEntries = useRecordEntriesListEntries;
|
|
65482
|
+
exports.useRecordEntriesListEntryKinds = useRecordEntriesListEntryKinds;
|
|
65483
|
+
exports.useRecordEntriesPutEntry = useRecordEntriesPutEntry;
|
|
64695
65484
|
exports.useRecordedPlayback = useRecordedPlayback;
|
|
64696
65485
|
exports.useRecordingArchiveApplyDeviceSettingsPatch = useRecordingArchiveApplyDeviceSettingsPatch;
|
|
64697
65486
|
exports.useRecordingArchiveCancelRelocateJob = useRecordingArchiveCancelRelocateJob;
|