prince-ui-bo4e 0.3.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,592 @@
1
+ import { parseAbsolute } from '@internationalized/date';
2
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
+ import { Icon, Popover, Select, SelectItem, Switch, NumberField, DateField, TextField } from 'prince-ui';
4
+ import { DialogTrigger, Button, Dialog } from 'react-aria-components';
5
+ import { useState, useMemo } from 'react';
6
+
7
+ // src/core/datetime.ts
8
+ var BERLIN = "Europe/Berlin";
9
+ function isIsoDate(v) {
10
+ return typeof v === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/.test(v);
11
+ }
12
+ function parts(iso) {
13
+ const f = new Intl.DateTimeFormat("de-DE", {
14
+ timeZone: BERLIN,
15
+ year: "numeric",
16
+ month: "2-digit",
17
+ day: "2-digit",
18
+ hour: "2-digit",
19
+ minute: "2-digit",
20
+ hour12: false
21
+ });
22
+ const p = {};
23
+ for (const { type, value } of f.formatToParts(new Date(iso))) p[type] = value;
24
+ return p;
25
+ }
26
+ function formatDateDE(iso, opts = {}) {
27
+ if (!isIsoDate(iso)) return iso;
28
+ const p = parts(iso);
29
+ const date = `${p.day}.${p.month}.${p.year}`;
30
+ return opts.withTime ? `${date} ${p.hour}:${p.minute}` : date;
31
+ }
32
+ function toUtcLabel(iso) {
33
+ if (!isIsoDate(iso)) return iso;
34
+ return new Date(iso).toISOString().replace(/\.\d{3}Z$/, "Z");
35
+ }
36
+ function zonedLabelWithTz(iso) {
37
+ if (!isIsoDate(iso)) return iso;
38
+ const tz = new Intl.DateTimeFormat("de-DE", { timeZone: BERLIN, timeZoneName: "short" }).formatToParts(new Date(iso)).find((x) => x.type === "timeZoneName")?.value ?? "";
39
+ return `${formatDateDE(iso, { withTime: true })}${tz ? ` ${tz}` : ""}`;
40
+ }
41
+
42
+ // src/core/validity.ts
43
+ function validityStatus(range, now = /* @__PURE__ */ new Date()) {
44
+ const t = now.getTime();
45
+ const start = range.startdatum ? new Date(range.startdatum).getTime() : null;
46
+ const end = range.enddatum ? new Date(range.enddatum).getTime() : null;
47
+ if (start != null && start > t) return "zukuenftig";
48
+ if (end != null) {
49
+ if (end < t) return "abgelaufen";
50
+ if (end < t + 30 * 864e5) return "laeuftBald";
51
+ }
52
+ return "aktiv";
53
+ }
54
+
55
+ // src/core/anomalies.ts
56
+ var END_KEYS = /gueltigBis$|enddatum$|vertragsende$/i;
57
+ var DUMMY_OBJECT_KEYS = /geokoordinaten|katasteradresse/i;
58
+ function isDummyOnes(o) {
59
+ const strings = Object.values(o).filter((x) => typeof x === "string");
60
+ return strings.length > 0 && strings.every((x) => x === "1");
61
+ }
62
+ function scanAnomalies(node, opts = {}) {
63
+ const now = (opts.now ?? /* @__PURE__ */ new Date()).getTime();
64
+ const out = [];
65
+ const walk = (v, path, key) => {
66
+ if (v == null) return;
67
+ if (typeof v === "string") {
68
+ if (/#.+#/.test(v)) {
69
+ out.push({ severity: "warn", path, value: v, rule: "placeholder", message: `Platzhalter nicht ersetzt: ${v}` });
70
+ }
71
+ if (/[<>]/.test(v)) {
72
+ out.push({ severity: "warn", path, value: v, rule: "suspiciousChar", message: `Sonderzeichen im Wert: ${v}` });
73
+ }
74
+ if (v === "00000000") {
75
+ out.push({ severity: "warn", path, value: v, rule: "defaultValue", message: `Leerer Default-Wert (${key})` });
76
+ }
77
+ if (isIsoDate(v) && new Date(v).getUTCFullYear() <= 1950) {
78
+ out.push({ severity: "warn", path, value: v, rule: "implausibleDate", message: `Unplausibles Datum: ${v}` });
79
+ }
80
+ if (isIsoDate(v) && END_KEYS.test(key) && new Date(v).getTime() < now) {
81
+ out.push({ severity: "warn", path, value: v, rule: "expired", message: `Abgelaufen (${key}): ${v}` });
82
+ }
83
+ return;
84
+ }
85
+ if (Array.isArray(v)) {
86
+ v.forEach((it, i) => walk(it, `${path}[${i}]`, key));
87
+ return;
88
+ }
89
+ if (typeof v === "object") {
90
+ const obj = v;
91
+ if (DUMMY_OBJECT_KEYS.test(key) && isDummyOnes(obj)) {
92
+ out.push({ severity: "warn", path, value: v, rule: "placeholderObject", message: `Platzhalter-Daten (${key}): alle Werte \u201E1"` });
93
+ }
94
+ for (const k of Object.keys(obj)) {
95
+ walk(obj[k], path ? `${path}.${k}` : k, k);
96
+ }
97
+ }
98
+ };
99
+ walk(node, "", "");
100
+ const seen = /* @__PURE__ */ new Set();
101
+ return out.filter((a) => {
102
+ const key = `${a.rule}|${typeof a.value === "object" ? a.path : String(a.value)}`;
103
+ if (seen.has(key)) return false;
104
+ seen.add(key);
105
+ return true;
106
+ });
107
+ }
108
+
109
+ // src/core/humanize.ts
110
+ var LAB = {
111
+ marktlokationsId: "Marktlokations-ID",
112
+ messlokationsId: "Messlokations-ID",
113
+ boTyp: "BO-Typ",
114
+ versionStruktur: "Versionsstruktur",
115
+ eMailAdresse: "E-Mail-Adresse",
116
+ netzbetreiberCodeNr: "Netzbetreiber-Codenr.",
117
+ grundversorgerCodeNr: "Grundversorger-Codenr.",
118
+ rollencodenummer: "Rollencodenummer",
119
+ rollencodetyp: "Rollencodetyp",
120
+ pruefidentifikator: "Pr\xFCfidentifikator",
121
+ zugehoerigeMesslokationen: "Zugeh\xF6rige Messlokationen",
122
+ messtechnischeEinordnung: "Messtechnische Einordnung",
123
+ partneradresse: "Partneradresse",
124
+ lokationsadresse: "Lokationsadresse",
125
+ marktrollen: "Marktrollen",
126
+ zaehlwerke: "Z\xE4hlwerke",
127
+ verbrauchsmenge: "Verbrauchsmenge",
128
+ geschaeftspartnerrolle: "Gesch\xE4ftspartnerrolle",
129
+ messstellenbetreiberEigenschaft: "MSB-Eigenschaft",
130
+ transaktionsReferenznummer: "Transaktions-Referenznr.",
131
+ datenaustauschreferenz: "Datenaustauschreferenz",
132
+ ediTyp: "EDI-Typ",
133
+ ediVersion: "EDI-Version",
134
+ ediEmpfangsDatum: "EDI-Empfangsdatum",
135
+ marktpartnerRolle: "Marktpartner-Rolle",
136
+ contrlReferenz: "CONTRL-Referenz",
137
+ transaktionsgrund: "Transaktionsgrund",
138
+ vorgangsnummer: "Vorgangsnummer",
139
+ dokumentennummer: "Dokumentennummer",
140
+ nachrichtendatum: "Nachrichtendatum",
141
+ nachrichtenreferenznummer: "Nachrichtenreferenznummer"
142
+ };
143
+ function humanize(key) {
144
+ const known = LAB[key];
145
+ if (known) return known;
146
+ if (/^[A-Z0-9]+(_[A-Z0-9]+)*$/.test(key)) {
147
+ return key.split("_").map((w) => w.charAt(0) + w.slice(1).toLowerCase()).join(" ");
148
+ }
149
+ const s = key.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/_/g, " ");
150
+ return s.charAt(0).toUpperCase() + s.slice(1);
151
+ }
152
+ var BERLIN2 = "Europe/Berlin";
153
+ function isoToBerlin(iso) {
154
+ return parseAbsolute(iso, BERLIN2);
155
+ }
156
+ function berlinToIso(z) {
157
+ return z.toAbsoluteString().replace(/\.\d{3}(?=Z$)/, "");
158
+ }
159
+
160
+ // src/schema/load-schema.ts
161
+ function loadBo4eSchema(src) {
162
+ return {
163
+ fields: src.fields,
164
+ enums: src.enums,
165
+ bos: src.bos
166
+ };
167
+ }
168
+ function resolveField(schema, boTyp, key) {
169
+ const raw = schema.fields[boTyp]?.[key];
170
+ let enumDoc;
171
+ const enumRef = raw?.enumRef;
172
+ if (enumRef) {
173
+ const e = schema.enums[enumRef];
174
+ if (e) enumDoc = { description: e.description, values: e.values.map((v) => v.value) };
175
+ }
176
+ return {
177
+ translation: raw?.translation ?? humanize(key),
178
+ description: raw?.description,
179
+ example: raw?.example,
180
+ pattern: raw?.pattern,
181
+ enumRef,
182
+ enum: enumDoc
183
+ };
184
+ }
185
+ function getFieldOrder(schema, boTyp) {
186
+ return schema.bos[boTyp]?.properties ?? Object.keys(schema.fields[boTyp] ?? {});
187
+ }
188
+
189
+ // src/normalize.ts
190
+ function isCDoc(x) {
191
+ return typeof x === "object" && x != null && !Array.isArray(x) && "content" in x && !("boTyp" in x);
192
+ }
193
+ function normalizeToCDoc(input) {
194
+ if (isCDoc(input)) return input;
195
+ const list = Array.isArray(input) ? input : [input];
196
+ const stammdaten = {};
197
+ for (const o of list) {
198
+ const k = o.boTyp ?? "UNBEKANNT";
199
+ const bucket = stammdaten[k] ??= [];
200
+ bucket.push(o);
201
+ }
202
+ return { id: "", businessKey: "", content: { OUTBOUND: { stammdaten } } };
203
+ }
204
+ function ValidityRange({ start, end, now }) {
205
+ const state = validityStatus({ startdatum: start ?? void 0, enddatum: end ?? void 0 }, now);
206
+ const hint = state === "abgelaufen" ? "abgelaufen" : state === "laeuftBald" ? "l\xE4uft bald" : state === "zukuenftig" ? "k\xFCnftig" : "";
207
+ const text = [start ? formatDateDE(start) : "", end ? formatDateDE(end) : ""].filter(Boolean).join(" \u2013 ");
208
+ return /* @__PURE__ */ jsxs("span", { className: "prn-bo-vrange", "data-state": state, children: [
209
+ /* @__PURE__ */ jsx("span", { className: "dot" }),
210
+ text,
211
+ hint ? ` \xB7 ${hint}` : ""
212
+ ] });
213
+ }
214
+ function AddressBlock({ adresse }) {
215
+ const a = adresse;
216
+ const line1 = [a.strasse, a.hausnummer].filter(Boolean).join(" ");
217
+ const line2 = [a.postleitzahl, a.ort].filter(Boolean).join(" ") + (a.ortsteil ? ` \xB7 ${a.ortsteil}` : "");
218
+ return /* @__PURE__ */ jsxs("div", { className: "prn-bo-addr", children: [
219
+ /* @__PURE__ */ jsx(Icon, { name: "pin" }),
220
+ /* @__PURE__ */ jsxs("div", { children: [
221
+ line1 ? /* @__PURE__ */ jsx("div", { children: line1 }) : null,
222
+ /* @__PURE__ */ jsx("div", { className: "l2", children: line2 }),
223
+ a.landescode ? /* @__PURE__ */ jsx("div", { className: "l3", children: a.landescode }) : null
224
+ ] })
225
+ ] });
226
+ }
227
+ function CodeResolved({ code, codetyp, resolvers }) {
228
+ const info = resolvers?.marktpartner?.(code, codetyp);
229
+ return /* @__PURE__ */ jsxs("span", { className: "prn-bo-code", children: [
230
+ info ? /* @__PURE__ */ jsx("span", { className: "nm", children: info.name }) : /* @__PURE__ */ jsx("span", { children: code }),
231
+ info ? /* @__PURE__ */ jsx("span", { className: "raw", children: code }) : null
232
+ ] });
233
+ }
234
+ function IdentityHeader({ icon, title, subtitle, trailing }) {
235
+ return /* @__PURE__ */ jsxs("div", { className: "prn-bo-idh", children: [
236
+ icon ? /* @__PURE__ */ jsx("span", { className: "ico", children: icon }) : null,
237
+ /* @__PURE__ */ jsxs("div", { children: [
238
+ /* @__PURE__ */ jsx("div", { className: "ttl", children: title }),
239
+ subtitle ? /* @__PURE__ */ jsx("div", { className: "sub", children: subtitle }) : null
240
+ ] }),
241
+ trailing ? /* @__PURE__ */ jsx("div", { className: "pill", children: trailing }) : null
242
+ ] });
243
+ }
244
+ var MAP = {
245
+ SPARTE: { STROM: "bolt", GAS: "flame", WASSER: "droplet", ABWASSER: "droplet" },
246
+ ENERGIERICHTUNG: { AUSSP: "arrow-down-right", EINSP: "arrow-up-right" }
247
+ };
248
+ function EnumIcon({ enumName, value }) {
249
+ const name = MAP[enumName]?.[value];
250
+ if (!name) return null;
251
+ return /* @__PURE__ */ jsx(Icon, { name });
252
+ }
253
+ function EnumBadge({ tone = "neutral", icon, children }) {
254
+ return /* @__PURE__ */ jsxs("span", { className: "prn-bo-badge", "data-tone": tone, children: [
255
+ icon,
256
+ children
257
+ ] });
258
+ }
259
+ function SchemaPopoverBody({ fieldKey, doc, value }) {
260
+ const isDate = isIsoDate(value);
261
+ return /* @__PURE__ */ jsxs("div", { children: [
262
+ /* @__PURE__ */ jsx("h4", { children: doc.translation }),
263
+ /* @__PURE__ */ jsxs("div", { className: "key", children: [
264
+ fieldKey,
265
+ doc.enumRef ? ` \xB7 enum ${doc.enumRef}` : ""
266
+ ] }),
267
+ doc.description ? /* @__PURE__ */ jsx("div", { className: "desc", children: doc.description }) : /* @__PURE__ */ jsx("div", { className: "desc", "data-nodoc": "true", children: "Keine Schema-Beschreibung hinterlegt \u2013 im echten Editor aus dem JSON-Schema-Repo." }),
268
+ isDate ? /* @__PURE__ */ jsxs(Fragment, { children: [
269
+ /* @__PURE__ */ jsxs("div", { className: "row", children: [
270
+ /* @__PURE__ */ jsx("span", { children: "Angezeigt (Berlin)" }),
271
+ /* @__PURE__ */ jsx("span", { className: "mono", children: zonedLabelWithTz(value) })
272
+ ] }),
273
+ /* @__PURE__ */ jsxs("div", { className: "row", children: [
274
+ /* @__PURE__ */ jsx("span", { children: "\xDCbermittelt (UTC)" }),
275
+ /* @__PURE__ */ jsx("span", { className: "mono", children: toUtcLabel(value) })
276
+ ] })
277
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
278
+ doc.pattern ? /* @__PURE__ */ jsxs("div", { className: "row", children: [
279
+ /* @__PURE__ */ jsx("span", { children: "Pattern" }),
280
+ /* @__PURE__ */ jsx("span", { className: "mono", children: doc.pattern })
281
+ ] }) : null,
282
+ doc.example ? /* @__PURE__ */ jsxs("div", { className: "row", children: [
283
+ /* @__PURE__ */ jsx("span", { children: "Beispiel" }),
284
+ /* @__PURE__ */ jsx("span", { className: "mono", children: doc.example })
285
+ ] }) : null
286
+ ] }),
287
+ doc.enum ? /* @__PURE__ */ jsxs(Fragment, { children: [
288
+ /* @__PURE__ */ jsx("div", { className: "enh", children: "M\xF6gliche Werte" }),
289
+ /* @__PURE__ */ jsx("div", { className: "enums", children: doc.enum.values.map((v) => /* @__PURE__ */ jsx("span", { className: "ev", "data-on": v === value, children: v }, v)) })
290
+ ] }) : null
291
+ ] });
292
+ }
293
+ function SchemaField({ schema, boTyp, fieldKey, value, children }) {
294
+ const doc = resolveField(schema, boTyp, fieldKey);
295
+ return /* @__PURE__ */ jsxs(DialogTrigger, { children: [
296
+ /* @__PURE__ */ jsx(Button, { className: "prn-bo-field", children }),
297
+ /* @__PURE__ */ jsx(Popover, { children: /* @__PURE__ */ jsx(Dialog, { className: "prn-bo-pop", children: /* @__PURE__ */ jsx(SchemaPopoverBody, { fieldKey, doc, value }) }) })
298
+ ] });
299
+ }
300
+ function ContactLine({ person }) {
301
+ const name = [person.anrede, person.name1, person.name2, person.name3].filter(Boolean).join(" ");
302
+ const init = `${person.name1?.[0] ?? ""}${person.name2?.[0] ?? ""}`.toUpperCase();
303
+ const kw = person.kontaktweg ?? [];
304
+ const sub = [(person.geschaeftspartnerrolle ?? []).join(", "), person.eMailAdresse].filter(Boolean).join(" \xB7 ");
305
+ return /* @__PURE__ */ jsxs("div", { className: "prn-bo-contact", children: [
306
+ /* @__PURE__ */ jsx("div", { className: "av", children: init }),
307
+ /* @__PURE__ */ jsxs("div", { children: [
308
+ /* @__PURE__ */ jsx("div", { className: "cn", children: name }),
309
+ sub ? /* @__PURE__ */ jsx("div", { className: "csub", children: sub }) : null
310
+ ] }),
311
+ /* @__PURE__ */ jsxs("div", { className: "cways", children: [
312
+ /* @__PURE__ */ jsx(
313
+ "a",
314
+ {
315
+ className: "cway",
316
+ "data-off": !kw.includes("E_MAIL"),
317
+ href: person.eMailAdresse ? `mailto:${person.eMailAdresse}` : void 0,
318
+ "aria-label": "E-Mail",
319
+ children: /* @__PURE__ */ jsx(Icon, { name: "mail" })
320
+ }
321
+ ),
322
+ /* @__PURE__ */ jsx("span", { className: "cway", "data-off": !kw.includes("TELEFONAT"), "aria-label": "Telefon", children: /* @__PURE__ */ jsx(Icon, { name: "phone" }) })
323
+ ] })
324
+ ] });
325
+ }
326
+ function MarktpartnerRow({ row, resolvers, now }) {
327
+ const gz = row.gueltigkeitszeitraum ?? {};
328
+ const dim = validityStatus({ startdatum: gz.startdatum, enddatum: gz.enddatum }, now) === "abgelaufen";
329
+ return /* @__PURE__ */ jsxs("div", { className: "prn-bo-mpr", "data-dim": dim, children: [
330
+ /* @__PURE__ */ jsx("span", { className: "rtag", children: String(row.marktrolle ?? "\u2014") }),
331
+ /* @__PURE__ */ jsxs("div", { className: "mid", children: [
332
+ /* @__PURE__ */ jsx("div", { className: "mnm", children: /* @__PURE__ */ jsx(
333
+ CodeResolved,
334
+ {
335
+ code: String(row.rollencodenummer ?? ""),
336
+ codetyp: row.rollencodetyp ? String(row.rollencodetyp) : void 0,
337
+ resolvers
338
+ }
339
+ ) }),
340
+ row.messstellenbetreiberEigenschaft ? /* @__PURE__ */ jsx("div", { className: "meig", children: row.messstellenbetreiberEigenschaft === "GRUNDZUSTAENDIGER_MESSSTELLENBETREIBER" ? "grundzust\xE4ndig" : humanize(String(row.messstellenbetreiberEigenschaft)) }) : null
341
+ ] }),
342
+ gz.startdatum || gz.enddatum ? /* @__PURE__ */ jsx(ValidityRange, { start: gz.startdatum, end: gz.enddatum, now }) : null
343
+ ] });
344
+ }
345
+ function EditableValue({ doc, value, onChange }) {
346
+ const label = doc.translation ?? "";
347
+ if (doc.enum) {
348
+ return /* @__PURE__ */ jsx(
349
+ Select,
350
+ {
351
+ "aria-label": label,
352
+ selectedKey: value == null ? null : String(value),
353
+ onSelectionChange: (k) => onChange(k),
354
+ children: doc.enum.values.map((v) => /* @__PURE__ */ jsx(SelectItem, { id: v, children: v }, v))
355
+ }
356
+ );
357
+ }
358
+ if (typeof value === "boolean") {
359
+ return /* @__PURE__ */ jsx(Switch, { isSelected: value, onChange, children: value ? "Ja" : "Nein" });
360
+ }
361
+ if (typeof value === "number") {
362
+ return /* @__PURE__ */ jsx(NumberField, { "aria-label": label, value, onChange });
363
+ }
364
+ if (isIsoDate(value)) {
365
+ return /* @__PURE__ */ jsx(
366
+ DateField,
367
+ {
368
+ "aria-label": label,
369
+ granularity: "minute",
370
+ value: isoToBerlin(value),
371
+ onChange: (z) => z && onChange(berlinToIso(z))
372
+ }
373
+ );
374
+ }
375
+ return /* @__PURE__ */ jsx(TextField, { "aria-label": label, value: value == null ? "" : String(value), onChange });
376
+ }
377
+
378
+ // src/view/value-format.ts
379
+ function isScalarish(v) {
380
+ if (v == null) return true;
381
+ if (Array.isArray(v)) return v.every((x) => x == null || typeof x !== "object");
382
+ return typeof v !== "object";
383
+ }
384
+ function displayValue(v) {
385
+ if (v == null) return { text: "\u2014", isNull: true };
386
+ if (Array.isArray(v)) return { text: v.join(", "), isNull: false };
387
+ if (isIsoDate(v)) return { text: formatDateDE(v, { withTime: true }), isNull: false };
388
+ if (typeof v === "boolean") return { text: v ? "Ja" : "Nein", isNull: false };
389
+ return { text: String(v), isNull: false };
390
+ }
391
+ function FullDetail({ schema, boTyp, obj, editable = false }) {
392
+ const [draft, setDraft] = useState(obj);
393
+ const source = editable ? draft : obj;
394
+ const keys = Object.keys(source).filter((k) => isScalarish(source[k]));
395
+ if (!editable) {
396
+ return /* @__PURE__ */ jsx("div", { className: "prn-bo-kvgrid", children: keys.map((k) => {
397
+ const d = displayValue(obj[k]);
398
+ return /* @__PURE__ */ jsxs("div", { className: "prn-bo-kv", children: [
399
+ /* @__PURE__ */ jsx("span", { className: "k", children: /* @__PURE__ */ jsx(SchemaField, { schema, boTyp, fieldKey: k, value: obj[k], children: /* @__PURE__ */ jsx("span", { children: humanize(k) }) }) }),
400
+ /* @__PURE__ */ jsx("span", { className: "v", "data-null": d.isNull, title: d.text, children: d.text })
401
+ ] }, k);
402
+ }) });
403
+ }
404
+ const setField = (k, v) => setDraft((d) => ({ ...d, [k]: v }));
405
+ const candidates = getFieldOrder(schema, boTyp).filter((k) => !(k in draft));
406
+ return /* @__PURE__ */ jsxs("div", { children: [
407
+ /* @__PURE__ */ jsx("div", { className: "prn-bo-kvgrid", children: keys.map((k) => /* @__PURE__ */ jsxs("div", { className: "prn-bo-kv", children: [
408
+ /* @__PURE__ */ jsx("span", { className: "k", children: /* @__PURE__ */ jsx(SchemaField, { schema, boTyp, fieldKey: k, value: draft[k], children: /* @__PURE__ */ jsx("span", { children: humanize(k) }) }) }),
409
+ /* @__PURE__ */ jsx("span", { className: "v", children: /* @__PURE__ */ jsx(EditableValue, { doc: resolveField(schema, boTyp, k), value: draft[k], onChange: (v) => setField(k, v) }) })
410
+ ] }, k)) }),
411
+ candidates.length ? /* @__PURE__ */ jsx("div", { className: "prn-bo-addfield", children: /* @__PURE__ */ jsx(
412
+ Select,
413
+ {
414
+ "aria-label": "Feld hinzuf\xFCgen",
415
+ placeholder: "+ Feld hinzuf\xFCgen",
416
+ selectedKey: null,
417
+ onSelectionChange: (k) => k && setField(String(k), ""),
418
+ children: candidates.map((c) => /* @__PURE__ */ jsx(SelectItem, { id: c, children: humanize(c) }, c))
419
+ }
420
+ ) }) : null
421
+ ] });
422
+ }
423
+ function SmartObjectCard({ schema, boTyp, obj, header, children }) {
424
+ const [full, setFull] = useState(false);
425
+ const [edit, setEdit] = useState(false);
426
+ return /* @__PURE__ */ jsxs("div", { className: "prn-bo-card", children: [
427
+ header,
428
+ children,
429
+ full ? /* @__PURE__ */ jsxs(Fragment, { children: [
430
+ /* @__PURE__ */ jsx("div", { className: "prn-bo-detailbar", children: /* @__PURE__ */ jsx("button", { type: "button", className: "prn-bo-editbtn", "aria-pressed": edit, onClick: () => setEdit((e) => !e), children: edit ? "Fertig" : "Bearbeiten" }) }),
431
+ /* @__PURE__ */ jsx(FullDetail, { schema, boTyp, obj, editable: edit })
432
+ ] }) : /* @__PURE__ */ jsx("button", { type: "button", className: "prn-bo-moreb", onClick: () => setFull(true), children: "+ Alle Details" })
433
+ ] });
434
+ }
435
+ function GenericBody({ schema, boTyp, obj }) {
436
+ const ordered = getFieldOrder(schema, boTyp).filter((k) => k in obj && isScalarish(obj[k]));
437
+ const extra = Object.keys(obj).filter((k) => isScalarish(obj[k]) && !ordered.includes(k));
438
+ const keys = [...ordered, ...extra].slice(0, 6);
439
+ return /* @__PURE__ */ jsx("div", { className: "prn-bo-grp", children: keys.map((k) => /* @__PURE__ */ jsxs("div", { className: "prn-bo-line", children: [
440
+ /* @__PURE__ */ jsx(SchemaField, { schema, boTyp, fieldKey: k, value: obj[k], children: /* @__PURE__ */ jsx("span", { className: "lk", children: humanize(k) }) }),
441
+ /* @__PURE__ */ jsx("span", { className: "lv", children: displayValue(obj[k]).text })
442
+ ] }, k)) });
443
+ }
444
+ function MarktlokationHeader({ schema, obj }) {
445
+ const typArr = obj.marktlokationsTyp;
446
+ const typ = typArr?.[0]?.typ;
447
+ const trailing = typ === "STANDARD_MARKTLOKATION" ? "Standard" : typ;
448
+ return /* @__PURE__ */ jsx(
449
+ IdentityHeader,
450
+ {
451
+ icon: /* @__PURE__ */ jsx(SchemaField, { schema, boTyp: "MARKTLOKATION", fieldKey: "sparte", value: obj.sparte, children: /* @__PURE__ */ jsx(EnumIcon, { enumName: "SPARTE", value: String(obj.sparte) }) }),
452
+ title: `MaLo ${String(obj.marktlokationsId ?? "")}`,
453
+ subtitle: "Marktlokation",
454
+ trailing
455
+ }
456
+ );
457
+ }
458
+ function MarktlokationBody({
459
+ schema,
460
+ obj,
461
+ resolvers,
462
+ now
463
+ }) {
464
+ const rollen = obj.marktrollen ?? [];
465
+ const expired = (r) => validityStatus({ enddatum: r.gueltigkeitszeitraum?.enddatum }, now) === "abgelaufen" ? 1 : 0;
466
+ const sorted = [...rollen].sort((a, b) => expired(a) - expired(b));
467
+ const endkunde = obj.endkunde;
468
+ const badge = (key, tone, label, icon) => /* @__PURE__ */ jsx(SchemaField, { schema, boTyp: "MARKTLOKATION", fieldKey: key, value: obj[key], children: /* @__PURE__ */ jsx(EnumBadge, { tone, icon, children: label }) });
469
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
470
+ /* @__PURE__ */ jsxs("div", { className: "prn-bo-grp prn-bo-badges", children: [
471
+ obj.energierichtung ? badge(
472
+ "energierichtung",
473
+ "neutral",
474
+ obj.energierichtung === "AUSSP" ? "Verbrauch" : "Erzeugung",
475
+ /* @__PURE__ */ jsx(EnumIcon, { enumName: "ENERGIERICHTUNG", value: String(obj.energierichtung) })
476
+ ) : null,
477
+ obj.bilanzierungsmethode ? badge("bilanzierungsmethode", "neutral", String(obj.bilanzierungsmethode)) : null,
478
+ obj.netzebene ? badge("netzebene", "neutral", String(obj.netzebene)) : null,
479
+ obj.sperrstatus ? badge("sperrstatus", obj.sperrstatus === "ENTSPERRT" ? "positive" : "negative", String(obj.sperrstatus).toLowerCase()) : null
480
+ ] }),
481
+ endkunde ? /* @__PURE__ */ jsxs("div", { className: "prn-bo-grp", children: [
482
+ /* @__PURE__ */ jsx("div", { className: "prn-bo-grph", children: "Endkunde" }),
483
+ /* @__PURE__ */ jsx(ContactLine, { person: endkunde }),
484
+ endkunde.partneradresse ? /* @__PURE__ */ jsx(AddressBlock, { adresse: endkunde.partneradresse }) : null
485
+ ] }) : null,
486
+ sorted.length ? /* @__PURE__ */ jsxs("div", { className: "prn-bo-grp", children: [
487
+ /* @__PURE__ */ jsxs("div", { className: "prn-bo-grph", children: [
488
+ "Marktrollen \xB7 ",
489
+ sorted.length
490
+ ] }),
491
+ sorted.map((r, i) => /* @__PURE__ */ jsx(MarktpartnerRow, { row: r, resolvers, now }, i))
492
+ ] }) : null
493
+ ] });
494
+ }
495
+ function SmartObjectView({ schema, obj, resolvers, now }) {
496
+ const boTyp = obj.boTyp ?? "UNKNOWN";
497
+ if (boTyp === "MARKTLOKATION") {
498
+ return /* @__PURE__ */ jsx(
499
+ SmartObjectCard,
500
+ {
501
+ schema,
502
+ boTyp,
503
+ obj,
504
+ header: /* @__PURE__ */ jsx(MarktlokationHeader, { schema, obj }),
505
+ children: /* @__PURE__ */ jsx(MarktlokationBody, { schema, obj, resolvers, now })
506
+ }
507
+ );
508
+ }
509
+ return /* @__PURE__ */ jsx(SmartObjectCard, { schema, boTyp, obj, children: /* @__PURE__ */ jsx(GenericBody, { schema, boTyp, obj }) });
510
+ }
511
+ var TRANS = "__TRANS__";
512
+ var ZUSATZ = "__ZUSATZ__";
513
+ function SydocView({ doc, schema, resolvers, now }) {
514
+ const cdoc = normalizeToCDoc(doc);
515
+ const directions = Object.keys(cdoc.content);
516
+ const [dir, setDir] = useState(() => directions.includes("OUTBOUND") ? "OUTBOUND" : directions[0] ?? "");
517
+ const [tab, setTab] = useState(null);
518
+ const dd = cdoc.content[dir];
519
+ const anomalies = useMemo(() => dd ? scanAnomalies(dd, { now }) : [], [dd, now]);
520
+ if (!dd) return null;
521
+ const tabs = [
522
+ ...Object.keys(dd.stammdaten),
523
+ ...dd.transaktionsdaten ? [TRANS] : [],
524
+ ...dd.zusatzdaten ? [ZUSATZ] : []
525
+ ];
526
+ const active = tab && tabs.includes(tab) ? tab : tabs[0] ?? "";
527
+ const tabLabel = (t) => t === TRANS ? "Transaktionsdaten" : t === ZUSATZ ? "Zusatzdaten" : humanize(t);
528
+ const renderActive = () => {
529
+ if (active === TRANS && dd.transaktionsdaten) {
530
+ return /* @__PURE__ */ jsx(SmartObjectView, { schema, obj: dd.transaktionsdaten, resolvers, now });
531
+ }
532
+ if (active === ZUSATZ && dd.zusatzdaten) {
533
+ return /* @__PURE__ */ jsx(SmartObjectView, { schema, obj: dd.zusatzdaten, resolvers, now });
534
+ }
535
+ const list = dd.stammdaten[active] ?? [];
536
+ return list.map((obj, i) => /* @__PURE__ */ jsx(SmartObjectView, { schema, obj, resolvers, now }, i));
537
+ };
538
+ return /* @__PURE__ */ jsxs("div", { className: "prn-bo-app", children: [
539
+ directions.length > 1 ? /* @__PURE__ */ jsx("div", { className: "prn-bo-tabs", role: "tablist", "aria-label": "Richtung", children: directions.map((d) => /* @__PURE__ */ jsx(
540
+ "button",
541
+ {
542
+ type: "button",
543
+ className: "prn-bo-tab",
544
+ role: "tab",
545
+ "aria-selected": d === dir,
546
+ onClick: () => {
547
+ setDir(d);
548
+ setTab(null);
549
+ },
550
+ children: d
551
+ },
552
+ d
553
+ )) }) : null,
554
+ anomalies.length ? /* @__PURE__ */ jsxs("details", { className: "prn-bo-warn", children: [
555
+ /* @__PURE__ */ jsxs("summary", { children: [
556
+ anomalies.length,
557
+ " Auff\xE4lligkeiten erkannt"
558
+ ] }),
559
+ /* @__PURE__ */ jsx("ul", { children: anomalies.map((a, i) => /* @__PURE__ */ jsxs("li", { children: [
560
+ a.message,
561
+ " ",
562
+ /* @__PURE__ */ jsxs("code", { children: [
563
+ "\u2014 ",
564
+ a.path
565
+ ] })
566
+ ] }, i)) })
567
+ ] }) : null,
568
+ /* @__PURE__ */ jsx("div", { className: "prn-bo-tabs", role: "tablist", "aria-label": "Objekte", children: tabs.map((t) => {
569
+ const list = dd.stammdaten[t];
570
+ return /* @__PURE__ */ jsxs(
571
+ "button",
572
+ {
573
+ type: "button",
574
+ className: "prn-bo-tab",
575
+ role: "tab",
576
+ "aria-selected": t === active,
577
+ onClick: () => setTab(t),
578
+ children: [
579
+ tabLabel(t),
580
+ list ? ` (${list.length})` : ""
581
+ ]
582
+ },
583
+ t
584
+ );
585
+ }) }),
586
+ /* @__PURE__ */ jsx("div", { children: renderActive() })
587
+ ] });
588
+ }
589
+
590
+ export { AddressBlock, CodeResolved, ContactLine, EditableValue, EnumBadge, EnumIcon, FullDetail, IdentityHeader, MarktpartnerRow, SchemaField, SchemaPopoverBody, SmartObjectCard, SmartObjectView, SydocView, ValidityRange, berlinToIso, formatDateDE, getFieldOrder, humanize, isIsoDate, isoToBerlin, loadBo4eSchema, normalizeToCDoc, resolveField, scanAnomalies, toUtcLabel, validityStatus, zonedLabelWithTz };
591
+ //# sourceMappingURL=index.js.map
592
+ //# sourceMappingURL=index.js.map