@datawheel/bespoke 0.1.11 → 0.1.13

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
@@ -6,7 +6,7 @@ import { formatAbbreviate } from 'd3plus-format';
6
6
  import { date } from 'd3plus-axis';
7
7
  import { assign, closest, merge, isObject } from 'd3plus-common';
8
8
  import { strip, titleCase } from 'd3plus-text';
9
- import yn from 'yn';
9
+ import yn3 from 'yn';
10
10
  import axios from 'axios';
11
11
  import { schema, normalize } from 'normalizr';
12
12
  import { createSlice, configureStore } from '@reduxjs/toolkit';
@@ -15,13 +15,14 @@ import { NotificationsProvider, showNotification } from '@mantine/notifications'
15
15
  import React, { forwardRef, useMemo, useState, useCallback, useContext, useEffect, createContext, useRef, createElement, Fragment as Fragment$1 } from 'react';
16
16
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
17
17
  import { useDispatch, useSelector } from 'react-redux';
18
- import { Stack, Text, Badge, Group, Tooltip, ActionIcon, Center, useMantineTheme, Modal, Button, Title, Select, MultiSelect, MantineProvider, UnstyledButton, ThemeIcon, Burger, Navbar, AppShell, Skeleton, Container, TextInput, Loader, LoadingOverlay, Alert, Autocomplete, Chip, Card, Space, Code, Divider, SegmentedControl, Overlay, Grid, Popover, Checkbox, Radio, Switch, ScrollArea, Drawer, Header, Input, Textarea, Avatar, Tabs, FileInput, SimpleGrid, Image, Accordion, NumberInput, CopyButton, Col, Menu } from '@mantine/core';
19
- import { IconInfoCircle, IconBoxMargin, IconTable, IconMathFunction, IconSearch, IconTrash, IconDatabase, IconServer, IconPencil, IconAlertCircle, IconCircleCheck, IconPlayerPlay, IconCirclePlus, IconCircleX, IconFileAnalytics, IconPlus, IconCircleDashed, IconChevronLeft, IconSettings, IconAlignLeft, IconAlignCenter, IconAlignRight, IconMenu, IconRefresh, IconPolaroid, IconCircleMinus, IconEyeOff, IconLogout, IconLogin, IconWorld, IconCamera, IconShare, IconVariable, IconArrowRightCircle, IconUpload, IconPalette, IconCode, IconExternalLink, IconDownload, IconTemplate, IconChartBar, IconLink, IconClipboardCheck, IconClipboardCopy, IconEye, IconRss, IconGlobe } from '@tabler/icons';
18
+ import { Stack, Text, Badge, Group, Tooltip, ActionIcon, Center, useMantineTheme, Modal, Button, Title, Select, MultiSelect, Skeleton, Container, TextInput, Loader, LoadingOverlay, Alert, Autocomplete, Chip, Menu, Avatar, Card, Space, Code, Divider, SegmentedControl, Overlay, Flex, MantineProvider, Burger, Navbar, ScrollArea, Box, AppShell, UnstyledButton, Popover, Checkbox, Radio, Switch, Drawer, Header, Input, Grid, ThemeIcon, Textarea, Tabs, Image, Accordion, NumberInput, SimpleGrid, FileInput, CopyButton, Col } from '@mantine/core';
19
+ import { IconInfoCircle, IconSearch, IconTrash, IconUserCircle, IconLogout, IconDatabase, IconServer, IconPencil, IconAlertCircle, IconCircleCheck, IconPlayerPlay, IconCirclePlus, IconFileAnalytics, IconBoxMargin, IconTable, IconMathFunction, IconUsers, IconChevronLeft, IconSettings, IconAlignLeft, IconAlignCenter, IconAlignRight, IconPlus, IconMenu, IconCircleX, IconCircleDashed, IconLogin, IconWorld, IconEyeOff, IconCamera, IconShare, IconRefresh, IconPolaroid, IconCircleMinus, IconVariable, IconArrowRightCircle, IconPalette, IconCode, IconExternalLink, IconDownload, IconTemplate, IconChartBar, IconUpload, IconLink, IconClipboardCheck, IconClipboardCopy, IconEye, IconRss, IconGlobe } from '@tabler/icons';
20
20
  import { dataConcat } from 'd3plus-viz';
21
21
  import * as d3plus from 'd3plus-react';
22
- import { useDisclosure, useDebouncedValue, useMediaQuery, useHotkeys, useUncontrolled, getHotkeyHandler } from '@mantine/hooks';
22
+ import { useDebouncedValue, useMediaQuery, useDisclosure, useHotkeys, useUncontrolled, getHotkeyHandler } from '@mantine/hooks';
23
23
  import Link from 'next/link';
24
- import { useRouter } from 'next/router';
24
+ import Router, { useRouter } from 'next/router';
25
+ import { UserProvider, withPageAuthRequired, useUser } from '@auth0/nextjs-auth0/client';
25
26
  import Head from 'next/head';
26
27
  import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
27
28
  import slugifyFn from 'slugify';
@@ -82,11 +83,11 @@ var init_getLocales = __esm({
82
83
  "libs/configs/getLocales.ts"() {
83
84
  init_esm_shims();
84
85
  getLocales_default = () => {
85
- const localeDefault8 = process.env.NEXT_PUBLIC_REPORTS_LOCALE_DEFAULT || "en";
86
- const locales3 = process.env.NEXT_PUBLIC_REPORTS_LOCALES?.split(",") || [localeDefault8];
87
- if (!locales3.includes(localeDefault8))
88
- locales3.push(localeDefault8);
89
- return { localeDefault: localeDefault8, locales: locales3 };
86
+ const localeDefault9 = process.env.NEXT_PUBLIC_REPORTS_LOCALE_DEFAULT || "en";
87
+ const locales3 = process.env.NEXT_PUBLIC_REPORTS_LOCALES?.split(",") || [localeDefault9];
88
+ if (!locales3.includes(localeDefault9))
89
+ locales3.push(localeDefault9);
90
+ return { localeDefault: localeDefault9, locales: locales3 };
90
91
  };
91
92
  }
92
93
  });
@@ -305,12 +306,12 @@ var init_stats = __esm({
305
306
  };
306
307
  }
307
308
  });
308
- var libs, libs_default;
309
+ var libraries;
309
310
  var init_libs = __esm({
310
311
  "libs/libs.ts"() {
311
312
  init_esm_shims();
312
313
  init_stats();
313
- libs = {
314
+ libraries = {
314
315
  d3: {
315
316
  ...d3Array,
316
317
  ...d3Collection,
@@ -328,7 +329,6 @@ var init_libs = __esm({
328
329
  },
329
330
  stats: stats_default
330
331
  };
331
- libs_default = libs;
332
332
  }
333
333
  });
334
334
  function mortarEval(varInnerName, varOuterValue, logic, formatterFunctions, attributes = void 0, blockContext) {
@@ -352,10 +352,10 @@ function mortarEval(varInnerName, varOuterValue, logic, formatterFunctions, attr
352
352
  let output;
353
353
  if (attributes) {
354
354
  const fn = new Function(varInnerName, "console", "libs", "formatters", "locale", "variables", logic);
355
- output = fn(varOuterValue, pConsole, libs_default, formatterFunctions, locale, attributes);
355
+ output = fn(varOuterValue, pConsole, libraries, formatterFunctions, locale, attributes);
356
356
  } else {
357
357
  const fn = new Function(varInnerName, "console", "libs", "formatters", "locale", logic);
358
- output = fn(varOuterValue, pConsole, libs_default, formatterFunctions, locale);
358
+ output = fn(varOuterValue, pConsole, libraries, formatterFunctions, locale);
359
359
  }
360
360
  if (typeof output === "object" && Object.keys(output).length > 0) {
361
361
  Object.assign(vars, output);
@@ -376,7 +376,7 @@ var init_mortarEval = __esm({
376
376
  init_esm_shims();
377
377
  init_block();
378
378
  init_libs();
379
- verbose = yn(process.env.REPORTS_LOGGING);
379
+ verbose = yn3(process.env.REPORTS_LOGGING);
380
380
  mortarEval_default = mortarEval;
381
381
  }
382
382
  });
@@ -590,7 +590,7 @@ function varSwap(sourceString, formatterFunctions, blockContext, passive = false
590
590
  return passive ? match2 : "N/A";
591
591
  }
592
592
  try {
593
- return formatter(value, libs, formatterFunctions);
593
+ return formatter(value, libraries, formatterFunctions);
594
594
  } catch (err) {
595
595
  console.error(
596
596
  `Error using formatter ${formatter.name}
@@ -784,13 +784,13 @@ var getLogging_default;
784
784
  var init_getLogging = __esm({
785
785
  "libs/configs/getLogging.ts"() {
786
786
  init_esm_shims();
787
- getLogging_default = (env = process.env) => yn(env.REPORTS_LOGGING);
787
+ getLogging_default = (env = process.env) => yn3(env.REPORTS_LOGGING);
788
788
  }
789
789
  });
790
790
 
791
791
  // api/http/lib.ts
792
- function http(axios7, config) {
793
- return axios7.request(config).then((response) => {
792
+ function http(axios6, config) {
793
+ return axios6.request(config).then((response) => {
794
794
  const { status, data } = response;
795
795
  return "error" in data ? { ok: false, status, error: data.error } : { ok: true, status, data: data.data };
796
796
  }, (err) => {
@@ -801,25 +801,25 @@ function http(axios7, config) {
801
801
  return { ok: false, status: 500, error: err.message };
802
802
  });
803
803
  }
804
- function httpGET(axios7, request, transformParams) {
804
+ function httpGET(axios6, request, transformParams) {
805
805
  const config = typeof request === "string" ? { url: request } : request;
806
- return (params) => http(axios7, {
806
+ return (params) => http(axios6, {
807
807
  ...config,
808
808
  method: "GET",
809
809
  params: transformParams ? transformParams(params) : params
810
810
  });
811
811
  }
812
- function httpPOST(axios7, request, transformPayload) {
812
+ function httpPOST(axios6, request, transformPayload) {
813
813
  const config = typeof request === "string" ? { url: request } : request;
814
- return (payload) => http(axios7, {
814
+ return (payload) => http(axios6, {
815
815
  ...config,
816
816
  method: "POST",
817
817
  data: transformPayload ? transformPayload(payload) : payload
818
818
  });
819
819
  }
820
- function httpDELETE(axios7, request, transformPayload) {
820
+ function httpDELETE(axios6, request, transformPayload) {
821
821
  const config = typeof request === "string" ? { url: request } : request;
822
- return (payload) => http(axios7, {
822
+ return (payload) => http(axios6, {
823
823
  ...config,
824
824
  method: "DELETE",
825
825
  data: transformPayload ? transformPayload(payload) : payload
@@ -832,8 +832,8 @@ var init_lib = __esm({
832
832
  });
833
833
 
834
834
  // api/http/image/imageSave.ts
835
- function httpImageSaveFactory(axios7, provider) {
836
- return (params) => http(axios7, {
835
+ function httpImageSaveFactory(axios6, provider) {
836
+ return (params) => http(axios6, {
837
837
  method: "GET",
838
838
  url: `search/images/${provider}`,
839
839
  params: { prompt: params.prompt, provider }
@@ -847,8 +847,8 @@ var init_imageSave = __esm({
847
847
  });
848
848
 
849
849
  // api/http/image/imageSearch.ts
850
- function httpImageSearchFactory(axios7, provider) {
851
- return (params) => http(axios7, {
850
+ function httpImageSearchFactory(axios6, provider) {
851
+ return (params) => http(axios6, {
852
852
  method: "GET",
853
853
  url: `search/images/${provider}`,
854
854
  params: { prompt: params.prompt, provider }
@@ -861,51 +861,56 @@ var init_imageSearch = __esm({
861
861
  }
862
862
  });
863
863
  function apiFactory(baseURL) {
864
- const axios7 = axios.create({ baseURL });
864
+ const axios6 = axios.create({ baseURL });
865
865
  return {
866
- createBlock: httpPOST(axios7, "create/block"),
867
- createDimension: httpPOST(axios7, "create/dimension"),
868
- createFormatter: httpPOST(axios7, "create/formatter"),
869
- createReport: httpPOST(axios7, "create/report"),
870
- createSection: httpPOST(axios7, "create/section"),
871
- createVariant: httpPOST(axios7, "create/variant"),
872
- deleteBlock: httpDELETE(axios7, "delete/block", transformDeletePayload),
873
- deleteDimension: httpDELETE(axios7, "delete/dimension", transformDeletePayload),
874
- deleteFormatter: httpDELETE(axios7, "delete/formatter", transformDeletePayload),
875
- deleteReport: httpDELETE(axios7, "delete/report", transformDeletePayload),
876
- deleteSection: httpDELETE(axios7, "delete/section", transformDeletePayload),
877
- deleteVariant: httpDELETE(axios7, "delete/variant", transformDeletePayload),
878
- readBlock: httpGET(axios7, "read/block"),
879
- readDimension: httpGET(axios7, "read/dimension"),
880
- readFormatter: httpGET(axios7, "read/formatter"),
881
- readReport: httpGET(axios7, "read/report"),
882
- readSection: httpGET(axios7, "read/section"),
883
- readVariant: httpGET(axios7, "read/variant"),
884
- updateBlock: httpPOST(axios7, "update/block"),
885
- updateDimension: httpPOST(axios7, "update/dimension"),
886
- updateFormatter: httpPOST(axios7, "update/formatter"),
887
- updateReport: httpPOST(axios7, "update/report"),
888
- updateSection: httpPOST(axios7, "update/section"),
889
- updateVariant: httpPOST(axios7, "update/variant"),
890
- searchReport: httpGET(axios7, "search/reports"),
891
- validateVariantSlug: httpGET(axios7, "validate/variant"),
892
- readMember: httpGET(axios7, "read/members"),
893
- readMemberImage: httpGET(axios7, {
866
+ createBlock: httpPOST(axios6, "create/block"),
867
+ createDimension: httpPOST(axios6, "create/dimension"),
868
+ createFormatter: httpPOST(axios6, "create/formatter"),
869
+ createReport: httpPOST(axios6, "create/report"),
870
+ createSection: httpPOST(axios6, "create/section"),
871
+ createVariant: httpPOST(axios6, "create/variant"),
872
+ deleteBlock: httpDELETE(axios6, "delete/block", transformDeletePayload),
873
+ deleteDimension: httpDELETE(axios6, "delete/dimension", transformDeletePayload),
874
+ deleteFormatter: httpDELETE(axios6, "delete/formatter", transformDeletePayload),
875
+ deleteReport: httpDELETE(axios6, "delete/report", transformDeletePayload),
876
+ deleteSection: httpDELETE(axios6, "delete/section", transformDeletePayload),
877
+ deleteVariant: httpDELETE(axios6, "delete/variant", transformDeletePayload),
878
+ readBlock: httpGET(axios6, "read/block"),
879
+ readDimension: httpGET(axios6, "read/dimension"),
880
+ readFormatter: httpGET(axios6, "read/formatter"),
881
+ readReport: httpGET(axios6, "read/report"),
882
+ readSection: httpGET(axios6, "read/section"),
883
+ readVariant: httpGET(axios6, "read/variant"),
884
+ updateBlock: httpPOST(axios6, "update/block"),
885
+ updateDimension: httpPOST(axios6, "update/dimension"),
886
+ updateFormatter: httpPOST(axios6, "update/formatter"),
887
+ updateReport: httpPOST(axios6, "update/report"),
888
+ updateSection: httpPOST(axios6, "update/section"),
889
+ updateVariant: httpPOST(axios6, "update/variant"),
890
+ searchReport: httpGET(axios6, "search/reports"),
891
+ validateVariantSlug: httpGET(axios6, "validate/variant"),
892
+ readMember: httpGET(axios6, "read/members"),
893
+ readMemberImage: httpGET(axios6, {
894
894
  url: "member/image.png",
895
895
  responseType: "blob"
896
896
  }),
897
- searchMember: httpGET(axios7, "search/members"),
898
- updateMember: httpGET(axios7, "update/member"),
899
- imageLocalSearch: httpImageSearchFactory(axios7, "local"),
900
- imageLocalSave: httpImageSaveFactory(axios7, "local"),
901
- imageFlickrSearch: httpImageSearchFactory(axios7, "flickr"),
902
- imageFlickrSave: httpImageSaveFactory(axios7, "flickr"),
903
- imageUnsplashSearch: httpImageSearchFactory(axios7, "unsplash"),
904
- imageUnsplashSave: httpImageSaveFactory(axios7, "unsplash"),
905
- imageUploadSave: httpImageSaveFactory(axios7, "upload"),
906
- readMetadata: httpGET(axios7, "read/metadata"),
907
- regenerateSearch: httpPOST(axios7, "search/regenerate"),
908
- urlProxy: httpGET(axios7, "url/proxy")
897
+ searchMember: httpGET(axios6, "search/members"),
898
+ updateMember: httpGET(axios6, "update/members"),
899
+ imageLocalSearch: httpImageSearchFactory(axios6, "local"),
900
+ imageLocalSave: httpImageSaveFactory(axios6, "local"),
901
+ imageFlickrSearch: httpImageSearchFactory(axios6, "flickr"),
902
+ imageFlickrSave: httpImageSaveFactory(axios6, "flickr"),
903
+ imageUnsplashSearch: httpImageSearchFactory(axios6, "unsplash"),
904
+ imageUnsplashSave: httpImageSaveFactory(axios6, "unsplash"),
905
+ imageUploadSave: httpImageSaveFactory(axios6, "upload"),
906
+ readMetadata: httpGET(axios6, "read/metadata"),
907
+ regenerateSearch: httpPOST(axios6, "search/regenerate"),
908
+ urlProxy: httpGET(axios6, "url/proxy"),
909
+ searchRole: httpGET(axios6, "auth/search/roles"),
910
+ searchUser: httpGET(axios6, "auth/search/users"),
911
+ readUser: httpGET(axios6, "auth/read/user"),
912
+ updateUser: httpPOST(axios6, "auth/update/user"),
913
+ addNewReportToCurrentUser: httpPOST(axios6, "auth/update/me")
909
914
  };
910
915
  }
911
916
  var transformDeletePayload;
@@ -1071,7 +1076,7 @@ var init_runConsumers = __esm({
1071
1076
  }
1072
1077
  };
1073
1078
  const crawlDown = async (bid2, cascader = false) => {
1074
- if (cascader === false) {
1079
+ if (cascader) {
1075
1080
  variablesById[bid2] = {};
1076
1081
  statusById[bid2] = { hiddenByCascade: cascader };
1077
1082
  for (let i = 0; i < blocks[bid2].consumers.length; i += 1) {
@@ -1305,7 +1310,7 @@ var init_FUNC = __esm({
1305
1310
  setVariables: actions.onSetVariables ? actions.onSetVariables : (d) => d,
1306
1311
  openModal: actions.onOpenModal ? actions.onOpenModal : (d) => d,
1307
1312
  formatters,
1308
- libs: libs_default,
1313
+ libs: libraries,
1309
1314
  locale
1310
1315
  };
1311
1316
  function parseFunction(obj) {
@@ -1357,7 +1362,16 @@ var init_lib2 = __esm({
1357
1362
  init_FUNC();
1358
1363
  init_getLocales();
1359
1364
  ({ localeDefault: localeDefault4 } = getLocales_default());
1360
- blockSchema = new schema.Entity("block" /* BLOCKS */);
1365
+ blockSchema = new schema.Entity("block" /* BLOCKS */, {}, {
1366
+ mergeStrategy(a, b) {
1367
+ if (b.block_input)
1368
+ return a || {};
1369
+ return {
1370
+ ...a,
1371
+ ...b
1372
+ };
1373
+ }
1374
+ });
1361
1375
  blockSchema.define({
1362
1376
  consumers: [blockSchema],
1363
1377
  inputs: [blockSchema]
@@ -1437,6 +1451,9 @@ var init_lib2 = __esm({
1437
1451
  });
1438
1452
 
1439
1453
  // api/lib.ts
1454
+ function failureResult(err) {
1455
+ return { ok: false, status: BackendError.is(err) ? err.code : 500, error: err.message };
1456
+ }
1440
1457
  function pickMethod(api, operation, entity) {
1441
1458
  const name = `${operation}${capitalize(entity)}`;
1442
1459
  return api[name];
@@ -1444,9 +1461,22 @@ function pickMethod(api, operation, entity) {
1444
1461
  function capitalize(str) {
1445
1462
  return str[0].toUpperCase() + str.slice(1);
1446
1463
  }
1464
+ var BackendError;
1447
1465
  var init_lib3 = __esm({
1448
1466
  "api/lib.ts"() {
1449
1467
  init_esm_shims();
1468
+ BackendError = class extends Error {
1469
+ constructor(code, message) {
1470
+ super(message);
1471
+ this.code = code;
1472
+ }
1473
+ toJSON() {
1474
+ return failureResult(this);
1475
+ }
1476
+ static is(obj) {
1477
+ return Object.prototype.hasOwnProperty.call(obj, "code") && typeof obj.code === "number";
1478
+ }
1479
+ };
1450
1480
  }
1451
1481
  });
1452
1482
  function combineRecords(prev, next) {
@@ -1883,6 +1913,7 @@ var init_variablesSlice = __esm({
1883
1913
  // store/actions.ts
1884
1914
  var actions_exports = {};
1885
1915
  __export(actions_exports, {
1916
+ addNewReportToCurrentUser: () => addNewReportToCurrentUser,
1886
1917
  createEntity: () => createEntity,
1887
1918
  deleteEntity: () => deleteEntity,
1888
1919
  deleteQueryParam: () => deleteQueryParam,
@@ -1890,12 +1921,16 @@ __export(actions_exports, {
1890
1921
  readEntity: () => readEntity,
1891
1922
  readMember: () => readMember,
1892
1923
  readMetadata: () => readMetadata,
1924
+ readUser: () => readUser,
1893
1925
  recalculateVariables: () => recalculateVariables,
1894
1926
  reportSearch: () => reportSearch,
1895
1927
  searchMember: () => searchMember,
1896
1928
  searchRegenerate: () => searchRegenerate,
1929
+ searchRole: () => searchRole,
1930
+ searchUser: () => searchUser,
1897
1931
  setQueryParam: () => setQueryParam,
1898
1932
  updateEntity: () => updateEntity,
1933
+ updateUser: () => updateUser,
1899
1934
  urlProxy: () => urlProxy2,
1900
1935
  variantValidateSlug: () => variantValidateSlug
1901
1936
  });
@@ -2014,6 +2049,51 @@ function urlProxy2(url) {
2014
2049
  return result.data;
2015
2050
  };
2016
2051
  }
2052
+ function searchRole() {
2053
+ return async (_, __, api) => {
2054
+ const result = await api.searchRole("");
2055
+ if ("error" in result) {
2056
+ throw new Error(result.error);
2057
+ }
2058
+ return result.data;
2059
+ };
2060
+ }
2061
+ function searchUser(filters) {
2062
+ return async (_, __, api) => {
2063
+ const result = await api.searchUser(filters);
2064
+ if ("error" in result) {
2065
+ throw new Error(result.error);
2066
+ }
2067
+ return result.data;
2068
+ };
2069
+ }
2070
+ function readUser(userId) {
2071
+ return async (_, __, api) => {
2072
+ const result = await api.readUser({ user_id: userId });
2073
+ if ("error" in result) {
2074
+ throw new Error(result.error);
2075
+ }
2076
+ return result.data;
2077
+ };
2078
+ }
2079
+ function updateUser({ user }) {
2080
+ return async (_, __, api) => {
2081
+ const result = await api.updateUser(user);
2082
+ if ("error" in result) {
2083
+ throw new Error(result.error);
2084
+ }
2085
+ return result.data;
2086
+ };
2087
+ }
2088
+ function addNewReportToCurrentUser(params) {
2089
+ return async (_, __, api) => {
2090
+ const result = await api.addNewReportToCurrentUser(params);
2091
+ if ("error" in result) {
2092
+ throw new Error(result.error);
2093
+ }
2094
+ return result.data;
2095
+ };
2096
+ }
2017
2097
  var init_actions = __esm({
2018
2098
  "store/actions.ts"() {
2019
2099
  init_esm_shims();
@@ -2066,12 +2146,13 @@ function useEntityListFactory(entity) {
2066
2146
  const dispatch = useAppDispatch();
2067
2147
  const key = useMemo(() => crudKeyMaker.read(entity, { include: true }), []);
2068
2148
  const selector = useCallback((state) => {
2069
- const data = Object.values(state.records.entities[entity]);
2149
+ const dataRaw = Object.values(state.records.entities[entity]);
2070
2150
  const req = state.records.requests[entity][key] || {
2071
2151
  key,
2072
2152
  type: entity,
2073
- status: data == null ? "UNINITIALIZED" /* UNINITIALIZED */ : "SUCCESS" /* SUCCESS */
2153
+ status: dataRaw == null ? "UNINITIALIZED" /* UNINITIALIZED */ : "SUCCESS" /* SUCCESS */
2074
2154
  };
2155
+ const data = id ? dataRaw.filter((dr) => id.includes(dr.id)) : dataRaw;
2075
2156
  return processResult(data, req);
2076
2157
  }, [key]);
2077
2158
  const value = useAppSelector(selector);
@@ -2132,7 +2213,7 @@ function processResult(data, req) {
2132
2213
  }
2133
2214
  return { ...result, data };
2134
2215
  }
2135
- var localeDefault5, useAppDispatch, useAppSelector, useReportList, useFormatterList, useReportRef, useSectionRef, useBlockRef, useDimensionRef, useVariantRef, useFormatterRef, useInputVariablesFlat, useInputVariablesHash, useBlockStatus, useFormatterFunctionsForLocale, useBlockContext, getFormatterUsageCount, useFormatterUsageCount, useFormatterUsageCountList;
2216
+ var localeDefault5, useAppDispatch, useAppSelector, useReportList, useVariantList, useFormatterList, useReportRef, useSectionRef, useBlockRef, useDimensionRef, useVariantRef, useFormatterRef, useInputVariablesFlat, useInputVariablesHash, useBlockStatus, useFormatterFunctionsForLocale, useBlockContext, getFormatterUsageCount, useFormatterUsageCount, useFormatterUsageCountList;
2136
2217
  var init_hooks = __esm({
2137
2218
  "store/hooks.ts"() {
2138
2219
  init_esm_shims();
@@ -2145,6 +2226,7 @@ var init_hooks = __esm({
2145
2226
  useAppDispatch = useDispatch;
2146
2227
  useAppSelector = useSelector;
2147
2228
  useReportList = useEntityListFactory("report");
2229
+ useVariantList = useEntityListFactory("variant");
2148
2230
  useFormatterList = useEntityListFactory("formatter");
2149
2231
  useReportRef = useEntityRefFactory("report");
2150
2232
  useSectionRef = useEntityRefFactory("section");
@@ -2187,12 +2269,12 @@ var init_hooks = __esm({
2187
2269
  return resource.formatterFunctions[locale];
2188
2270
  };
2189
2271
  useBlockContext = (id = void 0, locale = void 0) => {
2190
- const localeDefault8 = useAppSelector((state) => state.status.localeDefault);
2272
+ const localeDefault9 = useAppSelector((state) => state.status.localeDefault);
2191
2273
  const query = useAppSelector((state) => state.status.query);
2192
2274
  const variables = useInputVariablesFlat(id);
2193
2275
  return {
2194
2276
  variables,
2195
- locale: locale || localeDefault8,
2277
+ locale: locale || localeDefault9,
2196
2278
  query
2197
2279
  };
2198
2280
  };
@@ -2458,12 +2540,12 @@ var init_Table = __esm({
2458
2540
  Table_default = Table;
2459
2541
  }
2460
2542
  });
2461
- var localeDefault7, frontEndMessage, errorStub, d3plusPropify_default;
2543
+ var localeDefault8, frontEndMessage, errorStub, d3plusPropify_default;
2462
2544
  var init_d3plusPropify = __esm({
2463
2545
  "libs/d3plusPropify.ts"() {
2464
2546
  init_esm_shims();
2465
2547
  init_FUNC();
2466
- localeDefault7 = process.env.NEXT_PUBLIC_REPORTS_LOCALE_DEFAULT || "en";
2548
+ localeDefault8 = process.env.NEXT_PUBLIC_REPORTS_LOCALE_DEFAULT || "en";
2467
2549
  frontEndMessage = "Error Rendering Visualization";
2468
2550
  errorStub = {
2469
2551
  data: [],
@@ -2473,7 +2555,7 @@ var init_d3plusPropify = __esm({
2473
2555
  <strong>${frontEndMessage}</strong>
2474
2556
  </p>`
2475
2557
  };
2476
- d3plusPropify_default = (logic, formatters, variables = {}, locale = localeDefault7, id = null, actions = {}) => {
2558
+ d3plusPropify_default = (logic, formatters, variables = {}, locale = localeDefault8, id = null, actions = {}) => {
2477
2559
  let config;
2478
2560
  try {
2479
2561
  config = parse({ vars: ["variables"], logic }, formatters, locale, actions)(variables);
@@ -2723,7 +2805,6 @@ function ExploreFilters({ metadata, onFilter, initialReportId, initialVariantId
2723
2805
  const combinatory = variantsArrays.reduce((a, b) => a.reduce((r, v) => r.concat(b.map((w) => [].concat(v, w))), [])).map((combination) => {
2724
2806
  if (!Array.isArray(combination))
2725
2807
  return combination;
2726
- console.log(combination);
2727
2808
  return {
2728
2809
  id: combination.map((c) => c.id).join(","),
2729
2810
  name: combination.map((c) => c.name).join(" / ")
@@ -2743,7 +2824,7 @@ function ExploreFilters({ metadata, onFilter, initialReportId, initialVariantId
2743
2824
  }
2744
2825
  };
2745
2826
  useEffect(() => {
2746
- let newFilters = {};
2827
+ const newFilters = {};
2747
2828
  if (selectedProfile) {
2748
2829
  newFilters.profile = selectedProfile.id;
2749
2830
  }
@@ -3002,7 +3083,6 @@ function BespokeSearch({ locale, profilePrefix, autocompleteProps = {} }) {
3002
3083
  const [initialized, setInitialized] = useState(false);
3003
3084
  const inputRef = useRef();
3004
3085
  const doSearch = () => {
3005
- console.log("doSearch", debouncedQuery);
3006
3086
  setLoading(true);
3007
3087
  const params = {
3008
3088
  query: debouncedQuery,
@@ -3127,6 +3207,92 @@ function Report({ members }) {
3127
3207
  }
3128
3208
  var Report_default = Report;
3129
3209
 
3210
+ // frontend/components/auth/LoginButton.tsx
3211
+ init_esm_shims();
3212
+ function BespokeLoginBtn({
3213
+ options = "",
3214
+ buttonProps = {},
3215
+ menuProps = {}
3216
+ }) {
3217
+ const { user, error, isLoading } = useUser();
3218
+ const buttonConfig = {
3219
+ leftIcon: /* @__PURE__ */ jsx(IconUserCircle, {}),
3220
+ ...buttonProps
3221
+ };
3222
+ if (user && !error && !isLoading) {
3223
+ const menuConfig = {
3224
+ shadow: "md",
3225
+ width: "auto",
3226
+ position: "bottom",
3227
+ ...menuProps
3228
+ };
3229
+ return /* @__PURE__ */ jsxs(Menu, { ...menuConfig, children: [
3230
+ /* @__PURE__ */ jsx(Menu.Target, { children: /* @__PURE__ */ jsx(Button, { ...buttonConfig, children: `Hi, ${user.name || user.email}!` }) }),
3231
+ /* @__PURE__ */ jsxs(Menu.Dropdown, { children: [
3232
+ options,
3233
+ options && /* @__PURE__ */ jsx(Menu.Divider, {}),
3234
+ /* @__PURE__ */ jsx(Menu.Label, { children: "Session" }),
3235
+ /* @__PURE__ */ jsxs(
3236
+ Menu.Item,
3237
+ {
3238
+ component: "span",
3239
+ style: { pointerEvents: "none" },
3240
+ icon: /* @__PURE__ */ jsx(Avatar, { src: user.picture, radius: "md" }),
3241
+ children: [
3242
+ /* @__PURE__ */ jsx(Text, { size: "sm", weight: 500, children: user.name }),
3243
+ /* @__PURE__ */ jsx(Text, { color: "dimmed", size: "xs", children: user.email })
3244
+ ]
3245
+ }
3246
+ ),
3247
+ /* @__PURE__ */ jsx(
3248
+ Menu.Item,
3249
+ {
3250
+ component: "a",
3251
+ href: "/api/auth/logout",
3252
+ icon: /* @__PURE__ */ jsx(IconLogout, { size: 14 }),
3253
+ children: "Disconnect"
3254
+ }
3255
+ )
3256
+ ] })
3257
+ ] });
3258
+ }
3259
+ return /* @__PURE__ */ jsx(
3260
+ Button,
3261
+ {
3262
+ component: "a",
3263
+ href: "/api/auth/login",
3264
+ loading: isLoading,
3265
+ ...buttonConfig,
3266
+ children: isLoading ? "Signing in..." : "Sign in"
3267
+ }
3268
+ );
3269
+ }
3270
+ var LoginButton_default = BespokeLoginBtn;
3271
+
3272
+ // frontend/components/auth/UserProvider.tsx
3273
+ init_esm_shims();
3274
+ var UserProvider_default = UserProvider;
3275
+
3276
+ // frontend/components/auth/withPageRoleAuthRequired.tsx
3277
+ init_esm_shims();
3278
+ var withPageRoleAuthRequired = (Success, allowedRoles, Err, options) => {
3279
+ function hoc({ user }) {
3280
+ function arrContains(arr1, arr2) {
3281
+ return arr1.some((item) => arr2.includes(item));
3282
+ }
3283
+ const usersRoles = user?.bespoke_roles;
3284
+ if (!usersRoles || allowedRoles.length > 0 && !arrContains(allowedRoles, usersRoles)) {
3285
+ return /* @__PURE__ */ jsx(Err, {});
3286
+ }
3287
+ return /* @__PURE__ */ jsx(Success, { user });
3288
+ }
3289
+ return withPageAuthRequired(hoc, options);
3290
+ };
3291
+
3292
+ // frontend/components/auth/useUser.tsx
3293
+ init_esm_shims();
3294
+ var useUser_default = useUser;
3295
+
3130
3296
  // index.ts
3131
3297
  init_store2();
3132
3298
 
@@ -3150,7 +3316,6 @@ function BespokeLogo(props) {
3150
3316
  return /* @__PURE__ */ jsx(Link, { href: props.href, passHref: true, children: /* @__PURE__ */ jsx(
3151
3317
  UnstyledButton,
3152
3318
  {
3153
- component: "a",
3154
3319
  sx: (theme) => ({
3155
3320
  display: "block",
3156
3321
  width: "100%",
@@ -3213,74 +3378,250 @@ init_actions();
3213
3378
  // components/builder/DimensionAutocomplete.tsx
3214
3379
  init_esm_shims();
3215
3380
 
3216
- // libs/js/paramString.ts
3381
+ // api/endpoints/member.ts
3382
+ init_esm_shims();
3383
+ init_getLocales();
3384
+
3385
+ // libs/js/stripHTML.ts
3386
+ init_esm_shims();
3387
+ function stripHTML(n) {
3388
+ const entities = {
3389
+ "&amp;": "&",
3390
+ "&lt;": "<",
3391
+ "&gt;": ">",
3392
+ "&quot;": '"',
3393
+ "&#x27;": "'",
3394
+ "&#x60;": "`",
3395
+ "&nbsp;": ""
3396
+ };
3397
+ const source = `(?:${Object.keys(entities).join("|")})`;
3398
+ const testRegexp = RegExp(source);
3399
+ const replaceRegexp = RegExp(source, "g");
3400
+ const s = String(n).replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
3401
+ return testRegexp.test(s) ? s.replace(replaceRegexp, (match) => entities[match]) : s;
3402
+ }
3403
+
3404
+ // api/endpoints/member.ts
3405
+ init_lib3();
3406
+
3407
+ // api/endpoints/lib.ts
3408
+ init_esm_shims();
3409
+ init_lib3();
3410
+ function parseFiniteNumber(value) {
3411
+ const num = Number.parseInt(`${value || ""}`, 10);
3412
+ if (Number.isNaN(num) || !Number.isFinite(num)) {
3413
+ throw new BackendError(400, `Invalid numeric parameter: ${value}`);
3414
+ }
3415
+ return num;
3416
+ }
3417
+ function normalizeList(value) {
3418
+ if (value == null || value === "") {
3419
+ return [];
3420
+ }
3421
+ if (Array.isArray(value)) {
3422
+ return value;
3423
+ }
3424
+ return value.toString().split(",");
3425
+ }
3426
+
3427
+ // types/auth.ts
3217
3428
  init_esm_shims();
3218
- var paramString_default = (obj) => Object.keys(obj).map((key) => `${key}=${obj[key]}`).join("&");
3429
+ var CMS_ROLES = {
3430
+ ADMIN: "Admin",
3431
+ EDITOR: "Editor",
3432
+ WRITER: "Writer"
3433
+ };
3434
+
3435
+ // api/endpoints/member.ts
3436
+ var { localeDefault: localeDefault7 } = getLocales_default();
3437
+ function parseReadMemberParams(query) {
3438
+ let ids = normalizeList(query.ids);
3439
+ if (ids.length === 0) {
3440
+ ids = normalizeList(query["ids[]"]);
3441
+ }
3442
+ let content_ids = normalizeList(query.content_ids).map(parseFiniteNumber);
3443
+ if (content_ids.length === 0) {
3444
+ content_ids = normalizeList(query["content_ids[]"]).map(parseFiniteNumber);
3445
+ }
3446
+ const locale = normalizeList(query.locale)[0] || localeDefault7 || "en";
3447
+ const localeIsAll = locale === "all";
3448
+ return {
3449
+ all: localeIsAll,
3450
+ locale: localeIsAll ? localeDefault7 : stripHTML(locale),
3451
+ mode: ids.length > 0 ? "ids" : content_ids.length > 0 ? "content_ids" : "slugs",
3452
+ ids,
3453
+ content_ids,
3454
+ slugs: normalizeList(query.slugs).map((token) => {
3455
+ const [variantSlug, memberSlug] = token.split("/");
3456
+ return { variantSlug, memberSlug };
3457
+ })
3458
+ };
3459
+ }
3460
+ function parseSearchMemberParams(query) {
3461
+ const locale = normalizeList(query.locale)[0] || localeDefault7 || "en";
3462
+ const localeIsAll = locale === "all";
3463
+ const format2 = normalizeList(query.format)[0];
3464
+ const formatIsNested = format2 ? format2 === "nested" : localeIsAll;
3465
+ let variant = normalizeList(query.variant).map(parseFiniteNumber);
3466
+ if (variant.length === 0) {
3467
+ variant = normalizeList(query["variant[]"]).map(parseFiniteNumber);
3468
+ }
3469
+ let dimension = normalizeList(query.dimension).map(parseFiniteNumber);
3470
+ if (dimension.length === 0) {
3471
+ dimension = normalizeList(query["dimension[]"]).map(parseFiniteNumber);
3472
+ }
3473
+ let report = normalizeList(query.report).map(parseFiniteNumber);
3474
+ if (report.length === 0) {
3475
+ report = normalizeList(query["report[]"]).map(parseFiniteNumber);
3476
+ }
3477
+ return {
3478
+ query: normalizeList(query.query || query.q)[0] || "",
3479
+ locale: localeIsAll ? localeDefault7 : locale,
3480
+ limit: normalizeList(query.limit).map(parseFiniteNumber)[0] ?? 5,
3481
+ format: formatIsNested ? "nested" : "plain",
3482
+ includes: yn3(query.includes, { default: true }),
3483
+ visible: yn3(query.visible, { default: true }),
3484
+ noImage: yn3(query.noImage, { default: false }),
3485
+ variant,
3486
+ dimension,
3487
+ report,
3488
+ all: localeIsAll,
3489
+ sort: normalizeList(query.sort)[0],
3490
+ direction: normalizeList(query.direction)[0]
3491
+ };
3492
+ }
3219
3493
 
3220
3494
  // components/builder/DimensionAutocomplete.tsx
3221
3495
  init_store2();
3222
- function DimensionAutocomplete({ id, locale, onSelect }) {
3496
+ function DimensionAutocomplete({ id, locale, onSelect, initialSelection = void 0 }) {
3497
+ const dispatch = useAppDispatch();
3223
3498
  const dimensionRef = useDimensionRef(id);
3224
3499
  const dimension = dimensionRef.data;
3225
- const variants = useAppSelector(
3226
- (state) => dimension ? dimension.variants.map((vId) => state.records.entities.variant[vId]) : []
3227
- );
3500
+ const variantsList = useVariantList(dimension ? dimension.variants : []);
3228
3501
  const [query, setQuery] = useState("");
3229
3502
  const [debouncedQuery] = useDebouncedValue(query, 500);
3230
3503
  const [results, setResults] = useState([]);
3231
3504
  const [data, setData] = useState([]);
3232
3505
  const [currentVariant, setCurrentVariant] = useState();
3233
3506
  const [currentMember, setCurrentMember] = useState();
3507
+ const [searchMode, setSearchMode] = useState(true);
3508
+ const [noMembers, setNoMembers] = useState(false);
3234
3509
  const inputRef = useRef(null);
3235
- const variantsOptions = Object.values(variants).map((d) => ({ value: String(d.id), label: `${d.name}` }));
3510
+ const variantsOptions = useMemo(() => {
3511
+ if (!variantsList.isSuccess)
3512
+ return [];
3513
+ return variantsList.data.map((d) => ({ value: `${d.id}`, label: d.name }));
3514
+ }, [variantsList.status]);
3236
3515
  useEffect(() => {
3237
- const url = `/api/cms/search/members?${paramString_default({
3238
- query: debouncedQuery,
3239
- locale,
3240
- dimension: dimension.id,
3241
- variant: currentVariant ? currentVariant.id : ""
3242
- })}`;
3243
- axios.get(url).then((resp) => {
3244
- if (resp.data.data && resp.data.data.results) {
3245
- const res = resp.data.data.results;
3246
- setResults(res);
3247
- }
3248
- }).catch((e) => console.error(e));
3249
- }, [debouncedQuery]);
3516
+ async function fetchMembers() {
3517
+ const searchParams = parseSearchMemberParams({
3518
+ query: debouncedQuery,
3519
+ locale,
3520
+ dimension: dimension.id,
3521
+ variant: currentVariant ? currentVariant.id : "",
3522
+ limit: "5",
3523
+ visible: "true",
3524
+ includes: "true",
3525
+ noImage: "false",
3526
+ format: "plain",
3527
+ report: void 0,
3528
+ all: "false"
3529
+ });
3530
+ const response = await dispatch(actions_exports.searchMember(searchParams));
3531
+ if (response && response.results) {
3532
+ const members = response.results;
3533
+ setResults(members);
3534
+ if (members.length === 0 && debouncedQuery === "") {
3535
+ setNoMembers(true);
3536
+ }
3537
+ }
3538
+ }
3539
+ if (currentVariant) {
3540
+ fetchMembers();
3541
+ }
3542
+ }, [debouncedQuery, currentVariant]);
3543
+ const fetchInitialMembers = useCallback(
3544
+ async (memberId) => {
3545
+ const response = await dispatch(actions_exports.readMember(
3546
+ parseReadMemberParams(
3547
+ {
3548
+ locale,
3549
+ content_ids: `${memberId}`,
3550
+ slugs: [],
3551
+ all: "false",
3552
+ mode: "ids"
3553
+ }
3554
+ )
3555
+ ));
3556
+ if (response && response.results && response.results[0]) {
3557
+ const initialMember = response.results[0];
3558
+ setCurrentMember(initialMember);
3559
+ setSearchMode(false);
3560
+ setQuery(initialMember.name || "");
3561
+ } else {
3562
+ console.error("default member is not found");
3563
+ }
3564
+ },
3565
+ []
3566
+ );
3250
3567
  useEffect(() => {
3251
- if (!currentVariant && variants[0]) {
3252
- setCurrentVariant(variants[0]);
3568
+ let initialCurrentVariant;
3569
+ if (!currentMember && variantsList && variantsList.data) {
3570
+ if (!currentMember || initialSelection && currentMember.contentId !== initialSelection.contentId) {
3571
+ if (initialSelection && initialSelection.variantId) {
3572
+ const initialVariantObj = variantsList.data.find((v) => v.id === initialSelection.variantId);
3573
+ if (initialVariantObj) {
3574
+ initialCurrentVariant = initialVariantObj;
3575
+ if (initialSelection.contentId) {
3576
+ fetchInitialMembers(initialSelection.contentId);
3577
+ }
3578
+ }
3579
+ }
3580
+ }
3581
+ if (!initialCurrentVariant && variantsList.data && variantsList.data[0]) {
3582
+ initialCurrentVariant = variantsList.data[0];
3583
+ }
3253
3584
  }
3254
- }, [variants]);
3585
+ if (initialCurrentVariant)
3586
+ setCurrentVariant(initialCurrentVariant);
3587
+ }, [initialSelection]);
3255
3588
  useEffect(() => {
3256
- setData(results.map((d) => ({ value: d.id, name: d.name })));
3589
+ setData(results.map((d) => ({ value: `${d.id}`, name: d.name })));
3257
3590
  }, [results]);
3258
3591
  useEffect(() => {
3259
3592
  if (currentMember) {
3260
3593
  onSelect(currentMember);
3261
3594
  }
3262
3595
  }, [currentMember]);
3596
+ useEffect(() => {
3597
+ if (searchMode) {
3598
+ inputRef.current?.select();
3599
+ }
3600
+ }, [searchMode]);
3263
3601
  const onSelectPreview = (previewMember) => {
3264
3602
  const fullMember = results.find((d) => d.id === previewMember.value);
3265
3603
  if (fullMember) {
3266
- setQuery(fullMember.name);
3604
+ setQuery(fullMember.name ? fullMember.name : "");
3267
3605
  setCurrentMember(fullMember);
3606
+ setSearchMode(false);
3268
3607
  }
3269
3608
  };
3270
3609
  const onChangeVariant = (value) => {
3271
3610
  const newVariantId = parseInt(value, 10);
3272
3611
  if (currentVariant && currentVariant.id !== newVariantId) {
3273
- const newVariant = variants.find((v) => v.id === parseInt(value, 10));
3612
+ const newVariant = variantsList.data ? variantsList.data.find((v) => v.id === parseInt(value, 10)) : {};
3274
3613
  if (newVariant)
3275
3614
  setCurrentVariant(newVariant);
3276
3615
  setQuery("");
3277
3616
  onSelect(void 0);
3617
+ setCurrentMember(void 0);
3618
+ setSearchMode(true);
3278
3619
  }
3279
3620
  };
3280
3621
  if (!dimensionRef.isSuccess) {
3281
3622
  return /* @__PURE__ */ jsx("div", { children: `Error loading Dimension '${id}'` });
3282
3623
  }
3283
- if (variants.length === 0)
3624
+ if (variantsList && variantsList.data && variantsList.data.length === 0)
3284
3625
  return null;
3285
3626
  return /* @__PURE__ */ jsxs(Fragment, { children: [
3286
3627
  /* @__PURE__ */ jsx(
@@ -3297,16 +3638,39 @@ function DimensionAutocomplete({ id, locale, onSelect }) {
3297
3638
  currentVariant && `/${currentVariant.slug}`,
3298
3639
  currentMember && `/${currentMember.slug}`
3299
3640
  ] }),
3641
+ currentMember && !searchMode && /* @__PURE__ */ jsx("div", { style: { maxWidth: 200 }, children: /* @__PURE__ */ jsx(
3642
+ Badge,
3643
+ {
3644
+ variant: "filled",
3645
+ sx: { paddingRight: 3 },
3646
+ fullWidth: true,
3647
+ rightSection: /* @__PURE__ */ jsx(
3648
+ ActionIcon,
3649
+ {
3650
+ size: "xs",
3651
+ color: "dark",
3652
+ radius: "xl",
3653
+ variant: "transparent",
3654
+ onClick: () => {
3655
+ setSearchMode(true);
3656
+ },
3657
+ children: /* @__PURE__ */ jsx(IconPencil, {})
3658
+ }
3659
+ ),
3660
+ children: currentMember.name
3661
+ }
3662
+ ) }),
3663
+ noMembers && /* @__PURE__ */ jsx(Text, { c: "dimmed", fz: "sm", children: "No members ingested yet." }),
3300
3664
  /* @__PURE__ */ jsx(
3301
3665
  Autocomplete,
3302
3666
  {
3303
- style: { width: 200 },
3667
+ style: { width: !noMembers && searchMode ? 200 : 0, overflow: "hidden" },
3304
3668
  filter: () => true,
3305
3669
  size: "xs",
3306
3670
  limit: 10,
3307
3671
  ref: inputRef,
3308
3672
  onClick: () => inputRef.current?.select(),
3309
- placeholder: `Choose a ${dimension.name} to preview`,
3673
+ placeholder: `Choose a ${currentVariant ? currentVariant.name : dimension.name} to preview`,
3310
3674
  itemComponent: forwardRef(({ id: id2, name, ...others }, ref) => /* @__PURE__ */ createElement("div", { ...others, key: `${id2}-${name}`, ref }, name)),
3311
3675
  value: query,
3312
3676
  onChange: (e) => setQuery(e),
@@ -3314,7 +3678,7 @@ function DimensionAutocomplete({ id, locale, onSelect }) {
3314
3678
  data,
3315
3679
  onDropdownClose: () => setResults([])
3316
3680
  },
3317
- dimension.id
3681
+ `${dimension.id}-${currentVariant?.id}`
3318
3682
  )
3319
3683
  ] });
3320
3684
  }
@@ -3458,25 +3822,6 @@ init_esm_shims();
3458
3822
  // libs/js/slugify.ts
3459
3823
  init_esm_shims();
3460
3824
 
3461
- // libs/js/stripHTML.ts
3462
- init_esm_shims();
3463
- function stripHTML(n) {
3464
- const entities = {
3465
- "&amp;": "&",
3466
- "&lt;": "<",
3467
- "&gt;": ">",
3468
- "&quot;": '"',
3469
- "&#x27;": "'",
3470
- "&#x60;": "`",
3471
- "&nbsp;": ""
3472
- };
3473
- const source = `(?:${Object.keys(entities).join("|")})`;
3474
- const testRegexp = RegExp(source);
3475
- const replaceRegexp = RegExp(source, "g");
3476
- const s = String(n).replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
3477
- return testRegexp.test(s) ? s.replace(replaceRegexp, (match) => entities[match]) : s;
3478
- }
3479
-
3480
3825
  // libs/js/stripEntities.ts
3481
3826
  init_esm_shims();
3482
3827
  function stripEntities(n) {
@@ -3840,11 +4185,11 @@ function DrawerContentWithScroll({ content, buttons, size }) {
3840
4185
  }
3841
4186
  function VariantEditor({ for: variant, onClose: closeHandler }) {
3842
4187
  const dispatch = useAppDispatch();
3843
- const localeDefault8 = useAppSelector((state) => state.status.localeDefault);
4188
+ const localeDefault9 = useAppSelector((state) => state.status.localeDefault);
3844
4189
  const locales3 = useAppSelector((state) => state.status.locales);
3845
4190
  const localesItems = locales3.map((locale) => ({ value: locale, label: locale.toUpperCase() }));
3846
4191
  const [sample, setSample] = useState();
3847
- const [selectedLocale, setSelectedLocale] = useState(localeDefault8);
4192
+ const [selectedLocale, setSelectedLocale] = useState(localeDefault9);
3848
4193
  const [variantConfig, setVariantConfig] = useState(variant);
3849
4194
  const [loading, setLoading] = useState(false);
3850
4195
  const [loadingFetch, setLoadingFetch] = useState(false);
@@ -3899,8 +4244,8 @@ function VariantEditor({ for: variant, onClose: closeHandler }) {
3899
4244
  useEffect(() => {
3900
4245
  setPayload(void 0);
3901
4246
  let attributesList = [];
3902
- if (selectedLocale !== localeDefault8) {
3903
- const defaultAttributes = variantConfig.config[localeDefault8] ? [...variantConfig.config[localeDefault8].attributes].map((item) => {
4247
+ if (selectedLocale !== localeDefault9) {
4248
+ const defaultAttributes = variantConfig.config[localeDefault9] ? [...variantConfig.config[localeDefault9].attributes].map((item) => {
3904
4249
  const newItem = { ...item };
3905
4250
  newItem.value = "";
3906
4251
  return newItem;
@@ -4139,8 +4484,8 @@ function VariantEditor({ for: variant, onClose: closeHandler }) {
4139
4484
  value: variantConfig.config[selectedLocale].attributes[ix].type,
4140
4485
  label: "Type",
4141
4486
  data: [{ value: "variable", label: "Data based" }, { value: "constant", label: "Constant" }],
4142
- readOnly: selectedLocale !== localeDefault8,
4143
- disabled: selectedLocale !== localeDefault8,
4487
+ readOnly: selectedLocale !== localeDefault9,
4488
+ disabled: selectedLocale !== localeDefault9,
4144
4489
  onChange: (e) => onChangeAttribute("type", ix, e)
4145
4490
  }
4146
4491
  ),
@@ -4149,8 +4494,8 @@ function VariantEditor({ for: variant, onClose: closeHandler }) {
4149
4494
  {
4150
4495
  value: variantConfig.config[selectedLocale].attributes[ix].name,
4151
4496
  label: "Name",
4152
- readOnly: selectedLocale !== localeDefault8,
4153
- disabled: selectedLocale !== localeDefault8,
4497
+ readOnly: selectedLocale !== localeDefault9,
4498
+ disabled: selectedLocale !== localeDefault9,
4154
4499
  onChange: (e) => onChangeAttribute("name", ix, e.target.value)
4155
4500
  }
4156
4501
  ),
@@ -4176,7 +4521,7 @@ function VariantEditor({ for: variant, onClose: closeHandler }) {
4176
4521
  {
4177
4522
  color: "red",
4178
4523
  onClick: () => deleteAttribute(ix),
4179
- disabled: selectedLocale !== localeDefault8,
4524
+ disabled: selectedLocale !== localeDefault9,
4180
4525
  children: /* @__PURE__ */ jsx(IconTrash, { size: 20 })
4181
4526
  }
4182
4527
  )
@@ -4187,7 +4532,7 @@ function VariantEditor({ for: variant, onClose: closeHandler }) {
4187
4532
  {
4188
4533
  onClick: addAttributes,
4189
4534
  leftIcon: "+",
4190
- disabled: loading || selectedLocale !== localeDefault8,
4535
+ disabled: loading || selectedLocale !== localeDefault9,
4191
4536
  children: "Add Attribute"
4192
4537
  }
4193
4538
  )
@@ -4389,49 +4734,72 @@ function CMSHeader(props) {
4389
4734
  const location = useManagerLocation();
4390
4735
  const dispatch = useAppDispatch();
4391
4736
  const resource = useContext(ResourceContext);
4392
- const localeDefault8 = useAppSelector((state) => state.status.localeDefault);
4737
+ const localeDefault9 = useAppSelector((state) => state.status.localeDefault);
4393
4738
  const localeOptions = useAppSelector(selectLocaleOptions);
4394
4739
  const reportList = useReportList();
4395
4740
  const reportRef = useReportRef(id);
4396
4741
  const [opened, handlers] = useDisclosure(false);
4397
4742
  const [currentLocale, setCurrentLocale] = useUncontrolled({
4398
- defaultValue: localeDefault8
4743
+ defaultValue: localeDefault9
4399
4744
  });
4745
+ const { user } = useUser();
4746
+ const castedUser = user;
4400
4747
  const reportOptions = useMemo(() => {
4401
4748
  if (!reportList.isSuccess)
4402
4749
  return [];
4403
4750
  return reportList.data.map((d) => ({ value: `${d.id}`, label: d.name }));
4404
4751
  }, [reportList.status]);
4752
+ const currentReport = useMemo(() => {
4753
+ if (!reportRef.isSuccess)
4754
+ return void 0;
4755
+ return reportRef.data;
4756
+ }, [reportRef.status]);
4757
+ const dimensions = useMemo(() => {
4758
+ return currentReport && currentReport.dimensions ? currentReport.dimensions : [];
4759
+ }, [currentReport]);
4760
+ const showPreviewSelector = useMemo(() => {
4761
+ return dimensions.length > 0;
4762
+ }, [dimensions]);
4763
+ const [previewMembers, setPreviewMembers] = useState(dimensions.map(() => void 0));
4764
+ const [initialFavorites, setInitialFavorites] = useState([]);
4405
4765
  const onChangeReport = useCallback((value) => {
4406
4766
  location.push(`/reports/${value}`);
4407
4767
  }, [location]);
4408
4768
  const onSelectPreview = (previewMember) => {
4409
- const newPreviews = currentReport.dimensions.map((dId) => {
4410
- let member;
4411
- if (previewMember) {
4412
- if (previewMember.dimension.id === dId) {
4413
- member = previewMember;
4414
- } else {
4415
- const anotherMember = previewMembers.find((m) => m ? m.dimension.id === dId : false);
4416
- if (anotherMember) {
4417
- member = anotherMember;
4769
+ if (currentReport) {
4770
+ const newPreviews = currentReport.dimensions.map((dId) => {
4771
+ let member;
4772
+ if (previewMember) {
4773
+ if (previewMember.dimension_id === dId) {
4774
+ member = previewMember;
4775
+ } else {
4776
+ const anotherMember = previewMembers.find((m) => m ? m.dimension_id === dId : false);
4777
+ if (anotherMember) {
4778
+ member = anotherMember;
4779
+ }
4418
4780
  }
4419
4781
  }
4420
- }
4421
- return member;
4422
- });
4423
- setPreviewMembers(newPreviews);
4782
+ return member;
4783
+ });
4784
+ setPreviewMembers(newPreviews);
4785
+ }
4424
4786
  };
4425
- const currentReport = reportRef.data;
4426
- const dimensions = currentReport?.dimensions || [];
4427
- const showPreviewSelector = dimensions.length > 0;
4428
- const [previewMembers, setPreviewMembers] = useState(dimensions.map(() => void 0));
4429
4787
  useEffect(() => {
4430
4788
  setPreviewMembers(dimensions.map(() => void 0));
4431
4789
  }, [dimensions]);
4432
4790
  useEffect(() => {
4433
4791
  dispatch(recalculateVariables(resource, { previews: previewMembers }));
4434
4792
  }, [previewMembers]);
4793
+ useEffect(() => {
4794
+ if (castedUser) {
4795
+ if (castedUser.bespoke_app_metadata && castedUser.bespoke_app_metadata.reports && Array.isArray(castedUser.bespoke_app_metadata.reports)) {
4796
+ const currentReportConfig = castedUser.bespoke_app_metadata.reports.find((r) => r.reportId === id);
4797
+ if (currentReportConfig && currentReportConfig.favorites) {
4798
+ setInitialFavorites(currentReportConfig.favorites);
4799
+ }
4800
+ }
4801
+ }
4802
+ }, [castedUser, id, previewMembers]);
4435
4803
  if (reportRef.isUninitialized || reportRef.isFetching) {
4436
4804
  return /* @__PURE__ */ jsx(
4437
4805
  HeaderLayout,
@@ -4457,7 +4825,7 @@ function CMSHeader(props) {
4457
4825
  style: { width: 60 },
4458
4826
  size: "xs",
4459
4827
  data: localeOptions,
4460
- defaultValue: localeDefault8,
4828
+ defaultValue: localeDefault9,
4461
4829
  onChange: setCurrentLocale
4462
4830
  }
4463
4831
  ),
@@ -4471,11 +4839,12 @@ function CMSHeader(props) {
4471
4839
  onChange: onChangeReport
4472
4840
  }
4473
4841
  ),
4474
- showPreviewSelector && currentReport.dimensions.map((dId) => /* @__PURE__ */ jsx(
4842
+ showPreviewSelector && currentReport && currentReport.dimensions.map((dId, ix) => /* @__PURE__ */ jsx(
4475
4843
  DimensionAutocomplete,
4476
4844
  {
4477
4845
  id: dId,
4478
4846
  onSelect: onSelectPreview,
4847
+ initialSelection: initialFavorites[ix] ? initialFavorites[ix] : void 0,
4479
4848
  locale: currentLocale
4480
4849
  },
4481
4850
  dId
@@ -4497,7 +4866,7 @@ function CMSHeader(props) {
4497
4866
  {
4498
4867
  opened,
4499
4868
  onClose: handlers.close,
4500
- title: /* @__PURE__ */ jsx(IconTitle, { icon: /* @__PURE__ */ jsx(IconFileAnalytics, { size: 24 }), children: `Editing Report: ${currentReport.name}` }),
4869
+ title: /* @__PURE__ */ jsx(IconTitle, { icon: /* @__PURE__ */ jsx(IconFileAnalytics, { size: 24 }), children: `Editing Report: ${currentReport ? currentReport.name : ""}` }),
4501
4870
  size: "50%",
4502
4871
  padding: "xl",
4503
4872
  position: "right",
@@ -6131,7 +6500,7 @@ function Block({
6131
6500
  setInlineId
6132
6501
  } = callbacks;
6133
6502
  const block = useBlockRef(id).data;
6134
- const localeDefault8 = useAppSelector((state) => state.status.localeDefault);
6503
+ const localeDefault9 = useAppSelector((state) => state.status.localeDefault);
6135
6504
  const localeDerived = getLocaleDerived(block);
6136
6505
  const [blockState, setBlockState] = useState();
6137
6506
  const [ready, setReady] = useState(false);
@@ -6144,7 +6513,7 @@ function Block({
6144
6513
  if (!block || !ready || !blockState)
6145
6514
  return null;
6146
6515
  const blockContent = getBlockContent(blockState);
6147
- const setBlockContent = (content = {}, _locale = localeDefault8, flagModified = true, isValidated = void 0) => {
6516
+ const setBlockContent = (content = {}, _locale = localeDefault9, flagModified = true, isValidated = void 0) => {
6148
6517
  const locale = getLocaleDerived(block, _locale);
6149
6518
  if (flagModified && !modified)
6150
6519
  setModified(true);
@@ -6224,7 +6593,7 @@ function Block({
6224
6593
  currentMode,
6225
6594
  executeButton,
6226
6595
  id,
6227
- locale: localeDefault8,
6596
+ locale: localeDefault9,
6228
6597
  onSave,
6229
6598
  setBlockContent,
6230
6599
  setBlockSettings,
@@ -6309,7 +6678,7 @@ function BlockElement({
6309
6678
  isConsumer,
6310
6679
  active
6311
6680
  }) {
6312
- const localeDefault8 = useAppSelector((state) => state.status.localeDefault);
6681
+ const localeDefault9 = useAppSelector((state) => state.status.localeDefault);
6313
6682
  const block = useBlockRef(id).data;
6314
6683
  const blockStatus = useBlockStatus(id);
6315
6684
  const { newConfirmation } = useDialog();
@@ -6395,7 +6764,7 @@ function BlockElement({
6395
6764
  id,
6396
6765
  debug: block.type === "generator" || block.type === "visualization",
6397
6766
  active,
6398
- locale: localeDefault8,
6767
+ locale: localeDefault9,
6399
6768
  allowed
6400
6769
  },
6401
6770
  "bp"
@@ -7430,9 +7799,22 @@ init_ordering();
7430
7799
  init_store2();
7431
7800
  init_actions();
7432
7801
  init_ResourceProvider();
7802
+
7803
+ // libs/auth/utils.ts
7804
+ init_esm_shims();
7805
+ var getReportAccess = (user) => {
7806
+ const reportsMetadata = user && user.bespoke_app_metadata && user.bespoke_app_metadata.reports && Array.isArray(user.bespoke_app_metadata.reports) ? user.bespoke_app_metadata.reports : [];
7807
+ return reportsMetadata.map((rm) => rm.reportId);
7808
+ };
7433
7809
  function ReportEditor2(props) {
7434
7810
  const { id } = props;
7435
7811
  const ref = useReportRef(id);
7812
+ const { user } = useUser();
7813
+ const reportAccess = getReportAccess(user);
7814
+ const location = useManagerLocation();
7815
+ if (reportAccess.length !== 0 && !reportAccess.includes(id)) {
7816
+ location.replace("/unauthorize");
7817
+ }
7436
7818
  if (ref.isError) {
7437
7819
  return /* @__PURE__ */ jsx(Center, { style: { height: 300 }, children: /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconCircleX, { size: 20 }), title: `Error ${ref.error.code}`, color: "red", children: ref.error.message }) });
7438
7820
  }
@@ -7687,7 +8069,7 @@ var getCastedValue = (type, value) => {
7687
8069
  }
7688
8070
  };
7689
8071
  function FormatterForm({ formatterId, onEditEnd }) {
7690
- const localeDefault8 = useAppSelector((state) => state.status.localeDefault);
8072
+ const localeDefault9 = useAppSelector((state) => state.status.localeDefault);
7691
8073
  const locales3 = useAppSelector((state) => state.status.locales);
7692
8074
  locales3.map((locale) => ({ value: locale, label: `${locale.toUpperCase()}` }));
7693
8075
  const opened = !!formatterId;
@@ -7751,7 +8133,7 @@ function FormatterForm({ formatterId, onEditEnd }) {
7751
8133
  if (formatter) {
7752
8134
  let results = "";
7753
8135
  try {
7754
- const newFunction = parse({ vars: ["n"], logic: formatter.content.logic }, {}, localeDefault8, {});
8136
+ const newFunction = parse({ vars: ["n"], logic: formatter.content.logic }, {}, localeDefault9, {});
7755
8137
  const castedValue = getCastedValue(formatter.content.inputType, formatter.content.testValue);
7756
8138
  results = newFunction(castedValue);
7757
8139
  setIsValid(true);
@@ -8087,7 +8469,7 @@ var buildOptions = (options) => {
8087
8469
  function FilterSidebar2({ onChange, loading }) {
8088
8470
  const dispatch = useAppDispatch();
8089
8471
  const reportsNested = useAppSelector((state) => state.records.tree);
8090
- const localeDefault8 = useAppSelector((state) => state.status.localeDefault);
8472
+ const localeDefault9 = useAppSelector((state) => state.status.localeDefault);
8091
8473
  const [query, setQuery] = useState("");
8092
8474
  const [loadingRegenerate, setLoadingRegenerate] = useState(false);
8093
8475
  const [debouncedQuery] = useDebouncedValue(query, 500);
@@ -8152,7 +8534,7 @@ function FilterSidebar2({ onChange, loading }) {
8152
8534
  /* @__PURE__ */ jsx(
8153
8535
  TextInput,
8154
8536
  {
8155
- label: `Search item by name (${localeDefault8.toUpperCase()})`,
8537
+ label: `Search item by name (${localeDefault9.toUpperCase()})`,
8156
8538
  value: query,
8157
8539
  onChange: (event) => setQuery(event.currentTarget.value),
8158
8540
  disabled: loading
@@ -8491,11 +8873,11 @@ function ImagePreview2({
8491
8873
  }
8492
8874
  var ImagePreview_default = ImagePreview2;
8493
8875
  function MemberForm({ memberId, onEditEnd }) {
8494
- const localeDefault8 = useAppSelector((state) => state.status.localeDefault);
8876
+ const localeDefault9 = useAppSelector((state) => state.status.localeDefault);
8495
8877
  const locales3 = useAppSelector((state) => state.status.locales);
8496
8878
  const localesItems = locales3.map((locale) => ({ value: locale, label: `${locale.toUpperCase()}` }));
8497
8879
  const opened = !!memberId;
8498
- const [selectedLocale, setSelectedLocale] = useState(localeDefault8);
8880
+ const [selectedLocale, setSelectedLocale] = useState(localeDefault9);
8499
8881
  const [member, setMember] = useState(null);
8500
8882
  const [loading, setLoading] = useState(true);
8501
8883
  const [showImageSelector, setShowImageSelector] = useState(false);
@@ -8835,7 +9217,7 @@ function MembersTable({ members, onClickEdit, onSort }) {
8835
9217
  useEffect(() => {
8836
9218
  setRecords(members);
8837
9219
  }, []);
8838
- const localeDefault8 = useAppSelector((state) => state.status.localeDefault);
9220
+ const localeDefault9 = useAppSelector((state) => state.status.localeDefault);
8839
9221
  const cols = [
8840
9222
  {
8841
9223
  title: "",
@@ -8872,12 +9254,12 @@ function MembersTable({ members, onClickEdit, onSort }) {
8872
9254
  ] })
8873
9255
  },
8874
9256
  {
8875
- title: `Name ${localeDefault8.toUpperCase()}`,
9257
+ title: `Name ${localeDefault9.toUpperCase()}`,
8876
9258
  sortable: true,
8877
9259
  accessor: "name",
8878
9260
  render: ({ contentByLocale }) => {
8879
- const defaultLocalized = contentByLocale.find((item) => item.locale === localeDefault8);
8880
- const othersLocalized = contentByLocale.filter((item) => item.locale !== localeDefault8);
9261
+ const defaultLocalized = contentByLocale.find((item) => item.locale === localeDefault9);
9262
+ const othersLocalized = contentByLocale.filter((item) => item.locale !== localeDefault9);
8881
9263
  const nameTooltipContent = othersLocalized.reduce((acc, item) => {
8882
9264
  const extraName = `${item.locale.toUpperCase()}: ${item.name}`;
8883
9265
  const newLine2 = acc !== "" ? "\n" : "";
@@ -8893,11 +9275,11 @@ function MembersTable({ members, onClickEdit, onSort }) {
8893
9275
  }
8894
9276
  },
8895
9277
  {
8896
- title: `Keywords ${localeDefault8.toUpperCase()}`,
9278
+ title: `Keywords ${localeDefault9.toUpperCase()}`,
8897
9279
  accessor: "keywords",
8898
9280
  render: ({ contentByLocale }) => {
8899
- const defaultLocalized = contentByLocale.find((item) => item.locale === localeDefault8);
8900
- const othersLocalized = contentByLocale.filter((item) => item.locale !== localeDefault8);
9281
+ const defaultLocalized = contentByLocale.find((item) => item.locale === localeDefault9);
9282
+ const othersLocalized = contentByLocale.filter((item) => item.locale !== localeDefault9);
8901
9283
  const keywords = (defaultLocalized.keywords ? defaultLocalized.keywords : []).map((k) => /* @__PURE__ */ jsx(Badge, { children: k }, k));
8902
9284
  const keywordsTooltipContent = othersLocalized.reduce((acc, item) => {
8903
9285
  const otherKeywords = item.keywords || [];
@@ -9006,7 +9388,7 @@ init_esm_shims();
9006
9388
  init_esm_shims();
9007
9389
  init_store2();
9008
9390
  function ReportCard(props) {
9009
- const { for: report } = props;
9391
+ const { for: report, enabled } = props;
9010
9392
  const theme = useMantineTheme();
9011
9393
  const location = useManagerLocation();
9012
9394
  const secondaryColor = theme.colorScheme === "dark" ? theme.colors.dark[1] : theme.colors.gray[7];
@@ -9022,8 +9404,8 @@ function ReportCard(props) {
9022
9404
  /* @__PURE__ */ jsx(Text, { size: "sm", style: { color: secondaryColor, lineHeight: 2 }, children: "Description of report will go here" }),
9023
9405
  /* @__PURE__ */ jsx(Space, { w: "xs" }),
9024
9406
  /* @__PURE__ */ jsxs(Group, { position: "apart", children: [
9025
- /* @__PURE__ */ jsx(Link, { href: location.href("reports", report.id), passHref: true, children: /* @__PURE__ */ jsx(Button, { component: "a", leftIcon: /* @__PURE__ */ jsx(IconPencil, {}), compact: true, children: "Edit" }) }),
9026
- /* @__PURE__ */ jsx(EntityDeleteButton, { type: "report", id: report.id })
9407
+ /* @__PURE__ */ jsx(Link, { href: location.href("reports", report.id), passHref: true, children: /* @__PURE__ */ jsx(Button, { leftIcon: /* @__PURE__ */ jsx(IconPencil, {}), compact: true, children: "Edit" }) }),
9408
+ /* @__PURE__ */ jsx(EntityDeleteButton, { type: "report", id: report.id, disabled: !enabled })
9027
9409
  ] })
9028
9410
  ] });
9029
9411
  }
@@ -9034,9 +9416,22 @@ init_store2();
9034
9416
  init_hooks();
9035
9417
  function ReportPicker() {
9036
9418
  const dispatch = useAppDispatch();
9419
+ const [createLoading, setCreateLoading] = useState(false);
9420
+ const { user } = useUser();
9421
+ const reportAccess = getReportAccess(user);
9037
9422
  const ref = useReportList();
9038
9423
  const createHandler = useCallback((value) => {
9039
- dispatch(actions_exports.createEntity("report", value));
9424
+ setCreateLoading(true);
9425
+ dispatch(actions_exports.createEntity("report", value)).then((response) => {
9426
+ if (response.ok && reportAccess.length > 0) {
9427
+ dispatch(actions_exports.addNewReportToCurrentUser({ report_id: response.data.id })).then(() => {
9428
+ Router.reload();
9429
+ setCreateLoading(false);
9430
+ });
9431
+ } else {
9432
+ setCreateLoading(false);
9433
+ }
9434
+ });
9040
9435
  }, []);
9041
9436
  const content = useMemo(() => {
9042
9437
  if (ref.isUninitialized || ref.isFetching) {
@@ -9048,11 +9443,23 @@ function ReportPicker() {
9048
9443
  if (ref.data.length === 0) {
9049
9444
  return /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconFileAnalytics, { size: 22 }), title: "No Saved Reports", children: "Please use the button below to create your first report." });
9050
9445
  }
9051
- return ref.data.map((report) => /* @__PURE__ */ jsx(ReportCard, { for: report }, report.id));
9052
- }, [ref.data, ref.error, ref.status]);
9053
- return /* @__PURE__ */ jsxs(Center, { style: { flexDirection: "column", height: "100vh" }, children: [
9446
+ return ref.data.map(
9447
+ (report) => /* @__PURE__ */ jsx(
9448
+ ReportCard,
9449
+ {
9450
+ for: report,
9451
+ enabled: reportAccess.length === 0 || reportAccess.includes(report.id) ? true : false
9452
+ },
9453
+ report.id
9454
+ )
9455
+ );
9456
+ }, [ref.data, ref.error, ref.status, reportAccess]);
9457
+ return /* @__PURE__ */ jsxs(Center, { style: { flexDirection: "column", height: "100vh", position: "relative" }, children: [
9054
9458
  /* @__PURE__ */ jsx("h1", { children: "Reports" }),
9055
- /* @__PURE__ */ jsx(Group, { position: "center", style: { width: "100%" }, children: content }),
9459
+ /* @__PURE__ */ jsxs(Group, { position: "center", style: { width: "100%" }, children: [
9460
+ /* @__PURE__ */ jsx(LoadingOverlay, { visible: createLoading, overlayBlur: 2, overlayOpacity: 50 }),
9461
+ content
9462
+ ] }),
9056
9463
  /* @__PURE__ */ jsx(Space, { h: "xl", w: "xl" }),
9057
9464
  /* @__PURE__ */ jsx(
9058
9465
  EntityCreateButton,
@@ -9069,73 +9476,747 @@ function ReportPicker() {
9069
9476
  )
9070
9477
  ] });
9071
9478
  }
9072
- function BespokeManager(options) {
9073
- const {
9074
- title = "Bespoke CMS",
9075
- pathSegment = "path"
9076
- } = options;
9077
- const notifications = {
9078
- position: "bottom-center",
9079
- ...options.notifications
9080
- };
9081
- return BespokeManagerPage;
9082
- function BespokeManagerPage() {
9083
- return /* @__PURE__ */ jsxs(ResourceProvider, { pathSegment, children: [
9084
- /* @__PURE__ */ jsx(Head, { children: /* @__PURE__ */ jsx("title", { children: title }) }),
9085
- /* @__PURE__ */ jsx(MantineProvider, { children: /* @__PURE__ */ jsx(NotificationsProvider, { ...notifications, children: /* @__PURE__ */ jsx(DialogProvider, { children: /* @__PURE__ */ jsx(BespokeManagerShell, {}) }) }) })
9086
- ] });
9087
- }
9479
+
9480
+ // views/UnathorizeView.tsx
9481
+ init_esm_shims();
9482
+ function UnauthorizeView() {
9483
+ const { user } = useUser();
9484
+ return /* @__PURE__ */ jsxs(
9485
+ Flex,
9486
+ {
9487
+ mih: "100vh",
9488
+ gap: "md",
9489
+ justify: "center",
9490
+ align: "center",
9491
+ direction: "column",
9492
+ wrap: "wrap",
9493
+ children: [
9494
+ /* @__PURE__ */ jsx(Title, { children: "Unauthorize or forbidden access." }),
9495
+ /* @__PURE__ */ jsxs(Title, { order: 2, children: [
9496
+ "Sorry, ",
9497
+ user?.name,
9498
+ "!"
9499
+ ] }),
9500
+ /* @__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." }),
9501
+ /* @__PURE__ */ jsx(Button, { component: "a", href: "/", children: "Back to homepage" })
9502
+ ]
9503
+ }
9504
+ );
9088
9505
  }
9089
- function BespokeManagerShell(props) {
9090
- const [opened, { toggle: toggleOpened }] = useDisclosure(true);
9091
- const location = useManagerLocation();
9092
- const navbar = useMemo(() => {
9093
- const links = [{
9094
- icon: /* @__PURE__ */ jsx(IconBoxMargin, { size: 16 }),
9095
- color: "blue",
9096
- label: "Reports Builder",
9097
- href: "reports"
9098
- }, {
9099
- icon: /* @__PURE__ */ jsx(IconTable, { size: 16 }),
9100
- color: "teal",
9101
- label: "Metadata Editor",
9102
- href: "metadata"
9103
- }, {
9104
- icon: /* @__PURE__ */ jsx(IconMathFunction, { size: 16 }),
9105
- color: "violet",
9106
- label: "Formatters List",
9107
- href: "formatters"
9108
- }].map((item) => /* @__PURE__ */ jsx(Link, { href: location.href(item.href), passHref: true, children: /* @__PURE__ */ jsx(
9109
- UnstyledButton,
9110
- {
9111
- component: "a",
9112
- mt: "xs",
9113
- sx: (theme) => ({
9114
- border: "1px solid white",
9115
- borderColor: location.matches(item.href) ? "#ccc" : "transparent",
9116
- backgroundColor: "transparent",
9117
- display: "block",
9118
- width: "100%",
9119
- padding: theme.spacing.xs,
9120
- borderRadius: theme.radius.sm,
9121
- "&:hover": {
9122
- backgroundColor: "#f9f9f9"
9123
- }
9124
- }),
9125
- children: /* @__PURE__ */ jsxs(Group, { children: [
9126
- /* @__PURE__ */ jsx(ThemeIcon, { color: item.color, variant: "light", children: item.icon }),
9127
- /* @__PURE__ */ jsx(Text, { size: "sm", children: item.label })
9128
- ] })
9129
- }
9130
- ) }, item.label));
9131
- return /* @__PURE__ */ jsxs(Fragment, { children: [
9132
- /* @__PURE__ */ jsx(
9133
- Burger,
9134
- {
9135
- style: {
9136
- position: "fixed",
9137
- top: opened && location.page === "reports" && location.params.length > 0 ? 60 : 10,
9138
- left: opened ? 215 : 10,
9506
+
9507
+ // views/NotFoundView.tsx
9508
+ init_esm_shims();
9509
+ function NotFoundView() {
9510
+ const { user } = useUser();
9511
+ return /* @__PURE__ */ jsxs(
9512
+ Flex,
9513
+ {
9514
+ mih: "100vh",
9515
+ gap: "md",
9516
+ justify: "center",
9517
+ align: "center",
9518
+ direction: "column",
9519
+ wrap: "wrap",
9520
+ children: [
9521
+ /* @__PURE__ */ jsx(Title, { children: "Not found." }),
9522
+ /* @__PURE__ */ jsxs(Title, { order: 2, children: [
9523
+ "Sorry, ",
9524
+ user?.name,
9525
+ "!"
9526
+ ] }),
9527
+ /* @__PURE__ */ jsx(Text, { children: "The page you are looking for is not here." }),
9528
+ /* @__PURE__ */ jsx(Button, { component: "a", href: "/", children: "Back to homepage" })
9529
+ ]
9530
+ }
9531
+ );
9532
+ }
9533
+
9534
+ // views/UsersEditor.tsx
9535
+ init_esm_shims();
9536
+ init_store2();
9537
+
9538
+ // components/users/FilterSidebar.tsx
9539
+ init_esm_shims();
9540
+ var allOption3 = { value: "all", label: "All" };
9541
+ var buildOptions2 = (options) => {
9542
+ if (!options)
9543
+ return [];
9544
+ const selectOptions = options.map((d) => ({ value: String(d.id), label: `${d.name} (${d.type})` }));
9545
+ return [allOption3, ...selectOptions];
9546
+ };
9547
+ function FilterSidebar3({ onChange, loading, roles }) {
9548
+ const initialFilterState = {
9549
+ role_id: allOption3.value,
9550
+ query: "",
9551
+ page: 0
9552
+ };
9553
+ const [filters, setFilters] = useState(initialFilterState);
9554
+ const [debouncedQuery] = useDebouncedValue(filters.query, 500);
9555
+ const options = useMemo(() => ({
9556
+ roles: buildOptions2(roles)
9557
+ }), [roles]);
9558
+ const onChangeFilter = useCallback((key, value) => {
9559
+ const newFilters = {
9560
+ ...filters,
9561
+ [key]: value
9562
+ };
9563
+ setFilters(newFilters);
9564
+ }, [filters]);
9565
+ const onClearFilters = () => {
9566
+ setFilters(initialFilterState);
9567
+ };
9568
+ useEffect(() => {
9569
+ onChange({ ...filters, query: debouncedQuery });
9570
+ }, [filters.role_id, filters.page, debouncedQuery]);
9571
+ return /* @__PURE__ */ jsxs("div", { children: [
9572
+ /* @__PURE__ */ jsx(
9573
+ Select,
9574
+ {
9575
+ label: "Roles",
9576
+ data: options.roles,
9577
+ value: filters.role_id,
9578
+ onChange: (value) => onChangeFilter("role_id", value),
9579
+ disabled: loading
9580
+ }
9581
+ ),
9582
+ /* @__PURE__ */ jsx(
9583
+ TextInput,
9584
+ {
9585
+ label: "Search by name or email",
9586
+ value: filters.role_id !== "all" ? "" : filters.query,
9587
+ onChange: (event) => onChangeFilter("query", event.currentTarget.value),
9588
+ disabled: loading || filters.role_id !== "all"
9589
+ }
9590
+ ),
9591
+ /* @__PURE__ */ jsx(Space, { h: "md" }),
9592
+ /* @__PURE__ */ jsx(
9593
+ Button,
9594
+ {
9595
+ fullWidth: true,
9596
+ disabled: loading,
9597
+ leftIcon: /* @__PURE__ */ jsx(IconCircleX, {}),
9598
+ onClick: onClearFilters,
9599
+ children: "Clear filters"
9600
+ }
9601
+ )
9602
+ ] });
9603
+ }
9604
+
9605
+ // components/users/UserForm.tsx
9606
+ init_esm_shims();
9607
+
9608
+ // components/users/FavoriteMemberSelector.tsx
9609
+ init_esm_shims();
9610
+ init_store2();
9611
+ function FavoriteMemberSelector({ reportId, initialFavorites = [], onSelectFavorites, onClickDelete }) {
9612
+ const reportRef = useReportRef(reportId);
9613
+ const localeDefault9 = useAppSelector((state) => state.status.localeDefault);
9614
+ const currentReport = reportRef.data;
9615
+ const [selectedMembers, setSelectedMembers] = useState(
9616
+ currentReport ? currentReport.dimensions.map((d) => void 0) : [void 0]
9617
+ );
9618
+ const onSelectPreview = (ix, selectedPreview) => {
9619
+ const newSelectedMembers = [...selectedMembers];
9620
+ newSelectedMembers[ix] = selectedPreview ? {
9621
+ variantId: parseInt(selectedPreview.variant_id, 10),
9622
+ contentId: parseInt(selectedPreview.content_id, 10)
9623
+ } : void 0;
9624
+ setSelectedMembers(newSelectedMembers);
9625
+ if (!newSelectedMembers.some((nsm) => typeof nsm === "undefined")) {
9626
+ onSelectFavorites(reportId, newSelectedMembers);
9627
+ }
9628
+ };
9629
+ useEffect(() => {
9630
+ if (reportRef.isError) {
9631
+ onClickDelete(reportId);
9632
+ }
9633
+ }, [reportRef.isError]);
9634
+ return /* @__PURE__ */ jsx(Stack, { children: currentReport && /* @__PURE__ */ jsxs(Fragment, { children: [
9635
+ /* @__PURE__ */ jsxs(Group, { children: [
9636
+ /* @__PURE__ */ jsxs(Text, { children: [
9637
+ "Report: ",
9638
+ currentReport.name
9639
+ ] }),
9640
+ /* @__PURE__ */ jsx(ActionIcon, { onClick: () => onClickDelete(reportId), children: /* @__PURE__ */ jsx(IconTrash, {}) })
9641
+ ] }),
9642
+ currentReport.dimensions.length === 0 && /* @__PURE__ */ jsx(Group, { children: /* @__PURE__ */ jsxs(Text, { c: "dimmed", fz: "sm", children: [
9643
+ "No dimensions created in report ",
9644
+ currentReport.name,
9645
+ " yet."
9646
+ ] }) }),
9647
+ currentReport.dimensions.map((dId, ix) => /* @__PURE__ */ jsx(Group, { children: /* @__PURE__ */ jsx(
9648
+ DimensionAutocomplete,
9649
+ {
9650
+ id: dId,
9651
+ initialSelection: initialFavorites[ix] ? initialFavorites[ix] : void 0,
9652
+ onSelect: (selectedPreview) => onSelectPreview(ix, selectedPreview),
9653
+ locale: localeDefault9
9654
+ }
9655
+ ) }, dId))
9656
+ ] }) });
9657
+ }
9658
+
9659
+ // components/users/UserForm.tsx
9660
+ init_store2();
9661
+
9662
+ // components/builder/ReportSelect.tsx
9663
+ init_esm_shims();
9664
+ init_store2();
9665
+ function ReportSelect({ selectedReportId = "", onChangeReport, exclude = [] }) {
9666
+ const reportList = useReportList();
9667
+ const onChangeReportAction = (value) => {
9668
+ onChangeReport(value);
9669
+ };
9670
+ const reportOptions = useMemo(() => {
9671
+ if (!reportList.isSuccess)
9672
+ return [];
9673
+ return reportList.data.map((d) => ({ value: `${d.id}`, label: d.name }));
9674
+ }, [reportList.status]);
9675
+ const availableOptions = reportOptions.filter((ro) => !exclude.includes(ro.value));
9676
+ return /* @__PURE__ */ jsx(
9677
+ Select,
9678
+ {
9679
+ style: { width: 100 },
9680
+ size: "xs",
9681
+ placeholder: "Choose a report",
9682
+ data: availableOptions,
9683
+ value: String(selectedReportId),
9684
+ onChange: onChangeReportAction,
9685
+ disabled: availableOptions.length === 0
9686
+ }
9687
+ );
9688
+ }
9689
+ var buildOptions3 = (options) => {
9690
+ if (!options)
9691
+ return [];
9692
+ return options.map((d) => ({ value: String(d.id), label: d.name, group: d.type }));
9693
+ };
9694
+ function UserForm({ userId, onEditEnd, roles }) {
9695
+ const dispatch = useAppDispatch();
9696
+ const { user } = useUser();
9697
+ const [userData, setUserData] = useState(null);
9698
+ const [loading, setLoading] = useState(true);
9699
+ const [error, setError] = useState(false);
9700
+ const [multiselectError, setMultiselectError] = useState("");
9701
+ const [editAll, setEditAll] = useState(true);
9702
+ const [selectedReportId, setSelectedReportId] = useState("");
9703
+ const mainForm = useForm();
9704
+ const isYou = useMemo(() => {
9705
+ return user && user.sub === userId ? true : false;
9706
+ }, [user]);
9707
+ const opened = !!userId;
9708
+ const options = useMemo(() => ({
9709
+ roles: buildOptions3(roles)
9710
+ }), [roles]);
9711
+ const reportsMetadata = userData && userData.app_metadata && userData.app_metadata.reports ? userData.app_metadata.reports : [];
9712
+ const reportsMetadataIds = reportsMetadata.map((rm) => String(rm.reportId));
9713
+ const fetchUserData = async () => {
9714
+ setLoading(true);
9715
+ setError(false);
9716
+ const userRecord = await dispatch(actions_exports.readUser(userId)).then((response) => {
9717
+ return response;
9718
+ });
9719
+ setUserData(userRecord);
9720
+ mainForm.setValues(userRecord);
9721
+ const reportList = userRecord && userRecord.app_metadata && userRecord.app_metadata.reports ? userRecord.app_metadata.reports : [];
9722
+ setEditAll(reportList.length === 0);
9723
+ setLoading(false);
9724
+ };
9725
+ useEffect(() => {
9726
+ if (userId) {
9727
+ fetchUserData();
9728
+ } else {
9729
+ setUserData(null);
9730
+ }
9731
+ }, [userId]);
9732
+ const onSave = async () => {
9733
+ setLoading(true);
9734
+ if (userData) {
9735
+ await dispatch(actions_exports.updateUser({ user: {
9736
+ user_id: userData.user_id,
9737
+ roles: userData.roles,
9738
+ app_metadata: userData.app_metadata
9739
+ } })).then((response) => {
9740
+ if (isYou) {
9741
+ console.log("its YOU! Refreshing page to grab new change on session");
9742
+ Router.reload();
9743
+ } else {
9744
+ setLoading(false);
9745
+ onEditEnd();
9746
+ }
9747
+ });
9748
+ }
9749
+ };
9750
+ const onRolesChanges = (newRolesIds) => {
9751
+ const hydratedRoles = newRolesIds.map((nr) => roles.find((r) => r.id === nr)).filter((r) => r);
9752
+ const hasEditor = hydratedRoles.find((hr) => hr.name === CMS_ROLES.EDITOR);
9753
+ const hasAdmin = hydratedRoles.find((hr) => hr.name === CMS_ROLES.ADMIN);
9754
+ let metadata = userData ? userData.app_metadata : {};
9755
+ if (!hasEditor) {
9756
+ metadata = {
9757
+ ...metadata,
9758
+ reports: []
9759
+ };
9760
+ setEditAll(true);
9761
+ }
9762
+ if (isYou && !hasAdmin) {
9763
+ setMultiselectError("You can't remove Admin role from yourself.");
9764
+ } else {
9765
+ setMultiselectError("");
9766
+ setUserData({
9767
+ ...userData,
9768
+ roles: hydratedRoles,
9769
+ app_metadata: metadata
9770
+ });
9771
+ }
9772
+ };
9773
+ const onChangeReport = (value) => {
9774
+ setSelectedReportId(value);
9775
+ };
9776
+ const addReportType = () => {
9777
+ if (userData && selectedReportId) {
9778
+ const reports = userData.app_metadata && userData.app_metadata.reports ? userData.app_metadata.reports : [];
9779
+ reports.push({ reportId: parseInt(selectedReportId, 10), favorites: [] });
9780
+ setUserData({
9781
+ ...userData,
9782
+ app_metadata: {
9783
+ ...userData.app_metadata,
9784
+ reports
9785
+ }
9786
+ });
9787
+ setSelectedReportId("");
9788
+ }
9789
+ };
9790
+ const removeReportType = (reportId) => {
9791
+ if (userData && reportId) {
9792
+ const reports = userData.app_metadata && userData.app_metadata.reports ? userData.app_metadata.reports : [];
9793
+ setUserData({
9794
+ ...userData,
9795
+ app_metadata: {
9796
+ ...userData.app_metadata,
9797
+ reports: reports.filter((r) => parseInt(`${r.reportId}`, 10) !== parseInt(reportId, 10))
9798
+ }
9799
+ });
9800
+ }
9801
+ };
9802
+ const editAllSwitched = (event) => {
9803
+ setEditAll(event.currentTarget.checked);
9804
+ if (userData) {
9805
+ setUserData({
9806
+ ...userData,
9807
+ app_metadata: {
9808
+ ...userData.app_metadata,
9809
+ reports: []
9810
+ }
9811
+ });
9812
+ }
9813
+ };
9814
+ const onSelectFavorites = (reportId, favorites) => {
9815
+ if (userData) {
9816
+ const reports = userData.app_metadata && userData.app_metadata.reports ? [...userData.app_metadata.reports] : [];
9817
+ const newReports = reports.map((report) => {
9818
+ if (report.reportId === reportId) {
9819
+ report.favorites = favorites;
9820
+ }
9821
+ return report;
9822
+ });
9823
+ setUserData({
9824
+ ...userData,
9825
+ app_metadata: {
9826
+ ...userData.app_metadata,
9827
+ reports: newReports
9828
+ }
9829
+ });
9830
+ }
9831
+ };
9832
+ return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
9833
+ Drawer,
9834
+ {
9835
+ opened,
9836
+ onClose: () => onEditEnd(),
9837
+ title: /* @__PURE__ */ jsxs(Fragment, { children: [
9838
+ "Edit user ID ",
9839
+ userId,
9840
+ " ",
9841
+ isYou ? /* @__PURE__ */ jsx(Badge, { children: "YOU" }) : ""
9842
+ ] }),
9843
+ padding: "lg",
9844
+ closeOnClickOutside: false,
9845
+ closeOnEscape: false,
9846
+ position: "right",
9847
+ size: "50%",
9848
+ lockScroll: true,
9849
+ withCloseButton: false,
9850
+ children: /* @__PURE__ */ jsx(
9851
+ DrawerContentWithScroll,
9852
+ {
9853
+ content: /* @__PURE__ */ jsxs(Fragment, { children: [
9854
+ /* @__PURE__ */ jsx(LoadingOverlay, { visible: loading, overlayBlur: 2, overlayOpacity: 50 }),
9855
+ error && /* @__PURE__ */ jsx(Alert, { title: "Member Form", color: "red", children: "Error, please try again." }),
9856
+ user && !error && /* @__PURE__ */ jsxs(Stack, { justify: "space-between", children: [
9857
+ /* @__PURE__ */ jsxs(Group, { position: "apart", grow: true, children: [
9858
+ /* @__PURE__ */ jsx(TextInput, { mt: "xs", disabled: true, label: "Name", ...mainForm.getInputProps("name") }),
9859
+ /* @__PURE__ */ jsx(TextInput, { mt: "xs", disabled: true, label: "email", ...mainForm.getInputProps("email") })
9860
+ ] }),
9861
+ /* @__PURE__ */ jsxs(Stack, { children: [
9862
+ /* @__PURE__ */ jsx(Title, { order: 2, children: "Roles:" }),
9863
+ /* @__PURE__ */ jsx(
9864
+ MultiSelect,
9865
+ {
9866
+ data: options.roles,
9867
+ onChange: onRolesChanges,
9868
+ value: userData?.roles.map((r) => r.id),
9869
+ error: multiselectError
9870
+ }
9871
+ )
9872
+ ] }),
9873
+ userData && userData.roles.map((r) => r.name).includes(CMS_ROLES.EDITOR) && /* @__PURE__ */ jsxs(Stack, { children: [
9874
+ /* @__PURE__ */ jsx(Title, { order: 4, children: "Editor settings:" }),
9875
+ /* @__PURE__ */ jsxs(Group, { children: [
9876
+ /* @__PURE__ */ jsx(Text, { children: "Edit selected reports only" }),
9877
+ /* @__PURE__ */ jsx(
9878
+ Switch,
9879
+ {
9880
+ label: "",
9881
+ checked: editAll,
9882
+ onChange: editAllSwitched
9883
+ }
9884
+ ),
9885
+ /* @__PURE__ */ jsx(Text, { children: "Edit all reports" })
9886
+ ] }),
9887
+ !editAll && /* @__PURE__ */ jsxs(Fragment, { children: [
9888
+ /* @__PURE__ */ jsxs(Group, { grow: true, children: [
9889
+ /* @__PURE__ */ jsx(
9890
+ ReportSelect,
9891
+ {
9892
+ selectedReportId,
9893
+ onChangeReport,
9894
+ exclude: reportsMetadataIds
9895
+ }
9896
+ ),
9897
+ /* @__PURE__ */ jsx(
9898
+ Button,
9899
+ {
9900
+ variant: "outline",
9901
+ disabled: loading || !selectedReportId && selectedReportId === "",
9902
+ onClick: addReportType,
9903
+ children: "Add Report"
9904
+ }
9905
+ )
9906
+ ] }),
9907
+ reportsMetadata.map((rm) => /* @__PURE__ */ jsx(Stack, { children: /* @__PURE__ */ jsx(
9908
+ FavoriteMemberSelector,
9909
+ {
9910
+ reportId: rm.reportId,
9911
+ initialFavorites: rm.favorites,
9912
+ onSelectFavorites,
9913
+ onClickDelete: removeReportType
9914
+ }
9915
+ ) }, rm.reportId))
9916
+ ] })
9917
+ ] })
9918
+ ] })
9919
+ ] }),
9920
+ buttons: /* @__PURE__ */ jsxs(Fragment, { children: [
9921
+ /* @__PURE__ */ jsx(
9922
+ Button,
9923
+ {
9924
+ variant: "outline",
9925
+ disabled: loading,
9926
+ onClick: () => onEditEnd(),
9927
+ children: "Cancel"
9928
+ }
9929
+ ),
9930
+ /* @__PURE__ */ jsx(
9931
+ Button,
9932
+ {
9933
+ onClick: onSave,
9934
+ loading,
9935
+ disabled: loading,
9936
+ children: "Save User"
9937
+ }
9938
+ )
9939
+ ] }),
9940
+ size: 145
9941
+ }
9942
+ )
9943
+ }
9944
+ ) });
9945
+ }
9946
+
9947
+ // components/users/UsersTable.tsx
9948
+ init_esm_shims();
9949
+ init_store2();
9950
+ function UsersTable({ response, onClickEdit, onSort, onPageChange }) {
9951
+ const [sortStatus, setSortStatus] = useState();
9952
+ const [records, setRecords] = useState([]);
9953
+ const [paginationStatus, setPaginationStatus] = useState({
9954
+ start: 0,
9955
+ limit: 1,
9956
+ total: 0,
9957
+ page: 0
9958
+ });
9959
+ useEffect(() => {
9960
+ if (sortStatus) {
9961
+ onSort(sortStatus);
9962
+ }
9963
+ }, [sortStatus]);
9964
+ useEffect(() => {
9965
+ if (response) {
9966
+ setRecords(response.users);
9967
+ setPaginationStatus({
9968
+ start: response.start,
9969
+ limit: response.limit,
9970
+ total: response.total,
9971
+ page: response.start / response.limit + 1
9972
+ });
9973
+ }
9974
+ }, [response]);
9975
+ useEffect(() => {
9976
+ setRecords(response);
9977
+ }, []);
9978
+ useAppSelector((state) => state.status.localeDefault);
9979
+ const cols = [
9980
+ {
9981
+ title: "",
9982
+ accessor: "image",
9983
+ width: 50,
9984
+ render: (member) => /* @__PURE__ */ jsx(Avatar, { src: member.picture, alt: "it's me", radius: 20 })
9985
+ },
9986
+ {
9987
+ title: "Name",
9988
+ accessor: "name"
9989
+ },
9990
+ {
9991
+ title: "Email",
9992
+ accessor: "email"
9993
+ },
9994
+ {
9995
+ title: "",
9996
+ accessor: "actions",
9997
+ render: (member) => /* @__PURE__ */ jsx(ActionIcon, { onClick: () => onClickEdit(member), children: /* @__PURE__ */ jsx(IconPencil, { size: 20 }) }, "edit")
9998
+ }
9999
+ ];
10000
+ return /* @__PURE__ */ jsx(
10001
+ DataTable,
10002
+ {
10003
+ striped: true,
10004
+ minHeight: 150,
10005
+ records,
10006
+ columns: cols,
10007
+ sortStatus,
10008
+ onSortStatusChange: (s) => setSortStatus(s),
10009
+ idAccessor: "email",
10010
+ withBorder: true,
10011
+ totalRecords: paginationStatus.total,
10012
+ recordsPerPage: paginationStatus.limit,
10013
+ page: paginationStatus.page,
10014
+ onPageChange
10015
+ }
10016
+ );
10017
+ }
10018
+ function UsersEditor() {
10019
+ const [editingId, setEditingId] = useState(null);
10020
+ const [results, setResults] = useState([]);
10021
+ const [roles, setRoles] = useState([]);
10022
+ const [loading, setLoading] = useState(true);
10023
+ const [internalFilters, setInternalFilters] = useState({ query: "" });
10024
+ const [internalSort, setInternalSort] = useState();
10025
+ const dispatch = useAppDispatch();
10026
+ const onChangeFilters = (filters) => {
10027
+ setInternalFilters(filters);
10028
+ };
10029
+ const onChangeSort = (sort) => {
10030
+ setInternalSort(sort);
10031
+ };
10032
+ const onPageChange = (page) => {
10033
+ setInternalFilters({
10034
+ ...internalFilters,
10035
+ page
10036
+ });
10037
+ };
10038
+ const setEditUser = (user) => {
10039
+ setEditingId(user.user_id);
10040
+ };
10041
+ const onEditClose = () => {
10042
+ setEditingId(null);
10043
+ doSearch();
10044
+ };
10045
+ const doSearch = async () => {
10046
+ setLoading(true);
10047
+ const params = {
10048
+ query: internalFilters.query
10049
+ };
10050
+ if (internalFilters.role_id && internalFilters.role_id !== "all") {
10051
+ params.role_id = internalFilters.role_id;
10052
+ }
10053
+ params.page = internalFilters.page ? internalFilters.page - 1 : 0;
10054
+ dispatch(actions_exports.searchUser(params)).then((response) => {
10055
+ setResults(response);
10056
+ setLoading(false);
10057
+ });
10058
+ };
10059
+ useEffect(() => {
10060
+ setLoading(true);
10061
+ dispatch(actions_exports.searchRole()).then((response) => {
10062
+ setRoles(response.roles);
10063
+ setLoading(false);
10064
+ });
10065
+ }, [dispatch]);
10066
+ useEffect(() => {
10067
+ doSearch();
10068
+ }, [internalFilters, internalSort]);
10069
+ return /* @__PURE__ */ jsxs(Container, { fluid: true, children: [
10070
+ /* @__PURE__ */ jsxs(Grid, { children: [
10071
+ /* @__PURE__ */ jsxs(Grid.Col, { span: 2, children: [
10072
+ /* @__PURE__ */ jsx(Text, { ml: "xl", my: "sm", weight: "bold", children: "Users Management" }),
10073
+ /* @__PURE__ */ jsx(FilterSidebar3, { onChange: onChangeFilters, loading, roles })
10074
+ ] }),
10075
+ /* @__PURE__ */ jsxs(Grid.Col, { span: 10, style: { height: "100vh", position: "relative" }, children: [
10076
+ /* @__PURE__ */ jsx(LoadingOverlay, { visible: loading, overlayBlur: 2, overlayOpacity: 50 }),
10077
+ /* @__PURE__ */ jsx(Space, { h: "xs" }),
10078
+ /* @__PURE__ */ jsx(
10079
+ UsersTable,
10080
+ {
10081
+ response: results,
10082
+ onClickEdit: setEditUser,
10083
+ onSort: onChangeSort,
10084
+ onPageChange
10085
+ }
10086
+ )
10087
+ ] })
10088
+ ] }),
10089
+ editingId && /* @__PURE__ */ jsx(UserForm, { userId: editingId, onEditEnd: onEditClose, roles })
10090
+ ] });
10091
+ }
10092
+
10093
+ // views/HelloView.tsx
10094
+ init_esm_shims();
10095
+ function HelloView() {
10096
+ const { user } = useUser();
10097
+ return /* @__PURE__ */ jsxs(
10098
+ Flex,
10099
+ {
10100
+ mih: "100vh",
10101
+ gap: "md",
10102
+ justify: "center",
10103
+ align: "center",
10104
+ direction: "column",
10105
+ wrap: "wrap",
10106
+ children: [
10107
+ /* @__PURE__ */ jsxs(Title, { children: [
10108
+ "Hi, ",
10109
+ user.name,
10110
+ "!"
10111
+ ] }),
10112
+ /* @__PURE__ */ jsx(Text, { children: "Welcome to Bespoke CMS. Have fun creating some amazing automated reports here!" })
10113
+ ]
10114
+ }
10115
+ );
10116
+ }
10117
+ function BespokeManagerPanel(options) {
10118
+ const {
10119
+ title = "Bespoke CMS",
10120
+ pathSegment = "path"
10121
+ } = options;
10122
+ const notifications = {
10123
+ position: "bottom-center",
10124
+ ...options.notifications
10125
+ };
10126
+ return BespokeManagerPage;
10127
+ function BespokeManagerPage() {
10128
+ return /* @__PURE__ */ jsxs(ResourceProvider, { pathSegment, children: [
10129
+ /* @__PURE__ */ jsx(Head, { children: /* @__PURE__ */ jsx("title", { children: title }) }),
10130
+ /* @__PURE__ */ jsx(MantineProvider, { children: /* @__PURE__ */ jsx(NotificationsProvider, { ...notifications, children: /* @__PURE__ */ jsx(DialogProvider, { children: /* @__PURE__ */ jsx(BespokeManagerShell, {}) }) }) })
10131
+ ] });
10132
+ }
10133
+ }
10134
+ function MainLink({ item, disabled }) {
10135
+ const { page } = useManagerLocation();
10136
+ return /* @__PURE__ */ jsx(Link, { href: item.href, passHref: true, children: /* @__PURE__ */ jsx(
10137
+ UnstyledButton,
10138
+ {
10139
+ disabled,
10140
+ mt: "xs",
10141
+ sx: (theme) => ({
10142
+ border: "1px solid white",
10143
+ borderColor: page && page.includes(item.href) ? "#ccc" : "transparent",
10144
+ backgroundColor: "transparent",
10145
+ display: "block",
10146
+ width: "100%",
10147
+ padding: theme.spacing.xs,
10148
+ borderRadius: theme.radius.sm,
10149
+ "&:hover": {
10150
+ backgroundColor: "#f9f9f9"
10151
+ }
10152
+ }),
10153
+ children: /* @__PURE__ */ jsxs(Group, { children: [
10154
+ /* @__PURE__ */ jsx(ThemeIcon, { color: disabled ? "gray" : item.color, variant: "light", children: item.icon }),
10155
+ /* @__PURE__ */ jsx(Text, { size: "sm", children: item.label })
10156
+ ] })
10157
+ }
10158
+ ) }, item.label);
10159
+ }
10160
+ function BespokeManagerShell(props) {
10161
+ const [opened, { toggle: toggleOpened }] = useDisclosure(true);
10162
+ const { user, error, isLoading } = useUser();
10163
+ const location = useManagerLocation();
10164
+ const links = useMemo(() => {
10165
+ return [
10166
+ {
10167
+ icon: /* @__PURE__ */ jsx(IconBoxMargin, { size: 16 }),
10168
+ color: "blue",
10169
+ label: "Reports Builder",
10170
+ href: "reports",
10171
+ roleRequired: CMS_ROLES.EDITOR
10172
+ },
10173
+ {
10174
+ icon: /* @__PURE__ */ jsx(IconTable, { size: 16 }),
10175
+ color: "teal",
10176
+ label: "Metadata Editor",
10177
+ href: "metadata",
10178
+ roleRequired: CMS_ROLES.EDITOR
10179
+ },
10180
+ {
10181
+ icon: /* @__PURE__ */ jsx(IconMathFunction, { size: 16 }),
10182
+ color: "violet",
10183
+ label: "Formatters List",
10184
+ href: "formatters",
10185
+ roleRequired: CMS_ROLES.EDITOR
10186
+ },
10187
+ {
10188
+ icon: /* @__PURE__ */ jsx(IconUsers, { size: 16 }),
10189
+ color: "pink",
10190
+ label: "Users Management",
10191
+ href: "users",
10192
+ roleRequired: CMS_ROLES.ADMIN
10193
+ }
10194
+ ];
10195
+ }, []);
10196
+ const buildLinks = useCallback((user2) => {
10197
+ let pastRole;
10198
+ const menuLinks = [];
10199
+ links.forEach((link) => {
10200
+ if (pastRole !== link.roleRequired) {
10201
+ menuLinks.push(/* @__PURE__ */ jsx(Divider, { my: "xs", label: link.roleRequired, labelPosition: "center" }, link.roleRequired));
10202
+ }
10203
+ menuLinks.push(
10204
+ /* @__PURE__ */ jsx(MainLink, { item: link, disabled: !user2.bespoke_roles.includes(link.roleRequired) }, link.label)
10205
+ );
10206
+ pastRole = link.roleRequired;
10207
+ });
10208
+ return menuLinks;
10209
+ }, [links]);
10210
+ const navbar = useMemo(() => {
10211
+ const linkButtons = buildLinks(user);
10212
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
10213
+ /* @__PURE__ */ jsx(
10214
+ Burger,
10215
+ {
10216
+ style: {
10217
+ position: "fixed",
10218
+ top: opened && location.page === "reports" && location.params.length > 0 ? 60 : 10,
10219
+ left: opened ? 215 : 10,
9139
10220
  zIndex: 101
9140
10221
  },
9141
10222
  opened,
@@ -9151,31 +10232,79 @@ function BespokeManagerShell(props) {
9151
10232
  hiddenBreakpoint: 1e5,
9152
10233
  hidden: !opened,
9153
10234
  children: [
9154
- /* @__PURE__ */ jsx(Navbar.Section, { style: { position: "relative" }, children: /* @__PURE__ */ jsx(BespokeLogo, { href: location.href("reports") }) }),
9155
- /* @__PURE__ */ jsx(Navbar.Section, { grow: true, children: links })
10235
+ /* @__PURE__ */ jsx(Navbar.Section, { style: { position: "relative" }, children: /* @__PURE__ */ jsx(BespokeLogo, { href: location.href("") }) }),
10236
+ /* @__PURE__ */ jsx(Navbar.Section, { grow: true, component: ScrollArea, children: linkButtons }),
10237
+ /* @__PURE__ */ jsx(Navbar.Section, { children: user && !error && !isLoading && /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(Stack, { children: [
10238
+ /* @__PURE__ */ jsxs(Group, { children: [
10239
+ /* @__PURE__ */ jsx(
10240
+ Avatar,
10241
+ {
10242
+ src: user.picture,
10243
+ radius: "xl"
10244
+ }
10245
+ ),
10246
+ /* @__PURE__ */ jsxs(Box, { sx: { flex: 1 }, children: [
10247
+ /* @__PURE__ */ jsx(Text, { size: "sm", weight: 500, children: user.name }),
10248
+ /* @__PURE__ */ jsx(Text, { color: "dimmed", size: "xs", children: user.email })
10249
+ ] })
10250
+ ] }),
10251
+ /* @__PURE__ */ jsx(
10252
+ Button,
10253
+ {
10254
+ component: "a",
10255
+ href: "/api/auth/logout",
10256
+ compact: true,
10257
+ size: "xs",
10258
+ variant: "outline",
10259
+ leftIcon: /* @__PURE__ */ jsx(IconLogout, { size: 14 }),
10260
+ children: "Disconnect"
10261
+ }
10262
+ )
10263
+ ] }) }) })
9156
10264
  ]
9157
10265
  }
9158
10266
  )
9159
10267
  ] });
9160
- }, [location, opened]);
10268
+ }, [location, opened, user]);
9161
10269
  const route = useMemo(() => {
9162
- if (location.page === "metadata") {
9163
- return /* @__PURE__ */ jsx(MetadataEditor, {});
9164
- }
9165
- if (location.page === "formatters") {
9166
- return /* @__PURE__ */ jsx(FormatterEditor, {});
9167
- }
9168
- if (location.page === "reports") {
9169
- if (location.params.length > 0) {
9170
- const report_id = Number.parseInt(location.params[0]);
9171
- return /* @__PURE__ */ jsx(ReportEditor2, { id: report_id });
10270
+ const item = links.find((page) => page.href.includes(location.page));
10271
+ if (location.page === void 0) {
10272
+ return /* @__PURE__ */ jsx(HelloView, {});
10273
+ }
10274
+ if (item) {
10275
+ if (user && Array.isArray(user.bespoke_roles) && user.bespoke_roles.includes(item.roleRequired)) {
10276
+ if (location.page === "metadata") {
10277
+ return /* @__PURE__ */ jsx(MetadataEditor, {});
10278
+ }
10279
+ if (location.page === "formatters") {
10280
+ return /* @__PURE__ */ jsx(FormatterEditor, {});
10281
+ }
10282
+ if (location.page === "reports") {
10283
+ if (location.params.length > 0) {
10284
+ const report_id = Number.parseInt(location.params[0]);
10285
+ return /* @__PURE__ */ jsx(ReportEditor2, { id: report_id });
10286
+ }
10287
+ return /* @__PURE__ */ jsx(ReportPicker, {});
10288
+ }
10289
+ if (location.page === "users") {
10290
+ return /* @__PURE__ */ jsx(UsersEditor, {});
10291
+ }
10292
+ } else {
10293
+ return /* @__PURE__ */ jsx(UnauthorizeView, {});
9172
10294
  }
9173
- return /* @__PURE__ */ jsx(ReportPicker, {});
9174
10295
  }
9175
- return null;
9176
- }, [location]);
10296
+ return /* @__PURE__ */ jsx(NotFoundView, {});
10297
+ }, [location, user]);
9177
10298
  return /* @__PURE__ */ jsx(AppShell, { fixed: true, padding: 0, navbar, children: route });
9178
10299
  }
10300
+ function BespokeManager(params) {
10301
+ return withPageRoleAuthRequired(
10302
+ BespokeManagerPanel(params),
10303
+ [CMS_ROLES.ADMIN, CMS_ROLES.EDITOR, CMS_ROLES.WRITER],
10304
+ () => /* @__PURE__ */ jsx(UnauthorizeView, {}),
10305
+ {}
10306
+ );
10307
+ }
9179
10308
 
9180
10309
  // views/BespokeRenderer.tsx
9181
10310
  init_esm_shims();
@@ -9183,4 +10312,4 @@ function BespokeRenderer() {
9183
10312
  return /* @__PURE__ */ jsx("div", { children: "Report COMING SOON" });
9184
10313
  }
9185
10314
 
9186
- export { Explore_default as BespokeExplore, ExploreModal_default as BespokeExploreModal, BespokeManager, BespokeRenderer, Report_default as BespokeReport, Search_default as BespokeSearch, DialogProvider, actions_exports as actions, storeWrapper, useAppDispatch as useBespokeDispatch, useAppSelector as useBespokeSelector, useDialog };
10315
+ export { Explore_default as BespokeExplore, ExploreModal_default as BespokeExploreModal, LoginButton_default as BespokeLoginBtn, BespokeManager, BespokeRenderer, Report_default as BespokeReport, Search_default as BespokeSearch, UserProvider_default as BespokeUserProvider, withPageRoleAuthRequired as BespokeWithPageRoleAuthRequired, CMS_ROLES, DialogProvider, actions_exports as actions, storeWrapper, useAppDispatch as useBespokeDispatch, useAppSelector as useBespokeSelector, useUser_default as useBespokeUser, useDialog };