@datawheel/bespoke 0.1.39 → 0.2.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 CHANGED
@@ -16,11 +16,11 @@ import { Notifications, notifications } from '@mantine/notifications';
16
16
  import React, { forwardRef, useMemo, useState, useCallback, useContext, useEffect, createContext, 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, Title, Tooltip, ActionIcon, Center, Modal, Button, SegmentedControl, Select, MultiSelect, Grid, Card, Image, 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";
@@ -1168,6 +1172,80 @@ async function runSingleBlock(block, formatterFunctions, blockContext, readMembe
1168
1172
  }
1169
1173
  };
1170
1174
  }
1175
+ if (block.type === BLOCK_TYPES.RELATED) {
1176
+ const relatedCompare = block.contentByLocale?.logic?.content?.simple?.compare || "variant";
1177
+ const relatedLimit = block.contentByLocale?.logic?.content?.simple?.limit || 4;
1178
+ const reportIds = Object.keys(variables).filter((key) => key.startsWith("report_id")).map((key) => variables[key]);
1179
+ const dimensionIds = Object.keys(variables).filter((key) => key.startsWith("dimension_id")).map((key) => variables[key]);
1180
+ const variantIds = Object.keys(variables).filter((key) => key.startsWith("variant_id")).map((key) => variables[key]);
1181
+ const memberIds = Object.keys(variables).filter((key) => key.startsWith("id")).map((key) => variables[key]);
1182
+ if (!memberIds.length)
1183
+ return {
1184
+ outputVariables: { related_reports: [] },
1185
+ renderVariables: {},
1186
+ status: {
1187
+ log: [],
1188
+ allowed: false
1189
+ }
1190
+ };
1191
+ const membersContentIds = await readMemberFn({
1192
+ mode: "ids",
1193
+ locale,
1194
+ ids: memberIds,
1195
+ output: "lite"
1196
+ }).then((response) => {
1197
+ let data = [];
1198
+ if (isResult(response)) {
1199
+ if (response.ok) {
1200
+ data = response.data.results;
1201
+ }
1202
+ } else {
1203
+ data = response.results;
1204
+ }
1205
+ return data && Array.isArray(data) ? data : [];
1206
+ }).catch((err) => {
1207
+ console.log("Error getting related members", err);
1208
+ return [];
1209
+ });
1210
+ const current_ids = membersContentIds.map((member) => member.content_id);
1211
+ const memberParams = {
1212
+ mode: "related",
1213
+ locale,
1214
+ related: relatedLimit ?? 4,
1215
+ current_ids,
1216
+ output: "full"
1217
+ };
1218
+ if (relatedCompare) {
1219
+ if (relatedCompare === "report")
1220
+ memberParams.report_ids = reportIds;
1221
+ else if (relatedCompare === "dimension")
1222
+ memberParams.dimension_ids = dimensionIds;
1223
+ else
1224
+ memberParams.variant_ids = variantIds;
1225
+ }
1226
+ const bespokeMembers = await readMemberFn(memberParams).then((response) => {
1227
+ let data = [];
1228
+ if (isResult(response)) {
1229
+ if (response.ok) {
1230
+ data = response.data.results;
1231
+ }
1232
+ } else {
1233
+ data = response.results;
1234
+ }
1235
+ return data && Array.isArray(data) ? data : [];
1236
+ }).catch((err) => {
1237
+ console.log("Error getting related members", err);
1238
+ return [];
1239
+ });
1240
+ return {
1241
+ outputVariables: { related_reports: bespokeMembers },
1242
+ renderVariables: {},
1243
+ status: {
1244
+ log: [],
1245
+ allowed: bespokeMembers.length > 0
1246
+ }
1247
+ };
1248
+ }
1171
1249
  const { logic } = getBlockContent(block, locale);
1172
1250
  const swappedLogic = varSwap_default(logic, formatterFunctions, blockContext);
1173
1251
  const evalResults = mortarEval_default("resp", resp || {}, swappedLogic, formatterFunctions, variables, blockContext);
@@ -1345,7 +1423,7 @@ var init_runConsumers = __esm({
1345
1423
  ).then(({ outputVariables, status }) => {
1346
1424
  if (
1347
1425
  // store output variables for block that:
1348
- (block.consumers.length > 0 || block.type === BLOCK_TYPES.GENERATOR) && status.allowed && Object.keys(outputVariables).length > 0
1426
+ (block.consumers.length > 0 || [BLOCK_TYPES.GENERATOR, BLOCK_TYPES.RELATED].includes(block.type)) && status.allowed && Object.keys(outputVariables).length > 0
1349
1427
  )
1350
1428
  variablesById[bid2] = outputVariables;
1351
1429
  if (mode === "report") {
@@ -4276,9 +4354,11 @@ function useContentOutline(min = 1, max = 6, headings = []) {
4276
4354
  blocks.sort((a, b) => orderSort(a, b, "blockrow"))
4277
4355
  );
4278
4356
  });
4357
+ if (!Object.keys(status).length)
4358
+ return [];
4279
4359
  return titleBlocksNormalized.filter((title) => {
4280
4360
  const currentOrder = parseInt(title.settings.order || "1", 10);
4281
- return currentOrder >= min && currentOrder <= max && titleStatus[title.id] ? titleStatus[title.id].allowed : true;
4361
+ return currentOrder >= min && currentOrder <= max && titleStatus[title.id]?.allowed && !titleStatus[title.id]?.hiddenByCascade;
4282
4362
  });
4283
4363
  }
4284
4364
  }, [headings, sectionList, titleBlocks, titleStatus]);
@@ -4399,6 +4479,54 @@ function NavView({ headings, settings }) {
4399
4479
  return smallerThanMd ? /* @__PURE__ */ jsx(MobileNav, { contentOutline }) : /* @__PURE__ */ jsx(DesktopNav, { contentOutline });
4400
4480
  }
4401
4481
 
4482
+ // frontend/components/report/blocks/Related.tsx
4483
+ init_esm_shims();
4484
+ init_store2();
4485
+ init_hooks();
4486
+ function RelatedView(props) {
4487
+ const { related_reports } = props;
4488
+ const currentLocale = useAppSelector((state) => state.status.currentLocale);
4489
+ const localeDefault9 = useAppSelector((state) => state.status.localeDefault);
4490
+ const profilePrefix = useProfilePrefix();
4491
+ const router = useRouter();
4492
+ if (!related_reports?.length)
4493
+ return;
4494
+ const localePrefix = currentLocale === localeDefault9 ? "" : `/${currentLocale}`;
4495
+ const onItemSubmit = (innerUrl) => {
4496
+ router.push(`${innerUrl}`);
4497
+ };
4498
+ return /* @__PURE__ */ jsx(Grid, { children: related_reports.map((report) => {
4499
+ const url = `${localePrefix}${profilePrefix}/${report.variant_slug}/${report.slug}`;
4500
+ 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: [
4501
+ /* @__PURE__ */ jsx(Card.Section, { children: /* @__PURE__ */ jsx(Group, { grow: true, spacing: 0, children: /* @__PURE__ */ jsx(
4502
+ Image,
4503
+ {
4504
+ src: `/api/cms/member/image.png?member=${report.content_id}&size=thumb`,
4505
+ height: 160,
4506
+ imageProps: { loading: "lazy" },
4507
+ alt: report.name
4508
+ },
4509
+ report.content_id
4510
+ ) }) }),
4511
+ /* @__PURE__ */ jsx(Group, { position: "apart", mt: "md", mb: "xs", children: /* @__PURE__ */ jsxs(Stack, { children: [
4512
+ /* @__PURE__ */ jsx(Text, { weight: 500, children: report.name }),
4513
+ /* @__PURE__ */ jsx(Badge, { color: "green", variant: "light", children: report.variant_name })
4514
+ ] }, report.content_id) }),
4515
+ /* @__PURE__ */ jsx(
4516
+ "a",
4517
+ {
4518
+ href: url,
4519
+ onClick: (evt) => {
4520
+ evt.preventDefault();
4521
+ onItemSubmit(url);
4522
+ },
4523
+ children: /* @__PURE__ */ jsx(Button, { variant: "light", color: "blue", fullWidth: true, mt: "md", radius: "md", children: `See ${report.name} Report` })
4524
+ }
4525
+ )
4526
+ ] }) }, report.content_id);
4527
+ }) });
4528
+ }
4529
+
4402
4530
  // frontend/components/report/blocks/ResetButton.tsx
4403
4531
  init_esm_shims();
4404
4532
  init_hooks();
@@ -4438,6 +4566,7 @@ var TypeRenderers = {
4438
4566
  [BLOCK_TYPES.GENERATOR]: Generator,
4439
4567
  [BLOCK_TYPES.IMAGE]: ImageView,
4440
4568
  [BLOCK_TYPES.NAV]: NavView,
4569
+ [BLOCK_TYPES.RELATED]: RelatedView,
4441
4570
  [BLOCK_TYPES.RESET_BUTTON]: ResetButtonView
4442
4571
  };
4443
4572
  var blocks_default = TypeRenderers;
@@ -4870,6 +4999,46 @@ function NavUI() {
4870
4999
  return /* @__PURE__ */ jsx(Title, { order: 4, children: "Nav UI" });
4871
5000
  }
4872
5001
 
5002
+ // components/blocks/types/simpleEditors/RelatedUI.tsx
5003
+ init_esm_shims();
5004
+ function RelatedUI({ executeButton, onChange, simpleState }) {
5005
+ const [compare, setCompare] = useState(simpleState?.compare ?? "variant");
5006
+ const [limit, setLimit] = useState(simpleState?.limit ?? 4);
5007
+ useEffect(() => {
5008
+ const simpleState2 = {
5009
+ compare,
5010
+ limit
5011
+ };
5012
+ onChange(simpleState2);
5013
+ }, [compare, limit]);
5014
+ return /* @__PURE__ */ jsxs(Stack, { children: [
5015
+ /* @__PURE__ */ jsx(
5016
+ Radio.Group,
5017
+ {
5018
+ label: "Compare with members of",
5019
+ onChange: setCompare,
5020
+ value: compare,
5021
+ children: /* @__PURE__ */ jsxs(Group, { children: [
5022
+ /* @__PURE__ */ jsx(Radio, { label: "Same report", value: "report" }),
5023
+ /* @__PURE__ */ jsx(Radio, { label: "Same dimension", value: "dimension" }),
5024
+ /* @__PURE__ */ jsx(Radio, { label: "Same variant", value: "variant" })
5025
+ ] })
5026
+ }
5027
+ ),
5028
+ /* @__PURE__ */ jsx(
5029
+ NumberInput,
5030
+ {
5031
+ label: "Related reports limit",
5032
+ min: 1,
5033
+ onChange: (value) => setLimit(value),
5034
+ type: "number",
5035
+ value: limit
5036
+ }
5037
+ ),
5038
+ executeButton
5039
+ ] });
5040
+ }
5041
+
4873
5042
  // components/blocks/types/simpleEditors/ResetButtonUI.tsx
4874
5043
  init_esm_shims();
4875
5044
  function ResetButtonUI({ id, locale, executeButton, onChange, simpleState }) {
@@ -4930,6 +5099,7 @@ function ResetButtonUI({ id, locale, executeButton, onChange, simpleState }) {
4930
5099
  var simpleEditors_default = {
4931
5100
  [BLOCK_TYPES.SELECTOR]: SelectorUI_default,
4932
5101
  [BLOCK_TYPES.NAV]: NavUI,
5102
+ [BLOCK_TYPES.RELATED]: RelatedUI,
4933
5103
  [BLOCK_TYPES.RESET_BUTTON]: ResetButtonUI
4934
5104
  };
4935
5105
 
@@ -5438,6 +5608,15 @@ function NavPreview({ headings, settings }) {
5438
5608
  ] });
5439
5609
  }
5440
5610
 
5611
+ // components/blocks/types/renderers/Related.tsx
5612
+ init_esm_shims();
5613
+ function RelatedPreview(props) {
5614
+ const { related_reports } = props;
5615
+ if (!related_reports?.length)
5616
+ return /* @__PURE__ */ jsx(Text, { children: "There are no available members related to your current selection and report." });
5617
+ return /* @__PURE__ */ jsx(List, { children: related_reports.map((report) => /* @__PURE__ */ jsx(List.Item, { children: report.name }, report.content_id)) });
5618
+ }
5619
+
5441
5620
  // components/blocks/types/renderers/ResetButton.tsx
5442
5621
  init_esm_shims();
5443
5622
  init_hooks();
@@ -5478,6 +5657,7 @@ var renderersMap = {
5478
5657
  [BLOCK_TYPES.GENERATOR]: Generator_default,
5479
5658
  [BLOCK_TYPES.IMAGE]: ImagePreview,
5480
5659
  [BLOCK_TYPES.NAV]: NavPreview,
5660
+ [BLOCK_TYPES.RELATED]: RelatedPreview,
5481
5661
  [BLOCK_TYPES.RESET_BUTTON]: ResetButtonPreview
5482
5662
  };
5483
5663
  var renderers_default = renderersMap;
@@ -5506,6 +5686,12 @@ var allBlocks = {
5506
5686
  renderPreviewOnEdit: true,
5507
5687
  evalWhenNonActive: true
5508
5688
  },
5689
+ [BLOCK_TYPES.RELATED]: {
5690
+ type: BLOCK_TYPES.RELATED,
5691
+ renderer: renderers_default[BLOCK_TYPES.RELATED],
5692
+ renderPreviewOnEdit: false,
5693
+ evalWhenNonActive: true
5694
+ },
5509
5695
  [BLOCK_TYPES.SELECTOR]: {
5510
5696
  type: BLOCK_TYPES.SELECTOR,
5511
5697
  renderer: renderers_default[BLOCK_TYPES.SELECTOR],
@@ -5633,6 +5819,11 @@ function Block({ blockId, active = true }) {
5633
5819
  });
5634
5820
  return;
5635
5821
  }
5822
+ if (block.type === BLOCK_TYPES.RELATED) {
5823
+ const { related_reports } = generatorVariables;
5824
+ setContent({ related_reports });
5825
+ return;
5826
+ }
5636
5827
  if (block.type === BLOCK_TYPES.RESET_BUTTON) {
5637
5828
  setContent({ id: block.id, ...blockContent.simple });
5638
5829
  return;
@@ -7183,6 +7374,7 @@ function Section({ section }) {
7183
7374
  };
7184
7375
  const state = useAppSelector((state2) => state2);
7185
7376
  const status = useAppSelector((state2) => state2.variables.status);
7377
+ const previews = useAppSelector((state2) => state2.status.previews);
7186
7378
  const sectionStyles = useBespokeStyles()["Section"];
7187
7379
  const blockRecords = selectBlockRecords(state);
7188
7380
  const sectionBlocks = Object.values(blockRecords || {}).filter((d) => d.section_id === id);
@@ -7206,6 +7398,7 @@ function Section({ section }) {
7206
7398
  const colsQty = Object.keys(columns).length;
7207
7399
  if (!displaySection)
7208
7400
  return null;
7401
+ const showCredits = sectionSettings2.memberImageBg && previews.some(({ image }) => image && (image.url || image.author));
7209
7402
  return /* @__PURE__ */ jsxs(
7210
7403
  PositionWrapper,
7211
7404
  {
@@ -7227,53 +7420,82 @@ function Section({ section }) {
7227
7420
  /* @__PURE__ */ jsx(SectionResetButton, { id: section.id }),
7228
7421
  sectionSettings2.optionsMenu && /* @__PURE__ */ jsx(Options, { sectionId: section.id })
7229
7422
  ] }),
7230
- /* @__PURE__ */ jsx(
7423
+ /* @__PURE__ */ jsxs(
7231
7424
  StyleWrapper,
7232
7425
  {
7233
7426
  className: `bespoke-Section-${id} bespoke-Section-container`,
7234
7427
  settings: sectionSettings2,
7235
7428
  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
- }) })
7429
+ children: [
7430
+ /* @__PURE__ */ jsx(ColumnsWrapper, { children: Object.keys(columns).sort((a, b) => orderSort(a, b, "blockcol")).map((columnIndex) => {
7431
+ const column = columns[columnIndex];
7432
+ const columnSettings = settings.columnSettings ? settings.columnSettings[columnIndex] : {};
7433
+ const columnBlocks = Object.values(column).sort((a, b) => orderSort(a, b, "blockrow"));
7434
+ if (!columnBlocks.some(
7435
+ (block) => status[block.id].allowed || block.type === BLOCK_TYPES.NAV
7436
+ ))
7437
+ return null;
7438
+ return /* @__PURE__ */ jsx(
7439
+ SectionColumn,
7440
+ {
7441
+ column,
7442
+ columnSettings,
7443
+ sx: { flexBasis: `${100 / colsQty}%` },
7444
+ children: columnBlocks.map((item) => {
7445
+ if (!item.id || !status[item.id]?.allowed && item.type !== BLOCK_TYPES.NAV)
7446
+ return null;
7447
+ const { settings: settings2, type } = blockRecords[item.id];
7448
+ const blockWidth = settings2.width && !settings2.width.stretch && settings2.width.unit ? formatters[settings2.width.unit](settings2.width.value) : settings2.display === "inline" ? "auto" : "100%";
7449
+ const blockStyles = {
7450
+ alignSelf: type === "visualization" ? "stretch" : "flex-start",
7451
+ flexGrow: 0,
7452
+ margin: "0",
7453
+ textAlign: settings2.align || blockSettings.align.defaultValue,
7454
+ width: blockWidth,
7455
+ minWidth: 300
7456
+ };
7457
+ return /* @__PURE__ */ jsx(
7458
+ Box,
7459
+ {
7460
+ className: "bespoke-Block-wrapper",
7461
+ id: `bespoke-Block-${item.id}`,
7462
+ sx: blockStyles,
7463
+ py: site_default.block.padding,
7464
+ children: /* @__PURE__ */ jsx(Block, { blockId: item.id }, item.id)
7465
+ },
7466
+ item.id
7467
+ );
7468
+ })
7469
+ },
7470
+ columnIndex
7471
+ );
7472
+ }) }),
7473
+ showCredits && /* @__PURE__ */ jsxs(Popover, { position: "right", children: [
7474
+ /* @__PURE__ */ jsx(Popover.Target, { children: /* @__PURE__ */ jsx(ActionIcon, { variant: "light", radius: "xl", children: /* @__PURE__ */ jsx(IconPhoto, { size: 20 }) }) }),
7475
+ /* @__PURE__ */ jsx(Popover.Dropdown, { children: previews.map(
7476
+ ({ image, name }, idx) => (image.author || image.url) && /* @__PURE__ */ jsxs("div", { children: [
7477
+ image.author && /* @__PURE__ */ jsxs(Text, { size: "xs", children: [
7478
+ name,
7479
+ " image by ",
7480
+ /* @__PURE__ */ jsx("strong", { children: image.author })
7481
+ ] }),
7482
+ image.url && /* @__PURE__ */ jsxs(Text, { size: "xs", children: [
7483
+ "URL: ",
7484
+ " ",
7485
+ /* @__PURE__ */ jsx(
7486
+ Anchor,
7487
+ {
7488
+ href: image.url,
7489
+ target: "_blank",
7490
+ children: image.url
7491
+ }
7492
+ )
7493
+ ] }),
7494
+ idx + 1 < previews.length && /* @__PURE__ */ jsx(Divider, {})
7495
+ ] }, image.image_id)
7496
+ ) })
7497
+ ] })
7498
+ ]
7277
7499
  }
7278
7500
  )
7279
7501
  ]
@@ -7542,6 +7764,7 @@ function parseReadMemberParams(query) {
7542
7764
  const mode = normalizeList(query.mode)[0];
7543
7765
  const outputParam = normalizeList(query.output)[0] || "full";
7544
7766
  const output = outputParam === "lite" ? "lite" : "full";
7767
+ const relatedLimit = parseInt(normalizeList(query.related)[0], 10) || 5;
7545
7768
  let variant = normalizeList(query.variant).map(parseFiniteNumber);
7546
7769
  if (variant.length === 0) {
7547
7770
  variant = normalizeList(query["variant[]"]).map(parseFiniteNumber);
@@ -7579,6 +7802,19 @@ function parseReadMemberParams(query) {
7579
7802
  })
7580
7803
  };
7581
7804
  }
7805
+ if (mode === "related") {
7806
+ return {
7807
+ all,
7808
+ mode,
7809
+ locale: all ? localeDefault8 : stripHTML(locale),
7810
+ related: relatedLimit,
7811
+ current_ids: normalizeList(query.current_ids || query["current_ids[]"]).map(parseFiniteNumber),
7812
+ report_ids: normalizeList(query.report_ids || query["report_ids[]"]).map(parseFiniteNumber),
7813
+ dimension_ids: normalizeList(query.dimension_ids || query["dimension_ids[]"]).map(parseFiniteNumber),
7814
+ variant_ids: normalizeList(query.variant_ids || query["variant_ids[]"]).map(parseFiniteNumber),
7815
+ output
7816
+ };
7817
+ }
7582
7818
  throw new BackendError(400, `Invalid mode: '${mode}'`);
7583
7819
  }
7584
7820
  function parseSearchMemberParams(query) {
@@ -9234,6 +9470,12 @@ function BlockPreview(props) {
9234
9470
  setContent({ headings: block.inputs, settings: block.settings });
9235
9471
  return;
9236
9472
  }
9473
+ if (block.type === BLOCK_TYPES.RELATED) {
9474
+ const { outputVariables } = await runSingleBlock(block, formatterFunctions, blockContext, readMemberFn);
9475
+ const { related_reports } = outputVariables;
9476
+ setContent({ related_reports });
9477
+ return;
9478
+ }
9237
9479
  if (block.type === BLOCK_TYPES.RESET_BUTTON) {
9238
9480
  setContent(blockContent.simple);
9239
9481
  return;
@@ -9459,6 +9701,16 @@ function BlockSettings({ id, setBlockSettings, setBlockContent }) {
9459
9701
  [BLOCK_SETTINGS.ALLOWED_LOGIC]: allowedLogic
9460
9702
  });
9461
9703
  };
9704
+ const blocks = useAppSelector((state) => state.records.entities.block);
9705
+ const consumers = block.consumers.filter((cid) => blocks[cid].section_id !== block.section_id);
9706
+ const isShared = Boolean(consumers.length);
9707
+ const consumersP = consumers.slice(0, 3).map(
9708
+ (cid, i) => /* @__PURE__ */ jsxs(Fragment, { children: [
9709
+ i === consumers.length - 1 && consumers.length > 1 ? "and " : "",
9710
+ /* @__PURE__ */ jsx("strong", { children: `${blocks[cid].type} ${cid} [Section ${blocks[cid].section_id}]` }, cid),
9711
+ i < consumers.length - 1 && consumers.length > 1 ? ", " : ""
9712
+ ] })
9713
+ );
9462
9714
  return /* @__PURE__ */ jsxs(Group, { grow: true, align: "flex-start", children: [
9463
9715
  /* @__PURE__ */ jsxs(Stack, { justify: "flex-start", children: [
9464
9716
  /* @__PURE__ */ jsx(Title, { order: 4, children: "Allowed" }),
@@ -9480,10 +9732,20 @@ function BlockSettings({ id, setBlockSettings, setBlockContent }) {
9480
9732
  }
9481
9733
  ),
9482
9734
  /* @__PURE__ */ jsx(Title, { order: 4, children: "Sharing" }),
9735
+ isShared && block.shared && /* @__PURE__ */ jsxs(Text, { size: "xs", color: "red.4", sx: { display: "flex", alignItems: "flex-start", gap: "0.4rem" }, children: [
9736
+ /* @__PURE__ */ jsx(IconAlertCircle, { size: "1.2rem" }),
9737
+ /* @__PURE__ */ jsxs(Text, { color: "black", span: true, inherit: true, children: [
9738
+ "This block is being consumed by ",
9739
+ consumersP,
9740
+ consumers.length > 3 && " and others",
9741
+ ". Please, make sure you remove this block dependencies before changing this setting."
9742
+ ] })
9743
+ ] }),
9483
9744
  /* @__PURE__ */ jsx(
9484
9745
  SegmentedControl,
9485
9746
  {
9486
9747
  defaultValue: String(block.shared),
9748
+ disabled: isShared && block.shared,
9487
9749
  onChange: (e) => handleChange("shared", e === "true"),
9488
9750
  data: shared
9489
9751
  }
@@ -9513,6 +9775,8 @@ var Icons = {
9513
9775
  function InputMenu({ id }) {
9514
9776
  const dispatch = useAppDispatch();
9515
9777
  const resource = useContext(ResourceContext);
9778
+ const [searchTerm, setSearchTerm] = useState("");
9779
+ const [debouncedSearchTerm] = useDebouncedValue(searchTerm, 200);
9516
9780
  const blocks = useAppSelector((state) => {
9517
9781
  if (state.records.reports && state.records.reports[0]) {
9518
9782
  const reportId = state.records.reports[0];
@@ -9541,8 +9805,11 @@ function InputMenu({ id }) {
9541
9805
  () => blocks.filter((d) => d.id !== block.id && (d.section_id === block.section_id || d.shared)).sort(inputMenuSort),
9542
9806
  [blocks]
9543
9807
  );
9544
- if (!block)
9545
- return null;
9808
+ const filteredBlocks = useMemo(() => availableBlocks.filter((block2) => {
9809
+ const matchId = String(block2.id).includes(debouncedSearchTerm.trim());
9810
+ const matchVariables = Object.keys(variables[block2.id] || {}).some((varName) => varName.includes(debouncedSearchTerm.trim()));
9811
+ return matchId || matchVariables;
9812
+ }), [availableBlocks, debouncedSearchTerm, variables]);
9546
9813
  const handleClick = (blockId) => {
9547
9814
  const operation = inputBlocks[blockId] ? "delete" : "create";
9548
9815
  dispatch(actions_exports.updateEntity("block", {
@@ -9558,6 +9825,8 @@ function InputMenu({ id }) {
9558
9825
  dispatch(recalculateVariables(resource));
9559
9826
  });
9560
9827
  };
9828
+ if (!block)
9829
+ return null;
9561
9830
  const determineIcon = (blockId) => {
9562
9831
  if (block.consumers.includes(blockId))
9563
9832
  return Icons.Consumer;
@@ -9579,23 +9848,41 @@ function InputMenu({ id }) {
9579
9848
  children: "Add New Input"
9580
9849
  }
9581
9850
  ) }),
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
- )) })
9851
+ /* @__PURE__ */ jsxs(Menu.Dropdown, { children: [
9852
+ /* @__PURE__ */ jsx(
9853
+ TextInput,
9854
+ {
9855
+ placeholder: "Search by variable name or block id.",
9856
+ size: "xs",
9857
+ value: searchTerm,
9858
+ onChange: (e) => setSearchTerm(e.target.value)
9859
+ }
9860
+ ),
9861
+ filteredBlocks.map(({ id: id2, shared }) => /* @__PURE__ */ jsx(
9862
+ Menu.Item,
9863
+ {
9864
+ disabled: block.consumers.includes(id2),
9865
+ onClick: () => handleClick(id2),
9866
+ icon: determineIcon(id2),
9867
+ rightSection: /* @__PURE__ */ jsxs(Stack, { ml: "xs", spacing: "xs", align: "flex-end", children: [
9868
+ /* @__PURE__ */ jsxs(Text, { size: "xs", color: "gray.6", children: [
9869
+ "[",
9870
+ id2,
9871
+ "]"
9872
+ ] }),
9873
+ shared ? /* @__PURE__ */ jsx(ThemeIcon, { size: "xs", radius: "xl", children: /* @__PURE__ */ jsx(IconGlobe, {}) }) : null
9874
+ ] }),
9875
+ children: /* @__PURE__ */ jsx(Box, { style: { maxWidth: "400px" }, children: /* @__PURE__ */ jsx(
9876
+ InputMenuItem_default,
9877
+ {
9878
+ id: id2,
9879
+ variables: variables[id2]
9880
+ }
9881
+ ) })
9882
+ },
9883
+ id2
9884
+ ))
9885
+ ] })
9599
9886
  ]
9600
9887
  }
9601
9888
  );
@@ -10931,7 +11218,7 @@ function BuilderEditor(props) {
10931
11218
  {
10932
11219
  report: reportRef.data,
10933
11220
  locale: props.locale,
10934
- isLoading: reportRef.isFetching
11221
+ isLoading: false
10935
11222
  }
10936
11223
  );
10937
11224
  }, [reportRef.status, reportRef.data?.sections]);
@@ -11316,6 +11603,7 @@ function FormatterForm({ formatterId, onEditEnd }) {
11316
11603
  {
11317
11604
  monacoOptions: {
11318
11605
  onChange: (newLogic) => onContentValueChange("logic", newLogic),
11606
+ height: "30vh",
11319
11607
  value: formatter.content.logic
11320
11608
  }
11321
11609
  },
@@ -12545,6 +12833,7 @@ function MetadataEditor() {
12545
12833
  init_esm_shims();
12546
12834
  function NotFoundView() {
12547
12835
  const { user } = useUser();
12836
+ const textStyle = { color: "#000" };
12548
12837
  return /* @__PURE__ */ jsxs(
12549
12838
  Flex,
12550
12839
  {
@@ -12554,14 +12843,15 @@ function NotFoundView() {
12554
12843
  align: "center",
12555
12844
  direction: "column",
12556
12845
  wrap: "wrap",
12846
+ style: { background: "white" },
12557
12847
  children: [
12558
- /* @__PURE__ */ jsx(Title, { children: "Not found." }),
12559
- /* @__PURE__ */ jsxs(Title, { order: 2, children: [
12848
+ /* @__PURE__ */ jsx(Title, { style: textStyle, children: "Not found." }),
12849
+ /* @__PURE__ */ jsxs(Title, { order: 2, style: textStyle, children: [
12560
12850
  "Sorry, ",
12561
12851
  user?.name,
12562
12852
  "!"
12563
12853
  ] }),
12564
- /* @__PURE__ */ jsx(Text, { children: "The page you are looking for is not here." }),
12854
+ /* @__PURE__ */ jsx(Text, { style: textStyle, children: "The page you are looking for is not here." }),
12565
12855
  /* @__PURE__ */ jsx(Button, { component: "a", href: "/", children: "Back to homepage" })
12566
12856
  ]
12567
12857
  }
@@ -12675,6 +12965,7 @@ function ReportPicker() {
12675
12965
  init_esm_shims();
12676
12966
  function UnauthorizeView() {
12677
12967
  const { user } = useUser();
12968
+ const textStyle = { color: "#000" };
12678
12969
  return /* @__PURE__ */ jsxs(
12679
12970
  Flex,
12680
12971
  {
@@ -12684,15 +12975,39 @@ function UnauthorizeView() {
12684
12975
  align: "center",
12685
12976
  direction: "column",
12686
12977
  wrap: "wrap",
12978
+ style: { background: "white" },
12687
12979
  children: [
12688
- /* @__PURE__ */ jsx(Title, { children: "Unauthorize or forbidden access." }),
12689
- /* @__PURE__ */ jsxs(Title, { order: 2, children: [
12980
+ /* @__PURE__ */ jsx(Title, { style: textStyle, children: "Unauthorize or forbidden access." }),
12981
+ /* @__PURE__ */ jsxs(Title, { style: textStyle, order: 2, children: [
12690
12982
  "Sorry, ",
12691
12983
  user?.name,
12692
12984
  "!"
12693
12985
  ] }),
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" })
12986
+ /* @__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." }),
12987
+ /* @__PURE__ */ jsxs(Group, { children: [
12988
+ /* @__PURE__ */ jsx(
12989
+ Button,
12990
+ {
12991
+ component: "a",
12992
+ href: "/",
12993
+ leftIcon: /* @__PURE__ */ jsx(IconHome, { size: 14 }),
12994
+ variant: "outline",
12995
+ color: "dark",
12996
+ children: "Back to homepage"
12997
+ }
12998
+ ),
12999
+ /* @__PURE__ */ jsx(
13000
+ Button,
13001
+ {
13002
+ component: "a",
13003
+ href: "/api/auth/logout",
13004
+ leftIcon: /* @__PURE__ */ jsx(IconLogout, { size: 14 }),
13005
+ variant: "outline",
13006
+ color: "dark",
13007
+ children: "Disconnect"
13008
+ }
13009
+ )
13010
+ ] })
12696
13011
  ]
12697
13012
  }
12698
13013
  );
package/dist/server.js CHANGED
@@ -506,6 +506,7 @@ var BLOCK_CONTENT_TYPES = {
506
506
  };
507
507
  var BLOCK_LOGIC_TYPES = {
508
508
  GENERATOR: "generator",
509
+ RELATED: "related",
509
510
  VIZ: "visualization"
510
511
  };
511
512
  var BLOCK_LOGIC_LOCALE = "logic";
@@ -1974,6 +1975,26 @@ function readMemberFactory(db) {
1974
1975
  }
1975
1976
  });
1976
1977
  whereClause.content_id = entities.map((item) => item.content_id);
1978
+ } else if (mode === "related") {
1979
+ const levelWhereClause = () => {
1980
+ if (params.variant_ids?.length)
1981
+ return { variant_id: params.variant_ids.map((item) => item) };
1982
+ if (params.dimension_ids?.length)
1983
+ return { dimension_id: params.dimension_ids.map((item) => item) };
1984
+ if (params.report_ids?.length)
1985
+ return { report_id: params.report_ids.map((item) => item) };
1986
+ };
1987
+ const entities = await Search.findAll({
1988
+ attributes: ["content_id"],
1989
+ where: {
1990
+ ...levelWhereClause(),
1991
+ content_id: { [Op.notIn]: params.current_ids },
1992
+ visible: true
1993
+ },
1994
+ order: [Sequelize.fn("RANDOM")],
1995
+ limit: params.related || 4
1996
+ });
1997
+ whereClause.content_id = entities.map((item) => item.content_id);
1977
1998
  }
1978
1999
  const contentWhereClause = {
1979
2000
  locale: all ? { [Op.ne]: null } : locale
@@ -2001,7 +2022,7 @@ function readMemberFactory(db) {
2001
2022
  where: contentWhereClause
2002
2023
  }]
2003
2024
  });
2004
- if (memberSearchResults.length !== params[mode].length) {
2025
+ if (mode !== "related" && memberSearchResults.length !== params[mode].length) {
2005
2026
  throw new BackendError(404, "One or more members were not found.");
2006
2027
  }
2007
2028
  memberSearchResults.sort(
@@ -2703,7 +2724,7 @@ function dbSectionFactory(db) {
2703
2724
  const { id, blocks, ...sectionData } = data;
2704
2725
  const entity = await Section.findByPk(id, {
2705
2726
  include: sectionQuery.include,
2706
- rejectOnEmpty: true
2727
+ rejectOnEmpty: false
2707
2728
  });
2708
2729
  entity.set(sectionData);
2709
2730
  await entity.save();
@@ -3112,6 +3133,7 @@ function parseReadMemberParams(query) {
3112
3133
  const mode = normalizeList(query.mode)[0];
3113
3134
  const outputParam = normalizeList(query.output)[0] || "full";
3114
3135
  const output = outputParam === "lite" ? "lite" : "full";
3136
+ const relatedLimit = parseInt(normalizeList(query.related)[0], 10) || 5;
3115
3137
  let variant = normalizeList(query.variant).map(parseFiniteNumber);
3116
3138
  if (variant.length === 0) {
3117
3139
  variant = normalizeList(query["variant[]"]).map(parseFiniteNumber);
@@ -3149,6 +3171,19 @@ function parseReadMemberParams(query) {
3149
3171
  })
3150
3172
  };
3151
3173
  }
3174
+ if (mode === "related") {
3175
+ return {
3176
+ all,
3177
+ mode,
3178
+ locale: all ? localeDefault3 : stripHTML(locale),
3179
+ related: relatedLimit,
3180
+ current_ids: normalizeList(query.current_ids || query["current_ids[]"]).map(parseFiniteNumber),
3181
+ report_ids: normalizeList(query.report_ids || query["report_ids[]"]).map(parseFiniteNumber),
3182
+ dimension_ids: normalizeList(query.dimension_ids || query["dimension_ids[]"]).map(parseFiniteNumber),
3183
+ variant_ids: normalizeList(query.variant_ids || query["variant_ids[]"]).map(parseFiniteNumber),
3184
+ output
3185
+ };
3186
+ }
3152
3187
  throw new BackendError(400, `Invalid mode: '${mode}'`);
3153
3188
  }
3154
3189
  function parseSearchMemberParams(query) {
@@ -4360,6 +4395,9 @@ var transformReadMembers = (params) => {
4360
4395
  ).join(",");
4361
4396
  return { ...params, slugs };
4362
4397
  }
4398
+ if (params.mode === "related") {
4399
+ return { ...params };
4400
+ }
4363
4401
  return { ...params, [params.mode]: params[params.mode].join(",") };
4364
4402
  };
4365
4403
  function apiFactory2(baseURL) {
@@ -4809,6 +4847,80 @@ async function runSingleBlock(block, formatterFunctions, blockContext, readMembe
4809
4847
  }
4810
4848
  };
4811
4849
  }
4850
+ if (block.type === BLOCK_TYPES.RELATED) {
4851
+ const relatedCompare = block.contentByLocale?.logic?.content?.simple?.compare || "variant";
4852
+ const relatedLimit = block.contentByLocale?.logic?.content?.simple?.limit || 4;
4853
+ const reportIds = Object.keys(variables).filter((key) => key.startsWith("report_id")).map((key) => variables[key]);
4854
+ const dimensionIds = Object.keys(variables).filter((key) => key.startsWith("dimension_id")).map((key) => variables[key]);
4855
+ const variantIds = Object.keys(variables).filter((key) => key.startsWith("variant_id")).map((key) => variables[key]);
4856
+ const memberIds = Object.keys(variables).filter((key) => key.startsWith("id")).map((key) => variables[key]);
4857
+ if (!memberIds.length)
4858
+ return {
4859
+ outputVariables: { related_reports: [] },
4860
+ renderVariables: {},
4861
+ status: {
4862
+ log: [],
4863
+ allowed: false
4864
+ }
4865
+ };
4866
+ const membersContentIds = await readMemberFn({
4867
+ mode: "ids",
4868
+ locale,
4869
+ ids: memberIds,
4870
+ output: "lite"
4871
+ }).then((response) => {
4872
+ let data = [];
4873
+ if (isResult(response)) {
4874
+ if (response.ok) {
4875
+ data = response.data.results;
4876
+ }
4877
+ } else {
4878
+ data = response.results;
4879
+ }
4880
+ return data && Array.isArray(data) ? data : [];
4881
+ }).catch((err) => {
4882
+ console.log("Error getting related members", err);
4883
+ return [];
4884
+ });
4885
+ const current_ids = membersContentIds.map((member) => member.content_id);
4886
+ const memberParams = {
4887
+ mode: "related",
4888
+ locale,
4889
+ related: relatedLimit ?? 4,
4890
+ current_ids,
4891
+ output: "full"
4892
+ };
4893
+ if (relatedCompare) {
4894
+ if (relatedCompare === "report")
4895
+ memberParams.report_ids = reportIds;
4896
+ else if (relatedCompare === "dimension")
4897
+ memberParams.dimension_ids = dimensionIds;
4898
+ else
4899
+ memberParams.variant_ids = variantIds;
4900
+ }
4901
+ const bespokeMembers = await readMemberFn(memberParams).then((response) => {
4902
+ let data = [];
4903
+ if (isResult(response)) {
4904
+ if (response.ok) {
4905
+ data = response.data.results;
4906
+ }
4907
+ } else {
4908
+ data = response.results;
4909
+ }
4910
+ return data && Array.isArray(data) ? data : [];
4911
+ }).catch((err) => {
4912
+ console.log("Error getting related members", err);
4913
+ return [];
4914
+ });
4915
+ return {
4916
+ outputVariables: { related_reports: bespokeMembers },
4917
+ renderVariables: {},
4918
+ status: {
4919
+ log: [],
4920
+ allowed: bespokeMembers.length > 0
4921
+ }
4922
+ };
4923
+ }
4812
4924
  const { logic } = getBlockContent(block, locale);
4813
4925
  const swappedLogic = varSwap_default(logic, formatterFunctions, blockContext);
4814
4926
  const evalResults = mortarEval_default("resp", resp || {}, swappedLogic, formatterFunctions, variables, blockContext);
@@ -4952,7 +5064,7 @@ var runConsumersV2 = async (blocks, sections, bid, formatterFunctions, blockCont
4952
5064
  ).then(({ outputVariables, status }) => {
4953
5065
  if (
4954
5066
  // store output variables for block that:
4955
- (block.consumers.length > 0 || block.type === BLOCK_TYPES.GENERATOR) && status.allowed && Object.keys(outputVariables).length > 0
5067
+ (block.consumers.length > 0 || [BLOCK_TYPES.GENERATOR, BLOCK_TYPES.RELATED].includes(block.type)) && status.allowed && Object.keys(outputVariables).length > 0
4956
5068
  )
4957
5069
  variablesById[bid2] = outputVariables;
4958
5070
  if (mode === "report") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datawheel/bespoke",
3
- "version": "0.1.39",
3
+ "version": "0.2.0",
4
4
  "description": "Content management system for creating automated data reports",
5
5
  "exports": {
6
6
  ".": {