@broberg/app-shell 0.5.0 → 0.7.1
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/README.md +52 -0
- package/css/app-shell.css +48 -0
- package/dist/index.cjs +124 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +119 -1
- package/dist/index.d.ts +119 -1
- package/dist/index.js +117 -3
- package/dist/index.js.map +1 -1
- package/dist/preact.cjs +297 -3
- package/dist/preact.cjs.map +1 -1
- package/dist/preact.d.cts +154 -1
- package/dist/preact.d.ts +154 -1
- package/dist/preact.js +288 -4
- package/dist/preact.js.map +1 -1
- package/package.json +3 -1
package/dist/preact.cjs
CHANGED
|
@@ -226,6 +226,80 @@ function createFetchAccountAdapter(opts = {}) {
|
|
|
226
226
|
removeAvatar: () => call("/profile/avatar/remove", { method: "POST" })
|
|
227
227
|
};
|
|
228
228
|
}
|
|
229
|
+
var AnalyticsError = class extends Error {
|
|
230
|
+
constructor(status, code) {
|
|
231
|
+
super(code ? `HTTP ${status} ${code}` : `HTTP ${status}`);
|
|
232
|
+
this.status = status;
|
|
233
|
+
this.code = code;
|
|
234
|
+
this.name = "AnalyticsError";
|
|
235
|
+
}
|
|
236
|
+
status;
|
|
237
|
+
code;
|
|
238
|
+
};
|
|
239
|
+
function analyticsErrorKind(err) {
|
|
240
|
+
return err instanceof AnalyticsError && err.status === 503 && err.code === "analytics_unconfigured" ? "unconfigured" : "unavailable";
|
|
241
|
+
}
|
|
242
|
+
function createFetchAnalyticsAdapter(opts = {}) {
|
|
243
|
+
const url = opts.url ?? "/api/analytics";
|
|
244
|
+
return {
|
|
245
|
+
async load(days) {
|
|
246
|
+
let res;
|
|
247
|
+
try {
|
|
248
|
+
res = await (opts.fetch ?? fetch)(`${url}${url.includes("?") ? "&" : "?"}days=${encodeURIComponent(String(days))}`, {
|
|
249
|
+
credentials: opts.credentials ?? "same-origin",
|
|
250
|
+
headers: { accept: "application/json" }
|
|
251
|
+
});
|
|
252
|
+
} catch {
|
|
253
|
+
throw new AnalyticsError(0);
|
|
254
|
+
}
|
|
255
|
+
const body = await res.json().catch(() => null);
|
|
256
|
+
if (!res.ok) throw new AnalyticsError(res.status, typeof body?.error === "string" ? body.error : void 0);
|
|
257
|
+
const ok = body !== null && typeof body.totals?.visitors === "number" && Array.isArray(body.series) && Array.isArray(body.topPages);
|
|
258
|
+
if (!ok) throw new AnalyticsError(res.status, "bad_response");
|
|
259
|
+
return body;
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
function copenhagenDay(at = /* @__PURE__ */ new Date()) {
|
|
264
|
+
return new Intl.DateTimeFormat("en-CA", { timeZone: "Europe/Copenhagen", year: "numeric", month: "2-digit", day: "2-digit" }).format(at);
|
|
265
|
+
}
|
|
266
|
+
function fillDays(series, days, now = /* @__PURE__ */ new Date()) {
|
|
267
|
+
const byDay = new Map(series.map((d2) => [d2.day, d2]));
|
|
268
|
+
const [y, m, d] = copenhagenDay(now).split("-").map(Number);
|
|
269
|
+
return Array.from({ length: days }, (_, i) => {
|
|
270
|
+
const day = new Date(Date.UTC(y, m - 1, d - (days - 1 - i))).toISOString().slice(0, 10);
|
|
271
|
+
return byDay.get(day) ?? { day, visitors: 0, pageviews: 0 };
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
function changePct(now, before) {
|
|
275
|
+
return before === 0 ? null : Math.round((now - before) / before * 100);
|
|
276
|
+
}
|
|
277
|
+
var TenantLoadError = class extends Error {
|
|
278
|
+
constructor(status) {
|
|
279
|
+
super(`HTTP ${status}`);
|
|
280
|
+
this.status = status;
|
|
281
|
+
this.name = "TenantLoadError";
|
|
282
|
+
}
|
|
283
|
+
status;
|
|
284
|
+
};
|
|
285
|
+
function createFetchTenantAdapter(opts = {}) {
|
|
286
|
+
return {
|
|
287
|
+
async load() {
|
|
288
|
+
let res;
|
|
289
|
+
try {
|
|
290
|
+
res = await (opts.fetch ?? fetch)(opts.url ?? "/api/me/memberships", { credentials: opts.credentials ?? "same-origin", headers: { accept: "application/json" } });
|
|
291
|
+
} catch {
|
|
292
|
+
throw new TenantLoadError(0);
|
|
293
|
+
}
|
|
294
|
+
const body = await res.json().catch(() => null);
|
|
295
|
+
if (!res.ok) throw new TenantLoadError(res.status);
|
|
296
|
+
if (!Array.isArray(body) || !body.every((r) => r && typeof r.role === "string" && r.tenant && typeof r.tenant.slug === "string")) {
|
|
297
|
+
throw new TenantLoadError(res.status);
|
|
298
|
+
}
|
|
299
|
+
return body;
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
}
|
|
229
303
|
var TEXT = {
|
|
230
304
|
da: {
|
|
231
305
|
openMenu: "\xC5bn menu",
|
|
@@ -273,7 +347,27 @@ var TEXT = {
|
|
|
273
347
|
pictureRemoved: "Billedet er fjernet",
|
|
274
348
|
pictureTooLarge: "Billedet er for stort \u2014 h\xF8jst 2 MB.",
|
|
275
349
|
pictureWrongType: "V\xE6lg et PNG-, JPEG- eller WebP-billede.",
|
|
276
|
-
bidSecurity: "Sikkerhed i Broberg ID"
|
|
350
|
+
bidSecurity: "Sikkerhed i Broberg ID",
|
|
351
|
+
analyticsPeriod: "Periode",
|
|
352
|
+
analyticsDays: "{n} dage",
|
|
353
|
+
analyticsVisitors: "Bes\xF8gende",
|
|
354
|
+
analyticsPageviews: "Sidevisninger",
|
|
355
|
+
analyticsVsPrev: "{pct} % mod forrige periode",
|
|
356
|
+
analyticsNoPrev: "Ingen sammenligning",
|
|
357
|
+
analyticsOverTime: "Sidevisninger pr. dag",
|
|
358
|
+
analyticsTopPages: "Mest bes\xF8gte sider",
|
|
359
|
+
analyticsDay: "Dag",
|
|
360
|
+
analyticsPage: "Side",
|
|
361
|
+
analyticsEmpty: "Ingen bes\xF8g i perioden.",
|
|
362
|
+
analyticsLoading: "Henter tal\u2026",
|
|
363
|
+
analyticsUnconfigured: "Analytics er ikke sat op for denne app.",
|
|
364
|
+
analyticsUnavailable: "Upmetrics svarer ikke lige nu. Pr\xF8v igen om lidt.",
|
|
365
|
+
tenantSwitch: "Skift organisation",
|
|
366
|
+
tenantLoadFailed: "Kunne ikke hente dine organisationer.",
|
|
367
|
+
tenantSwitchFailed: "Kunne ikke skifte organisation. Du er stadig i {name}.",
|
|
368
|
+
tenantRetry: "Pr\xF8v igen",
|
|
369
|
+
tenantSuspended: "lukket",
|
|
370
|
+
tenantActive: "aktiv"
|
|
277
371
|
},
|
|
278
372
|
en: {
|
|
279
373
|
openMenu: "Open menu",
|
|
@@ -321,7 +415,27 @@ var TEXT = {
|
|
|
321
415
|
pictureRemoved: "Picture removed",
|
|
322
416
|
pictureTooLarge: "The picture is too large \u2014 2 MB at most.",
|
|
323
417
|
pictureWrongType: "Choose a PNG, JPEG or WebP image.",
|
|
324
|
-
bidSecurity: "Security in Broberg ID"
|
|
418
|
+
bidSecurity: "Security in Broberg ID",
|
|
419
|
+
analyticsPeriod: "Period",
|
|
420
|
+
analyticsDays: "{n} days",
|
|
421
|
+
analyticsVisitors: "Visitors",
|
|
422
|
+
analyticsPageviews: "Page views",
|
|
423
|
+
analyticsVsPrev: "{pct}% vs previous period",
|
|
424
|
+
analyticsNoPrev: "No comparison",
|
|
425
|
+
analyticsOverTime: "Page views per day",
|
|
426
|
+
analyticsTopPages: "Most visited pages",
|
|
427
|
+
analyticsDay: "Day",
|
|
428
|
+
analyticsPage: "Page",
|
|
429
|
+
analyticsEmpty: "No visits in this period.",
|
|
430
|
+
analyticsLoading: "Loading figures\u2026",
|
|
431
|
+
analyticsUnconfigured: "Analytics is not set up for this app.",
|
|
432
|
+
analyticsUnavailable: "Upmetrics is not answering right now. Try again shortly.",
|
|
433
|
+
tenantSwitch: "Switch organisation",
|
|
434
|
+
tenantLoadFailed: "Could not load your organisations.",
|
|
435
|
+
tenantSwitchFailed: "Could not switch organisation. You are still in {name}.",
|
|
436
|
+
tenantRetry: "Try again",
|
|
437
|
+
tenantSuspended: "suspended",
|
|
438
|
+
tenantActive: "active"
|
|
325
439
|
}
|
|
326
440
|
};
|
|
327
441
|
var MOBILE_QUERY = "(max-width: 767px)";
|
|
@@ -1130,7 +1244,7 @@ function AccountPage({ lang, adapter, reauthHref, onProfileChange }) {
|
|
|
1130
1244
|
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", class: "bas-account__btn is-quiet", onClick: () => setConfirming(false), "data-testid": "account-avatar-remove-cancel", children: t.cancel })
|
|
1131
1245
|
] }) : null
|
|
1132
1246
|
] }),
|
|
1133
|
-
/* @__PURE__ */ jsxRuntime.jsxs("form", { class: "bas-account__form", onSubmit: onSaveName, children: [
|
|
1247
|
+
/* @__PURE__ */ jsxRuntime.jsxs("form", { class: "bas-account__form", onSubmit: onSaveName, "data-testid": "account-name-form", children: [
|
|
1134
1248
|
/* @__PURE__ */ jsxRuntime.jsx("label", { class: "bas-account__label", for: "bas-account-name", children: t.name }),
|
|
1135
1249
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { class: "bas-account__row", children: [
|
|
1136
1250
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -1157,11 +1271,183 @@ function AccountPage({ lang, adapter, reauthHref, onProfileChange }) {
|
|
|
1157
1271
|
] }) : null
|
|
1158
1272
|
] });
|
|
1159
1273
|
}
|
|
1274
|
+
function AnalyticsPage({ lang, adapter, periods = [7, 30, 90], initialDays }) {
|
|
1275
|
+
const t = TEXT[lang];
|
|
1276
|
+
const api = hooks.useMemo(() => adapter ?? createFetchAnalyticsAdapter(), [adapter]);
|
|
1277
|
+
const [days, setDays] = hooks.useState(initialDays ?? (periods.includes(30) ? 30 : periods[0]));
|
|
1278
|
+
const [load, setLoad] = hooks.useState({ state: "loading" });
|
|
1279
|
+
const locale = lang === "da" ? "da-DK" : "en-GB";
|
|
1280
|
+
const num = (n) => n.toLocaleString(locale);
|
|
1281
|
+
hooks.useEffect(() => {
|
|
1282
|
+
let live = true;
|
|
1283
|
+
setLoad({ state: "loading" });
|
|
1284
|
+
api.load(days).then(
|
|
1285
|
+
(data) => live && setLoad({ state: "ok", days, data }),
|
|
1286
|
+
(e) => live && setLoad({ state: "error", reason: analyticsErrorKind(e) })
|
|
1287
|
+
);
|
|
1288
|
+
return () => {
|
|
1289
|
+
live = false;
|
|
1290
|
+
};
|
|
1291
|
+
}, [api, days]);
|
|
1292
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("section", { class: "bas-analytics", "data-testid": "analytics-page", "aria-busy": load.state === "loading", children: [
|
|
1293
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { class: "bas-analytics__periods", role: "group", "aria-label": t.analyticsPeriod, children: periods.map((d) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1294
|
+
"button",
|
|
1295
|
+
{
|
|
1296
|
+
type: "button",
|
|
1297
|
+
class: "bas-analytics__period" + (d === days ? " is-active" : ""),
|
|
1298
|
+
"aria-pressed": d === days,
|
|
1299
|
+
onClick: () => setDays(d),
|
|
1300
|
+
"data-testid": `analytics-period-${d}`,
|
|
1301
|
+
children: t.analyticsDays.replace("{n}", String(d))
|
|
1302
|
+
},
|
|
1303
|
+
d
|
|
1304
|
+
)) }),
|
|
1305
|
+
load.state === "loading" ? /* @__PURE__ */ jsxRuntime.jsx("p", { class: "bas-analytics__muted", "data-testid": "analytics-loading", children: t.analyticsLoading }) : null,
|
|
1306
|
+
load.state === "error" ? /* @__PURE__ */ jsxRuntime.jsx("p", { class: "bas-analytics__note", role: "alert", "data-testid": "analytics-error", "data-reason": load.reason, children: load.reason === "unconfigured" ? t.analyticsUnconfigured : t.analyticsUnavailable }) : null,
|
|
1307
|
+
load.state === "ok" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1308
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { class: "bas-analytics__tiles", children: ["visitors", "pageviews"].map((k) => {
|
|
1309
|
+
const c = changePct(load.data.totals[k], load.data.prev[k]);
|
|
1310
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { class: "bas-analytics__tile", "data-testid": `analytics-${k}`, children: [
|
|
1311
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { class: "bas-analytics__label", children: k === "visitors" ? t.analyticsVisitors : t.analyticsPageviews }),
|
|
1312
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { class: "bas-analytics__value", "data-testid": `analytics-${k}-value`, children: num(load.data.totals[k]) }),
|
|
1313
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { class: "bas-analytics__muted", "data-testid": `analytics-${k}-change`, children: c === null ? t.analyticsNoPrev : t.analyticsVsPrev.replace("{pct}", `${c > 0 ? "+" : ""}${c}`) })
|
|
1314
|
+
] }, k);
|
|
1315
|
+
}) }),
|
|
1316
|
+
/* @__PURE__ */ jsxRuntime.jsx(AnalyticsSeries, { data: fillDays(load.data.series, load.days), t, locale, num }),
|
|
1317
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { class: "bas-analytics__card", "data-testid": "analytics-top-pages", children: [
|
|
1318
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { class: "bas-analytics__h", children: t.analyticsTopPages }),
|
|
1319
|
+
load.data.topPages.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { class: "bas-analytics__muted", "data-testid": "analytics-top-pages-empty", children: t.analyticsEmpty }) : /* @__PURE__ */ jsxRuntime.jsx("ol", { class: "bas-analytics__pages", children: load.data.topPages.map((p) => /* @__PURE__ */ jsxRuntime.jsxs("li", { class: "bas-analytics__page", children: [
|
|
1320
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { class: "bas-analytics__path", title: p.value, children: p.value }),
|
|
1321
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { class: "bas-analytics__count", children: num(p.count) })
|
|
1322
|
+
] }, p.value)) })
|
|
1323
|
+
] })
|
|
1324
|
+
] }) : null
|
|
1325
|
+
] });
|
|
1326
|
+
}
|
|
1327
|
+
function dayLabel(day, locale) {
|
|
1328
|
+
return (/* @__PURE__ */ new Date(`${day}T12:00:00Z`)).toLocaleDateString(locale, { day: "numeric", month: "short", timeZone: "UTC" });
|
|
1329
|
+
}
|
|
1330
|
+
function AnalyticsSeries({ data, t, locale, num }) {
|
|
1331
|
+
const max = Math.max(1, ...data.map((d) => d.pageviews));
|
|
1332
|
+
const empty = data.every((d) => d.pageviews === 0);
|
|
1333
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { class: "bas-analytics__card", "data-testid": "analytics-series", children: [
|
|
1334
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { class: "bas-analytics__h", children: t.analyticsOverTime }),
|
|
1335
|
+
empty ? /* @__PURE__ */ jsxRuntime.jsx("p", { class: "bas-analytics__muted", "data-testid": "analytics-series-empty", children: t.analyticsEmpty }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1336
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { class: "bas-analytics__bars", "aria-hidden": "true", children: data.map((d) => /* @__PURE__ */ jsxRuntime.jsx("div", { class: "bas-analytics__col", title: `${dayLabel(d.day, locale)}: ${num(d.pageviews)}`, "data-day": d.day, children: /* @__PURE__ */ jsxRuntime.jsx("div", { class: "bas-analytics__bar", style: { height: d.pageviews === 0 ? "0" : `max(2px, ${d.pageviews / max * 100}%)` } }) }, d.day)) }),
|
|
1337
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { class: "bas-analytics__axis", "aria-hidden": "true", children: [
|
|
1338
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: dayLabel(data[0].day, locale) }),
|
|
1339
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: dayLabel(data[data.length - 1].day, locale) })
|
|
1340
|
+
] }),
|
|
1341
|
+
/* @__PURE__ */ jsxRuntime.jsxs("table", { class: "bas-analytics__sr", "data-testid": "analytics-series-table", children: [
|
|
1342
|
+
/* @__PURE__ */ jsxRuntime.jsx("caption", { children: t.analyticsOverTime }),
|
|
1343
|
+
/* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
|
|
1344
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", children: t.analyticsDay }),
|
|
1345
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", children: t.analyticsPageviews })
|
|
1346
|
+
] }) }),
|
|
1347
|
+
/* @__PURE__ */ jsxRuntime.jsx("tbody", { children: data.map((d) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
|
|
1348
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { children: dayLabel(d.day, locale) }),
|
|
1349
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { children: num(d.pageviews) })
|
|
1350
|
+
] }, d.day)) })
|
|
1351
|
+
] })
|
|
1352
|
+
] })
|
|
1353
|
+
] });
|
|
1354
|
+
}
|
|
1355
|
+
function TenantSwitcher({ lang, activeSlug, adapter, onSwitch }) {
|
|
1356
|
+
const t = TEXT[lang];
|
|
1357
|
+
const api = hooks.useMemo(() => adapter ?? createFetchTenantAdapter(), [adapter]);
|
|
1358
|
+
const [rows, setRows] = hooks.useState(null);
|
|
1359
|
+
const [loadFailed, setLoadFailed] = hooks.useState(false);
|
|
1360
|
+
const [open, setOpen] = hooks.useState(false);
|
|
1361
|
+
const [busy, setBusy] = hooks.useState(null);
|
|
1362
|
+
const [switchFailed, setSwitchFailed] = hooks.useState(false);
|
|
1363
|
+
const root = hooks.useRef(null);
|
|
1364
|
+
const load = () => {
|
|
1365
|
+
setLoadFailed(false);
|
|
1366
|
+
api.load().then(setRows, () => setLoadFailed(true));
|
|
1367
|
+
};
|
|
1368
|
+
hooks.useEffect(load, [api]);
|
|
1369
|
+
hooks.useEffect(() => {
|
|
1370
|
+
if (!open) return;
|
|
1371
|
+
const onDown = (e) => {
|
|
1372
|
+
if (root.current && !root.current.contains(e.target)) setOpen(false);
|
|
1373
|
+
};
|
|
1374
|
+
const onKey = (e) => e.key === "Escape" && setOpen(false);
|
|
1375
|
+
document.addEventListener("mousedown", onDown);
|
|
1376
|
+
document.addEventListener("keydown", onKey);
|
|
1377
|
+
return () => {
|
|
1378
|
+
document.removeEventListener("mousedown", onDown);
|
|
1379
|
+
document.removeEventListener("keydown", onKey);
|
|
1380
|
+
};
|
|
1381
|
+
}, [open]);
|
|
1382
|
+
const active = rows?.find((r) => r.tenant.slug === activeSlug);
|
|
1383
|
+
const label = active?.tenant.name ?? activeSlug;
|
|
1384
|
+
const pick = async (slug) => {
|
|
1385
|
+
if (slug === activeSlug || busy) return;
|
|
1386
|
+
setBusy(slug);
|
|
1387
|
+
setSwitchFailed(false);
|
|
1388
|
+
try {
|
|
1389
|
+
await onSwitch(slug);
|
|
1390
|
+
setOpen(false);
|
|
1391
|
+
} catch {
|
|
1392
|
+
setSwitchFailed(true);
|
|
1393
|
+
} finally {
|
|
1394
|
+
setBusy(null);
|
|
1395
|
+
}
|
|
1396
|
+
};
|
|
1397
|
+
if (rows && rows.length <= 1 && !loadFailed) {
|
|
1398
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { class: "bas-tenant", "data-testid": "tenant-switcher", children: /* @__PURE__ */ jsxRuntime.jsx("span", { class: "bas-tenant__label", "data-testid": "tenant-switcher-label", children: label }) });
|
|
1399
|
+
}
|
|
1400
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { class: "bas-tenant", ref: root, "data-testid": "tenant-switcher", children: [
|
|
1401
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1402
|
+
"button",
|
|
1403
|
+
{
|
|
1404
|
+
type: "button",
|
|
1405
|
+
class: "bas-tenant__btn" + (open ? " is-open" : ""),
|
|
1406
|
+
"aria-haspopup": "menu",
|
|
1407
|
+
"aria-expanded": open,
|
|
1408
|
+
"aria-label": t.tenantSwitch,
|
|
1409
|
+
onClick: () => setOpen((v) => !v),
|
|
1410
|
+
"data-testid": "tenant-switcher-button",
|
|
1411
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { class: "bas-tenant__label", "data-testid": "tenant-switcher-label", children: label })
|
|
1412
|
+
}
|
|
1413
|
+
),
|
|
1414
|
+
open ? /* @__PURE__ */ jsxRuntime.jsxs("div", { class: "bas-menu bas-tenant__menu", role: "menu", "data-testid": "tenant-switcher-menu", children: [
|
|
1415
|
+
loadFailed ? /* @__PURE__ */ jsxRuntime.jsxs("div", { class: "bas-tenant__note", role: "alert", "data-testid": "tenant-switcher-load-error", children: [
|
|
1416
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: t.tenantLoadFailed }),
|
|
1417
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", class: "bas-mi", onClick: load, "data-testid": "tenant-switcher-retry", children: t.tenantRetry })
|
|
1418
|
+
] }) : null,
|
|
1419
|
+
switchFailed ? /* @__PURE__ */ jsxRuntime.jsx("p", { class: "bas-tenant__note is-error", role: "alert", "data-testid": "tenant-switcher-switch-error", children: t.tenantSwitchFailed.replace("{name}", label) }) : null,
|
|
1420
|
+
(rows ?? []).map((r) => {
|
|
1421
|
+
const isActive = r.tenant.slug === activeSlug;
|
|
1422
|
+
const suspended = r.tenant.status === "suspended";
|
|
1423
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1424
|
+
"button",
|
|
1425
|
+
{
|
|
1426
|
+
type: "button",
|
|
1427
|
+
role: "menuitemradio",
|
|
1428
|
+
"aria-checked": isActive,
|
|
1429
|
+
class: "bas-mi bas-tenant__item" + (isActive ? " is-active" : ""),
|
|
1430
|
+
disabled: suspended || busy !== null,
|
|
1431
|
+
onClick: () => void pick(r.tenant.slug),
|
|
1432
|
+
"data-testid": `tenant-switcher-item-${r.tenant.slug}`,
|
|
1433
|
+
children: [
|
|
1434
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { class: "bas-tenant__name", children: r.tenant.name }),
|
|
1435
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { class: "bas-tenant__role", children: suspended ? t.tenantSuspended : isActive ? t.tenantActive : r.role })
|
|
1436
|
+
]
|
|
1437
|
+
},
|
|
1438
|
+
r.tenant.id
|
|
1439
|
+
);
|
|
1440
|
+
})
|
|
1441
|
+
] }) : null
|
|
1442
|
+
] });
|
|
1443
|
+
}
|
|
1160
1444
|
|
|
1161
1445
|
exports.AVATAR_MAX_BYTES = AVATAR_MAX_BYTES;
|
|
1162
1446
|
exports.AVATAR_TYPES = AVATAR_TYPES;
|
|
1163
1447
|
exports.AccountError = AccountError;
|
|
1164
1448
|
exports.AccountPage = AccountPage;
|
|
1449
|
+
exports.AnalyticsError = AnalyticsError;
|
|
1450
|
+
exports.AnalyticsPage = AnalyticsPage;
|
|
1165
1451
|
exports.AppShell = AppShell;
|
|
1166
1452
|
exports.Avatar = Avatar;
|
|
1167
1453
|
exports.NotificationBell = NotificationBell;
|
|
@@ -1169,16 +1455,24 @@ exports.PageHeader = PageHeader;
|
|
|
1169
1455
|
exports.PageTabs = PageTabs;
|
|
1170
1456
|
exports.Sidebar = Sidebar;
|
|
1171
1457
|
exports.TEXT = TEXT;
|
|
1458
|
+
exports.TenantLoadError = TenantLoadError;
|
|
1459
|
+
exports.TenantSwitcher = TenantSwitcher;
|
|
1172
1460
|
exports.TopBar = TopBar;
|
|
1173
1461
|
exports.UserMenu = UserMenu;
|
|
1174
1462
|
exports.accountErrorKind = accountErrorKind;
|
|
1175
1463
|
exports.activeNavLabel = activeNavLabel;
|
|
1464
|
+
exports.analyticsErrorKind = analyticsErrorKind;
|
|
1176
1465
|
exports.badgeLabel = badgeLabel;
|
|
1466
|
+
exports.changePct = changePct;
|
|
1177
1467
|
exports.checkAvatarFile = checkAvatarFile;
|
|
1468
|
+
exports.copenhagenDay = copenhagenDay;
|
|
1178
1469
|
exports.createFetchAccountAdapter = createFetchAccountAdapter;
|
|
1470
|
+
exports.createFetchAnalyticsAdapter = createFetchAnalyticsAdapter;
|
|
1179
1471
|
exports.createFetchNotificationSource = createFetchNotificationSource;
|
|
1472
|
+
exports.createFetchTenantAdapter = createFetchTenantAdapter;
|
|
1180
1473
|
exports.createMemoryNotificationSource = createMemoryNotificationSource;
|
|
1181
1474
|
exports.defaultReauthHref = defaultReauthHref;
|
|
1475
|
+
exports.fillDays = fillDays;
|
|
1182
1476
|
exports.initials = initials;
|
|
1183
1477
|
exports.isActivePath = isActivePath;
|
|
1184
1478
|
exports.isExternal = isExternal;
|