feat(map): render classified unit identity aspects

This commit is contained in:
Codex
2026-07-24 11:43:20 +03:00
parent 49f0c449c1
commit c859ae4db0
11 changed files with 320 additions and 7 deletions
+75
View File
@@ -199,6 +199,81 @@ test("restricted joined aspects render declared identifiers and remain fail-clos
assert.equal(rejected.tabs[0].empty, true);
});
test("restricted identity aspects render full identifiers and bounded string lists", () => {
const profile = {
id: "map.subject-detail.identity",
version: "1.0.0",
title: "Identity",
semanticTypes: ["map.moving_object"],
defaultTabId: "identity",
tabs: [{
id: "identity",
label: "Идентификация",
emptyMessage: "Нет данных",
sections: [{
id: "device",
label: "Устройство и доступ",
fields: [
{
id: "identity.imei",
aspectId: "identity",
source: "attribute",
field: "device_imei",
label: "IMEI",
format: "text",
dataClass: "restricted",
},
{
id: "identity.users",
aspectId: "identity",
source: "attribute",
field: "available_user_logins",
label: "Доступно пользователям",
format: "string_list",
dataClass: "restricted",
},
{
id: "identity.custom",
aspectId: "identity",
source: "attribute",
field: "custom_fields",
label: "Пользовательские поля",
format: "string_list",
dataClass: "restricted",
},
],
}],
}],
};
const card = buildMapSubjectCardModel(fact, {
profile,
aspects: {
identity: {
bindingId: "trike-unit-identity",
dataProductId: "fleet.units.identity.current.v1",
dataClass: "restricted",
fact: {
sourceId: fact.sourceId,
semanticType: "map.moving_object",
attributes: {
device_imei: "867236078012345",
available_user_logins: ["vasya", "dispatcher"],
custom_fields: ["Serial number=5015", "Battery number=8032"],
},
},
},
},
});
assert.deepEqual(
card.tabs[0].sections[0].rows.map((row) => row.value),
[
"867236078012345",
"vasya • dispatcher",
"Serial number: 5015 • Battery number: 8032",
],
);
});
test("Cesium pin and label share one entity selection that opens the subject card", async () => {
const [renderer, preview] = await Promise.all([
readFile(new URL("../apps/catalog/src/CesiumMapRenderer.tsx", import.meta.url), "utf8"),
+1 -1
View File
@@ -104,7 +104,7 @@ for (const policy of consumerPolicies) {
requireValue(contract?.fieldProjection?.every((value) => typeof value === "string" && /^[A-Za-z][A-Za-z0-9_.-]{0,127}$/.test(value)), `invalid consumer projection field: ${policy.id ?? "unknown"}`);
requireValue(contract?.fieldTypes && typeof contract.fieldTypes === "object" && !Array.isArray(contract.fieldTypes), `invalid consumer field types: ${policy.id ?? "unknown"}`);
requireValue(JSON.stringify(Object.keys(contract?.fieldTypes ?? {})) === JSON.stringify(contract?.fieldProjection ?? []), `consumer field type order/scope mismatch: ${policy.id ?? "unknown"}`);
requireValue(Object.values(contract?.fieldTypes ?? {}).every((value) => ["string", "number", "boolean"].includes(value)), `invalid consumer field type: ${policy.id ?? "unknown"}`);
requireValue(Object.values(contract?.fieldTypes ?? {}).every((value) => ["string", "number", "boolean", "string_array"].includes(value)), `invalid consumer field type: ${policy.id ?? "unknown"}`);
const supportedGeometryTypes = ["Point", "LineString", "Polygon", "MultiPolygon"];
requireValue(
Array.isArray(contract?.geometryTypes)