@datawheel/bespoke 0.1.39 → 0.2.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.
Files changed (3) hide show
  1. package/dist/index.js +545 -181
  2. package/dist/server.js +465 -850
  3. package/package.json +8 -8
package/dist/index.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import axios from 'axios';
2
- import yn4 from 'yn';
3
2
  import * as d3Array from 'd3-array';
4
3
  import * as d3Collection from 'd3-collection';
5
4
  import * as d3Format from 'd3-format';
@@ -8,19 +7,20 @@ import { formatAbbreviate } from 'd3plus-format';
8
7
  import { date } from 'd3plus-axis';
9
8
  import { assign, closest, merge, isObject } from 'd3plus-common';
10
9
  import { strip, titleCase } from 'd3plus-text';
10
+ import yn3 from 'yn';
11
11
  import toposort from 'toposort';
12
12
  import { schema, normalize } from 'normalizr';
13
13
  import { createSlice, configureStore } from '@reduxjs/toolkit';
14
14
  import { HYDRATE, createWrapper } from 'next-redux-wrapper';
15
15
  import { Notifications, notifications } from '@mantine/notifications';
16
- import React, { forwardRef, useMemo, useState, useCallback, useContext, useEffect, createContext, useRef, Fragment as Fragment$1, createElement } from 'react';
16
+ import React, { forwardRef, useMemo, useState, useCallback, useContext, useEffect, createContext, memo, useRef, Fragment as Fragment$1, createElement } from 'react';
17
17
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
18
18
  import { useDispatch, useSelector } from 'react-redux';
19
- import { Stack, Text, Badge, Group, useMantineTheme, Flex, packSx, Title, Tooltip, ActionIcon, Center, Modal, Button, SegmentedControl, Select, MultiSelect, TextInput, Switch, Box, List, Menu, Anchor, MantineProvider, Divider, Burger, Navbar, ScrollArea, Avatar, AppShell, UnstyledButton, ThemeIcon, LoadingOverlay, Skeleton, Container, Loader, Alert, Collapse, Card, Space, Code, Textarea, rem, Paper, Grid, Input, Popover, Checkbox, Radio, Drawer, Overlay, SimpleGrid, Autocomplete, Tabs, Header, px, Image, FileInput, Accordion, HoverCard, CopyButton, NumberInput, Col } from '@mantine/core';
19
+ import { Stack, Text, Badge, Group, useMantineTheme, Flex, packSx, Tooltip, ActionIcon, Modal, Button, SegmentedControl, Select, MultiSelect, Center, Grid, Card, Image, Title, Radio, NumberInput, TextInput, Switch, Box, List, Menu, Anchor, MantineProvider, Divider, Burger, Navbar, ScrollArea, Avatar, AppShell, UnstyledButton, ThemeIcon, LoadingOverlay, Skeleton, Container, Loader, Alert, Collapse, Space, Code, Textarea, rem, Paper, Input, Popover, Checkbox, Drawer, Overlay, SimpleGrid, Autocomplete, Tabs, Header, px, FileInput, Accordion, HoverCard, CopyButton, Col } from '@mantine/core';
20
20
  import { dataConcat } from 'd3plus-viz';
21
21
  import * as d3plus from 'd3plus-react';
22
22
  import Router, { useRouter } from 'next/router';
23
- import { IconInfoCircle, IconRefresh, IconSearch, IconAlignLeft, IconAlignCenter, IconAlignRight, IconBoxMargin, IconTable, IconMathFunction, IconUsers, IconLogout, IconTrash, IconUserCircle, IconEdit, IconDatabase, IconServer, IconPencil, IconAlertCircle, IconCircleCheck, IconPlayerPlay, IconAlarmFilled, IconBox, IconLink, IconCircleX, IconFlag, IconCirclePlus, IconFileAnalytics, IconPlus, IconX, IconChevronDown, IconCamera, IconShare, IconCircleDashed, IconListSearch, IconExternalLink, IconSettings, IconFileOff, IconFilesOff, IconHierarchy3, IconMenu, IconApi, IconPolaroid, IconCircleMinus, IconEyeOff, IconChevronLeft, IconChevronRight, IconLogin, IconWorld, IconLock, IconVariable, IconArrowRightCircle, IconDownload, IconTemplate, IconChartBar, IconCode, IconUpload, IconCodePlus, IconClipboardCheck, IconClipboardCopy, IconPalette, IconEye, IconMinimize, IconMaximize, IconRss, IconGlobe, IconLinkOff } from '@tabler/icons-react';
23
+ import { IconInfoCircle, IconRefresh, IconSearch, IconAlignLeft, IconAlignCenter, IconAlignRight, IconBoxMargin, IconTable, IconMathFunction, IconUsers, IconLogout, IconTrash, IconUserCircle, IconEdit, IconDatabase, IconServer, IconPencil, IconAlertCircle, IconCircleCheck, IconPlayerPlay, IconAlarmFilled, IconBox, IconLink, IconCircleX, IconFlag, IconCirclePlus, IconFileAnalytics, IconPlus, IconHome, IconX, IconChevronDown, IconCamera, IconShare, IconCircleDashed, IconListSearch, IconExternalLink, IconSettings, IconFileOff, IconFilesOff, IconHierarchy3, IconMenu, IconApi, IconPolaroid, IconCircleMinus, IconEyeOff, IconPhoto, IconChevronLeft, IconChevronRight, IconLogin, IconWorld, IconLock, IconVariable, IconArrowRightCircle, IconDownload, IconTemplate, IconChartBar, IconCode, IconUpload, IconCodePlus, IconClipboardCheck, IconClipboardCopy, IconPalette, IconEye, IconMinimize, IconMaximize, IconRss, IconGlobe, IconLinkOff } from '@tabler/icons-react';
24
24
  import { useMediaQuery, useDisclosure, useDebouncedValue, useHotkeys, useFullscreen, getHotkeyHandler } from '@mantine/hooks';
25
25
  import dynamic from 'next/dynamic';
26
26
  import Link from 'next/link';
@@ -205,6 +205,9 @@ var init_http = __esm({
205
205
  ).join(",");
206
206
  return { ...params, slugs };
207
207
  }
208
+ if (params.mode === "related") {
209
+ return { ...params };
210
+ }
208
211
  return { ...params, [params.mode]: params[params.mode].join(",") };
209
212
  };
210
213
  }
@@ -360,6 +363,7 @@ var init_cms = __esm({
360
363
  };
361
364
  BLOCK_LOGIC_TYPES = {
362
365
  GENERATOR: "generator",
366
+ RELATED: "related",
363
367
  VIZ: "visualization"
364
368
  };
365
369
  BLOCK_LOGIC_LOCALE = "logic";
@@ -469,13 +473,6 @@ var init_cms = __esm({
469
473
  };
470
474
  }
471
475
  });
472
- var getLogging_default;
473
- var init_getLogging = __esm({
474
- "libs/configs/getLogging.ts"() {
475
- init_esm_shims();
476
- getLogging_default = (env = process.env) => yn4(env.REPORTS_LOGGING);
477
- }
478
- });
479
476
 
480
477
  // libs/stats.js
481
478
  function logGamma(Z) {
@@ -741,7 +738,7 @@ var init_mortarEval = __esm({
741
738
  init_esm_shims();
742
739
  init_block();
743
740
  init_libs();
744
- verbose = yn4(process.env.REPORTS_LOGGING);
741
+ verbose = yn3(process.env.REPORTS_LOGGING);
745
742
  mortarEval_default = mortarEval;
746
743
  }
747
744
  });
@@ -1021,12 +1018,14 @@ var init_consts = __esm({
1021
1018
  function isResult(response) {
1022
1019
  return response.ok !== void 0;
1023
1020
  }
1021
+ function ApiFetchException(message) {
1022
+ this.message = message;
1023
+ this.name = "ApiFetchException";
1024
+ }
1024
1025
  async function apiFetch(url, settings, readMemberFn, locale = "en") {
1025
1026
  const start = Date.now();
1026
1027
  let finalUrl = url;
1027
1028
  let finalData;
1028
- if (verbose2)
1029
- console.log("apiFetch", url);
1030
1029
  const parsedUrl = new URL(url);
1031
1030
  const parsedQuery = parsedUrl.searchParams;
1032
1031
  if (parsedQuery.has("bespoke_slugs")) {
@@ -1035,11 +1034,26 @@ async function apiFetch(url, settings, readMemberFn, locale = "en") {
1035
1034
  }
1036
1035
  const useProxy = settings && settings.useProxy && settings.useProxy === "true" ? true : false;
1037
1036
  if (typeof window === "object" && useProxy) {
1038
- const result = await urlProxy({ url: finalUrl });
1039
- finalData = result.ok === true ? result.data : result.error;
1037
+ const result = await urlProxy({ url: finalUrl, timeout: apiFetchTimeout });
1038
+ if (result.ok === true) {
1039
+ finalData = result.data;
1040
+ } else {
1041
+ throw new ApiFetchException(result?.error);
1042
+ }
1040
1043
  } else {
1041
- const response = await axios({ url: finalUrl });
1042
- finalData = response.data;
1044
+ try {
1045
+ const response = await axios({
1046
+ url: finalUrl,
1047
+ timeout: apiFetchTimeout
1048
+ });
1049
+ if (response.status === 200) {
1050
+ finalData = response.data;
1051
+ } else {
1052
+ throw new ApiFetchException("No response");
1053
+ }
1054
+ } catch (e) {
1055
+ throw new ApiFetchException(e.message);
1056
+ }
1043
1057
  }
1044
1058
  if (parsedQuery.has("bespoke_slugs")) {
1045
1059
  const slugStrings = parsedQuery.get("bespoke_slugs");
@@ -1093,6 +1107,10 @@ async function apiFetch(url, settings, readMemberFn, locale = "en") {
1093
1107
  requestDuration: Date.now() - start
1094
1108
  };
1095
1109
  }
1110
+ function BlockException(message) {
1111
+ this.name = "BlockException";
1112
+ this.message = message;
1113
+ }
1096
1114
  async function runSingleBlock(block, formatterFunctions, blockContext, readMemberFn) {
1097
1115
  const { locale, variables, query } = parseBlockContext(blockContext);
1098
1116
  const allowed = isBlockAllowed(block, blockContext, variables, formatterFunctions);
@@ -1124,18 +1142,30 @@ async function runSingleBlock(block, formatterFunctions, blockContext, readMembe
1124
1142
  block.settings,
1125
1143
  readMemberFn,
1126
1144
  locale
1127
- ).then((result) => {
1128
- if (verbose2)
1129
- console.log("Variable loaded:", apiUrl, "response time: ", result.requestDuration);
1130
- return result;
1131
- }, (e) => {
1132
- if (verbose2)
1133
- console.error(`Error fetching ${apiUrl} block ${block.id}: ${e.message}`);
1134
- return { data: {}, requestDuration: 0 };
1135
- })
1145
+ ).catch(
1146
+ (e) => {
1147
+ throw new BlockException(` Excecution of ${block.type}-${block.id} in section ${block.section_id} in report ${variables.report_name} (${variables.report_id}) failed.
1148
+ Error fetching ${apiUrl}.
1149
+ Message: ${e.message}.
1150
+ `);
1151
+ }
1152
+ )
1136
1153
  );
1137
1154
  }
1138
- const apiResponses = await Promise.all(apiPromisesList);
1155
+ const apiResponses = await Promise.all(apiPromisesList).catch((e) => {
1156
+ return e.message;
1157
+ });
1158
+ if (typeof apiResponses === "string") {
1159
+ if (apiResponses) {
1160
+ return {
1161
+ outputVariables: {},
1162
+ renderVariables: {},
1163
+ status: {
1164
+ error: apiResponses
1165
+ }
1166
+ };
1167
+ }
1168
+ }
1139
1169
  resp = apiResponses.length === 1 ? apiResponses[0].data : apiResponses.map((r) => r.data);
1140
1170
  responseSize = apiResponses.reduce((acc, r) => acc + JSON.stringify(r.data).length / 1024, 0);
1141
1171
  duration = Math.max(...apiResponses.map((ar) => ar.requestDuration));
@@ -1168,6 +1198,80 @@ async function runSingleBlock(block, formatterFunctions, blockContext, readMembe
1168
1198
  }
1169
1199
  };
1170
1200
  }
1201
+ if (block.type === BLOCK_TYPES.RELATED) {
1202
+ const relatedCompare = block.contentByLocale?.logic?.content?.simple?.compare || "variant";
1203
+ const relatedLimit = block.contentByLocale?.logic?.content?.simple?.limit || 4;
1204
+ const reportIds = Object.keys(variables).filter((key) => key.startsWith("report_id")).map((key) => variables[key]);
1205
+ const dimensionIds = Object.keys(variables).filter((key) => key.startsWith("dimension_id")).map((key) => variables[key]);
1206
+ const variantIds = Object.keys(variables).filter((key) => key.startsWith("variant_id")).map((key) => variables[key]);
1207
+ const memberIds = Object.keys(variables).filter((key) => key.startsWith("id")).map((key) => variables[key]);
1208
+ if (!memberIds.length)
1209
+ return {
1210
+ outputVariables: { related_reports: [] },
1211
+ renderVariables: {},
1212
+ status: {
1213
+ log: [],
1214
+ allowed: false
1215
+ }
1216
+ };
1217
+ const membersContentIds = await readMemberFn({
1218
+ mode: "ids",
1219
+ locale,
1220
+ ids: memberIds,
1221
+ output: "lite"
1222
+ }).then((response) => {
1223
+ let data = [];
1224
+ if (isResult(response)) {
1225
+ if (response.ok) {
1226
+ data = response.data.results;
1227
+ }
1228
+ } else {
1229
+ data = response.results;
1230
+ }
1231
+ return data && Array.isArray(data) ? data : [];
1232
+ }).catch((err) => {
1233
+ console.log("Error getting related members", err);
1234
+ return [];
1235
+ });
1236
+ const current_ids = membersContentIds.map((member) => member.content_id);
1237
+ const memberParams = {
1238
+ mode: "related",
1239
+ locale,
1240
+ related: relatedLimit ?? 4,
1241
+ current_ids,
1242
+ output: "full"
1243
+ };
1244
+ if (relatedCompare) {
1245
+ if (relatedCompare === "report")
1246
+ memberParams.report_ids = reportIds;
1247
+ else if (relatedCompare === "dimension")
1248
+ memberParams.dimension_ids = dimensionIds;
1249
+ else
1250
+ memberParams.variant_ids = variantIds;
1251
+ }
1252
+ const bespokeMembers = await readMemberFn(memberParams).then((response) => {
1253
+ let data = [];
1254
+ if (isResult(response)) {
1255
+ if (response.ok) {
1256
+ data = response.data.results;
1257
+ }
1258
+ } else {
1259
+ data = response.results;
1260
+ }
1261
+ return data && Array.isArray(data) ? data : [];
1262
+ }).catch((err) => {
1263
+ console.log("Error getting related members", err);
1264
+ return [];
1265
+ });
1266
+ return {
1267
+ outputVariables: { related_reports: bespokeMembers },
1268
+ renderVariables: {},
1269
+ status: {
1270
+ log: [],
1271
+ allowed: bespokeMembers.length > 0
1272
+ }
1273
+ };
1274
+ }
1171
1275
  const { logic } = getBlockContent(block, locale);
1172
1276
  const swappedLogic = varSwap_default(logic, formatterFunctions, blockContext);
1173
1277
  const evalResults = mortarEval_default("resp", resp || {}, swappedLogic, formatterFunctions, variables, blockContext);
@@ -1202,14 +1306,13 @@ async function runSingleBlock(block, formatterFunctions, blockContext, readMembe
1202
1306
  }
1203
1307
  };
1204
1308
  }
1205
- var verbose2, debug, ORIGIN, swapAPI, urlProxy, getDependencies, isBlockAllowed, runConsumersV2, getDurationColor, getSizeColor;
1309
+ var debug, ORIGIN, swapAPI, urlProxy, apiFetchTimeout, getDependencies, isBlockAllowed, runConsumersV2, getDurationColor, getSizeColor;
1206
1310
  var init_runConsumers = __esm({
1207
1311
  "libs/blocks/runConsumers.ts"() {
1208
1312
  init_esm_shims();
1209
1313
  init_api();
1210
1314
  init_block();
1211
1315
  init_cms();
1212
- init_getLogging();
1213
1316
  init_runSelector();
1214
1317
  init_selectorQueryToVariable();
1215
1318
  init_mortarEval();
@@ -1218,8 +1321,7 @@ var init_runConsumers = __esm({
1218
1321
  init_getRootBlocksForSection();
1219
1322
  init_arrayUtils();
1220
1323
  init_consts();
1221
- verbose2 = getLogging_default();
1222
- debug = yn4(process.env.NEXT_PUBLIC_REPORTS_DEBUG);
1324
+ debug = yn3(process.env.NEXT_PUBLIC_REPORTS_DEBUG);
1223
1325
  ORIGIN = process.env.REPORTS_ORIGIN || "";
1224
1326
  axios.interceptors.request.use((config) => ({
1225
1327
  ...config,
@@ -1236,6 +1338,7 @@ var init_runConsumers = __esm({
1236
1338
  return url;
1237
1339
  };
1238
1340
  ({ urlProxy } = apiFactory("/api/cms"));
1341
+ apiFetchTimeout = process.env.NEXT_PUBLIC_REPORTS_API_FETCH_TIMEOUT || 0;
1239
1342
  getDependencies = (bid, blocks, acc = [], crawlUp = true, crawlDown = true, withinSection = true, visited = []) => {
1240
1343
  if (visited.includes(bid))
1241
1344
  return [];
@@ -1343,9 +1446,19 @@ var init_runConsumers = __esm({
1343
1446
  },
1344
1447
  readMemberFn
1345
1448
  ).then(({ outputVariables, status }) => {
1449
+ if (status.error) {
1450
+ if (typeof window === "undefined") {
1451
+ throw new Error(`Run consumers failed with message: ${status.error}`);
1452
+ } else {
1453
+ statusById[bid2] = {
1454
+ allowed: false,
1455
+ error: status.error
1456
+ };
1457
+ }
1458
+ }
1346
1459
  if (
1347
1460
  // store output variables for block that:
1348
- (block.consumers.length > 0 || block.type === BLOCK_TYPES.GENERATOR) && status.allowed && Object.keys(outputVariables).length > 0
1461
+ (block.consumers.length > 0 || [BLOCK_TYPES.GENERATOR, BLOCK_TYPES.RELATED].includes(block.type)) && status.allowed && Object.keys(outputVariables).length > 0
1349
1462
  )
1350
1463
  variablesById[bid2] = outputVariables;
1351
1464
  if (mode === "report") {
@@ -2387,17 +2500,21 @@ function recalculateVariables(resource, params) {
2387
2500
  };
2388
2501
  const section = state.records.entities.section[sid];
2389
2502
  const readMemberFn = async (innerParams) => dispatch(readMember(innerParams));
2390
- const data = await runConsumersV2(
2391
- blocks,
2392
- section && [section],
2393
- bid,
2394
- formatterFunctions,
2395
- blockContext,
2396
- void 0,
2397
- readMemberFn
2398
- );
2399
- const { variables, status } = data;
2400
- dispatch(variablesActions.setVariableChange({ attributes, variables, status }));
2503
+ try {
2504
+ const data = await runConsumersV2(
2505
+ blocks,
2506
+ section && [section],
2507
+ bid,
2508
+ formatterFunctions,
2509
+ blockContext,
2510
+ void 0,
2511
+ readMemberFn
2512
+ );
2513
+ const { variables, status } = data;
2514
+ dispatch(variablesActions.setVariableChange({ attributes, variables, status }));
2515
+ } catch (error) {
2516
+ console.log("Error in runConsumers client side: recalculateVariables", error);
2517
+ }
2401
2518
  };
2402
2519
  }
2403
2520
  function readMetadata(filters) {
@@ -2962,51 +3079,38 @@ function Viz(config) {
2962
3079
  const Visualization = vizTypes[fallbackType];
2963
3080
  const { _data, ...vizPropsConfig } = vizProps.config;
2964
3081
  const vizConfig = { locale, variables, ...vizPropsConfig, ...configOverride };
2965
- const { title } = vizConfig;
2966
- return /* @__PURE__ */ jsxs("div", { className: "bespoke-Viz", children: [
2967
- title && /* @__PURE__ */ jsx(
2968
- Title,
2969
- {
2970
- className: "bespoke-Viz-title",
2971
- order: 4,
2972
- size: "h6",
2973
- align: "center",
2974
- dangerouslySetInnerHTML: { __html: title }
2975
- }
2976
- ),
2977
- /* @__PURE__ */ jsx(
2978
- "div",
2979
- {
2980
- style: {
2981
- display: "flex",
2982
- flex: "1 1 100%",
2983
- minHeight: 400
2984
- },
2985
- children: /* @__PURE__ */ jsx(ErrorBoundary, { fallback: /* @__PURE__ */ jsx("p", { children: "Error in d3plus viz config." }), children: /* @__PURE__ */ jsx(
2986
- Visualization,
2987
- {
2988
- style: { flex: 1 },
2989
- dataFormat: (resp) => {
2990
- vizProps.data && Array.isArray(vizProps.data) && vizProps.data.length > 1 && vizProps.data.some((d) => typeof d === "string");
2991
- let data;
2992
- try {
2993
- data = vizProps.dataFormat(resp);
2994
- } catch (e) {
2995
- console.log("Error in dataFormat: ", e);
2996
- data = [];
2997
- }
2998
- return data;
2999
- },
3000
- linksFormat: vizProps.linksFormat,
3001
- nodesFormat: vizProps.nodesFormat,
3002
- topojsonFormat: vizProps.topojsonFormat,
3003
- config: { ...defaultConfig_default, ...vizConfig, sectionVariables: sectionVariablesStr, title: void 0 }
3082
+ return /* @__PURE__ */ jsx("div", { className: "bespoke-Viz", children: /* @__PURE__ */ jsx(
3083
+ "div",
3084
+ {
3085
+ style: {
3086
+ display: "flex",
3087
+ flex: "1 1 100%",
3088
+ minHeight: 400
3089
+ },
3090
+ children: /* @__PURE__ */ jsx(ErrorBoundary, { fallback: /* @__PURE__ */ jsx("p", { children: "Error in d3plus viz config." }), children: /* @__PURE__ */ jsx(
3091
+ Visualization,
3092
+ {
3093
+ style: { flex: 1 },
3094
+ dataFormat: (resp) => {
3095
+ vizProps.data && Array.isArray(vizProps.data) && vizProps.data.length > 1 && vizProps.data.some((d) => typeof d === "string");
3096
+ let data;
3097
+ try {
3098
+ data = vizProps.dataFormat(resp);
3099
+ } catch (e) {
3100
+ console.log("Error in dataFormat: ", e);
3101
+ data = [];
3102
+ }
3103
+ return data;
3004
3104
  },
3005
- "viz-key"
3006
- ) })
3007
- }
3008
- )
3009
- ] });
3105
+ linksFormat: vizProps.linksFormat,
3106
+ nodesFormat: vizProps.nodesFormat,
3107
+ topojsonFormat: vizProps.topojsonFormat,
3108
+ config: { ...defaultConfig_default, ...vizConfig, sectionVariables: sectionVariablesStr, title: void 0 }
3109
+ },
3110
+ "viz-key"
3111
+ ) })
3112
+ }
3113
+ ) });
3010
3114
  }
3011
3115
  var vizTypes;
3012
3116
  var init_Viz = __esm({
@@ -3160,7 +3264,7 @@ var init_Table = __esm({
3160
3264
  // components/blocks/types/renderers/Viz.tsx
3161
3265
  var Viz_exports2 = {};
3162
3266
  __export(Viz_exports2, {
3163
- default: () => Viz2
3267
+ default: () => Viz_default
3164
3268
  });
3165
3269
  function Viz2({
3166
3270
  block,
@@ -3191,7 +3295,7 @@ function Viz2({
3191
3295
  }
3192
3296
  };
3193
3297
  return d3plusPropify_default(transpiledLogic, formatterFunctions, variables, locale, block.id, {}, globals);
3194
- }, [block, active, variables, sectionVariables]);
3298
+ }, [content?.logic, active, variables, sectionVariables]);
3195
3299
  useMemo(() => {
3196
3300
  if (!active || !content?.logic)
3197
3301
  return {};
@@ -3247,7 +3351,7 @@ function Viz2({
3247
3351
  }
3248
3352
  );
3249
3353
  }
3250
- var useAppContext, CustomVizzes, vizTypes2;
3354
+ var useAppContext, CustomVizzes, vizTypes2, Viz_default;
3251
3355
  var init_Viz2 = __esm({
3252
3356
  "components/blocks/types/renderers/Viz.tsx"() {
3253
3357
  init_esm_shims();
@@ -3270,6 +3374,9 @@ var init_Viz2 = __esm({
3270
3374
  ...d3plus,
3271
3375
  ...CustomVizzes
3272
3376
  };
3377
+ Viz_default = memo(Viz2, (prevProps, nextProps) => {
3378
+ return prevProps.block.contentByLocale.logic.content === nextProps.block.contentByLocale.logic.content && prevProps.active === nextProps.active && prevProps.locale === nextProps.locale && JSON.stringify(prevProps.variables) === JSON.stringify(nextProps.variables) && JSON.stringify(prevProps.configOverride) === JSON.stringify(nextProps.configOverride);
3379
+ });
3273
3380
  }
3274
3381
  });
3275
3382
 
@@ -4276,9 +4383,11 @@ function useContentOutline(min = 1, max = 6, headings = []) {
4276
4383
  blocks.sort((a, b) => orderSort(a, b, "blockrow"))
4277
4384
  );
4278
4385
  });
4386
+ if (!Object.keys(status).length)
4387
+ return [];
4279
4388
  return titleBlocksNormalized.filter((title) => {
4280
4389
  const currentOrder = parseInt(title.settings.order || "1", 10);
4281
- return currentOrder >= min && currentOrder <= max && titleStatus[title.id] ? titleStatus[title.id].allowed : true;
4390
+ return currentOrder >= min && currentOrder <= max && titleStatus[title.id]?.allowed && !titleStatus[title.id]?.hiddenByCascade;
4282
4391
  });
4283
4392
  }
4284
4393
  }, [headings, sectionList, titleBlocks, titleStatus]);
@@ -4399,6 +4508,54 @@ function NavView({ headings, settings }) {
4399
4508
  return smallerThanMd ? /* @__PURE__ */ jsx(MobileNav, { contentOutline }) : /* @__PURE__ */ jsx(DesktopNav, { contentOutline });
4400
4509
  }
4401
4510
 
4511
+ // frontend/components/report/blocks/Related.tsx
4512
+ init_esm_shims();
4513
+ init_store2();
4514
+ init_hooks();
4515
+ function RelatedView(props) {
4516
+ const { related_reports } = props;
4517
+ const currentLocale = useAppSelector((state) => state.status.currentLocale);
4518
+ const localeDefault9 = useAppSelector((state) => state.status.localeDefault);
4519
+ const profilePrefix = useProfilePrefix();
4520
+ const router = useRouter();
4521
+ if (!related_reports?.length)
4522
+ return;
4523
+ const localePrefix = currentLocale === localeDefault9 ? "" : `/${currentLocale}`;
4524
+ const onItemSubmit = (innerUrl) => {
4525
+ router.push(`${innerUrl}`);
4526
+ };
4527
+ return /* @__PURE__ */ jsx(Grid, { children: related_reports.map((report) => {
4528
+ const url = `${localePrefix}${profilePrefix}/${report.variant_slug}/${report.slug}`;
4529
+ return /* @__PURE__ */ jsx(Grid.Col, { xs: 12, sm: 6, md: 4, lg: 4, xl: 3, children: /* @__PURE__ */ jsxs(Card, { shadow: "sm", p: "lg", radius: "md", withBorder: true, children: [
4530
+ /* @__PURE__ */ jsx(Card.Section, { children: /* @__PURE__ */ jsx(Group, { grow: true, spacing: 0, children: /* @__PURE__ */ jsx(
4531
+ Image,
4532
+ {
4533
+ src: `/api/cms/member/image.png?member=${report.content_id}&size=thumb`,
4534
+ height: 160,
4535
+ imageProps: { loading: "lazy" },
4536
+ alt: report.name
4537
+ },
4538
+ report.content_id
4539
+ ) }) }),
4540
+ /* @__PURE__ */ jsx(Group, { position: "apart", mt: "md", mb: "xs", children: /* @__PURE__ */ jsxs(Stack, { children: [
4541
+ /* @__PURE__ */ jsx(Text, { weight: 500, children: report.name }),
4542
+ /* @__PURE__ */ jsx(Badge, { color: "green", variant: "light", children: report.variant_name })
4543
+ ] }, report.content_id) }),
4544
+ /* @__PURE__ */ jsx(
4545
+ "a",
4546
+ {
4547
+ href: url,
4548
+ onClick: (evt) => {
4549
+ evt.preventDefault();
4550
+ onItemSubmit(url);
4551
+ },
4552
+ children: /* @__PURE__ */ jsx(Button, { variant: "light", color: "blue", fullWidth: true, mt: "md", radius: "md", children: `See ${report.name} Report` })
4553
+ }
4554
+ )
4555
+ ] }) }, report.content_id);
4556
+ }) });
4557
+ }
4558
+
4402
4559
  // frontend/components/report/blocks/ResetButton.tsx
4403
4560
  init_esm_shims();
4404
4561
  init_hooks();
@@ -4438,6 +4595,7 @@ var TypeRenderers = {
4438
4595
  [BLOCK_TYPES.GENERATOR]: Generator,
4439
4596
  [BLOCK_TYPES.IMAGE]: ImageView,
4440
4597
  [BLOCK_TYPES.NAV]: NavView,
4598
+ [BLOCK_TYPES.RELATED]: RelatedView,
4441
4599
  [BLOCK_TYPES.RESET_BUTTON]: ResetButtonView
4442
4600
  };
4443
4601
  var blocks_default = TypeRenderers;
@@ -4870,6 +5028,46 @@ function NavUI() {
4870
5028
  return /* @__PURE__ */ jsx(Title, { order: 4, children: "Nav UI" });
4871
5029
  }
4872
5030
 
5031
+ // components/blocks/types/simpleEditors/RelatedUI.tsx
5032
+ init_esm_shims();
5033
+ function RelatedUI({ executeButton, onChange, simpleState }) {
5034
+ const [compare, setCompare] = useState(simpleState?.compare ?? "variant");
5035
+ const [limit, setLimit] = useState(simpleState?.limit ?? 4);
5036
+ useEffect(() => {
5037
+ const simpleState2 = {
5038
+ compare,
5039
+ limit
5040
+ };
5041
+ onChange(simpleState2);
5042
+ }, [compare, limit]);
5043
+ return /* @__PURE__ */ jsxs(Stack, { children: [
5044
+ /* @__PURE__ */ jsx(
5045
+ Radio.Group,
5046
+ {
5047
+ label: "Compare with members of",
5048
+ onChange: setCompare,
5049
+ value: compare,
5050
+ children: /* @__PURE__ */ jsxs(Group, { children: [
5051
+ /* @__PURE__ */ jsx(Radio, { label: "Same report", value: "report" }),
5052
+ /* @__PURE__ */ jsx(Radio, { label: "Same dimension", value: "dimension" }),
5053
+ /* @__PURE__ */ jsx(Radio, { label: "Same variant", value: "variant" })
5054
+ ] })
5055
+ }
5056
+ ),
5057
+ /* @__PURE__ */ jsx(
5058
+ NumberInput,
5059
+ {
5060
+ label: "Related reports limit",
5061
+ min: 1,
5062
+ onChange: (value) => setLimit(value),
5063
+ type: "number",
5064
+ value: limit
5065
+ }
5066
+ ),
5067
+ executeButton
5068
+ ] });
5069
+ }
5070
+
4873
5071
  // components/blocks/types/simpleEditors/ResetButtonUI.tsx
4874
5072
  init_esm_shims();
4875
5073
  function ResetButtonUI({ id, locale, executeButton, onChange, simpleState }) {
@@ -4930,6 +5128,7 @@ function ResetButtonUI({ id, locale, executeButton, onChange, simpleState }) {
4930
5128
  var simpleEditors_default = {
4931
5129
  [BLOCK_TYPES.SELECTOR]: SelectorUI_default,
4932
5130
  [BLOCK_TYPES.NAV]: NavUI,
5131
+ [BLOCK_TYPES.RELATED]: RelatedUI,
4933
5132
  [BLOCK_TYPES.RESET_BUTTON]: ResetButtonUI
4934
5133
  };
4935
5134
 
@@ -5438,6 +5637,15 @@ function NavPreview({ headings, settings }) {
5438
5637
  ] });
5439
5638
  }
5440
5639
 
5640
+ // components/blocks/types/renderers/Related.tsx
5641
+ init_esm_shims();
5642
+ function RelatedPreview(props) {
5643
+ const { related_reports } = props;
5644
+ if (!related_reports?.length)
5645
+ return /* @__PURE__ */ jsx(Text, { children: "There are no available members related to your current selection and report." });
5646
+ return /* @__PURE__ */ jsx(List, { children: related_reports.map((report) => /* @__PURE__ */ jsx(List.Item, { children: report.name }, report.content_id)) });
5647
+ }
5648
+
5441
5649
  // components/blocks/types/renderers/ResetButton.tsx
5442
5650
  init_esm_shims();
5443
5651
  init_hooks();
@@ -5478,6 +5686,7 @@ var renderersMap = {
5478
5686
  [BLOCK_TYPES.GENERATOR]: Generator_default,
5479
5687
  [BLOCK_TYPES.IMAGE]: ImagePreview,
5480
5688
  [BLOCK_TYPES.NAV]: NavPreview,
5689
+ [BLOCK_TYPES.RELATED]: RelatedPreview,
5481
5690
  [BLOCK_TYPES.RESET_BUTTON]: ResetButtonPreview
5482
5691
  };
5483
5692
  var renderers_default = renderersMap;
@@ -5506,6 +5715,12 @@ var allBlocks = {
5506
5715
  renderPreviewOnEdit: true,
5507
5716
  evalWhenNonActive: true
5508
5717
  },
5718
+ [BLOCK_TYPES.RELATED]: {
5719
+ type: BLOCK_TYPES.RELATED,
5720
+ renderer: renderers_default[BLOCK_TYPES.RELATED],
5721
+ renderPreviewOnEdit: false,
5722
+ evalWhenNonActive: true
5723
+ },
5509
5724
  [BLOCK_TYPES.SELECTOR]: {
5510
5725
  type: BLOCK_TYPES.SELECTOR,
5511
5726
  renderer: renderers_default[BLOCK_TYPES.SELECTOR],
@@ -5633,6 +5848,11 @@ function Block({ blockId, active = true }) {
5633
5848
  });
5634
5849
  return;
5635
5850
  }
5851
+ if (block.type === BLOCK_TYPES.RELATED) {
5852
+ const { related_reports } = generatorVariables;
5853
+ setContent({ related_reports });
5854
+ return;
5855
+ }
5636
5856
  if (block.type === BLOCK_TYPES.RESET_BUTTON) {
5637
5857
  setContent({ id: block.id, ...blockContent.simple });
5638
5858
  return;
@@ -7183,6 +7403,7 @@ function Section({ section }) {
7183
7403
  };
7184
7404
  const state = useAppSelector((state2) => state2);
7185
7405
  const status = useAppSelector((state2) => state2.variables.status);
7406
+ const previews = useAppSelector((state2) => state2.status.previews);
7186
7407
  const sectionStyles = useBespokeStyles()["Section"];
7187
7408
  const blockRecords = selectBlockRecords(state);
7188
7409
  const sectionBlocks = Object.values(blockRecords || {}).filter((d) => d.section_id === id);
@@ -7206,6 +7427,7 @@ function Section({ section }) {
7206
7427
  const colsQty = Object.keys(columns).length;
7207
7428
  if (!displaySection)
7208
7429
  return null;
7430
+ const showCredits = sectionSettings2.memberImageBg && previews.some(({ image }) => image && (image.url || image.author));
7209
7431
  return /* @__PURE__ */ jsxs(
7210
7432
  PositionWrapper,
7211
7433
  {
@@ -7227,53 +7449,82 @@ function Section({ section }) {
7227
7449
  /* @__PURE__ */ jsx(SectionResetButton, { id: section.id }),
7228
7450
  sectionSettings2.optionsMenu && /* @__PURE__ */ jsx(Options, { sectionId: section.id })
7229
7451
  ] }),
7230
- /* @__PURE__ */ jsx(
7452
+ /* @__PURE__ */ jsxs(
7231
7453
  StyleWrapper,
7232
7454
  {
7233
7455
  className: `bespoke-Section-${id} bespoke-Section-container`,
7234
7456
  settings: sectionSettings2,
7235
7457
  styles: sectionStyles?.content,
7236
- children: /* @__PURE__ */ jsx(ColumnsWrapper, { children: Object.keys(columns).sort((a, b) => orderSort(a, b, "blockcol")).map((columnIndex) => {
7237
- const column = columns[columnIndex];
7238
- const columnSettings = settings.columnSettings ? settings.columnSettings[columnIndex] : {};
7239
- const columnBlocks = Object.values(column).sort((a, b) => orderSort(a, b, "blockrow"));
7240
- if (!columnBlocks.some((block) => status[block.id].allowed))
7241
- return null;
7242
- return /* @__PURE__ */ jsx(
7243
- SectionColumn,
7244
- {
7245
- column,
7246
- columnSettings,
7247
- sx: { flexBasis: `${100 / colsQty}%` },
7248
- children: columnBlocks.map((item) => {
7249
- if (!item.id || !status[item.id]?.allowed && item.type !== BLOCK_TYPES.NAV)
7250
- return null;
7251
- const { settings: settings2, type } = blockRecords[item.id];
7252
- const blockWidth = settings2.width && !settings2.width.stretch && settings2.width.unit ? formatters[settings2.width.unit](settings2.width.value) : settings2.display === "inline" ? "auto" : "100%";
7253
- const blockStyles = {
7254
- alignSelf: type === "visualization" ? "stretch" : "flex-start",
7255
- flexGrow: 0,
7256
- margin: "0",
7257
- textAlign: settings2.align || blockSettings.align.defaultValue,
7258
- width: blockWidth,
7259
- minWidth: 300
7260
- };
7261
- return /* @__PURE__ */ jsx(
7262
- Box,
7263
- {
7264
- className: "bespoke-Block-wrapper",
7265
- id: `bespoke-Block-${item.id}`,
7266
- sx: blockStyles,
7267
- py: site_default.block.padding,
7268
- children: /* @__PURE__ */ jsx(Block, { blockId: item.id }, item.id)
7269
- },
7270
- item.id
7271
- );
7272
- })
7273
- },
7274
- columnIndex
7275
- );
7276
- }) })
7458
+ children: [
7459
+ /* @__PURE__ */ jsx(ColumnsWrapper, { children: Object.keys(columns).sort((a, b) => orderSort(a, b, "blockcol")).map((columnIndex) => {
7460
+ const column = columns[columnIndex];
7461
+ const columnSettings = settings.columnSettings ? settings.columnSettings[columnIndex] : {};
7462
+ const columnBlocks = Object.values(column).sort((a, b) => orderSort(a, b, "blockrow"));
7463
+ if (!columnBlocks.some(
7464
+ (block) => status[block.id].allowed || block.type === BLOCK_TYPES.NAV
7465
+ ))
7466
+ return null;
7467
+ return /* @__PURE__ */ jsx(
7468
+ SectionColumn,
7469
+ {
7470
+ column,
7471
+ columnSettings,
7472
+ sx: { flexBasis: `${100 / colsQty}%` },
7473
+ children: columnBlocks.map((item) => {
7474
+ if (!item.id || !status[item.id]?.allowed && item.type !== BLOCK_TYPES.NAV)
7475
+ return null;
7476
+ const { settings: settings2, type } = blockRecords[item.id];
7477
+ const blockWidth = settings2.width && !settings2.width.stretch && settings2.width.unit ? formatters[settings2.width.unit](settings2.width.value) : settings2.display === "inline" ? "auto" : "100%";
7478
+ const blockStyles = {
7479
+ alignSelf: type === "visualization" ? "stretch" : "flex-start",
7480
+ flexGrow: 0,
7481
+ margin: "0",
7482
+ textAlign: settings2.align || blockSettings.align.defaultValue,
7483
+ width: blockWidth,
7484
+ minWidth: 300
7485
+ };
7486
+ return /* @__PURE__ */ jsx(
7487
+ Box,
7488
+ {
7489
+ className: "bespoke-Block-wrapper",
7490
+ id: `bespoke-Block-${item.id}`,
7491
+ sx: blockStyles,
7492
+ py: site_default.block.padding,
7493
+ children: /* @__PURE__ */ jsx(Block, { blockId: item.id }, item.id)
7494
+ },
7495
+ item.id
7496
+ );
7497
+ })
7498
+ },
7499
+ columnIndex
7500
+ );
7501
+ }) }),
7502
+ showCredits && /* @__PURE__ */ jsxs(Popover, { position: "right", children: [
7503
+ /* @__PURE__ */ jsx(Popover.Target, { children: /* @__PURE__ */ jsx(ActionIcon, { variant: "light", radius: "xl", children: /* @__PURE__ */ jsx(IconPhoto, { size: 20 }) }) }),
7504
+ /* @__PURE__ */ jsx(Popover.Dropdown, { children: previews.map(
7505
+ ({ image, name }, idx) => (image.author || image.url) && /* @__PURE__ */ jsxs("div", { children: [
7506
+ image.author && /* @__PURE__ */ jsxs(Text, { size: "xs", children: [
7507
+ name,
7508
+ " image by ",
7509
+ /* @__PURE__ */ jsx("strong", { children: image.author })
7510
+ ] }),
7511
+ image.url && /* @__PURE__ */ jsxs(Text, { size: "xs", children: [
7512
+ "URL: ",
7513
+ " ",
7514
+ /* @__PURE__ */ jsx(
7515
+ Anchor,
7516
+ {
7517
+ href: image.url,
7518
+ target: "_blank",
7519
+ children: image.url
7520
+ }
7521
+ )
7522
+ ] }),
7523
+ idx + 1 < previews.length && /* @__PURE__ */ jsx(Divider, {})
7524
+ ] }, image.image_id)
7525
+ ) })
7526
+ ] })
7527
+ ]
7277
7528
  }
7278
7529
  )
7279
7530
  ]
@@ -7404,14 +7655,14 @@ init_esm_shims();
7404
7655
  function withPageRoleAuthRequired(params) {
7405
7656
  const { SuccessComponent, FailureComponent, options } = params;
7406
7657
  const allowedRoles = new Set(params.allowedRoles);
7407
- return withPageAuthRequired(({ user }) => {
7658
+ return withPageAuthRequired(({ user, ...props }) => {
7408
7659
  return useMemo(() => {
7409
7660
  const userRoles = user.bespoke_roles || [];
7410
7661
  if (allowedRoles.size === 0 || userRoles.some((role) => allowedRoles.has(role))) {
7411
- return /* @__PURE__ */ jsx(SuccessComponent, { user });
7662
+ return /* @__PURE__ */ jsx(SuccessComponent, { user, ...props });
7412
7663
  }
7413
- return /* @__PURE__ */ jsx(FailureComponent, {});
7414
- }, [user]);
7664
+ return /* @__PURE__ */ jsx(FailureComponent, { ...props });
7665
+ }, [user, props]);
7415
7666
  }, options);
7416
7667
  }
7417
7668
 
@@ -7538,10 +7789,11 @@ function normalizeList(value) {
7538
7789
  var { localeDefault: localeDefault8 } = getLocales_default();
7539
7790
  function parseReadMemberParams(query) {
7540
7791
  const locale = normalizeList(query.locale)[0] || localeDefault8 || "en";
7541
- const all = locale === "all" || yn4(query.all);
7792
+ const all = locale === "all" || yn3(query.all);
7542
7793
  const mode = normalizeList(query.mode)[0];
7543
7794
  const outputParam = normalizeList(query.output)[0] || "full";
7544
7795
  const output = outputParam === "lite" ? "lite" : "full";
7796
+ const relatedLimit = parseInt(normalizeList(query.related)[0], 10) || 5;
7545
7797
  let variant = normalizeList(query.variant).map(parseFiniteNumber);
7546
7798
  if (variant.length === 0) {
7547
7799
  variant = normalizeList(query["variant[]"]).map(parseFiniteNumber);
@@ -7579,6 +7831,19 @@ function parseReadMemberParams(query) {
7579
7831
  })
7580
7832
  };
7581
7833
  }
7834
+ if (mode === "related") {
7835
+ return {
7836
+ all,
7837
+ mode,
7838
+ locale: all ? localeDefault8 : stripHTML(locale),
7839
+ related: relatedLimit,
7840
+ current_ids: normalizeList(query.current_ids || query["current_ids[]"]).map(parseFiniteNumber),
7841
+ report_ids: normalizeList(query.report_ids || query["report_ids[]"]).map(parseFiniteNumber),
7842
+ dimension_ids: normalizeList(query.dimension_ids || query["dimension_ids[]"]).map(parseFiniteNumber),
7843
+ variant_ids: normalizeList(query.variant_ids || query["variant_ids[]"]).map(parseFiniteNumber),
7844
+ output
7845
+ };
7846
+ }
7582
7847
  throw new BackendError(400, `Invalid mode: '${mode}'`);
7583
7848
  }
7584
7849
  function parseSearchMemberParams(query) {
@@ -7603,9 +7868,9 @@ function parseSearchMemberParams(query) {
7603
7868
  locale: localeIsAll ? localeDefault8 : locale,
7604
7869
  limit: normalizeList(query.limit).map(parseFiniteNumber)[0] ?? 5,
7605
7870
  format: formatIsNested ? "nested" : "plain",
7606
- includes: yn4(query.includes, { default: true }),
7607
- visible: yn4(query.visible, { default: true }),
7608
- noImage: yn4(query.noImage, { default: false }),
7871
+ includes: yn3(query.includes, { default: true }),
7872
+ visible: yn3(query.visible, { default: true }),
7873
+ noImage: yn3(query.noImage, { default: false }),
7609
7874
  variant,
7610
7875
  dimension,
7611
7876
  report,
@@ -9189,8 +9454,8 @@ function BlockPreview(props) {
9189
9454
  const variables = useInputVariablesFlat(block.id);
9190
9455
  const formatterFunctions = useFormatterFunctionsForLocale();
9191
9456
  const blockContext = useBlockContext(block.id, locale);
9192
- const generatorVariables = useAppSelector((state) => state.variables.variables[block.id] || {});
9193
- const generatorStatus = useAppSelector((state) => state.variables.status[block.id] || {});
9457
+ const blockVariables = useAppSelector((state) => state.variables.variables[block.id] || {});
9458
+ const blockStatus = useAppSelector((state) => state.variables.status[block.id] || {});
9194
9459
  const blockContent = types_default[block.type].renderPreviewOnEdit && blockStateContent ? blockStateContent : getBlockContent(block, locale);
9195
9460
  const [content, setContent] = useState(null);
9196
9461
  const [status, setStatus2] = useState(emptyStatus);
@@ -9219,8 +9484,8 @@ function BlockPreview(props) {
9219
9484
  }
9220
9485
  if (block.type === BLOCK_TYPES.GENERATOR) {
9221
9486
  const hasAPI = Boolean(block.contentByLocale?.logic?.content?.api);
9222
- setContent({ outputVariables: generatorVariables, hasAPI });
9223
- setStatus2(generatorStatus);
9487
+ setContent({ outputVariables: blockVariables, hasAPI });
9488
+ setStatus2(blockStatus);
9224
9489
  return;
9225
9490
  }
9226
9491
  if (block.type === BLOCK_TYPES.SELECTOR) {
@@ -9234,6 +9499,12 @@ function BlockPreview(props) {
9234
9499
  setContent({ headings: block.inputs, settings: block.settings });
9235
9500
  return;
9236
9501
  }
9502
+ if (block.type === BLOCK_TYPES.RELATED) {
9503
+ const { outputVariables } = await runSingleBlock(block, formatterFunctions, blockContext, readMemberFn);
9504
+ const { related_reports } = outputVariables;
9505
+ setContent({ related_reports });
9506
+ return;
9507
+ }
9237
9508
  if (block.type === BLOCK_TYPES.RESET_BUTTON) {
9238
9509
  setContent(blockContent.simple);
9239
9510
  return;
@@ -9253,6 +9524,17 @@ function BlockPreview(props) {
9253
9524
  };
9254
9525
  fetch();
9255
9526
  }, [blockContent, variables]);
9527
+ useEffect(() => {
9528
+ if (blockStatus && blockStatus.error && blockStatus.error !== "") {
9529
+ notifications.show({
9530
+ autoClose: 1e4,
9531
+ color: "red",
9532
+ id: "block-error",
9533
+ message: blockStatus.error,
9534
+ title: `Block error ID: ${block.id}`
9535
+ });
9536
+ }
9537
+ }, [blockStatus.error]);
9256
9538
  const Renderer = renderers_default[block.type];
9257
9539
  const overlayStyle = {
9258
9540
  width: "100%",
@@ -9459,6 +9741,16 @@ function BlockSettings({ id, setBlockSettings, setBlockContent }) {
9459
9741
  [BLOCK_SETTINGS.ALLOWED_LOGIC]: allowedLogic
9460
9742
  });
9461
9743
  };
9744
+ const blocks = useAppSelector((state) => state.records.entities.block);
9745
+ const consumers = block.consumers.filter((cid) => blocks[cid].section_id !== block.section_id);
9746
+ const isShared = Boolean(consumers.length);
9747
+ const consumersP = consumers.slice(0, 3).map(
9748
+ (cid, i) => /* @__PURE__ */ jsxs(Fragment, { children: [
9749
+ i === consumers.length - 1 && consumers.length > 1 ? "and " : "",
9750
+ /* @__PURE__ */ jsx("strong", { children: `${blocks[cid].type} ${cid} [Section ${blocks[cid].section_id}]` }, cid),
9751
+ i < consumers.length - 1 && consumers.length > 1 ? ", " : ""
9752
+ ] })
9753
+ );
9462
9754
  return /* @__PURE__ */ jsxs(Group, { grow: true, align: "flex-start", children: [
9463
9755
  /* @__PURE__ */ jsxs(Stack, { justify: "flex-start", children: [
9464
9756
  /* @__PURE__ */ jsx(Title, { order: 4, children: "Allowed" }),
@@ -9480,10 +9772,20 @@ function BlockSettings({ id, setBlockSettings, setBlockContent }) {
9480
9772
  }
9481
9773
  ),
9482
9774
  /* @__PURE__ */ jsx(Title, { order: 4, children: "Sharing" }),
9775
+ isShared && block.shared && /* @__PURE__ */ jsxs(Text, { size: "xs", color: "red.4", sx: { display: "flex", alignItems: "flex-start", gap: "0.4rem" }, children: [
9776
+ /* @__PURE__ */ jsx(IconAlertCircle, { size: "1.2rem" }),
9777
+ /* @__PURE__ */ jsxs(Text, { color: "black", span: true, inherit: true, children: [
9778
+ "This block is being consumed by ",
9779
+ consumersP,
9780
+ consumers.length > 3 && " and others",
9781
+ ". Please, make sure you remove this block dependencies before changing this setting."
9782
+ ] })
9783
+ ] }),
9483
9784
  /* @__PURE__ */ jsx(
9484
9785
  SegmentedControl,
9485
9786
  {
9486
9787
  defaultValue: String(block.shared),
9788
+ disabled: isShared && block.shared,
9487
9789
  onChange: (e) => handleChange("shared", e === "true"),
9488
9790
  data: shared
9489
9791
  }
@@ -9513,6 +9815,8 @@ var Icons = {
9513
9815
  function InputMenu({ id }) {
9514
9816
  const dispatch = useAppDispatch();
9515
9817
  const resource = useContext(ResourceContext);
9818
+ const [searchTerm, setSearchTerm] = useState("");
9819
+ const [debouncedSearchTerm] = useDebouncedValue(searchTerm, 200);
9516
9820
  const blocks = useAppSelector((state) => {
9517
9821
  if (state.records.reports && state.records.reports[0]) {
9518
9822
  const reportId = state.records.reports[0];
@@ -9541,8 +9845,11 @@ function InputMenu({ id }) {
9541
9845
  () => blocks.filter((d) => d.id !== block.id && (d.section_id === block.section_id || d.shared)).sort(inputMenuSort),
9542
9846
  [blocks]
9543
9847
  );
9544
- if (!block)
9545
- return null;
9848
+ const filteredBlocks = useMemo(() => availableBlocks.filter((block2) => {
9849
+ const matchId = String(block2.id).includes(debouncedSearchTerm.trim());
9850
+ const matchVariables = Object.keys(variables[block2.id] || {}).some((varName) => varName.includes(debouncedSearchTerm.trim()));
9851
+ return matchId || matchVariables;
9852
+ }), [availableBlocks, debouncedSearchTerm, variables]);
9546
9853
  const handleClick = (blockId) => {
9547
9854
  const operation = inputBlocks[blockId] ? "delete" : "create";
9548
9855
  dispatch(actions_exports.updateEntity("block", {
@@ -9558,6 +9865,8 @@ function InputMenu({ id }) {
9558
9865
  dispatch(recalculateVariables(resource));
9559
9866
  });
9560
9867
  };
9868
+ if (!block)
9869
+ return null;
9561
9870
  const determineIcon = (blockId) => {
9562
9871
  if (block.consumers.includes(blockId))
9563
9872
  return Icons.Consumer;
@@ -9579,23 +9888,41 @@ function InputMenu({ id }) {
9579
9888
  children: "Add New Input"
9580
9889
  }
9581
9890
  ) }),
9582
- /* @__PURE__ */ jsx(Menu.Dropdown, { children: availableBlocks.map(({ id: id2, shared }) => /* @__PURE__ */ jsx(
9583
- Menu.Item,
9584
- {
9585
- disabled: block.consumers.includes(id2),
9586
- onClick: () => handleClick(id2),
9587
- icon: determineIcon(id2),
9588
- rightSection: shared ? /* @__PURE__ */ jsx(ThemeIcon, { size: "xs", radius: "xl", children: /* @__PURE__ */ jsx(IconGlobe, {}) }) : null,
9589
- children: /* @__PURE__ */ jsx(Box, { style: { maxWidth: "400px" }, children: /* @__PURE__ */ jsx(
9590
- InputMenuItem_default,
9591
- {
9592
- id: id2,
9593
- variables: variables[id2]
9594
- }
9595
- ) })
9596
- },
9597
- id2
9598
- )) })
9891
+ /* @__PURE__ */ jsxs(Menu.Dropdown, { children: [
9892
+ /* @__PURE__ */ jsx(
9893
+ TextInput,
9894
+ {
9895
+ placeholder: "Search by variable name or block id.",
9896
+ size: "xs",
9897
+ value: searchTerm,
9898
+ onChange: (e) => setSearchTerm(e.target.value)
9899
+ }
9900
+ ),
9901
+ filteredBlocks.map(({ id: id2, shared }) => /* @__PURE__ */ jsx(
9902
+ Menu.Item,
9903
+ {
9904
+ disabled: block.consumers.includes(id2),
9905
+ onClick: () => handleClick(id2),
9906
+ icon: determineIcon(id2),
9907
+ rightSection: /* @__PURE__ */ jsxs(Stack, { ml: "xs", spacing: "xs", align: "flex-end", children: [
9908
+ /* @__PURE__ */ jsxs(Text, { size: "xs", color: "gray.6", children: [
9909
+ "[",
9910
+ id2,
9911
+ "]"
9912
+ ] }),
9913
+ shared ? /* @__PURE__ */ jsx(ThemeIcon, { size: "xs", radius: "xl", children: /* @__PURE__ */ jsx(IconGlobe, {}) }) : null
9914
+ ] }),
9915
+ children: /* @__PURE__ */ jsx(Box, { style: { maxWidth: "400px" }, children: /* @__PURE__ */ jsx(
9916
+ InputMenuItem_default,
9917
+ {
9918
+ id: id2,
9919
+ variables: variables[id2]
9920
+ }
9921
+ ) })
9922
+ },
9923
+ id2
9924
+ ))
9925
+ ] })
9599
9926
  ]
9600
9927
  }
9601
9928
  );
@@ -9911,7 +10238,7 @@ function BlockEditor({
9911
10238
  monacoOptions: {
9912
10239
  onChange: (logic) => onChangeCode(logic, locale),
9913
10240
  value: blockContent?.logic || "",
9914
- height: fullscreen ? "85vh" : "70vh",
10241
+ height: fullscreen ? "85vh" : "61vh",
9915
10242
  width: "100%"
9916
10243
  },
9917
10244
  variables
@@ -9931,6 +10258,7 @@ function BlockEditor({
9931
10258
  }
9932
10259
  );
9933
10260
  const isVizOrGenerator = [BLOCK_TYPES.GENERATOR, BLOCK_TYPES.VIZ].includes(blockType);
10261
+ const isLogicType = Object.values(BLOCK_LOGIC_TYPES).includes(blockType);
9934
10262
  return /* @__PURE__ */ jsxs(Grid, { children: [
9935
10263
  /* @__PURE__ */ jsxs(
9936
10264
  Col,
@@ -10001,10 +10329,17 @@ function BlockEditor({
10001
10329
  height: "100%"
10002
10330
  },
10003
10331
  children: [
10004
- isVizOrGenerator && /* @__PURE__ */ jsxs(Alert, { icon: /* @__PURE__ */ jsx(IconFlag, {}), children: [
10005
- "You are editing the ",
10006
- /* @__PURE__ */ jsx(Code, { children: locale.toUpperCase() }),
10007
- " version of the block."
10332
+ /* @__PURE__ */ jsxs(Alert, { icon: /* @__PURE__ */ jsx(IconFlag, {}), children: [
10333
+ !isLogicType && /* @__PURE__ */ jsxs(Fragment, { children: [
10334
+ "You are editing the ",
10335
+ /* @__PURE__ */ jsx(Code, { children: locale.toUpperCase() }),
10336
+ " version of the block."
10337
+ ] }),
10338
+ isLogicType && /* @__PURE__ */ jsxs(Fragment, { children: [
10339
+ "You are editing a ",
10340
+ /* @__PURE__ */ jsx(Code, { children: "logic" }),
10341
+ " type of block. You have to handle all the languages inside it."
10342
+ ] })
10008
10343
  ] }),
10009
10344
  /* @__PURE__ */ jsxs(
10010
10345
  Tabs,
@@ -10447,7 +10782,8 @@ function BlockElement({
10447
10782
  right: px(theme.spacing.xs) / 2,
10448
10783
  top: px(theme.spacing.xs) / 2,
10449
10784
  backgroundColor: theme.colors.gray[2],
10450
- borderRadius: theme.radius.sm
10785
+ borderRadius: theme.radius.sm,
10786
+ zIndex: 2
10451
10787
  },
10452
10788
  children: [
10453
10789
  isInput || isConsumer ? /* @__PURE__ */ jsx(ActionIcon, { children: isInput ? /* @__PURE__ */ jsx(IconLogout, { size: 20, color: theme.colors.red[5] }) : /* @__PURE__ */ jsx(IconLogin, { size: 20, color: theme.colors.green[5] }) }, "globe") : null,
@@ -10931,7 +11267,7 @@ function BuilderEditor(props) {
10931
11267
  {
10932
11268
  report: reportRef.data,
10933
11269
  locale: props.locale,
10934
- isLoading: reportRef.isFetching
11270
+ isLoading: false
10935
11271
  }
10936
11272
  );
10937
11273
  }, [reportRef.status, reportRef.data?.sections]);
@@ -11316,6 +11652,7 @@ function FormatterForm({ formatterId, onEditEnd }) {
11316
11652
  {
11317
11653
  monacoOptions: {
11318
11654
  onChange: (newLogic) => onContentValueChange("logic", newLogic),
11655
+ height: "30vh",
11319
11656
  value: formatter.content.logic
11320
11657
  }
11321
11658
  },
@@ -12545,6 +12882,7 @@ function MetadataEditor() {
12545
12882
  init_esm_shims();
12546
12883
  function NotFoundView() {
12547
12884
  const { user } = useUser();
12885
+ const textStyle = { color: "#000" };
12548
12886
  return /* @__PURE__ */ jsxs(
12549
12887
  Flex,
12550
12888
  {
@@ -12554,14 +12892,15 @@ function NotFoundView() {
12554
12892
  align: "center",
12555
12893
  direction: "column",
12556
12894
  wrap: "wrap",
12895
+ style: { background: "white" },
12557
12896
  children: [
12558
- /* @__PURE__ */ jsx(Title, { children: "Not found." }),
12559
- /* @__PURE__ */ jsxs(Title, { order: 2, children: [
12897
+ /* @__PURE__ */ jsx(Title, { style: textStyle, children: "Not found." }),
12898
+ /* @__PURE__ */ jsxs(Title, { order: 2, style: textStyle, children: [
12560
12899
  "Sorry, ",
12561
12900
  user?.name,
12562
12901
  "!"
12563
12902
  ] }),
12564
- /* @__PURE__ */ jsx(Text, { children: "The page you are looking for is not here." }),
12903
+ /* @__PURE__ */ jsx(Text, { style: textStyle, children: "The page you are looking for is not here." }),
12565
12904
  /* @__PURE__ */ jsx(Button, { component: "a", href: "/", children: "Back to homepage" })
12566
12905
  ]
12567
12906
  }
@@ -12675,6 +13014,7 @@ function ReportPicker() {
12675
13014
  init_esm_shims();
12676
13015
  function UnauthorizeView() {
12677
13016
  const { user } = useUser();
13017
+ const textStyle = { color: "#000" };
12678
13018
  return /* @__PURE__ */ jsxs(
12679
13019
  Flex,
12680
13020
  {
@@ -12684,15 +13024,39 @@ function UnauthorizeView() {
12684
13024
  align: "center",
12685
13025
  direction: "column",
12686
13026
  wrap: "wrap",
13027
+ style: { background: "white" },
12687
13028
  children: [
12688
- /* @__PURE__ */ jsx(Title, { children: "Unauthorize or forbidden access." }),
12689
- /* @__PURE__ */ jsxs(Title, { order: 2, children: [
13029
+ /* @__PURE__ */ jsx(Title, { style: textStyle, children: "Unauthorize or forbidden access." }),
13030
+ /* @__PURE__ */ jsxs(Title, { style: textStyle, order: 2, children: [
12690
13031
  "Sorry, ",
12691
13032
  user?.name,
12692
13033
  "!"
12693
13034
  ] }),
12694
- /* @__PURE__ */ jsx(Text, { children: "Your session or your user's roles doesn't satisfy the needs of the requested view. Please, ask your administrator to allow you." }),
12695
- /* @__PURE__ */ jsx(Button, { component: "a", href: "/", children: "Back to homepage" })
13035
+ /* @__PURE__ */ jsx(Text, { style: textStyle, children: "Your session or your user's roles doesn't satisfy the needs of the requested view. Please, ask your administrator to allow you." }),
13036
+ /* @__PURE__ */ jsxs(Group, { children: [
13037
+ /* @__PURE__ */ jsx(
13038
+ Button,
13039
+ {
13040
+ component: "a",
13041
+ href: "/",
13042
+ leftIcon: /* @__PURE__ */ jsx(IconHome, { size: 14 }),
13043
+ variant: "outline",
13044
+ color: "dark",
13045
+ children: "Back to homepage"
13046
+ }
13047
+ ),
13048
+ /* @__PURE__ */ jsx(
13049
+ Button,
13050
+ {
13051
+ component: "a",
13052
+ href: "/api/auth/logout",
13053
+ leftIcon: /* @__PURE__ */ jsx(IconLogout, { size: 14 }),
13054
+ variant: "outline",
13055
+ color: "dark",
13056
+ children: "Disconnect"
13057
+ }
13058
+ )
13059
+ ] })
12696
13060
  ]
12697
13061
  }
12698
13062
  );
@@ -13299,7 +13663,7 @@ function BespokeManager(options) {
13299
13663
  locale = localeDefault4,
13300
13664
  profilePrefix = "/profilePathManager"
13301
13665
  } = options;
13302
- const notifications4 = {
13666
+ const notifications5 = {
13303
13667
  position: "bottom-center",
13304
13668
  ...options.notifications
13305
13669
  };
@@ -13312,7 +13676,7 @@ function BespokeManager(options) {
13312
13676
  return /* @__PURE__ */ jsxs(ResourceProvider, { pathSegment, profilePrefix, children: [
13313
13677
  /* @__PURE__ */ jsx(Head, { children: /* @__PURE__ */ jsx("title", { children: title }) }),
13314
13678
  /* @__PURE__ */ jsxs(MantineProvider, { inherit: false, children: [
13315
- /* @__PURE__ */ jsx(Notifications, { ...notifications4 }),
13679
+ /* @__PURE__ */ jsx(Notifications, { ...notifications5 }),
13316
13680
  /* @__PURE__ */ jsx(DialogProvider, { children: /* @__PURE__ */ jsx(ErrorBoundary, { fallback: /* @__PURE__ */ jsx("p", { children: "Client side error in Bespoke. Check the console for details." }), children: /* @__PURE__ */ jsx(BespokeManagerShell, { locale }) }) })
13317
13681
  ] })
13318
13682
  ] });