@datawheel/bespoke 0.1.10 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +1453 -1380
  2. package/package.json +7 -5
package/dist/index.js CHANGED
@@ -15,14 +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, AppShell, Navbar, UnstyledButton, Skeleton, Container, TextInput, Loader, LoadingOverlay, Alert, Autocomplete, Chip, Card, Space, Code, Divider, SegmentedControl, Overlay, Grid, ThemeIcon, Burger, Popover, Checkbox, Radio, Switch, ScrollArea, Drawer, Header, Input, Avatar, Textarea, Tabs, FileInput, SimpleGrid, Image, Accordion, NumberInput, CopyButton, Col, Menu } from '@mantine/core';
19
- import { IconInfoCircle, IconCircleDashed, IconSearch, IconTrash, IconDatabase, IconServer, IconPencil, IconAlertCircle, IconCircleCheck, IconPlayerPlay, IconCirclePlus, IconCircleX, IconFileAnalytics, IconPlus, IconBoxMargin, IconTable, IconMathFunction, 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, 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';
20
20
  import { dataConcat } from 'd3plus-viz';
21
21
  import * as d3plus from 'd3plus-react';
22
- import { useDebouncedValue, useMediaQuery, useDisclosure, useHotkeys, useUncontrolled, getHotkeyHandler } from '@mantine/hooks';
23
- import Link$1 from 'next/link';
22
+ import { useDisclosure, useDebouncedValue, useMediaQuery, useHotkeys, useUncontrolled, getHotkeyHandler } from '@mantine/hooks';
23
+ import Link from 'next/link';
24
24
  import { useRouter } from 'next/router';
25
- import { HashRouter, Link, Routes, Route, useParams, useLocation, useNavigate } from 'react-router-dom';
25
+ import Head from 'next/head';
26
26
  import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
27
27
  import slugifyFn from 'slugify';
28
28
  import { Prism } from '@mantine/prism';
@@ -665,13 +665,13 @@ var init_valueInOptions = __esm({
665
665
  "libs/selectors/valueInOptions.ts"() {
666
666
  init_esm_shims();
667
667
  init_cms();
668
- valueInOptions_default = (type, value, options2) => {
669
- if (!options2)
668
+ valueInOptions_default = (type, value, options) => {
669
+ if (!options)
670
670
  return false;
671
671
  if (type === SELECTOR_TYPES.SINGLE) {
672
- return options2.find((obj) => obj.id === value);
672
+ return options.find((obj) => obj.id === value);
673
673
  }
674
- return Array.isArray(value) && value.every((d) => options2.map((o) => o.id).includes(d));
674
+ return Array.isArray(value) && value.every((d) => options.map((o) => o.id).includes(d));
675
675
  };
676
676
  }
677
677
  });
@@ -694,23 +694,23 @@ var init_runSelector = __esm({
694
694
  const { vars, log, error } = evalResult;
695
695
  const type = vars.type || SELECTOR_TYPES.SINGLE;
696
696
  const name = vars.name || "Unlabeled Selector";
697
- const options2 = scaffoldDynamic_default(vars.options || []);
697
+ const options = scaffoldDynamic_default(vars.options || []);
698
698
  const maybeFixForMulti2 = (defaultValue2) => type === SELECTOR_TYPES.MULTI && !Array.isArray(defaultValue2) ? [defaultValue2] : defaultValue2;
699
699
  let potentialDefaultValue = vars.defaultValue;
700
700
  let defaultValue;
701
701
  if (potentialDefaultValue) {
702
702
  potentialDefaultValue = maybeFixForMulti2(potentialDefaultValue);
703
- if (valueInOptions_default(type, potentialDefaultValue, options2))
703
+ if (valueInOptions_default(type, potentialDefaultValue, options))
704
704
  defaultValue = potentialDefaultValue;
705
705
  }
706
706
  if (!defaultValue) {
707
- const fallbackValue = options2 && options2[0] && options2[0].id || options2[0] || "";
707
+ const fallbackValue = options && options[0] && options[0].id || options[0] || "";
708
708
  defaultValue = maybeFixForMulti2(fallbackValue);
709
709
  }
710
710
  const config = {
711
711
  name,
712
712
  type,
713
- options: options2,
713
+ options,
714
714
  defaultValue
715
715
  };
716
716
  return { config, log, error };
@@ -729,10 +729,10 @@ var init_selectorQueryToVariable = __esm({
729
729
  const queryObject = new URLSearchParams(query);
730
730
  if (config.type === SELECTOR_TYPES.MULTI) {
731
731
  const queryIds = queryObject.get(accessor) ? queryObject.get(accessor).split(",") : config.defaultValue;
732
- const options2 = config.options.filter((d) => queryIds.includes(d.id));
732
+ const options = config.options.filter((d) => queryIds.includes(d.id));
733
733
  return {
734
734
  [`selector${id}id`]: queryIds,
735
- [`selector${id}label`]: (options2 || []).map((d) => d.label)
735
+ [`selector${id}label`]: (options || []).map((d) => d.label)
736
736
  };
737
737
  }
738
738
  const queryId = queryObject.get(accessor) ? queryObject.get(accessor) : config.defaultValue;
@@ -1892,6 +1892,7 @@ __export(actions_exports, {
1892
1892
  readMetadata: () => readMetadata,
1893
1893
  recalculateVariables: () => recalculateVariables,
1894
1894
  reportSearch: () => reportSearch,
1895
+ searchMember: () => searchMember,
1895
1896
  searchRegenerate: () => searchRegenerate,
1896
1897
  setQueryParam: () => setQueryParam,
1897
1898
  updateEntity: () => updateEntity,
@@ -1940,6 +1941,14 @@ function readMember(params) {
1940
1941
  return result.data;
1941
1942
  };
1942
1943
  }
1944
+ function searchMember(params) {
1945
+ return async (_, __, api) => {
1946
+ const result = await api.searchMember(params);
1947
+ if (result.ok === true)
1948
+ return result.data;
1949
+ throw new Error(result.error);
1950
+ };
1951
+ }
1943
1952
  async function setStatusQuery(resource, dispatch, getState, status) {
1944
1953
  const oldStatus = getState().status;
1945
1954
  const newStatus = { ...oldStatus, ...status };
@@ -2018,17 +2027,24 @@ var init_actions = __esm({
2018
2027
  init_recordsActions();
2019
2028
  }
2020
2029
  });
2021
- function ResourceProvider({ children }) {
2030
+ function ResourceProvider(props) {
2031
+ const { pathSegment } = props;
2022
2032
  const formatters = useFormatterList();
2023
2033
  const value = useMemo(() => ({
2024
- formatterFunctions: Object.fromEntries(
2025
- locales2.map((locale) => [
2026
- locale,
2027
- formatters.data ? funcifyFormattersByLocale(formatters.data, locale) : {}
2028
- ])
2029
- )
2030
- }), [formatters]);
2031
- return /* @__PURE__ */ jsx(ResourceContext.Provider, { value, children });
2034
+ pathSegment,
2035
+ formatterFunctions: locales2.reduce((acc, locale) => ({
2036
+ ...acc,
2037
+ [locale]: formatters.data ? funcifyFormattersByLocale(formatters.data, locale) : {}
2038
+ }), {})
2039
+ }), [formatters, pathSegment]);
2040
+ return /* @__PURE__ */ jsx(ResourceContext.Provider, { value, children: props.children });
2041
+ }
2042
+ function useBespoke() {
2043
+ const context = useContext(ResourceContext);
2044
+ if (context === void 0) {
2045
+ throw new Error("useBespoke must be used inside ResourceProvider");
2046
+ }
2047
+ return context;
2032
2048
  }
2033
2049
  var locales2, initialContext, ResourceContext;
2034
2050
  var init_ResourceProvider = __esm({
@@ -2039,6 +2055,7 @@ var init_ResourceProvider = __esm({
2039
2055
  init_store2();
2040
2056
  ({ locales: locales2 } = getLocales_default());
2041
2057
  initialContext = {
2058
+ pathSegment: "path",
2042
2059
  formatterFunctions: locales2.reduce((acc, d) => ({ ...acc, [d]: {} }), {})
2043
2060
  };
2044
2061
  ResourceContext = createContext(initialContext);
@@ -2806,7 +2823,7 @@ function ExploreTile({ profile, profilePrefix }) {
2806
2823
  return /* @__PURE__ */ jsx(Grid.Col, { xs: 12, sm: 6, md: 4, lg: 4, xl: 3, children: /* @__PURE__ */ jsxs(Card, { shadow: "sm", p: "lg", radius: "md", withBorder: true, children: [
2807
2824
  /* @__PURE__ */ jsx(Card.Section, { children: /* @__PURE__ */ jsx(Group, { grow: true, spacing: 0, children: images }) }),
2808
2825
  /* @__PURE__ */ jsx(Group, { position: "apart", mt: "md", mb: "xs", children: names }),
2809
- /* @__PURE__ */ jsx(Link$1, { href: url, children: /* @__PURE__ */ jsx(Button, { variant: "light", color: "blue", fullWidth: true, mt: "md", radius: "md", children: `See ${profile.report.name} Report` }) })
2826
+ /* @__PURE__ */ jsx(Link, { href: url, children: /* @__PURE__ */ jsx(Button, { variant: "light", color: "blue", fullWidth: true, mt: "md", radius: "md", children: `See ${profile.report.name} Report` }) })
2810
2827
  ] }) }, profile.id);
2811
2828
  }
2812
2829
  var ExploreTile_default = ExploreTile;
@@ -3116,8 +3133,74 @@ init_store2();
3116
3133
  // views/index.tsx
3117
3134
  init_esm_shims();
3118
3135
 
3119
- // views/BespokeCMS.tsx
3136
+ // views/BespokeManager.tsx
3137
+ init_esm_shims();
3138
+
3139
+ // components/commons/BespokeLogo.tsx
3140
+ init_esm_shims();
3141
+ function BespokeLogo(props) {
3142
+ const content = /* @__PURE__ */ jsxs(Group, { spacing: 0, children: [
3143
+ /* @__PURE__ */ jsx(IconCircleDashed, { size: 30, color: "black" }),
3144
+ /* @__PURE__ */ jsx(Space, { w: "sm" }),
3145
+ /* @__PURE__ */ jsx(Text, { fz: "lg", children: "Bespoke" }),
3146
+ /* @__PURE__ */ jsx(Text, { fz: "xs", sx: { alignSelf: "start" }, children: "beta" })
3147
+ ] });
3148
+ if (!props.href)
3149
+ return content;
3150
+ return /* @__PURE__ */ jsx(Link, { href: props.href, passHref: true, children: /* @__PURE__ */ jsx(
3151
+ UnstyledButton,
3152
+ {
3153
+ component: "a",
3154
+ sx: (theme) => ({
3155
+ display: "block",
3156
+ width: "100%",
3157
+ borderRadius: theme.radius.sm,
3158
+ padding: theme.spacing.xs
3159
+ }),
3160
+ children: content
3161
+ }
3162
+ ) });
3163
+ }
3164
+
3165
+ // components/commons/useLocation.tsx
3120
3166
  init_esm_shims();
3167
+ init_ResourceProvider();
3168
+ function useManagerLocation() {
3169
+ const { pathSegment } = useBespoke();
3170
+ const router = useRouter();
3171
+ return useMemo(() => {
3172
+ const pathParts = router.pathname.split("/");
3173
+ if (Object.keys(router.query).length > 0) {
3174
+ pathParts.pop();
3175
+ }
3176
+ const basePath = pathParts.join("/");
3177
+ const [page, ...params] = [].concat(router.query[pathSegment] || []);
3178
+ const href = (...pathTokens) => {
3179
+ return [basePath, ...pathTokens].join("/");
3180
+ };
3181
+ return {
3182
+ router,
3183
+ page,
3184
+ params,
3185
+ basePath,
3186
+ href,
3187
+ matches(...pathTokens) {
3188
+ return router.asPath === href(...pathTokens);
3189
+ },
3190
+ push(url, as, options) {
3191
+ const correctedUrl = href(url);
3192
+ router.push(correctedUrl, as, options);
3193
+ },
3194
+ replace(url, as, options) {
3195
+ const correctedUrl = href(url);
3196
+ router.replace(correctedUrl, as, options);
3197
+ }
3198
+ };
3199
+ }, [router.asPath]);
3200
+ }
3201
+
3202
+ // views/BespokeManager.tsx
3203
+ init_ResourceProvider();
3121
3204
 
3122
3205
  // views/EditReport.tsx
3123
3206
  init_esm_shims();
@@ -4303,7 +4386,7 @@ function ReportEditor({ id, onClose: closeHandler }) {
4303
4386
  init_ResourceProvider();
4304
4387
  function CMSHeader(props) {
4305
4388
  const { id } = props;
4306
- const nav = useNavigate();
4389
+ const location = useManagerLocation();
4307
4390
  const dispatch = useAppDispatch();
4308
4391
  const resource = useContext(ResourceContext);
4309
4392
  const localeDefault8 = useAppSelector((state) => state.status.localeDefault);
@@ -4320,8 +4403,8 @@ function CMSHeader(props) {
4320
4403
  return reportList.data.map((d) => ({ value: `${d.id}`, label: d.name }));
4321
4404
  }, [reportList.status]);
4322
4405
  const onChangeReport = useCallback((value) => {
4323
- nav(`/reports/${value}`);
4324
- }, []);
4406
+ location.push(`/reports/${value}`);
4407
+ }, [location]);
4325
4408
  const onSelectPreview = (previewMember) => {
4326
4409
  const newPreviews = currentReport.dimensions.map((dId) => {
4327
4410
  let member;
@@ -4353,7 +4436,7 @@ function CMSHeader(props) {
4353
4436
  return /* @__PURE__ */ jsx(
4354
4437
  HeaderLayout,
4355
4438
  {
4356
- left: /* @__PURE__ */ jsx(ActionIcon, { component: Link, to: "/", color: "blue", variant: "filled", children: /* @__PURE__ */ jsx(IconChevronLeft, { size: 24 }) }),
4439
+ left: /* @__PURE__ */ jsx(ActionIcon, { component: Link, href: location.href(), color: "blue", variant: "filled", children: /* @__PURE__ */ jsx(IconChevronLeft, { size: 24 }) }),
4357
4440
  center: /* @__PURE__ */ jsxs(Group, { spacing: "xs", children: [
4358
4441
  /* @__PURE__ */ jsx(Skeleton, { height: 30, width: 100 }),
4359
4442
  /* @__PURE__ */ jsx(Skeleton, { height: 30, width: 200 })
@@ -4533,14 +4616,14 @@ function DesignMenu({ id }) {
4533
4616
  }
4534
4617
  ) }),
4535
4618
  /* @__PURE__ */ jsx(Popover.Dropdown, { children: /* @__PURE__ */ jsxs("div", { children: [
4536
- Object.entries(block_default).map(([key, { label, defaultValue, options: options2 }]) => /* @__PURE__ */ jsxs(Group, { spacing: "xs", position: "apart", noWrap: true, style: { width: "100%" }, children: [
4619
+ Object.entries(block_default).map(([key, { label, defaultValue, options }]) => /* @__PURE__ */ jsxs(Group, { spacing: "xs", position: "apart", noWrap: true, style: { width: "100%" }, children: [
4537
4620
  label && /* @__PURE__ */ jsx("span", { children: label }),
4538
4621
  /* @__PURE__ */ jsx(
4539
4622
  SegmentedControl,
4540
4623
  {
4541
4624
  defaultValue: String(block.settings[key] || defaultValue),
4542
4625
  onChange: (e) => handleChange(key, e),
4543
- data: options2
4626
+ data: options
4544
4627
  }
4545
4628
  )
4546
4629
  ] }, key)),
@@ -4878,31 +4961,31 @@ init_cms();
4878
4961
  init_esm_shims();
4879
4962
  init_cms();
4880
4963
  init_valueInOptions();
4881
- var validateOptionsArray = (options2) => {
4882
- if (!Array.isArray(options2))
4964
+ var validateOptionsArray = (options) => {
4965
+ if (!Array.isArray(options))
4883
4966
  return [];
4884
- return options2.map((d) => typeof d === "string" ? { id: d, label: d } : d);
4967
+ return options.map((d) => typeof d === "string" ? { id: d, label: d } : d);
4885
4968
  };
4886
4969
  var maybeFixForMulti = (defaultValue, type) => type === SELECTOR_TYPES.MULTI && !Array.isArray(defaultValue) ? [defaultValue] : defaultValue;
4887
4970
  var Selector_default = (vars, id) => {
4888
4971
  const type = vars.type || SELECTOR_TYPES.SINGLE;
4889
4972
  const name = vars.name || "Unlabeled Selector";
4890
- const options2 = validateOptionsArray(vars.options || []);
4973
+ const options = validateOptionsArray(vars.options || []);
4891
4974
  let defaultValue;
4892
4975
  if (vars.defaultValue) {
4893
4976
  const potentialDefaultValue = maybeFixForMulti(vars.defaultValue, type);
4894
- if (valueInOptions_default(type, potentialDefaultValue, options2))
4977
+ if (valueInOptions_default(type, potentialDefaultValue, options))
4895
4978
  defaultValue = potentialDefaultValue;
4896
4979
  }
4897
4980
  if (!defaultValue) {
4898
- const fallbackValue = options2 && options2[0] && options2[0].id || options2[0] || "";
4981
+ const fallbackValue = options && options[0] && options[0].id || options[0] || "";
4899
4982
  defaultValue = maybeFixForMulti(fallbackValue, type);
4900
4983
  }
4901
4984
  return {
4902
4985
  id,
4903
4986
  name,
4904
4987
  type,
4905
- options: options2,
4988
+ options,
4906
4989
  defaultValue
4907
4990
  };
4908
4991
  };
@@ -4963,14 +5046,14 @@ var isValidSelectorState = (simpleState) => {
4963
5046
  name,
4964
5047
  type,
4965
5048
  optionsType,
4966
- options: options2
5049
+ options
4967
5050
  } = simpleState;
4968
- if (![name, type, optionsType, options2].every(Boolean))
5051
+ if (![name, type, optionsType, options].every(Boolean))
4969
5052
  return false;
4970
5053
  if (optionsType === OPTION_TYPE.STATIC)
4971
- return options2.static?.length > 0 && options2.static?.every((o) => o.id);
4972
- if (!options2.dynamic?.isPrimitiveType)
4973
- return !!options2.dynamic?.idKey;
5054
+ return options.static?.length > 0 && options.static?.every((o) => o.id);
5055
+ if (!options.dynamic?.isPrimitiveType)
5056
+ return !!options.dynamic?.idKey;
4974
5057
  return true;
4975
5058
  };
4976
5059
  function StaticSelectorOptionRow({ option, editOption, deleteOption }) {
@@ -5173,9 +5256,9 @@ function SelectorUI(props) {
5173
5256
  } = props;
5174
5257
  const [selectorIdentifier, setSelectorIdentifier] = useState("");
5175
5258
  const [selectorType, setSelectorType] = useState(SELECTOR_TYPE.SINGLE);
5176
- const [options2, setOptions] = useState({ static: [], dynamic: {} });
5259
+ const [options, setOptions] = useState({ static: [], dynamic: {} });
5177
5260
  const [optionsType, setOptionsType] = useState(OPTION_TYPE.STATIC);
5178
- const stateFields = [selectorIdentifier, selectorType, options2, optionsType];
5261
+ const stateFields = [selectorIdentifier, selectorType, options, optionsType];
5179
5262
  useEffect(() => {
5180
5263
  if (!simpleState)
5181
5264
  return;
@@ -5195,8 +5278,8 @@ function SelectorUI(props) {
5195
5278
  let defaultValue;
5196
5279
  let logicOptions;
5197
5280
  if (optionsType === OPTION_TYPE.STATIC) {
5198
- defaultValue = selectorType === SELECTOR_TYPE.SINGLE ? options2.static.find((o) => o.isDefault)?.id : options2.static.filter((o) => o.isDefault).map((o) => o.id);
5199
- logicOptions = options2.static.map(({ id: id2, label }) => ({ id: id2, label: label || id2 }));
5281
+ defaultValue = selectorType === SELECTOR_TYPE.SINGLE ? options.static.find((o) => o.isDefault)?.id : options.static.filter((o) => o.isDefault).map((o) => o.id);
5282
+ logicOptions = options.static.map(({ id: id2, label }) => ({ id: id2, label: label || id2 }));
5200
5283
  } else {
5201
5284
  const {
5202
5285
  optionsVar,
@@ -5204,7 +5287,7 @@ function SelectorUI(props) {
5204
5287
  labelKey,
5205
5288
  defaultVar,
5206
5289
  isPrimitiveType
5207
- } = options2.dynamic;
5290
+ } = options.dynamic;
5208
5291
  if (defaultVar)
5209
5292
  defaultValue = `variables["${defaultVar}"]`;
5210
5293
  logicOptions = isPrimitiveType ? `variables["${optionsVar}"]` : `(variables["${optionsVar}"] || []).map(d => ({id: String(d["${idKey}"]), label: String(d["${labelKey || idKey}"])}))`;
@@ -5217,7 +5300,7 @@ function SelectorUI(props) {
5217
5300
  name: selectorIdentifier,
5218
5301
  type: selectorType,
5219
5302
  optionsType,
5220
- options: options2
5303
+ options
5221
5304
  };
5222
5305
  const isValid = isValidSelectorState(simpleState2);
5223
5306
  onChange(simpleState2, logic, isValid);
@@ -5256,14 +5339,14 @@ function SelectorUI(props) {
5256
5339
  optionsType === OPTION_TYPE.STATIC ? /* @__PURE__ */ jsx(
5257
5340
  StaticSelectorOptionsEditor,
5258
5341
  {
5259
- staticOptions: options2.static,
5260
- setStaticOptions: (optionsState) => setOptions({ ...options2, static: optionsState })
5342
+ staticOptions: options.static,
5343
+ setStaticOptions: (optionsState) => setOptions({ ...options, static: optionsState })
5261
5344
  }
5262
5345
  ) : /* @__PURE__ */ jsx(
5263
5346
  DynamicSelectorOptionsEditor,
5264
5347
  {
5265
- dynamicOptions: options2.dynamic,
5266
- setDynamicOptions: (optionsState) => setOptions({ ...options2, dynamic: optionsState }),
5348
+ dynamicOptions: options.dynamic,
5349
+ setDynamicOptions: (optionsState) => setOptions({ ...options, dynamic: optionsState }),
5267
5350
  id
5268
5351
  }
5269
5352
  ),
@@ -7347,9 +7430,8 @@ init_ordering();
7347
7430
  init_store2();
7348
7431
  init_actions();
7349
7432
  init_ResourceProvider();
7350
- function ReportEditor2() {
7351
- const params = useParams();
7352
- const id = Number.parseInt(params.id || "", 10);
7433
+ function ReportEditor2(props) {
7434
+ const { id } = props;
7353
7435
  const ref = useReportRef(id);
7354
7436
  if (ref.isError) {
7355
7437
  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 }) });
@@ -7478,88 +7560,71 @@ function CreateSectionButton({ index, report }) {
7478
7560
  );
7479
7561
  }
7480
7562
 
7481
- // views/MetadataEditor.tsx
7563
+ // views/FormatterEditor.tsx
7482
7564
  init_esm_shims();
7483
7565
 
7484
- // components/metadata/FilterSidebar.tsx
7566
+ // components/formatters/FilterSidebar.tsx
7485
7567
  init_esm_shims();
7486
7568
  init_store2();
7569
+ init_cms();
7487
7570
  var allOption = { value: "all", label: "All" };
7488
- var buildOptions = (options2) => {
7489
- if (!options2)
7571
+ var buildOptionsFromMap = (options) => {
7572
+ if (!options)
7490
7573
  return [];
7491
- const selectOptions = options2.map((d) => ({ value: String(d.id), label: d.name }));
7574
+ const selectOptions = Object.keys(options).map((k) => ({ value: String(k), label: String(options[k]) }));
7492
7575
  return [allOption, ...selectOptions];
7493
7576
  };
7494
- function FilterSidebar({ onChange, loading }) {
7577
+ function FilterSidebar({ onChange, loading, onEditCreation }) {
7495
7578
  const dispatch = useAppDispatch();
7496
- const reportsNested = useAppSelector((state) => state.records.tree);
7497
- const localeDefault8 = useAppSelector((state) => state.status.localeDefault);
7498
7579
  const [query, setQuery] = useState("");
7499
- const [loadingRegenerate, setLoadingRegenerate] = useState(false);
7580
+ const [loadingCreate, setLoadingCreate] = useState(false);
7500
7581
  const [debouncedQuery] = useDebouncedValue(query, 500);
7501
7582
  const initialFilterState = {
7502
- report: allOption.value,
7503
- dimension: allOption.value,
7504
- variant: allOption.value,
7505
- visible: false,
7506
- noImage: false,
7507
- limit: "10"
7583
+ type: allOption.value
7508
7584
  };
7509
7585
  const [filters, setFilters] = useState(initialFilterState);
7510
- const options2 = useMemo(() => ({
7511
- report: buildOptions(reportsNested),
7512
- dimension: buildOptions(reportsNested.filter((report) => filters.report === allOption.value || filters.report === `${report.id}`).reduce((acc, report) => acc.concat(report.dimensions), [])),
7513
- variant: buildOptions(reportsNested.filter((report) => filters.report === allOption.value || filters.report === `${report.id}`).reduce((acc, report) => {
7514
- report.dimensions.filter((dim) => filters.dimension === allOption.value || filters.dimension === `${dim.id}`).forEach((dim) => {
7515
- acc = acc.concat(dim.variants);
7516
- });
7517
- return acc;
7518
- }, [])),
7519
- limit: [10, 25, 50].map((i) => ({ value: String(i), label: String(i) }))
7520
- }), [reportsNested, filters]);
7586
+ const onClearFilters = () => {
7587
+ setQuery("");
7588
+ setFilters(initialFilterState);
7589
+ };
7590
+ const options = useMemo(() => ({
7591
+ type: buildOptionsFromMap(FORMATTER_TYPES_NAMES)
7592
+ }), []);
7593
+ useEffect(() => {
7594
+ onChange({ ...filters, query: debouncedQuery });
7595
+ }, [filters.type, debouncedQuery]);
7596
+ const onAddNewFormatter = () => {
7597
+ setLoadingCreate(true);
7598
+ const newId = Math.random().toString(16).substring(2, 10);
7599
+ dispatch(actions_exports.createEntity("formatter", {
7600
+ name: `formatter_${newId}`,
7601
+ content: {
7602
+ type: "formatter",
7603
+ logic: "return n;",
7604
+ inputType: "string",
7605
+ testValue: "123456",
7606
+ description: "New formatter"
7607
+ }
7608
+ })).then((response) => {
7609
+ setLoadingCreate(false);
7610
+ onClearFilters();
7611
+ if (response.ok) {
7612
+ onEditCreation(response.data.id);
7613
+ }
7614
+ });
7615
+ };
7521
7616
  const onChangeFilter = useCallback((key, value) => {
7522
7617
  const newFilters = {
7523
7618
  ...filters,
7524
7619
  [key]: value
7525
7620
  };
7526
- switch (key) {
7527
- case "report":
7528
- newFilters.dimension = allOption.value;
7529
- break;
7530
- case "dimension":
7531
- newFilters.variant = allOption.value;
7532
- break;
7533
- }
7534
7621
  setFilters(newFilters);
7535
7622
  }, [filters]);
7536
- const onClearFilters = () => {
7537
- setQuery("");
7538
- setFilters(initialFilterState);
7539
- };
7540
- const onRegenarateSearchIndex = () => {
7541
- setLoadingRegenerate(true);
7542
- dispatch(actions_exports.searchRegenerate()).then(() => {
7543
- setLoadingRegenerate(false);
7544
- onChange(filters);
7545
- });
7546
- };
7547
- useEffect(() => {
7548
- onChange({ ...filters, query: debouncedQuery });
7549
- }, [
7550
- filters.report,
7551
- filters.dimension,
7552
- filters.variant,
7553
- filters.visible,
7554
- filters.limit,
7555
- filters.noImage,
7556
- debouncedQuery
7557
- ]);
7558
7623
  return /* @__PURE__ */ jsxs("div", { children: [
7559
7624
  /* @__PURE__ */ jsx(
7560
7625
  TextInput,
7561
7626
  {
7562
- label: `Search item by name (${localeDefault8.toUpperCase()})`,
7627
+ label: "Search by name or description",
7563
7628
  value: query,
7564
7629
  onChange: (event) => setQuery(event.currentTarget.value),
7565
7630
  disabled: loading
@@ -7568,62 +7633,14 @@ function FilterSidebar({ onChange, loading }) {
7568
7633
  /* @__PURE__ */ jsx(
7569
7634
  Select,
7570
7635
  {
7571
- label: "Reports",
7572
- data: options2.report,
7573
- value: filters.report,
7574
- onChange: (value) => onChangeFilter("report", value),
7575
- disabled: loading
7576
- }
7577
- ),
7578
- /* @__PURE__ */ jsx(
7579
- Select,
7580
- {
7581
- label: "Dimensions",
7582
- data: options2.dimension,
7583
- value: filters.dimension,
7584
- onChange: (value) => onChangeFilter("dimension", value),
7585
- disabled: loading
7586
- }
7587
- ),
7588
- /* @__PURE__ */ jsx(
7589
- Select,
7590
- {
7591
- label: "Variants",
7592
- data: options2.variant,
7593
- value: filters.variant,
7594
- onChange: (value) => onChangeFilter("variant", value),
7595
- disabled: loading
7596
- }
7597
- ),
7598
- /* @__PURE__ */ jsx(
7599
- Select,
7600
- {
7601
- label: "Rows limit",
7602
- data: options2.limit,
7603
- value: filters.limit,
7604
- onChange: (value) => onChangeFilter("limit", value),
7636
+ label: "Type",
7637
+ data: options.type,
7638
+ value: filters.type,
7639
+ onChange: (value) => onChangeFilter("type", value),
7605
7640
  disabled: loading
7606
7641
  }
7607
7642
  ),
7608
7643
  /* @__PURE__ */ jsx(Space, { h: "md" }),
7609
- /* @__PURE__ */ jsx(
7610
- Checkbox,
7611
- {
7612
- label: "Show visible members only",
7613
- checked: filters.visible,
7614
- onChange: (event) => onChangeFilter("visible", event.currentTarget.checked)
7615
- }
7616
- ),
7617
- /* @__PURE__ */ jsx(Space, { h: "md" }),
7618
- /* @__PURE__ */ jsx(
7619
- Checkbox,
7620
- {
7621
- label: "Show members with no image",
7622
- checked: filters.noImage,
7623
- onChange: (event) => onChangeFilter("noImage", event.currentTarget.checked)
7624
- }
7625
- ),
7626
- /* @__PURE__ */ jsx(Space, { h: "md" }),
7627
7644
  /* @__PURE__ */ jsx(
7628
7645
  Button,
7629
7646
  {
@@ -7640,696 +7657,372 @@ function FilterSidebar({ onChange, loading }) {
7640
7657
  {
7641
7658
  fullWidth: true,
7642
7659
  disabled: loading,
7643
- leftIcon: /* @__PURE__ */ jsx(IconRefresh, {}),
7644
- onClick: onRegenarateSearchIndex,
7660
+ leftIcon: /* @__PURE__ */ jsx(IconPlus, {}),
7661
+ onClick: onAddNewFormatter,
7645
7662
  variant: "light",
7646
- loading: loadingRegenerate,
7647
- children: "Re-Index Search"
7663
+ loading: loadingCreate,
7664
+ children: "New formatter"
7648
7665
  }
7649
7666
  )
7650
7667
  ] });
7651
7668
  }
7652
7669
 
7653
- // components/metadata/MemberForm.tsx
7670
+ // components/formatters/FormatterForm.tsx
7654
7671
  init_esm_shims();
7655
7672
  init_store2();
7656
-
7657
- // components/metadata/ImageSelector.tsx
7658
- init_esm_shims();
7659
- function ImageSelector({ member, onEditEnd }) {
7660
- const initialPrompt = member.contentByLocale && member.contentByLocale[0] ? member.contentByLocale[0].name : "";
7661
- const imageProviders = {
7662
- local: {
7663
- title: "Bespoke",
7664
- key: "local",
7665
- input: "text",
7666
- forceSearch: true,
7667
- getAxiosSearchConfig: (prompt2, image) => ({
7668
- url: "/api/cms/images/search/local",
7669
- method: "get",
7670
- params: { prompt: prompt2 }
7671
- })
7672
- },
7673
- flickr: {
7674
- title: "Flickr",
7675
- key: "flickr",
7676
- input: "text",
7677
- forceSearch: true,
7678
- getAxiosSearchConfig: (prompt2, image) => ({
7679
- url: "/api/cms/images/search/flickr",
7680
- method: "get",
7681
- params: { prompt: prompt2 }
7682
- })
7683
- },
7684
- unsplash: {
7685
- title: "Unsplash",
7686
- key: "unsplash",
7687
- input: "text",
7688
- forceSearch: true,
7689
- getAxiosSearchConfig: (prompt2, image) => ({
7690
- url: "/api/cms/images/search/unsplash",
7691
- method: "get",
7692
- params: { prompt: prompt2 }
7693
- })
7694
- },
7695
- upload: {
7696
- title: "Upload",
7697
- key: "upload",
7698
- input: "file",
7699
- forceSearch: false,
7700
- getAxiosSearchConfig: (prompt2, image) => ({})
7701
- }
7702
- };
7703
- const imageProvidersOptions = Object.keys(imageProviders).map((key) => ({ value: key, label: imageProviders[key].title }));
7704
- const [loading, setLoading] = useState(false);
7705
- const [selectedProvider, setSelectedProvider] = useState(Object.values(imageProviders)[0]);
7706
- const [scrollSize, setScrollSize] = useState(500);
7707
- const [prompt, setPrompt] = useState(initialPrompt);
7708
- const [imageToUpload, setImageToUpload] = useState();
7709
- const [results, setResults] = useState([]);
7710
- const [selectedImage, setSelectedImage] = useState();
7673
+ init_cms();
7674
+ init_FUNC();
7675
+ init_hooks();
7676
+ var buildOptionsFromMap2 = (options) => {
7677
+ if (!options)
7678
+ return [];
7679
+ return Object.keys(options).map((k) => ({ value: String(k), label: String(options[k]) }));
7680
+ };
7681
+ var getCastedValue = (type, value) => {
7682
+ switch (type) {
7683
+ case FORMATTER_INPUT_TYPES.OBJECT:
7684
+ return JSON.parse(value);
7685
+ default:
7686
+ return `${value}`;
7687
+ }
7688
+ };
7689
+ function FormatterForm({ formatterId, onEditEnd }) {
7690
+ const localeDefault8 = useAppSelector((state) => state.status.localeDefault);
7691
+ const locales3 = useAppSelector((state) => state.status.locales);
7692
+ locales3.map((locale) => ({ value: locale, label: `${locale.toUpperCase()}` }));
7693
+ const opened = !!formatterId;
7694
+ const [formatter, setFormatter] = useState();
7695
+ const [testResults, setTestResults] = useState("");
7696
+ const [formatterNames, setFormatterNames] = useState([]);
7697
+ const [initialLoading, setInitialLoading] = useState(true);
7698
+ const [saveLoading, setSaveLoading] = useState(false);
7699
+ const [isDirty, setIsDirty] = useState(true);
7700
+ const [isValid, setIsValid] = useState(true);
7711
7701
  const [error, setError] = useState(false);
7712
- const onSearch = async () => {
7713
- setError(false);
7714
- setLoading(true);
7715
- setResults([]);
7716
- const providerAxiosConfig = selectedProvider.getAxiosSearchConfig(prompt, imageToUpload);
7717
- const imageRecords = await axios(providerAxiosConfig).then((response) => response.data.data && response.data.data.results ? response.data.data.results : []).catch((e) => {
7718
- console.error(e);
7719
- setError(true);
7720
- });
7721
- setResults(imageRecords);
7722
- setLoading(false);
7723
- };
7724
- const onSelect = (img) => {
7725
- setSelectedImage(img);
7702
+ const dispatch = useAppDispatch();
7703
+ const ref = useFormatterRef(formatterId);
7704
+ const formattersList = useFormatterList();
7705
+ const usageCount = useFormatterUsageCount(ref.data?.name);
7706
+ const isLoading = initialLoading || saveLoading;
7707
+ useEffect(() => {
7708
+ if (!ref.isLoading) {
7709
+ setFormatter(ref.data);
7710
+ }
7711
+ }, [ref.isLoading]);
7712
+ useEffect(() => {
7713
+ if (!ref.isLoading && !formattersList.isLoading) {
7714
+ const formatterItem = !ref.isSuccess ? void 0 : ref.data;
7715
+ const formatters = !formattersList.isSuccess ? [] : formattersList.data;
7716
+ if (formatterItem) {
7717
+ const formatterNamesList = formatters.filter((f) => f.id !== formatterItem.id).map((f) => f.name);
7718
+ setFormatterNames(formatterNamesList);
7719
+ }
7720
+ onTest();
7721
+ setInitialLoading(false);
7722
+ }
7723
+ }, [ref.isLoading, formattersList.isLoading]);
7724
+ const isValidName = formatter && !formatterNames.includes(formatter.name) && formatter.name !== "";
7725
+ const onChangeName = (value) => {
7726
+ if (isValidName) {
7727
+ setIsValid(false);
7728
+ }
7729
+ if (formatter) {
7730
+ setFormatter({
7731
+ ...formatter,
7732
+ name: slugifyInput_default(value).replace("-", "")
7733
+ });
7734
+ }
7726
7735
  };
7727
- const onSave = async () => {
7728
- setLoading(true);
7729
- const formData = new FormData();
7730
- formData.append("content_id", member.content_id);
7731
- if (selectedImage && imageToUpload) {
7732
- formData.append("file", imageToUpload);
7733
- delete selectedImage.file;
7736
+ const onContentValueChange = (key, value) => {
7737
+ if (key !== "description") {
7738
+ setIsDirty(true);
7734
7739
  }
7735
- if (selectedImage) {
7736
- formData.append("image_id", selectedImage.id);
7737
- formData.append("image_source", selectedImage.source);
7740
+ if (formatter) {
7741
+ setFormatter({
7742
+ ...formatter,
7743
+ content: {
7744
+ ...formatter.content,
7745
+ [key]: value
7746
+ }
7747
+ });
7738
7748
  }
7739
- await axios({
7740
- method: "post",
7741
- url: `/api/cms/images/save/${selectedProvider.key}`,
7742
- data: formData
7743
- }).then((response) => {
7744
- onEditEnd(true);
7745
- }).catch((e) => {
7746
- console.error(e);
7747
- setError(true);
7748
- });
7749
- setLoading(false);
7750
7749
  };
7751
- const onChangeProvider = (providerKey) => {
7752
- setError(false);
7753
- setSelectedProvider(imageProviders[providerKey]);
7754
- setSelectedImage(void 0);
7750
+ const onTest = () => {
7751
+ if (formatter) {
7752
+ let results = "";
7753
+ try {
7754
+ const newFunction = parse({ vars: ["n"], logic: formatter.content.logic }, {}, localeDefault8, {});
7755
+ const castedValue = getCastedValue(formatter.content.inputType, formatter.content.testValue);
7756
+ results = newFunction(castedValue);
7757
+ setIsValid(true);
7758
+ } catch (e) {
7759
+ results = `ERROR: ${e.message}`;
7760
+ console.log(e);
7761
+ setIsValid(false);
7762
+ }
7763
+ setIsDirty(false);
7764
+ setTestResults(`${results}`);
7765
+ }
7755
7766
  };
7756
- useEffect(() => {
7757
- const { innerHeight } = window;
7758
- setScrollSize(innerHeight - 280);
7759
- onSearch();
7760
- }, []);
7761
- useEffect(() => {
7762
- setResults([]);
7763
- if (!loading && selectedProvider.forceSearch) {
7764
- onSearch();
7767
+ const onSave = () => {
7768
+ setSaveLoading(true);
7769
+ if (isValid && isValidName) {
7770
+ dispatch(actions_exports.updateEntity("formatter", formatter)).then(onEditEnd);
7765
7771
  }
7766
- }, [selectedProvider]);
7767
- const noResults = results && results.length === 0 && !loading && selectedProvider.key !== "upload";
7772
+ };
7768
7773
  return /* @__PURE__ */ jsx(
7769
7774
  Drawer,
7770
7775
  {
7771
- opened: true,
7772
- onClose: onEditEnd,
7773
- title: `Edit image for ${initialPrompt} (${member.id}) `,
7776
+ opened,
7777
+ onClose: () => onEditEnd(),
7778
+ title: `Edit formatter ID ${formatterId}`,
7774
7779
  padding: "lg",
7775
7780
  closeOnClickOutside: false,
7776
7781
  closeOnEscape: false,
7777
- position: "left",
7782
+ position: "right",
7778
7783
  size: "50%",
7779
7784
  lockScroll: true,
7780
7785
  withCloseButton: false,
7781
- children: /* @__PURE__ */ jsx("div", { style: { height: "100%" }, children: /* @__PURE__ */ jsxs(Stack, { children: [
7782
- /* @__PURE__ */ jsxs(Stack, { spacing: "xs", children: [
7783
- /* @__PURE__ */ jsxs(Group, { position: "apart", grow: true, align: "flex-end", children: [
7784
- selectedProvider.input === "text" && /* @__PURE__ */ jsx(
7785
- TextInput,
7786
+ children: /* @__PURE__ */ jsx(
7787
+ DrawerContentWithScroll,
7788
+ {
7789
+ content: /* @__PURE__ */ jsxs(Fragment, { children: [
7790
+ /* @__PURE__ */ jsx(LoadingOverlay, { visible: initialLoading, overlayBlur: 2, overlayOpacity: 50 }),
7791
+ error && /* @__PURE__ */ jsx(Alert, { title: "Formatter Form", color: "red", children: "Error, please try again." }),
7792
+ formatter && !error && /* @__PURE__ */ jsxs(Stack, { justify: "space-between", children: [
7793
+ /* @__PURE__ */ jsxs(Group, { grow: true, align: "flex-start", children: [
7794
+ /* @__PURE__ */ jsx(
7795
+ TextInput,
7796
+ {
7797
+ label: `Name ${isValidName && usageCount > 0 ? `(Read only, used in ${usageCount} blocks)` : ""}`,
7798
+ value: formatter.name,
7799
+ icon: isValidName ? /* @__PURE__ */ jsx(IconCircleCheck, {}) : /* @__PURE__ */ jsx(IconAlertCircle, {}),
7800
+ readOnly: isValidName && usageCount > 0,
7801
+ disabled: isValidName && usageCount > 0,
7802
+ onChange: (evt) => onChangeName(evt.target.value),
7803
+ error: isValidName ? false : "Duplicate or empty name, choose a different one."
7804
+ }
7805
+ ),
7806
+ /* @__PURE__ */ jsx(
7807
+ Select,
7808
+ {
7809
+ label: "Type",
7810
+ data: buildOptionsFromMap2(FORMATTER_TYPES_NAMES),
7811
+ value: formatter.content.type,
7812
+ onChange: (value) => onContentValueChange("type", value)
7813
+ }
7814
+ )
7815
+ ] }),
7816
+ /* @__PURE__ */ jsx(
7817
+ Textarea,
7818
+ {
7819
+ label: "Description",
7820
+ value: formatter.content.description,
7821
+ onChange: (evt) => onContentValueChange("description", evt.target.value)
7822
+ }
7823
+ ),
7824
+ /* @__PURE__ */ jsx(
7825
+ MonacoWrapper_default,
7826
+ {
7827
+ monacoOptions: {
7828
+ onChange: (newLogic) => onContentValueChange("logic", newLogic),
7829
+ value: formatter.content.logic
7830
+ }
7831
+ },
7832
+ "formatter-code-editor"
7833
+ ),
7834
+ /* @__PURE__ */ jsxs(Group, { grow: true, align: "flex-end", children: [
7835
+ /* @__PURE__ */ jsx(
7836
+ Select,
7837
+ {
7838
+ label: "Input Type",
7839
+ data: buildOptionsFromMap2(FORMATTER_INPUT_TYPES_NAMES),
7840
+ value: formatter.content.inputType,
7841
+ onChange: (value) => onContentValueChange("inputType", value)
7842
+ }
7843
+ ),
7844
+ /* @__PURE__ */ jsx(
7845
+ TextInput,
7846
+ {
7847
+ label: "Test Value",
7848
+ value: formatter.content.testValue,
7849
+ onChange: (evt) => onContentValueChange("testValue", evt.target.value),
7850
+ error: formatter.content.testValue === "" && !isValid
7851
+ }
7852
+ )
7853
+ ] }),
7854
+ /* @__PURE__ */ jsx(
7855
+ Textarea,
7856
+ {
7857
+ readOnly: true,
7858
+ icon: isValid ? /* @__PURE__ */ jsx(IconCircleCheck, {}) : /* @__PURE__ */ jsx(IconAlertCircle, {}),
7859
+ label: "Test Results",
7860
+ value: testResults,
7861
+ error: testResults.includes("ERROR") && !isValid ? "There is a problem in the function, input type or test value. Please check and run again." : false
7862
+ }
7863
+ )
7864
+ ] })
7865
+ ] }),
7866
+ buttons: /* @__PURE__ */ jsxs(Fragment, { children: [
7867
+ /* @__PURE__ */ jsx(
7868
+ Button,
7786
7869
  {
7787
- placeholder: "Search term",
7788
- label: "Search term",
7789
- value: prompt,
7790
- onChange: (e) => setPrompt(e.target.value)
7870
+ variant: "outline",
7871
+ disabled: isLoading,
7872
+ onClick: () => onEditEnd(),
7873
+ children: "Cancel"
7791
7874
  }
7792
7875
  ),
7793
- selectedProvider.input === "file" && /* @__PURE__ */ jsx(
7794
- FileInput,
7876
+ /* @__PURE__ */ jsx(
7877
+ Button,
7795
7878
  {
7796
- label: "Upload image",
7797
- placeholder: "Choose a PNG image from your computer",
7798
- accept: "image/png",
7799
- value: imageToUpload,
7800
- onChange: (image) => {
7801
- setSelectedImage(void 0);
7802
- if (image) {
7803
- const objectUrl = URL.createObjectURL(image);
7804
- setResults([{
7805
- id: "custom",
7806
- source: objectUrl,
7807
- file: image
7808
- }]);
7809
- setImageToUpload(image);
7810
- setSelectedImage("custom");
7811
- }
7812
- },
7813
- icon: /* @__PURE__ */ jsx(IconUpload, { size: 14 })
7879
+ onClick: onTest,
7880
+ disabled: !isDirty,
7881
+ color: isDirty ? "blue" : isValid ? "green" : "red",
7882
+ leftIcon: /* @__PURE__ */ jsx(IconPlayerPlay, {}),
7883
+ children: "Run Test"
7814
7884
  }
7815
7885
  ),
7816
- selectedProvider.forceSearch && /* @__PURE__ */ jsx(
7886
+ /* @__PURE__ */ jsx(
7817
7887
  Button,
7818
7888
  {
7819
- fullWidth: true,
7820
- disabled: loading || !prompt || prompt === "",
7821
- onClick: onSearch,
7822
- leftIcon: /* @__PURE__ */ jsx(IconPlayerPlay, {}),
7823
- children: "Search"
7889
+ onClick: onSave,
7890
+ loading: isLoading,
7891
+ disabled: isLoading || !isValid || isDirty || !isValidName,
7892
+ children: "Save"
7824
7893
  }
7825
7894
  )
7826
7895
  ] }),
7827
- /* @__PURE__ */ jsx(
7828
- SegmentedControl,
7829
- {
7830
- disabled: loading,
7831
- value: selectedProvider.key,
7832
- onChange: (providerKey) => onChangeProvider(providerKey),
7833
- data: imageProvidersOptions,
7834
- fullWidth: true
7835
- }
7836
- )
7837
- ] }),
7838
- /* @__PURE__ */ jsx(Group, { mt: "xs", grow: true, children: /* @__PURE__ */ jsxs(ScrollArea, { offsetScrollbars: true, type: "always", style: { height: scrollSize }, children: [
7839
- /* @__PURE__ */ jsx(LoadingOverlay, { visible: loading, overlayBlur: 2, overlayOpacity: 50 }),
7840
- error && /* @__PURE__ */ jsx(Alert, { title: `${selectedProvider.title}`, color: "red", children: "Error, please try again." }),
7841
- noResults && /* @__PURE__ */ jsx(Alert, { title: `${selectedProvider.title}`, color: "blue", children: "No results, for try another prompt or provider." }),
7842
- results && results.length > 0 && !loading && /* @__PURE__ */ jsx(SimpleGrid, { cols: 3, children: results.map((img) => /* @__PURE__ */ jsx(
7843
- Image,
7844
- {
7845
- radius: "md",
7846
- src: img.source,
7847
- style: {
7848
- cursor: "pointer",
7849
- borderRadius: 15,
7850
- border: selectedImage && selectedImage.id === img.id ? "5px solid black" : ""
7851
- },
7852
- onClick: () => onSelect(img)
7853
- },
7854
- img.id
7855
- )) })
7856
- ] }) }),
7857
- /* @__PURE__ */ jsxs(Group, { mt: "xs", grow: true, style: { height: 45 }, children: [
7858
- /* @__PURE__ */ jsx(
7859
- Button,
7860
- {
7861
- variant: "outline",
7862
- disabled: loading,
7863
- onClick: () => onEditEnd(false),
7864
- children: "Cancel"
7865
- }
7866
- ),
7867
- /* @__PURE__ */ jsx(
7868
- Button,
7869
- {
7870
- onClick: onSave,
7871
- loading,
7872
- disabled: loading || !selectedImage,
7873
- children: "Save"
7874
- }
7875
- )
7876
- ] })
7877
- ] }) })
7896
+ size: 145
7897
+ }
7898
+ )
7878
7899
  }
7879
7900
  );
7880
7901
  }
7881
- var ImageSelector_default = ImageSelector;
7882
7902
 
7883
- // views/ImagePreview.tsx
7903
+ // components/formatters/FormattersTable.tsx
7884
7904
  init_esm_shims();
7885
- function ImagePreview2({
7886
- member,
7887
- height = "auto",
7888
- width = "100%",
7889
- size = "thumb",
7890
- forceRefreshTimestamp = false
7891
- }) {
7892
- let extraParam = "";
7893
- if (forceRefreshTimestamp) {
7894
- extraParam = `&t=${new Date().getTime()}`;
7905
+ init_store2();
7906
+ init_hooks();
7907
+ init_cms();
7908
+ var getCastedValue2 = (type, value) => {
7909
+ switch (type) {
7910
+ case FORMATTER_INPUT_TYPES.OBJECT:
7911
+ return JSON.parse(value);
7912
+ default:
7913
+ return `${value}`;
7895
7914
  }
7896
- const imageUrl = member && member.image_id ? `/api/cms/member/image.png?member=${member.content_id}&size=${size}${extraParam}` : "https://placehold.jp/cccccc/ffffff/900x600.png?text=None";
7897
- return /* @__PURE__ */ jsx(Image, { height, width, radius: "md", src: imageUrl });
7898
- }
7899
- var ImagePreview_default = ImagePreview2;
7900
- function MemberForm({ memberId, onEditEnd }) {
7901
- const localeDefault8 = useAppSelector((state) => state.status.localeDefault);
7902
- const locales3 = useAppSelector((state) => state.status.locales);
7903
- const localesItems = locales3.map((locale) => ({ value: locale, label: `${locale.toUpperCase()}` }));
7904
- const opened = !!memberId;
7905
- const [selectedLocale, setSelectedLocale] = useState(localeDefault8);
7906
- const [member, setMember] = useState(null);
7907
- const [loading, setLoading] = useState(true);
7908
- const [showImageSelector, setShowImageSelector] = useState(false);
7909
- const [error, setError] = useState(false);
7910
- const mainForm = useForm();
7911
- const localizedForms = locales3.reduce((acc, item) => {
7912
- acc[item] = useForm({});
7913
- return acc;
7914
- }, {});
7915
- const imageForm = useForm();
7916
- const localizedImageForms = locales3.reduce((acc, item) => {
7917
- acc[item] = useForm({});
7918
- return acc;
7919
- }, {});
7920
- const dispatch = useAppDispatch();
7921
- const fetchMemberData = async () => {
7922
- setLoading(true);
7923
- setError(false);
7924
- const memberRecord = await dispatch(actions_exports.readMember(
7925
- {
7926
- locale: "all",
7927
- content_ids: memberId,
7928
- all: false,
7929
- mode: "content_ids"
7930
- }
7931
- )).then((response) => {
7932
- let obj;
7933
- if (response.results && response.results[0]) {
7934
- setError(false);
7935
- obj = response.results[0];
7936
- } else {
7937
- setError(true);
7938
- }
7939
- return obj;
7940
- }).catch((e) => {
7941
- console.error(e);
7942
- setError(true);
7943
- });
7944
- if (memberRecord) {
7945
- setMember(memberRecord);
7946
- mainForm.setValues(memberRecord);
7947
- memberRecord.contentByLocale.forEach((item) => {
7948
- localizedForms[item.locale].setValues({
7949
- name: item.name,
7950
- keywords: item.keywords ? item.keywords : [],
7951
- attributes: item.attributes ? Object.keys(item.attributes).map((ak) => ({ key: ak, value: item.attributes[ak] })) : []
7952
- });
7953
- });
7954
- if (memberRecord.image) {
7955
- imageForm.setValues(memberRecord.image);
7956
- memberRecord.image.contentByLocale.forEach((item) => {
7957
- localizedImageForms[item.locale].setValues({
7958
- meta: item.meta
7959
- });
7960
- });
7915
+ };
7916
+ function FormattersTable({ filters, onClickEdit }) {
7917
+ const [sortStatus, setSortStatus] = useState();
7918
+ const [records, setRecords] = useState([]);
7919
+ const formattersList = useFormatterList();
7920
+ const formatters = !formattersList.isSuccess ? [] : formattersList.data;
7921
+ const formatterFunctions = useFormatterFunctionsForLocale();
7922
+ const formattersUsageCount = useFormatterUsageCountList();
7923
+ const formattersFull = useMemo(() => formatters.map((f) => {
7924
+ const usageCount = formattersUsageCount[f.name] ? formattersUsageCount[f.name] : 0;
7925
+ let result = "";
7926
+ try {
7927
+ const castedValue = getCastedValue2(f.content.inputType, f.content.testValue);
7928
+ result = formatterFunctions[f.name](castedValue);
7929
+ } catch (e) {
7930
+ result = `ERROR: ${e.message}`;
7931
+ console.log(`Error in ${f.name}`);
7932
+ console.log(e);
7933
+ }
7934
+ return {
7935
+ ...f,
7936
+ result,
7937
+ usageCount
7938
+ };
7939
+ }), [formatters]);
7940
+ const doSearch = () => {
7941
+ let filteredResults = formattersFull.filter(
7942
+ (formatter) => {
7943
+ let findQuery = true;
7944
+ let findType = true;
7945
+ if (filters.query) {
7946
+ findQuery = formatter.name.toLowerCase().indexOf(filters.query.toLowerCase()) > -1 || formatter.content.description.indexOf(filters.query) > -1;
7947
+ }
7948
+ if (filters.type !== "all") {
7949
+ findType = formatter.content.type === filters.type;
7950
+ }
7951
+ return findQuery && findType;
7961
7952
  }
7953
+ );
7954
+ if (sortStatus) {
7955
+ const sortIndex = sortStatus.direction === "asc" ? -1 : 1;
7956
+ filteredResults = filteredResults.sort((a, b) => a[sortStatus.columnAccessor] > b[sortStatus.columnAccessor] ? 1 * sortIndex : -1 * sortIndex);
7962
7957
  }
7963
- setLoading(false);
7958
+ setRecords(filteredResults);
7964
7959
  };
7965
7960
  useEffect(() => {
7966
- if (memberId) {
7967
- fetchMemberData();
7968
- } else {
7969
- setMember(null);
7970
- }
7971
- }, [memberId]);
7972
- const onImageSelectorClose = async (edited) => {
7973
- if (edited) {
7974
- fetchMemberData();
7975
- }
7976
- setShowImageSelector(false);
7977
- };
7978
- const clearImage = () => {
7979
- setMember({
7980
- ...member,
7981
- image: null,
7982
- image_id: null
7983
- });
7984
- };
7985
- const onSave = async () => {
7986
- setLoading(true);
7987
- const payload = {
7988
- ...mainForm.values,
7989
- contentByLocale: member.contentByLocale.map((oldValues) => {
7990
- const localizedItem = localizedForms[oldValues.locale] ? { ...localizedForms[oldValues.locale].values } : {};
7991
- localizedItem.keywords = localizedItem.keywords && localizedItem.keywords.length > 0 ? localizedItem.keywords : null;
7992
- return {
7993
- ...oldValues,
7994
- ...localizedItem
7995
- };
7996
- }),
7997
- image: member.image ? {
7998
- ...imageForm.values,
7999
- contentByLocale: locales3.map((locale) => {
8000
- const oldValues = member.image.contentByLocale.find((e) => e.locale === locale);
8001
- const localizedItem = localizedImageForms[oldValues.locale] ? localizedImageForms[oldValues.locale].values : {};
8002
- return {
8003
- ...oldValues,
8004
- ...localizedItem
8005
- };
8006
- })
8007
- } : null,
8008
- image_id: member.image_id
8009
- };
8010
- await axios.post("/api/cms/update/member", payload).then((resp) => {
8011
- onEditEnd();
8012
- return resp.data.data;
8013
- });
8014
- };
8015
- const isValidLocalizedForm = Object.keys(localizedForms[selectedLocale].values).length > 0;
8016
- return /* @__PURE__ */ jsxs(Fragment, { children: [
8017
- /* @__PURE__ */ jsx(
8018
- Drawer,
8019
- {
8020
- opened,
8021
- onClose: () => onEditEnd(),
8022
- title: `Edit member ID ${memberId}`,
8023
- padding: "lg",
8024
- closeOnClickOutside: false,
8025
- closeOnEscape: false,
8026
- position: "right",
8027
- size: "50%",
8028
- lockScroll: true,
8029
- withCloseButton: false,
8030
- children: /* @__PURE__ */ jsx(
8031
- DrawerContentWithScroll,
8032
- {
8033
- content: /* @__PURE__ */ jsxs(Fragment, { children: [
8034
- /* @__PURE__ */ jsx(LoadingOverlay, { visible: loading, overlayBlur: 2, overlayOpacity: 50 }),
8035
- error && /* @__PURE__ */ jsx(Alert, { title: "Member Form", color: "red", children: "Error, please try again." }),
8036
- member && !error && /* @__PURE__ */ jsxs(Stack, { justify: "space-between", children: [
8037
- /* @__PURE__ */ jsxs(Group, { position: "apart", grow: true, children: [
8038
- /* @__PURE__ */ jsxs(Stack, { children: [
8039
- /* @__PURE__ */ jsx(ImagePreview_default, { member, forceRefreshTimestamp: true }),
8040
- /* @__PURE__ */ jsx(
8041
- Button,
8042
- {
8043
- variant: "outline",
8044
- disabled: loading,
8045
- leftIcon: /* @__PURE__ */ jsx(IconPolaroid, {}),
8046
- onClick: () => setShowImageSelector(true),
8047
- children: "Change image"
8048
- }
8049
- ),
8050
- member && member.image && /* @__PURE__ */ jsx(
8051
- Button,
8052
- {
8053
- variant: "outline",
8054
- disabled: loading,
8055
- leftIcon: /* @__PURE__ */ jsx(IconCircleMinus, {}),
8056
- onClick: () => clearImage(),
8057
- children: "Remove image"
8058
- }
8059
- ),
8060
- /* @__PURE__ */ jsx(Space, { h: "md" })
8061
- ] }),
8062
- /* @__PURE__ */ jsxs(Stack, { justify: "flex-start", children: [
8063
- /* @__PURE__ */ jsx(
8064
- TextInput,
8065
- {
8066
- disabled: !member || !member.image,
8067
- label: "Source",
8068
- ...imageForm.getInputProps("url")
8069
- }
8070
- ),
8071
- /* @__PURE__ */ jsx(
8072
- TextInput,
8073
- {
8074
- disabled: !member || !member.image,
8075
- label: "Author",
8076
- ...imageForm.getInputProps("author")
8077
- }
8078
- ),
8079
- /* @__PURE__ */ jsx(
8080
- TextInput,
8081
- {
8082
- disabled: !member || !member.image,
8083
- label: "License",
8084
- ...imageForm.getInputProps("license")
8085
- }
8086
- ),
8087
- /* @__PURE__ */ jsx(
8088
- SegmentedControl,
8089
- {
8090
- disabled: !member || !member.image,
8091
- value: selectedLocale,
8092
- onChange: setSelectedLocale,
8093
- data: localesItems
8094
- }
8095
- ),
8096
- /* @__PURE__ */ jsx(
8097
- TextInput,
8098
- {
8099
- disabled: !member || !member.image,
8100
- label: "Alternative image text",
8101
- ...localizedImageForms[selectedLocale].getInputProps("meta")
8102
- }
8103
- )
8104
- ] })
8105
- ] }),
8106
- /* @__PURE__ */ jsxs(Code, { block: true, children: [
8107
- "TO-DO apply same image also to some of these members? List of checkboxes",
8108
- /* @__PURE__ */ jsx("br", {}),
8109
- "(x) another member, similar name",
8110
- /* @__PURE__ */ jsx("br", {}),
8111
- "(x) another member, similar name",
8112
- /* @__PURE__ */ jsx("br", {})
8113
- ] }),
8114
- /* @__PURE__ */ jsx(Divider, { my: "sm" }),
8115
- /* @__PURE__ */ jsxs(Group, { position: "apart", grow: true, children: [
8116
- /* @__PURE__ */ jsx(TextInput, { mt: "xs", disabled: true, label: "Original ID", ...mainForm.getInputProps("id") }),
8117
- /* @__PURE__ */ jsx(TextInput, { mt: "xs", disabled: true, label: "Content ID", ...mainForm.getInputProps("content_id") })
8118
- ] }),
8119
- /* @__PURE__ */ jsxs(Group, { position: "apart", grow: true, children: [
8120
- /* @__PURE__ */ jsx(
8121
- TextInput,
8122
- {
8123
- mt: "xs",
8124
- icon: "/",
8125
- disabled: true,
8126
- label: "Variant Slug",
8127
- ...mainForm.getInputProps("variant_slug")
8128
- }
8129
- ),
8130
- /* @__PURE__ */ jsx(TextInput, { mt: "xs", icon: "/", disabled: true, label: "Member Slug", ...mainForm.getInputProps("slug") })
8131
- ] }),
8132
- /* @__PURE__ */ jsxs(Group, { position: "apart", grow: true, children: [
8133
- /* @__PURE__ */ jsx(TextInput, { mt: "xs", disabled: true, label: "Report", ...mainForm.getInputProps("report_name") }),
8134
- /* @__PURE__ */ jsx(TextInput, { mt: "xs", disabled: true, label: "Dimension", ...mainForm.getInputProps("dimension_name") }),
8135
- /* @__PURE__ */ jsx(TextInput, { mt: "xs", disabled: true, label: "Variant", ...mainForm.getInputProps("variant_name") })
8136
- ] }),
8137
- /* @__PURE__ */ jsx(Group, { position: "apart", grow: true, children: /* @__PURE__ */ jsx(
8138
- Checkbox,
8139
- {
8140
- mt: "xs",
8141
- label: "Visible",
8142
- ...mainForm.getInputProps("visible", { type: "checkbox" })
8143
- }
8144
- ) }),
8145
- /* @__PURE__ */ jsx(Group, { mt: "xs", position: "apart", grow: true, children: /* @__PURE__ */ jsx(
8146
- SegmentedControl,
8147
- {
8148
- value: selectedLocale,
8149
- onChange: setSelectedLocale,
8150
- data: localesItems
8151
- }
8152
- ) }),
8153
- !isValidLocalizedForm && /* @__PURE__ */ jsx(Alert, { title: "Unavailable locale", color: "blue", children: "This language was not ingested for the current member. Go to variant editor and provide the members there." }),
8154
- isValidLocalizedForm && /* @__PURE__ */ jsxs(Fragment, { children: [
8155
- /* @__PURE__ */ jsx(Group, { mt: "xs", position: "apart", grow: true, children: /* @__PURE__ */ jsx(TextInput, { label: "Name", ...localizedForms[selectedLocale].getInputProps("name") }) }),
8156
- /* @__PURE__ */ jsx(Group, { mt: "xs", position: "apart", align: "top", grow: true, children: /* @__PURE__ */ jsx(
8157
- MultiSelect,
8158
- {
8159
- label: "Keywords",
8160
- data: localizedForms[selectedLocale].values.keywords,
8161
- placeholder: "Enter keywords",
8162
- searchable: true,
8163
- creatable: true,
8164
- valueComponent: ({
8165
- value,
8166
- label,
8167
- image,
8168
- name
8169
- }) => /* @__PURE__ */ jsxs(
8170
- Badge,
8171
- {
8172
- onClick: () => {
8173
- const current = localizedForms[selectedLocale].values.keywords;
8174
- localizedForms[selectedLocale].setFieldValue(
8175
- "keywords",
8176
- current.filter((k) => k !== label)
8177
- );
8178
- },
8179
- children: [
8180
- label,
8181
- " ",
8182
- "x"
8183
- ]
8184
- }
8185
- ),
8186
- getCreateLabel: (query) => `+ Create ${query}`,
8187
- value: localizedForms[selectedLocale].values.keywords,
8188
- onCreate: (query) => {
8189
- const current = localizedForms[selectedLocale].values.keywords;
8190
- localizedForms[selectedLocale].setFieldValue("keywords", [...current, query]);
8191
- return query;
8192
- }
8193
- }
8194
- ) }),
8195
- /* @__PURE__ */ jsx(Input.Wrapper, { label: "Attributes", children: /* @__PURE__ */ jsx(SortableTable, { data: localizedForms[selectedLocale].values.attributes }) })
8196
- ] })
8197
- ] })
8198
- ] }),
8199
- buttons: /* @__PURE__ */ jsxs(Fragment, { children: [
8200
- /* @__PURE__ */ jsx(
8201
- Button,
8202
- {
8203
- variant: "outline",
8204
- disabled: loading,
8205
- onClick: () => onEditEnd(),
8206
- children: "Cancel"
8207
- }
8208
- ),
8209
- /* @__PURE__ */ jsx(
8210
- Button,
8211
- {
8212
- onClick: onSave,
8213
- loading,
8214
- disabled: loading,
8215
- children: "Save Member"
8216
- }
8217
- )
8218
- ] }),
8219
- size: 145
8220
- }
8221
- )
8222
- }
8223
- ),
8224
- showImageSelector && /* @__PURE__ */ jsx(ImageSelector_default, { member, onEditEnd: onImageSelectorClose })
8225
- ] });
8226
- }
8227
-
8228
- // components/metadata/MembersTable.tsx
8229
- init_esm_shims();
8230
- init_store2();
8231
- function MembersTable({ members, onClickEdit, onSort }) {
8232
- const [sortStatus, setSortStatus] = useState();
8233
- const [records, setRecords] = useState([]);
8234
- useEffect(() => {
8235
- if (sortStatus) {
8236
- onSort(sortStatus);
8237
- }
8238
- }, [sortStatus]);
8239
- useEffect(() => {
8240
- setRecords(members);
8241
- }, [members]);
8242
- useEffect(() => {
8243
- setRecords(members);
8244
- }, []);
8245
- const localeDefault8 = useAppSelector((state) => state.status.localeDefault);
8246
- const cols = [
8247
- {
8248
- title: "",
8249
- accessor: "visible",
8250
- width: "10px",
8251
- render: ({ visible }) => /* @__PURE__ */ jsx(Fragment, { children: !visible ? /* @__PURE__ */ jsx(Tooltip, { label: "Visible: false", children: /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(IconEyeOff, { size: 20 }) }) }) : /* @__PURE__ */ jsx("span", {}) })
8252
- },
8253
- {
8254
- title: "ID",
8255
- accessor: "id",
8256
- sortable: true,
8257
- textAlignment: "right"
8258
- },
8259
- {
8260
- title: "Report",
8261
- accessor: "report.name"
8262
- },
8263
- {
8264
- title: "Dimension",
8265
- accessor: "dimension.name"
8266
- },
8267
- {
8268
- title: "Variant",
8269
- accessor: "variant.name"
8270
- },
8271
- {
8272
- title: "Slug",
8273
- accessor: "slug",
8274
- render: ({ slug, variant }) => /* @__PURE__ */ jsxs("span", { children: [
8275
- "/",
8276
- variant.slug,
8277
- "/",
8278
- slug
8279
- ] })
8280
- },
8281
- {
8282
- title: `Name ${localeDefault8.toUpperCase()}`,
8283
- sortable: true,
8284
- accessor: "name",
8285
- render: ({ contentByLocale }) => {
8286
- const defaultLocalized = contentByLocale.find((item) => item.locale === localeDefault8);
8287
- const othersLocalized = contentByLocale.filter((item) => item.locale !== localeDefault8);
8288
- const nameTooltipContent = othersLocalized.reduce((acc, item) => {
8289
- const extraName = `${item.locale.toUpperCase()}: ${item.name}`;
8290
- const newLine2 = acc !== "" ? "\n" : "";
8291
- return `${acc}${newLine2}${extraName}`;
8292
- }, "");
8293
- return /* @__PURE__ */ jsxs(Group, { children: [
8294
- /* @__PURE__ */ jsx("span", { children: defaultLocalized.name }),
8295
- othersLocalized.length > 0 && /* @__PURE__ */ jsx(Tooltip, { label: nameTooltipContent, children: /* @__PURE__ */ jsxs(Avatar, { size: "sm", color: "pink", radius: "xl", children: [
8296
- "+",
8297
- othersLocalized.length
8298
- ] }) })
8299
- ] });
8300
- }
8301
- },
8302
- {
8303
- title: `Keywords ${localeDefault8.toUpperCase()}`,
8304
- accessor: "keywords",
8305
- render: ({ contentByLocale }) => {
8306
- const defaultLocalized = contentByLocale.find((item) => item.locale === localeDefault8);
8307
- const othersLocalized = contentByLocale.filter((item) => item.locale !== localeDefault8);
8308
- const keywords = (defaultLocalized.keywords ? defaultLocalized.keywords : []).map((k) => /* @__PURE__ */ jsx(Badge, { children: k }, k));
8309
- const keywordsTooltipContent = othersLocalized.reduce((acc, item) => {
8310
- const otherKeywords = item.keywords || [];
8311
- const extraKeywords = `${item.locale.toUpperCase()}: ${otherKeywords.length > 0 ? otherKeywords.join(",") : "None"}`;
8312
- const newLine2 = acc !== "" ? "\n" : "";
8313
- return `${acc}${newLine2}${extraKeywords}`;
8314
- }, "");
8315
- return /* @__PURE__ */ jsxs(Group, { children: [
8316
- /* @__PURE__ */ jsx("span", { children: keywords.length > 0 ? keywords : /* @__PURE__ */ jsx(Badge, { color: "gray", children: "None" }) }),
8317
- othersLocalized.length > 0 && /* @__PURE__ */ jsx(Tooltip, { label: keywordsTooltipContent, children: /* @__PURE__ */ jsxs(Avatar, { size: "sm", color: "pink", radius: "xl", children: [
8318
- "+",
8319
- othersLocalized.length
8320
- ] }) })
8321
- ] });
8322
- }
8323
- },
8324
- {
8325
- title: "Image",
8326
- accessor: "image",
8327
- render: (member) => /* @__PURE__ */ jsx(ImagePreview_default, { member, height: "50px" })
8328
- },
8329
- {
8330
- title: "",
8331
- accessor: "actions",
8332
- render: (member) => /* @__PURE__ */ jsx(ActionIcon, { onClick: () => onClickEdit(member), children: /* @__PURE__ */ jsx(IconPencil, { size: 20 }) }, "edit")
7961
+ doSearch();
7962
+ }, [sortStatus, filters, formattersFull]);
7963
+ const cols = [
7964
+ {
7965
+ title: "ID",
7966
+ accessor: "id",
7967
+ sortable: true,
7968
+ textAlignment: "right"
7969
+ },
7970
+ {
7971
+ title: "Name",
7972
+ accessor: "name",
7973
+ sortable: true,
7974
+ render: (member) => {
7975
+ const icon = member.content.type === FORMATTER_TYPES.FORMATTER ? /* @__PURE__ */ jsx(IconVariable, { size: "1.3em" }) : /* @__PURE__ */ jsx(IconMathFunction, { size: "1.3em" });
7976
+ return /* @__PURE__ */ jsxs(Group, { noWrap: true, align: "center", spacing: 2, children: [
7977
+ /* @__PURE__ */ jsx("span", { children: member.name }),
7978
+ /* @__PURE__ */ jsx(Tooltip, { label: FORMATTER_TYPES_NAMES[member.content.type], children: /* @__PURE__ */ jsx("span", { children: icon }) })
7979
+ ] });
7980
+ }
7981
+ },
7982
+ {
7983
+ title: "Description",
7984
+ accessor: "content.description"
7985
+ },
7986
+ {
7987
+ title: "Demo",
7988
+ accessor: "content.testValue",
7989
+ render: (member) => {
7990
+ let input = member.content.testValue;
7991
+ if (member.content.inputType === FORMATTER_INPUT_TYPES.STRING) {
7992
+ input = `"${input}"`;
7993
+ }
7994
+ let output = member.result;
7995
+ if (typeof member.result === "string") {
7996
+ output = `"${output}"`;
7997
+ }
7998
+ return /* @__PURE__ */ jsxs(Group, { spacing: 2, children: [
7999
+ /* @__PURE__ */ jsx(Tooltip, { label: `Input Type: ${FORMATTER_INPUT_TYPES_NAMES[member.content.inputType]}`, children: /* @__PURE__ */ jsx(Code, { color: "blue", children: input }) }),
8000
+ /* @__PURE__ */ jsx(IconArrowRightCircle, {}),
8001
+ /* @__PURE__ */ jsx(Code, { color: member.result && `${member.result}`.includes("ERROR") ? "red" : "teal", children: output })
8002
+ ] });
8003
+ }
8004
+ },
8005
+ {
8006
+ title: "Usage",
8007
+ accessor: "usageCount",
8008
+ textAlignment: "right",
8009
+ sortable: true
8010
+ },
8011
+ {
8012
+ title: "",
8013
+ accessor: "actions",
8014
+ render: (member) => /* @__PURE__ */ jsxs(Group, { position: "center", spacing: "xs", noWrap: true, children: [
8015
+ /* @__PURE__ */ jsx(ActionIcon, { onClick: () => onClickEdit(member), children: /* @__PURE__ */ jsx(IconPencil, { size: 20 }) }, "edit"),
8016
+ /* @__PURE__ */ jsx(
8017
+ EntityDeleteButton,
8018
+ {
8019
+ id: member.id,
8020
+ type: "formatter",
8021
+ disabled: member.usageCount > 0,
8022
+ warning: `Delete formatter called "${member.name}" (id:${member.id}) ? It is not used in any block.`
8023
+ }
8024
+ )
8025
+ ] })
8333
8026
  }
8334
8027
  ];
8335
8028
  return /* @__PURE__ */ jsx(
@@ -8341,472 +8034,815 @@ function MembersTable({ members, onClickEdit, onSort }) {
8341
8034
  columns: cols,
8342
8035
  sortStatus,
8343
8036
  onSortStatusChange: (s) => setSortStatus(s),
8344
- idAccessor: "content_id",
8037
+ idAccessor: "id",
8345
8038
  withBorder: true
8346
8039
  }
8347
8040
  );
8348
8041
  }
8349
- function MetadataEditor() {
8042
+ function FormatterEditor() {
8350
8043
  const [editingId, setEditingId] = useState(null);
8351
- const [results, setResults] = useState([]);
8352
- const [loading, setLoading] = useState(true);
8353
8044
  const [internalFilters, setInternalFilters] = useState(true);
8354
- const [internalSort, setInternalSort] = useState();
8355
8045
  const onChangeFilters = (filters) => {
8356
8046
  setInternalFilters(filters);
8357
8047
  };
8358
- const onChangeSort = (sort) => {
8359
- setInternalSort(sort);
8360
- };
8361
- const setEditMember = (member) => {
8362
- setEditingId(member.content_id);
8363
- };
8364
- const onEditClose = () => {
8365
- setEditingId(null);
8366
- doSearch();
8048
+ const onCreateFormatter = (id) => {
8049
+ setEditingId(id);
8367
8050
  };
8368
- const doSearch = async () => {
8369
- setLoading(true);
8370
- const getParams = Object.keys({ ...internalFilters }).reduce((acc, key) => {
8371
- if (internalFilters[key] !== "all")
8372
- acc[key] = internalFilters[key];
8373
- return acc;
8374
- }, {});
8375
- const entityRecords = await axios.get(
8376
- "/api/cms/search/members",
8377
- {
8378
- params: {
8379
- ...getParams,
8380
- locale: "all",
8381
- includes: true,
8382
- sort: internalSort ? internalSort.columnAccessor : "id",
8383
- direction: internalSort ? internalSort.direction : "asc"
8384
- }
8385
- }
8386
- ).then((response) => response.data && response.data.data && response.data.data.results ? response.data.data.results : []);
8387
- setResults(entityRecords);
8388
- setLoading(false);
8051
+ const setEditFormatter = (formatter) => {
8052
+ if (formatter) {
8053
+ setEditingId(formatter.id);
8054
+ }
8055
+ };
8056
+ const onEditClose = () => {
8057
+ setEditingId(null);
8389
8058
  };
8390
- useEffect(() => {
8391
- doSearch();
8392
- }, [internalFilters, internalSort]);
8393
8059
  return /* @__PURE__ */ jsxs(Container, { fluid: true, children: [
8394
8060
  /* @__PURE__ */ jsxs(Grid, { children: [
8395
8061
  /* @__PURE__ */ jsxs(Grid.Col, { span: 2, children: [
8396
- /* @__PURE__ */ jsx(Text, { ml: "xl", my: "sm", weight: "bold", children: "Metadata" }),
8397
- /* @__PURE__ */ jsx(FilterSidebar, { onChange: onChangeFilters, loading })
8062
+ /* @__PURE__ */ jsx(Text, { ml: "xl", my: "sm", weight: "bold", children: "Formatters" }),
8063
+ /* @__PURE__ */ jsx(FilterSidebar, { onChange: onChangeFilters, onEditCreation: onCreateFormatter, loading: false })
8398
8064
  ] }),
8399
- /* @__PURE__ */ jsxs(Grid.Col, { span: 10, style: { height: "100vh", position: "relative" }, children: [
8400
- /* @__PURE__ */ jsx(LoadingOverlay, { visible: loading, overlayBlur: 2, overlayOpacity: 50 }),
8065
+ /* @__PURE__ */ jsxs(Grid.Col, { span: 10, style: { height: "100vh" }, children: [
8401
8066
  /* @__PURE__ */ jsx(Space, { h: "xs" }),
8402
- /* @__PURE__ */ jsx(MembersTable, { members: results, onClickEdit: setEditMember, onSort: onChangeSort })
8067
+ /* @__PURE__ */ jsx(FormattersTable, { filters: internalFilters, onClickEdit: setEditFormatter })
8403
8068
  ] })
8404
8069
  ] }),
8405
- editingId && /* @__PURE__ */ jsx(MemberForm, { memberId: editingId, onEditEnd: onEditClose })
8070
+ editingId && /* @__PURE__ */ jsx(FormatterForm, { formatterId: editingId, onEditEnd: onEditClose })
8406
8071
  ] });
8407
8072
  }
8408
8073
 
8409
- // views/FormatterEditor.tsx
8074
+ // views/MetadataEditor.tsx
8410
8075
  init_esm_shims();
8411
8076
 
8412
- // components/formatters/FilterSidebar.tsx
8077
+ // components/metadata/FilterSidebar.tsx
8413
8078
  init_esm_shims();
8414
8079
  init_store2();
8415
- init_cms();
8416
8080
  var allOption2 = { value: "all", label: "All" };
8417
- var buildOptionsFromMap = (options2) => {
8418
- if (!options2)
8081
+ var buildOptions = (options) => {
8082
+ if (!options)
8419
8083
  return [];
8420
- const selectOptions = Object.keys(options2).map((k) => ({ value: String(k), label: String(options2[k]) }));
8084
+ const selectOptions = options.map((d) => ({ value: String(d.id), label: d.name }));
8421
8085
  return [allOption2, ...selectOptions];
8422
8086
  };
8423
- function FilterSidebar2({ onChange, loading, onEditCreation }) {
8087
+ function FilterSidebar2({ onChange, loading }) {
8424
8088
  const dispatch = useAppDispatch();
8089
+ const reportsNested = useAppSelector((state) => state.records.tree);
8090
+ const localeDefault8 = useAppSelector((state) => state.status.localeDefault);
8425
8091
  const [query, setQuery] = useState("");
8426
- const [loadingCreate, setLoadingCreate] = useState(false);
8092
+ const [loadingRegenerate, setLoadingRegenerate] = useState(false);
8427
8093
  const [debouncedQuery] = useDebouncedValue(query, 500);
8428
8094
  const initialFilterState = {
8429
- type: allOption2.value
8095
+ report: allOption2.value,
8096
+ dimension: allOption2.value,
8097
+ variant: allOption2.value,
8098
+ visible: false,
8099
+ noImage: false,
8100
+ limit: "10"
8101
+ };
8102
+ const [filters, setFilters] = useState(initialFilterState);
8103
+ const options = useMemo(() => ({
8104
+ report: buildOptions(reportsNested),
8105
+ dimension: buildOptions(reportsNested.filter((report) => filters.report === allOption2.value || filters.report === `${report.id}`).reduce((acc, report) => acc.concat(report.dimensions), [])),
8106
+ variant: buildOptions(reportsNested.filter((report) => filters.report === allOption2.value || filters.report === `${report.id}`).reduce((acc, report) => {
8107
+ report.dimensions.filter((dim) => filters.dimension === allOption2.value || filters.dimension === `${dim.id}`).forEach((dim) => {
8108
+ acc = acc.concat(dim.variants);
8109
+ });
8110
+ return acc;
8111
+ }, [])),
8112
+ limit: [10, 25, 50].map((i) => ({ value: String(i), label: String(i) }))
8113
+ }), [reportsNested, filters]);
8114
+ const onChangeFilter = useCallback((key, value) => {
8115
+ const newFilters = {
8116
+ ...filters,
8117
+ [key]: value
8118
+ };
8119
+ switch (key) {
8120
+ case "report":
8121
+ newFilters.dimension = allOption2.value;
8122
+ break;
8123
+ case "dimension":
8124
+ newFilters.variant = allOption2.value;
8125
+ break;
8126
+ }
8127
+ setFilters(newFilters);
8128
+ }, [filters]);
8129
+ const onClearFilters = () => {
8130
+ setQuery("");
8131
+ setFilters(initialFilterState);
8132
+ };
8133
+ const onRegenarateSearchIndex = () => {
8134
+ setLoadingRegenerate(true);
8135
+ dispatch(actions_exports.searchRegenerate()).then(() => {
8136
+ setLoadingRegenerate(false);
8137
+ onChange(filters);
8138
+ });
8139
+ };
8140
+ useEffect(() => {
8141
+ onChange({ ...filters, query: debouncedQuery });
8142
+ }, [
8143
+ filters.report,
8144
+ filters.dimension,
8145
+ filters.variant,
8146
+ filters.visible,
8147
+ filters.limit,
8148
+ filters.noImage,
8149
+ debouncedQuery
8150
+ ]);
8151
+ return /* @__PURE__ */ jsxs("div", { children: [
8152
+ /* @__PURE__ */ jsx(
8153
+ TextInput,
8154
+ {
8155
+ label: `Search item by name (${localeDefault8.toUpperCase()})`,
8156
+ value: query,
8157
+ onChange: (event) => setQuery(event.currentTarget.value),
8158
+ disabled: loading
8159
+ }
8160
+ ),
8161
+ /* @__PURE__ */ jsx(
8162
+ Select,
8163
+ {
8164
+ label: "Reports",
8165
+ data: options.report,
8166
+ value: filters.report,
8167
+ onChange: (value) => onChangeFilter("report", value),
8168
+ disabled: loading
8169
+ }
8170
+ ),
8171
+ /* @__PURE__ */ jsx(
8172
+ Select,
8173
+ {
8174
+ label: "Dimensions",
8175
+ data: options.dimension,
8176
+ value: filters.dimension,
8177
+ onChange: (value) => onChangeFilter("dimension", value),
8178
+ disabled: loading
8179
+ }
8180
+ ),
8181
+ /* @__PURE__ */ jsx(
8182
+ Select,
8183
+ {
8184
+ label: "Variants",
8185
+ data: options.variant,
8186
+ value: filters.variant,
8187
+ onChange: (value) => onChangeFilter("variant", value),
8188
+ disabled: loading
8189
+ }
8190
+ ),
8191
+ /* @__PURE__ */ jsx(
8192
+ Select,
8193
+ {
8194
+ label: "Rows limit",
8195
+ data: options.limit,
8196
+ value: filters.limit,
8197
+ onChange: (value) => onChangeFilter("limit", value),
8198
+ disabled: loading
8199
+ }
8200
+ ),
8201
+ /* @__PURE__ */ jsx(Space, { h: "md" }),
8202
+ /* @__PURE__ */ jsx(
8203
+ Checkbox,
8204
+ {
8205
+ label: "Show visible members only",
8206
+ checked: filters.visible,
8207
+ onChange: (event) => onChangeFilter("visible", event.currentTarget.checked)
8208
+ }
8209
+ ),
8210
+ /* @__PURE__ */ jsx(Space, { h: "md" }),
8211
+ /* @__PURE__ */ jsx(
8212
+ Checkbox,
8213
+ {
8214
+ label: "Show members with no image",
8215
+ checked: filters.noImage,
8216
+ onChange: (event) => onChangeFilter("noImage", event.currentTarget.checked)
8217
+ }
8218
+ ),
8219
+ /* @__PURE__ */ jsx(Space, { h: "md" }),
8220
+ /* @__PURE__ */ jsx(
8221
+ Button,
8222
+ {
8223
+ fullWidth: true,
8224
+ disabled: loading,
8225
+ leftIcon: /* @__PURE__ */ jsx(IconCircleX, {}),
8226
+ onClick: onClearFilters,
8227
+ children: "Clear filters"
8228
+ }
8229
+ ),
8230
+ /* @__PURE__ */ jsx(Space, { h: "md" }),
8231
+ /* @__PURE__ */ jsx(
8232
+ Button,
8233
+ {
8234
+ fullWidth: true,
8235
+ disabled: loading,
8236
+ leftIcon: /* @__PURE__ */ jsx(IconRefresh, {}),
8237
+ onClick: onRegenarateSearchIndex,
8238
+ variant: "light",
8239
+ loading: loadingRegenerate,
8240
+ children: "Re-Index Search"
8241
+ }
8242
+ )
8243
+ ] });
8244
+ }
8245
+
8246
+ // components/metadata/MemberForm.tsx
8247
+ init_esm_shims();
8248
+ init_store2();
8249
+
8250
+ // components/metadata/ImageSelector.tsx
8251
+ init_esm_shims();
8252
+ function ImageSelector({ member, onEditEnd }) {
8253
+ const initialPrompt = member.contentByLocale && member.contentByLocale[0] ? member.contentByLocale[0].name : "";
8254
+ const imageProviders = {
8255
+ local: {
8256
+ title: "Bespoke",
8257
+ key: "local",
8258
+ input: "text",
8259
+ forceSearch: true,
8260
+ getAxiosSearchConfig: (prompt2, image) => ({
8261
+ url: "/api/cms/images/search/local",
8262
+ method: "get",
8263
+ params: { prompt: prompt2 }
8264
+ })
8265
+ },
8266
+ flickr: {
8267
+ title: "Flickr",
8268
+ key: "flickr",
8269
+ input: "text",
8270
+ forceSearch: true,
8271
+ getAxiosSearchConfig: (prompt2, image) => ({
8272
+ url: "/api/cms/images/search/flickr",
8273
+ method: "get",
8274
+ params: { prompt: prompt2 }
8275
+ })
8276
+ },
8277
+ unsplash: {
8278
+ title: "Unsplash",
8279
+ key: "unsplash",
8280
+ input: "text",
8281
+ forceSearch: true,
8282
+ getAxiosSearchConfig: (prompt2, image) => ({
8283
+ url: "/api/cms/images/search/unsplash",
8284
+ method: "get",
8285
+ params: { prompt: prompt2 }
8286
+ })
8287
+ },
8288
+ upload: {
8289
+ title: "Upload",
8290
+ key: "upload",
8291
+ input: "file",
8292
+ forceSearch: false,
8293
+ getAxiosSearchConfig: (prompt2, image) => ({})
8294
+ }
8295
+ };
8296
+ const imageProvidersOptions = Object.keys(imageProviders).map((key) => ({ value: key, label: imageProviders[key].title }));
8297
+ const [loading, setLoading] = useState(false);
8298
+ const [selectedProvider, setSelectedProvider] = useState(Object.values(imageProviders)[0]);
8299
+ const [scrollSize, setScrollSize] = useState(500);
8300
+ const [prompt, setPrompt] = useState(initialPrompt);
8301
+ const [imageToUpload, setImageToUpload] = useState();
8302
+ const [results, setResults] = useState([]);
8303
+ const [selectedImage, setSelectedImage] = useState();
8304
+ const [error, setError] = useState(false);
8305
+ const onSearch = async () => {
8306
+ setError(false);
8307
+ setLoading(true);
8308
+ setResults([]);
8309
+ const providerAxiosConfig = selectedProvider.getAxiosSearchConfig(prompt, imageToUpload);
8310
+ const imageRecords = await axios(providerAxiosConfig).then((response) => response.data.data && response.data.data.results ? response.data.data.results : []).catch((e) => {
8311
+ console.error(e);
8312
+ setError(true);
8313
+ });
8314
+ setResults(imageRecords);
8315
+ setLoading(false);
8316
+ };
8317
+ const onSelect = (img) => {
8318
+ setSelectedImage(img);
8319
+ };
8320
+ const onSave = async () => {
8321
+ setLoading(true);
8322
+ const formData = new FormData();
8323
+ formData.append("content_id", member.content_id);
8324
+ if (selectedImage && imageToUpload) {
8325
+ formData.append("file", imageToUpload);
8326
+ delete selectedImage.file;
8327
+ }
8328
+ if (selectedImage) {
8329
+ formData.append("image_id", selectedImage.id);
8330
+ formData.append("image_source", selectedImage.source);
8331
+ }
8332
+ await axios({
8333
+ method: "post",
8334
+ url: `/api/cms/images/save/${selectedProvider.key}`,
8335
+ data: formData
8336
+ }).then((response) => {
8337
+ onEditEnd(true);
8338
+ }).catch((e) => {
8339
+ console.error(e);
8340
+ setError(true);
8341
+ });
8342
+ setLoading(false);
8430
8343
  };
8431
- const [filters, setFilters] = useState(initialFilterState);
8432
- const onClearFilters = () => {
8433
- setQuery("");
8434
- setFilters(initialFilterState);
8344
+ const onChangeProvider = (providerKey) => {
8345
+ setError(false);
8346
+ setSelectedProvider(imageProviders[providerKey]);
8347
+ setSelectedImage(void 0);
8435
8348
  };
8436
- const options2 = useMemo(() => ({
8437
- type: buildOptionsFromMap(FORMATTER_TYPES_NAMES)
8438
- }), []);
8439
8349
  useEffect(() => {
8440
- onChange({ ...filters, query: debouncedQuery });
8441
- }, [filters.type, debouncedQuery]);
8442
- const onAddNewFormatter = () => {
8443
- setLoadingCreate(true);
8444
- const newId = Math.random().toString(16).substring(2, 10);
8445
- dispatch(actions_exports.createEntity("formatter", {
8446
- name: `formatter_${newId}`,
8447
- content: {
8448
- type: "formatter",
8449
- logic: "return n;",
8450
- inputType: "string",
8451
- testValue: "123456",
8452
- description: "New formatter"
8453
- }
8454
- })).then((response) => {
8455
- setLoadingCreate(false);
8456
- onClearFilters();
8457
- if (response.ok) {
8458
- onEditCreation(response.data.id);
8459
- }
8460
- });
8461
- };
8462
- const onChangeFilter = useCallback((key, value) => {
8463
- const newFilters = {
8464
- ...filters,
8465
- [key]: value
8466
- };
8467
- setFilters(newFilters);
8468
- }, [filters]);
8469
- return /* @__PURE__ */ jsxs("div", { children: [
8470
- /* @__PURE__ */ jsx(
8471
- TextInput,
8472
- {
8473
- label: "Search by name or description",
8474
- value: query,
8475
- onChange: (event) => setQuery(event.currentTarget.value),
8476
- disabled: loading
8477
- }
8478
- ),
8479
- /* @__PURE__ */ jsx(
8480
- Select,
8481
- {
8482
- label: "Type",
8483
- data: options2.type,
8484
- value: filters.type,
8485
- onChange: (value) => onChangeFilter("type", value),
8486
- disabled: loading
8487
- }
8488
- ),
8489
- /* @__PURE__ */ jsx(Space, { h: "md" }),
8490
- /* @__PURE__ */ jsx(
8491
- Button,
8492
- {
8493
- fullWidth: true,
8494
- disabled: loading,
8495
- leftIcon: /* @__PURE__ */ jsx(IconCircleX, {}),
8496
- onClick: onClearFilters,
8497
- children: "Clear filters"
8498
- }
8499
- ),
8500
- /* @__PURE__ */ jsx(Space, { h: "md" }),
8501
- /* @__PURE__ */ jsx(
8502
- Button,
8503
- {
8504
- fullWidth: true,
8505
- disabled: loading,
8506
- leftIcon: /* @__PURE__ */ jsx(IconPlus, {}),
8507
- onClick: onAddNewFormatter,
8508
- variant: "light",
8509
- loading: loadingCreate,
8510
- children: "New formatter"
8511
- }
8512
- )
8513
- ] });
8350
+ const { innerHeight } = window;
8351
+ setScrollSize(innerHeight - 280);
8352
+ onSearch();
8353
+ }, []);
8354
+ useEffect(() => {
8355
+ setResults([]);
8356
+ if (!loading && selectedProvider.forceSearch) {
8357
+ onSearch();
8358
+ }
8359
+ }, [selectedProvider]);
8360
+ const noResults = results && results.length === 0 && !loading && selectedProvider.key !== "upload";
8361
+ return /* @__PURE__ */ jsx(
8362
+ Drawer,
8363
+ {
8364
+ opened: true,
8365
+ onClose: onEditEnd,
8366
+ title: `Edit image for ${initialPrompt} (${member.id}) `,
8367
+ padding: "lg",
8368
+ closeOnClickOutside: false,
8369
+ closeOnEscape: false,
8370
+ position: "left",
8371
+ size: "50%",
8372
+ lockScroll: true,
8373
+ withCloseButton: false,
8374
+ children: /* @__PURE__ */ jsx("div", { style: { height: "100%" }, children: /* @__PURE__ */ jsxs(Stack, { children: [
8375
+ /* @__PURE__ */ jsxs(Stack, { spacing: "xs", children: [
8376
+ /* @__PURE__ */ jsxs(Group, { position: "apart", grow: true, align: "flex-end", children: [
8377
+ selectedProvider.input === "text" && /* @__PURE__ */ jsx(
8378
+ TextInput,
8379
+ {
8380
+ placeholder: "Search term",
8381
+ label: "Search term",
8382
+ value: prompt,
8383
+ onChange: (e) => setPrompt(e.target.value)
8384
+ }
8385
+ ),
8386
+ selectedProvider.input === "file" && /* @__PURE__ */ jsx(
8387
+ FileInput,
8388
+ {
8389
+ label: "Upload image",
8390
+ placeholder: "Choose a PNG image from your computer",
8391
+ accept: "image/png",
8392
+ value: imageToUpload,
8393
+ onChange: (image) => {
8394
+ setSelectedImage(void 0);
8395
+ if (image) {
8396
+ const objectUrl = URL.createObjectURL(image);
8397
+ setResults([{
8398
+ id: "custom",
8399
+ source: objectUrl,
8400
+ file: image
8401
+ }]);
8402
+ setImageToUpload(image);
8403
+ setSelectedImage("custom");
8404
+ }
8405
+ },
8406
+ icon: /* @__PURE__ */ jsx(IconUpload, { size: 14 })
8407
+ }
8408
+ ),
8409
+ selectedProvider.forceSearch && /* @__PURE__ */ jsx(
8410
+ Button,
8411
+ {
8412
+ fullWidth: true,
8413
+ disabled: loading || !prompt || prompt === "",
8414
+ onClick: onSearch,
8415
+ leftIcon: /* @__PURE__ */ jsx(IconPlayerPlay, {}),
8416
+ children: "Search"
8417
+ }
8418
+ )
8419
+ ] }),
8420
+ /* @__PURE__ */ jsx(
8421
+ SegmentedControl,
8422
+ {
8423
+ disabled: loading,
8424
+ value: selectedProvider.key,
8425
+ onChange: (providerKey) => onChangeProvider(providerKey),
8426
+ data: imageProvidersOptions,
8427
+ fullWidth: true
8428
+ }
8429
+ )
8430
+ ] }),
8431
+ /* @__PURE__ */ jsx(Group, { mt: "xs", grow: true, children: /* @__PURE__ */ jsxs(ScrollArea, { offsetScrollbars: true, type: "always", style: { height: scrollSize }, children: [
8432
+ /* @__PURE__ */ jsx(LoadingOverlay, { visible: loading, overlayBlur: 2, overlayOpacity: 50 }),
8433
+ error && /* @__PURE__ */ jsx(Alert, { title: `${selectedProvider.title}`, color: "red", children: "Error, please try again." }),
8434
+ noResults && /* @__PURE__ */ jsx(Alert, { title: `${selectedProvider.title}`, color: "blue", children: "No results, for try another prompt or provider." }),
8435
+ results && results.length > 0 && !loading && /* @__PURE__ */ jsx(SimpleGrid, { cols: 3, children: results.map((img) => /* @__PURE__ */ jsx(
8436
+ Image,
8437
+ {
8438
+ radius: "md",
8439
+ src: img.source,
8440
+ style: {
8441
+ cursor: "pointer",
8442
+ borderRadius: 15,
8443
+ border: selectedImage && selectedImage.id === img.id ? "5px solid black" : ""
8444
+ },
8445
+ onClick: () => onSelect(img)
8446
+ },
8447
+ img.id
8448
+ )) })
8449
+ ] }) }),
8450
+ /* @__PURE__ */ jsxs(Group, { mt: "xs", grow: true, style: { height: 45 }, children: [
8451
+ /* @__PURE__ */ jsx(
8452
+ Button,
8453
+ {
8454
+ variant: "outline",
8455
+ disabled: loading,
8456
+ onClick: () => onEditEnd(false),
8457
+ children: "Cancel"
8458
+ }
8459
+ ),
8460
+ /* @__PURE__ */ jsx(
8461
+ Button,
8462
+ {
8463
+ onClick: onSave,
8464
+ loading,
8465
+ disabled: loading || !selectedImage,
8466
+ children: "Save"
8467
+ }
8468
+ )
8469
+ ] })
8470
+ ] }) })
8471
+ }
8472
+ );
8514
8473
  }
8474
+ var ImageSelector_default = ImageSelector;
8515
8475
 
8516
- // components/formatters/FormatterForm.tsx
8476
+ // views/ImagePreview.tsx
8517
8477
  init_esm_shims();
8518
- init_store2();
8519
- init_cms();
8520
- init_FUNC();
8521
- init_hooks();
8522
- var buildOptionsFromMap2 = (options2) => {
8523
- if (!options2)
8524
- return [];
8525
- return Object.keys(options2).map((k) => ({ value: String(k), label: String(options2[k]) }));
8526
- };
8527
- var getCastedValue = (type, value) => {
8528
- switch (type) {
8529
- case FORMATTER_INPUT_TYPES.OBJECT:
8530
- return JSON.parse(value);
8531
- default:
8532
- return `${value}`;
8478
+ function ImagePreview2({
8479
+ member,
8480
+ height = "auto",
8481
+ width = "100%",
8482
+ size = "thumb",
8483
+ forceRefreshTimestamp = false
8484
+ }) {
8485
+ let extraParam = "";
8486
+ if (forceRefreshTimestamp) {
8487
+ extraParam = `&t=${new Date().getTime()}`;
8533
8488
  }
8534
- };
8535
- function FormatterForm({ formatterId, onEditEnd }) {
8489
+ const imageUrl = member && member.image_id ? `/api/cms/member/image.png?member=${member.content_id}&size=${size}${extraParam}` : "https://placehold.jp/cccccc/ffffff/900x600.png?text=None";
8490
+ return /* @__PURE__ */ jsx(Image, { height, width, radius: "md", src: imageUrl });
8491
+ }
8492
+ var ImagePreview_default = ImagePreview2;
8493
+ function MemberForm({ memberId, onEditEnd }) {
8536
8494
  const localeDefault8 = useAppSelector((state) => state.status.localeDefault);
8537
8495
  const locales3 = useAppSelector((state) => state.status.locales);
8538
- locales3.map((locale) => ({ value: locale, label: `${locale.toUpperCase()}` }));
8539
- const opened = !!formatterId;
8540
- const [formatter, setFormatter] = useState();
8541
- const [testResults, setTestResults] = useState("");
8542
- const [formatterNames, setFormatterNames] = useState([]);
8543
- const [initialLoading, setInitialLoading] = useState(true);
8544
- const [saveLoading, setSaveLoading] = useState(false);
8545
- const [isDirty, setIsDirty] = useState(true);
8546
- const [isValid, setIsValid] = useState(true);
8496
+ const localesItems = locales3.map((locale) => ({ value: locale, label: `${locale.toUpperCase()}` }));
8497
+ const opened = !!memberId;
8498
+ const [selectedLocale, setSelectedLocale] = useState(localeDefault8);
8499
+ const [member, setMember] = useState(null);
8500
+ const [loading, setLoading] = useState(true);
8501
+ const [showImageSelector, setShowImageSelector] = useState(false);
8547
8502
  const [error, setError] = useState(false);
8503
+ const mainForm = useForm();
8504
+ const localizedForms = locales3.reduce((acc, item) => {
8505
+ acc[item] = useForm({});
8506
+ return acc;
8507
+ }, {});
8508
+ const imageForm = useForm();
8509
+ const localizedImageForms = locales3.reduce((acc, item) => {
8510
+ acc[item] = useForm({});
8511
+ return acc;
8512
+ }, {});
8548
8513
  const dispatch = useAppDispatch();
8549
- const ref = useFormatterRef(formatterId);
8550
- const formattersList = useFormatterList();
8551
- const usageCount = useFormatterUsageCount(ref.data?.name);
8552
- const isLoading = initialLoading || saveLoading;
8553
- useEffect(() => {
8554
- if (!ref.isLoading) {
8555
- setFormatter(ref.data);
8556
- }
8557
- }, [ref.isLoading]);
8558
- useEffect(() => {
8559
- if (!ref.isLoading && !formattersList.isLoading) {
8560
- const formatterItem = !ref.isSuccess ? void 0 : ref.data;
8561
- const formatters = !formattersList.isSuccess ? [] : formattersList.data;
8562
- if (formatterItem) {
8563
- const formatterNamesList = formatters.filter((f) => f.id !== formatterItem.id).map((f) => f.name);
8564
- setFormatterNames(formatterNamesList);
8514
+ const fetchMemberData = async () => {
8515
+ setLoading(true);
8516
+ setError(false);
8517
+ const memberRecord = await dispatch(actions_exports.readMember(
8518
+ {
8519
+ locale: "all",
8520
+ content_ids: memberId,
8521
+ all: false,
8522
+ mode: "content_ids"
8565
8523
  }
8566
- onTest();
8567
- setInitialLoading(false);
8568
- }
8569
- }, [ref.isLoading, formattersList.isLoading]);
8570
- const isValidName = formatter && !formatterNames.includes(formatter.name) && formatter.name !== "";
8571
- const onChangeName = (value) => {
8572
- if (isValidName) {
8573
- setIsValid(false);
8574
- }
8575
- if (formatter) {
8576
- setFormatter({
8577
- ...formatter,
8578
- name: slugifyInput_default(value).replace("-", "")
8524
+ )).then((response) => {
8525
+ let obj;
8526
+ if (response.results && response.results[0]) {
8527
+ setError(false);
8528
+ obj = response.results[0];
8529
+ } else {
8530
+ setError(true);
8531
+ }
8532
+ return obj;
8533
+ }).catch((e) => {
8534
+ console.error(e);
8535
+ setError(true);
8536
+ });
8537
+ if (memberRecord) {
8538
+ setMember(memberRecord);
8539
+ mainForm.setValues(memberRecord);
8540
+ memberRecord.contentByLocale.forEach((item) => {
8541
+ localizedForms[item.locale].setValues({
8542
+ name: item.name,
8543
+ keywords: item.keywords ? item.keywords : [],
8544
+ attributes: item.attributes ? Object.keys(item.attributes).map((ak) => ({ key: ak, value: item.attributes[ak] })) : []
8545
+ });
8579
8546
  });
8547
+ if (memberRecord.image) {
8548
+ imageForm.setValues(memberRecord.image);
8549
+ memberRecord.image.contentByLocale.forEach((item) => {
8550
+ localizedImageForms[item.locale].setValues({
8551
+ meta: item.meta
8552
+ });
8553
+ });
8554
+ }
8580
8555
  }
8556
+ setLoading(false);
8581
8557
  };
8582
- const onContentValueChange = (key, value) => {
8583
- if (key !== "description") {
8584
- setIsDirty(true);
8558
+ useEffect(() => {
8559
+ if (memberId) {
8560
+ fetchMemberData();
8561
+ } else {
8562
+ setMember(null);
8585
8563
  }
8586
- if (formatter) {
8587
- setFormatter({
8588
- ...formatter,
8589
- content: {
8590
- ...formatter.content,
8591
- [key]: value
8592
- }
8593
- });
8564
+ }, [memberId]);
8565
+ const onImageSelectorClose = async (edited) => {
8566
+ if (edited) {
8567
+ fetchMemberData();
8594
8568
  }
8569
+ setShowImageSelector(false);
8595
8570
  };
8596
- const onTest = () => {
8597
- if (formatter) {
8598
- let results = "";
8599
- try {
8600
- const newFunction = parse({ vars: ["n"], logic: formatter.content.logic }, {}, localeDefault8, {});
8601
- const castedValue = getCastedValue(formatter.content.inputType, formatter.content.testValue);
8602
- results = newFunction(castedValue);
8603
- setIsValid(true);
8604
- } catch (e) {
8605
- results = `ERROR: ${e.message}`;
8606
- console.log(e);
8607
- setIsValid(false);
8608
- }
8609
- setIsDirty(false);
8610
- setTestResults(`${results}`);
8611
- }
8571
+ const clearImage = () => {
8572
+ setMember({
8573
+ ...member,
8574
+ image: null,
8575
+ image_id: null
8576
+ });
8612
8577
  };
8613
- const onSave = () => {
8614
- setSaveLoading(true);
8615
- if (isValid && isValidName) {
8616
- dispatch(actions_exports.updateEntity("formatter", formatter)).then(onEditEnd);
8617
- }
8578
+ const onSave = async () => {
8579
+ setLoading(true);
8580
+ const payload = {
8581
+ ...mainForm.values,
8582
+ contentByLocale: member.contentByLocale.map((oldValues) => {
8583
+ const localizedItem = localizedForms[oldValues.locale] ? { ...localizedForms[oldValues.locale].values } : {};
8584
+ localizedItem.keywords = localizedItem.keywords && localizedItem.keywords.length > 0 ? localizedItem.keywords : null;
8585
+ return {
8586
+ ...oldValues,
8587
+ ...localizedItem
8588
+ };
8589
+ }),
8590
+ image: member.image ? {
8591
+ ...imageForm.values,
8592
+ contentByLocale: locales3.map((locale) => {
8593
+ const oldValues = member.image.contentByLocale.find((e) => e.locale === locale);
8594
+ const localizedItem = localizedImageForms[oldValues.locale] ? localizedImageForms[oldValues.locale].values : {};
8595
+ return {
8596
+ ...oldValues,
8597
+ ...localizedItem
8598
+ };
8599
+ })
8600
+ } : null,
8601
+ image_id: member.image_id
8602
+ };
8603
+ await axios.post("/api/cms/update/member", payload).then((resp) => {
8604
+ onEditEnd();
8605
+ return resp.data.data;
8606
+ });
8618
8607
  };
8619
- return /* @__PURE__ */ jsx(
8620
- Drawer,
8621
- {
8622
- opened,
8623
- onClose: () => onEditEnd(),
8624
- title: `Edit formatter ID ${formatterId}`,
8625
- padding: "lg",
8626
- closeOnClickOutside: false,
8627
- closeOnEscape: false,
8628
- position: "right",
8629
- size: "50%",
8630
- lockScroll: true,
8631
- withCloseButton: false,
8632
- children: /* @__PURE__ */ jsx(
8633
- DrawerContentWithScroll,
8634
- {
8635
- content: /* @__PURE__ */ jsxs(Fragment, { children: [
8636
- /* @__PURE__ */ jsx(LoadingOverlay, { visible: initialLoading, overlayBlur: 2, overlayOpacity: 50 }),
8637
- error && /* @__PURE__ */ jsx(Alert, { title: "Formatter Form", color: "red", children: "Error, please try again." }),
8638
- formatter && !error && /* @__PURE__ */ jsxs(Stack, { justify: "space-between", children: [
8639
- /* @__PURE__ */ jsxs(Group, { grow: true, align: "flex-start", children: [
8640
- /* @__PURE__ */ jsx(
8641
- TextInput,
8608
+ const isValidLocalizedForm = Object.keys(localizedForms[selectedLocale].values).length > 0;
8609
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
8610
+ /* @__PURE__ */ jsx(
8611
+ Drawer,
8612
+ {
8613
+ opened,
8614
+ onClose: () => onEditEnd(),
8615
+ title: `Edit member ID ${memberId}`,
8616
+ padding: "lg",
8617
+ closeOnClickOutside: false,
8618
+ closeOnEscape: false,
8619
+ position: "right",
8620
+ size: "50%",
8621
+ lockScroll: true,
8622
+ withCloseButton: false,
8623
+ children: /* @__PURE__ */ jsx(
8624
+ DrawerContentWithScroll,
8625
+ {
8626
+ content: /* @__PURE__ */ jsxs(Fragment, { children: [
8627
+ /* @__PURE__ */ jsx(LoadingOverlay, { visible: loading, overlayBlur: 2, overlayOpacity: 50 }),
8628
+ error && /* @__PURE__ */ jsx(Alert, { title: "Member Form", color: "red", children: "Error, please try again." }),
8629
+ member && !error && /* @__PURE__ */ jsxs(Stack, { justify: "space-between", children: [
8630
+ /* @__PURE__ */ jsxs(Group, { position: "apart", grow: true, children: [
8631
+ /* @__PURE__ */ jsxs(Stack, { children: [
8632
+ /* @__PURE__ */ jsx(ImagePreview_default, { member, forceRefreshTimestamp: true }),
8633
+ /* @__PURE__ */ jsx(
8634
+ Button,
8635
+ {
8636
+ variant: "outline",
8637
+ disabled: loading,
8638
+ leftIcon: /* @__PURE__ */ jsx(IconPolaroid, {}),
8639
+ onClick: () => setShowImageSelector(true),
8640
+ children: "Change image"
8641
+ }
8642
+ ),
8643
+ member && member.image && /* @__PURE__ */ jsx(
8644
+ Button,
8645
+ {
8646
+ variant: "outline",
8647
+ disabled: loading,
8648
+ leftIcon: /* @__PURE__ */ jsx(IconCircleMinus, {}),
8649
+ onClick: () => clearImage(),
8650
+ children: "Remove image"
8651
+ }
8652
+ ),
8653
+ /* @__PURE__ */ jsx(Space, { h: "md" })
8654
+ ] }),
8655
+ /* @__PURE__ */ jsxs(Stack, { justify: "flex-start", children: [
8656
+ /* @__PURE__ */ jsx(
8657
+ TextInput,
8658
+ {
8659
+ disabled: !member || !member.image,
8660
+ label: "Source",
8661
+ ...imageForm.getInputProps("url")
8662
+ }
8663
+ ),
8664
+ /* @__PURE__ */ jsx(
8665
+ TextInput,
8666
+ {
8667
+ disabled: !member || !member.image,
8668
+ label: "Author",
8669
+ ...imageForm.getInputProps("author")
8670
+ }
8671
+ ),
8672
+ /* @__PURE__ */ jsx(
8673
+ TextInput,
8674
+ {
8675
+ disabled: !member || !member.image,
8676
+ label: "License",
8677
+ ...imageForm.getInputProps("license")
8678
+ }
8679
+ ),
8680
+ /* @__PURE__ */ jsx(
8681
+ SegmentedControl,
8682
+ {
8683
+ disabled: !member || !member.image,
8684
+ value: selectedLocale,
8685
+ onChange: setSelectedLocale,
8686
+ data: localesItems
8687
+ }
8688
+ ),
8689
+ /* @__PURE__ */ jsx(
8690
+ TextInput,
8691
+ {
8692
+ disabled: !member || !member.image,
8693
+ label: "Alternative image text",
8694
+ ...localizedImageForms[selectedLocale].getInputProps("meta")
8695
+ }
8696
+ )
8697
+ ] })
8698
+ ] }),
8699
+ /* @__PURE__ */ jsxs(Code, { block: true, children: [
8700
+ "TO-DO apply same image also to some of these members? List of checkboxes",
8701
+ /* @__PURE__ */ jsx("br", {}),
8702
+ "(x) another member, similar name",
8703
+ /* @__PURE__ */ jsx("br", {}),
8704
+ "(x) another member, similar name",
8705
+ /* @__PURE__ */ jsx("br", {})
8706
+ ] }),
8707
+ /* @__PURE__ */ jsx(Divider, { my: "sm" }),
8708
+ /* @__PURE__ */ jsxs(Group, { position: "apart", grow: true, children: [
8709
+ /* @__PURE__ */ jsx(TextInput, { mt: "xs", disabled: true, label: "Original ID", ...mainForm.getInputProps("id") }),
8710
+ /* @__PURE__ */ jsx(TextInput, { mt: "xs", disabled: true, label: "Content ID", ...mainForm.getInputProps("content_id") })
8711
+ ] }),
8712
+ /* @__PURE__ */ jsxs(Group, { position: "apart", grow: true, children: [
8713
+ /* @__PURE__ */ jsx(
8714
+ TextInput,
8715
+ {
8716
+ mt: "xs",
8717
+ icon: "/",
8718
+ disabled: true,
8719
+ label: "Variant Slug",
8720
+ ...mainForm.getInputProps("variant_slug")
8721
+ }
8722
+ ),
8723
+ /* @__PURE__ */ jsx(TextInput, { mt: "xs", icon: "/", disabled: true, label: "Member Slug", ...mainForm.getInputProps("slug") })
8724
+ ] }),
8725
+ /* @__PURE__ */ jsxs(Group, { position: "apart", grow: true, children: [
8726
+ /* @__PURE__ */ jsx(TextInput, { mt: "xs", disabled: true, label: "Report", ...mainForm.getInputProps("report_name") }),
8727
+ /* @__PURE__ */ jsx(TextInput, { mt: "xs", disabled: true, label: "Dimension", ...mainForm.getInputProps("dimension_name") }),
8728
+ /* @__PURE__ */ jsx(TextInput, { mt: "xs", disabled: true, label: "Variant", ...mainForm.getInputProps("variant_name") })
8729
+ ] }),
8730
+ /* @__PURE__ */ jsx(Group, { position: "apart", grow: true, children: /* @__PURE__ */ jsx(
8731
+ Checkbox,
8642
8732
  {
8643
- label: `Name ${isValidName && usageCount > 0 ? `(Read only, used in ${usageCount} blocks)` : ""}`,
8644
- value: formatter.name,
8645
- icon: isValidName ? /* @__PURE__ */ jsx(IconCircleCheck, {}) : /* @__PURE__ */ jsx(IconAlertCircle, {}),
8646
- readOnly: isValidName && usageCount > 0,
8647
- disabled: isValidName && usageCount > 0,
8648
- onChange: (evt) => onChangeName(evt.target.value),
8649
- error: isValidName ? false : "Duplicate or empty name, choose a different one."
8733
+ mt: "xs",
8734
+ label: "Visible",
8735
+ ...mainForm.getInputProps("visible", { type: "checkbox" })
8650
8736
  }
8651
- ),
8652
- /* @__PURE__ */ jsx(
8653
- Select,
8737
+ ) }),
8738
+ /* @__PURE__ */ jsx(Group, { mt: "xs", position: "apart", grow: true, children: /* @__PURE__ */ jsx(
8739
+ SegmentedControl,
8654
8740
  {
8655
- label: "Type",
8656
- data: buildOptionsFromMap2(FORMATTER_TYPES_NAMES),
8657
- value: formatter.content.type,
8658
- onChange: (value) => onContentValueChange("type", value)
8741
+ value: selectedLocale,
8742
+ onChange: setSelectedLocale,
8743
+ data: localesItems
8659
8744
  }
8660
- )
8661
- ] }),
8745
+ ) }),
8746
+ !isValidLocalizedForm && /* @__PURE__ */ jsx(Alert, { title: "Unavailable locale", color: "blue", children: "This language was not ingested for the current member. Go to variant editor and provide the members there." }),
8747
+ isValidLocalizedForm && /* @__PURE__ */ jsxs(Fragment, { children: [
8748
+ /* @__PURE__ */ jsx(Group, { mt: "xs", position: "apart", grow: true, children: /* @__PURE__ */ jsx(TextInput, { label: "Name", ...localizedForms[selectedLocale].getInputProps("name") }) }),
8749
+ /* @__PURE__ */ jsx(Group, { mt: "xs", position: "apart", align: "top", grow: true, children: /* @__PURE__ */ jsx(
8750
+ MultiSelect,
8751
+ {
8752
+ label: "Keywords",
8753
+ data: localizedForms[selectedLocale].values.keywords,
8754
+ placeholder: "Enter keywords",
8755
+ searchable: true,
8756
+ creatable: true,
8757
+ valueComponent: ({
8758
+ value,
8759
+ label,
8760
+ image,
8761
+ name
8762
+ }) => /* @__PURE__ */ jsxs(
8763
+ Badge,
8764
+ {
8765
+ onClick: () => {
8766
+ const current = localizedForms[selectedLocale].values.keywords;
8767
+ localizedForms[selectedLocale].setFieldValue(
8768
+ "keywords",
8769
+ current.filter((k) => k !== label)
8770
+ );
8771
+ },
8772
+ children: [
8773
+ label,
8774
+ " ",
8775
+ "x"
8776
+ ]
8777
+ }
8778
+ ),
8779
+ getCreateLabel: (query) => `+ Create ${query}`,
8780
+ value: localizedForms[selectedLocale].values.keywords,
8781
+ onCreate: (query) => {
8782
+ const current = localizedForms[selectedLocale].values.keywords;
8783
+ localizedForms[selectedLocale].setFieldValue("keywords", [...current, query]);
8784
+ return query;
8785
+ }
8786
+ }
8787
+ ) }),
8788
+ /* @__PURE__ */ jsx(Input.Wrapper, { label: "Attributes", children: /* @__PURE__ */ jsx(SortableTable, { data: localizedForms[selectedLocale].values.attributes }) })
8789
+ ] })
8790
+ ] })
8791
+ ] }),
8792
+ buttons: /* @__PURE__ */ jsxs(Fragment, { children: [
8662
8793
  /* @__PURE__ */ jsx(
8663
- Textarea,
8794
+ Button,
8664
8795
  {
8665
- label: "Description",
8666
- value: formatter.content.description,
8667
- onChange: (evt) => onContentValueChange("description", evt.target.value)
8796
+ variant: "outline",
8797
+ disabled: loading,
8798
+ onClick: () => onEditEnd(),
8799
+ children: "Cancel"
8668
8800
  }
8669
8801
  ),
8670
8802
  /* @__PURE__ */ jsx(
8671
- MonacoWrapper_default,
8672
- {
8673
- monacoOptions: {
8674
- onChange: (newLogic) => onContentValueChange("logic", newLogic),
8675
- value: formatter.content.logic
8676
- }
8677
- },
8678
- "formatter-code-editor"
8679
- ),
8680
- /* @__PURE__ */ jsxs(Group, { grow: true, align: "flex-end", children: [
8681
- /* @__PURE__ */ jsx(
8682
- Select,
8683
- {
8684
- label: "Input Type",
8685
- data: buildOptionsFromMap2(FORMATTER_INPUT_TYPES_NAMES),
8686
- value: formatter.content.inputType,
8687
- onChange: (value) => onContentValueChange("inputType", value)
8688
- }
8689
- ),
8690
- /* @__PURE__ */ jsx(
8691
- TextInput,
8692
- {
8693
- label: "Test Value",
8694
- value: formatter.content.testValue,
8695
- onChange: (evt) => onContentValueChange("testValue", evt.target.value),
8696
- error: formatter.content.testValue === "" && !isValid
8697
- }
8698
- )
8699
- ] }),
8700
- /* @__PURE__ */ jsx(
8701
- Textarea,
8702
- {
8703
- readOnly: true,
8704
- icon: isValid ? /* @__PURE__ */ jsx(IconCircleCheck, {}) : /* @__PURE__ */ jsx(IconAlertCircle, {}),
8705
- label: "Test Results",
8706
- value: testResults,
8707
- error: testResults.includes("ERROR") && !isValid ? "There is a problem in the function, input type or test value. Please check and run again." : false
8708
- }
8709
- )
8710
- ] })
8711
- ] }),
8712
- buttons: /* @__PURE__ */ jsxs(Fragment, { children: [
8713
- /* @__PURE__ */ jsx(
8714
- Button,
8715
- {
8716
- variant: "outline",
8717
- disabled: isLoading,
8718
- onClick: () => onEditEnd(),
8719
- children: "Cancel"
8720
- }
8721
- ),
8722
- /* @__PURE__ */ jsx(
8723
- Button,
8724
- {
8725
- onClick: onTest,
8726
- disabled: !isDirty,
8727
- color: isDirty ? "blue" : isValid ? "green" : "red",
8728
- leftIcon: /* @__PURE__ */ jsx(IconPlayerPlay, {}),
8729
- children: "Run Test"
8730
- }
8731
- ),
8732
- /* @__PURE__ */ jsx(
8733
- Button,
8734
- {
8735
- onClick: onSave,
8736
- loading: isLoading,
8737
- disabled: isLoading || !isValid || isDirty || !isValidName,
8738
- children: "Save"
8739
- }
8740
- )
8741
- ] }),
8742
- size: 145
8743
- }
8744
- )
8745
- }
8746
- );
8747
- }
8748
-
8749
- // components/formatters/FormattersTable.tsx
8750
- init_esm_shims();
8751
- init_store2();
8752
- init_hooks();
8753
- init_cms();
8754
- var getCastedValue2 = (type, value) => {
8755
- switch (type) {
8756
- case FORMATTER_INPUT_TYPES.OBJECT:
8757
- return JSON.parse(value);
8758
- default:
8759
- return `${value}`;
8760
- }
8761
- };
8762
- function FormattersTable({ filters, onClickEdit }) {
8763
- const [sortStatus, setSortStatus] = useState();
8764
- const [records, setRecords] = useState([]);
8765
- const formattersList = useFormatterList();
8766
- const formatters = !formattersList.isSuccess ? [] : formattersList.data;
8767
- const formatterFunctions = useFormatterFunctionsForLocale();
8768
- const formattersUsageCount = useFormatterUsageCountList();
8769
- const formattersFull = useMemo(() => formatters.map((f) => {
8770
- const usageCount = formattersUsageCount[f.name] ? formattersUsageCount[f.name] : 0;
8771
- let result = "";
8772
- try {
8773
- const castedValue = getCastedValue2(f.content.inputType, f.content.testValue);
8774
- result = formatterFunctions[f.name](castedValue);
8775
- } catch (e) {
8776
- result = `ERROR: ${e.message}`;
8777
- console.log(`Error in ${f.name}`);
8778
- console.log(e);
8779
- }
8780
- return {
8781
- ...f,
8782
- result,
8783
- usageCount
8784
- };
8785
- }), [formatters]);
8786
- const doSearch = () => {
8787
- let filteredResults = formattersFull.filter(
8788
- (formatter) => {
8789
- let findQuery = true;
8790
- let findType = true;
8791
- if (filters.query) {
8792
- findQuery = formatter.name.toLowerCase().indexOf(filters.query.toLowerCase()) > -1 || formatter.content.description.indexOf(filters.query) > -1;
8793
- }
8794
- if (filters.type !== "all") {
8795
- findType = formatter.content.type === filters.type;
8796
- }
8797
- return findQuery && findType;
8803
+ Button,
8804
+ {
8805
+ onClick: onSave,
8806
+ loading,
8807
+ disabled: loading,
8808
+ children: "Save Member"
8809
+ }
8810
+ )
8811
+ ] }),
8812
+ size: 145
8813
+ }
8814
+ )
8798
8815
  }
8799
- );
8816
+ ),
8817
+ showImageSelector && /* @__PURE__ */ jsx(ImageSelector_default, { member, onEditEnd: onImageSelectorClose })
8818
+ ] });
8819
+ }
8820
+
8821
+ // components/metadata/MembersTable.tsx
8822
+ init_esm_shims();
8823
+ init_store2();
8824
+ function MembersTable({ members, onClickEdit, onSort }) {
8825
+ const [sortStatus, setSortStatus] = useState();
8826
+ const [records, setRecords] = useState([]);
8827
+ useEffect(() => {
8800
8828
  if (sortStatus) {
8801
- const sortIndex = sortStatus.direction === "asc" ? -1 : 1;
8802
- filteredResults = filteredResults.sort((a, b) => a[sortStatus.columnAccessor] > b[sortStatus.columnAccessor] ? 1 * sortIndex : -1 * sortIndex);
8829
+ onSort(sortStatus);
8803
8830
  }
8804
- setRecords(filteredResults);
8805
- };
8831
+ }, [sortStatus]);
8806
8832
  useEffect(() => {
8807
- doSearch();
8808
- }, [sortStatus, filters, formattersFull]);
8833
+ setRecords(members);
8834
+ }, [members]);
8835
+ useEffect(() => {
8836
+ setRecords(members);
8837
+ }, []);
8838
+ const localeDefault8 = useAppSelector((state) => state.status.localeDefault);
8809
8839
  const cols = [
8840
+ {
8841
+ title: "",
8842
+ accessor: "visible",
8843
+ width: "10px",
8844
+ render: ({ visible }) => /* @__PURE__ */ jsx(Fragment, { children: !visible ? /* @__PURE__ */ jsx(Tooltip, { label: "Visible: false", children: /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(IconEyeOff, { size: 20 }) }) }) : /* @__PURE__ */ jsx("span", {}) })
8845
+ },
8810
8846
  {
8811
8847
  title: "ID",
8812
8848
  accessor: "id",
@@ -8814,61 +8850,79 @@ function FormattersTable({ filters, onClickEdit }) {
8814
8850
  textAlignment: "right"
8815
8851
  },
8816
8852
  {
8817
- title: "Name",
8818
- accessor: "name",
8853
+ title: "Report",
8854
+ accessor: "report.name"
8855
+ },
8856
+ {
8857
+ title: "Dimension",
8858
+ accessor: "dimension.name"
8859
+ },
8860
+ {
8861
+ title: "Variant",
8862
+ accessor: "variant.name"
8863
+ },
8864
+ {
8865
+ title: "Slug",
8866
+ accessor: "slug",
8867
+ render: ({ slug, variant }) => /* @__PURE__ */ jsxs("span", { children: [
8868
+ "/",
8869
+ variant.slug,
8870
+ "/",
8871
+ slug
8872
+ ] })
8873
+ },
8874
+ {
8875
+ title: `Name ${localeDefault8.toUpperCase()}`,
8819
8876
  sortable: true,
8820
- render: (member) => {
8821
- const icon = member.content.type === FORMATTER_TYPES.FORMATTER ? /* @__PURE__ */ jsx(IconVariable, { size: "1.3em" }) : /* @__PURE__ */ jsx(IconMathFunction, { size: "1.3em" });
8822
- return /* @__PURE__ */ jsxs(Group, { noWrap: true, align: "center", spacing: 2, children: [
8823
- /* @__PURE__ */ jsx("span", { children: member.name }),
8824
- /* @__PURE__ */ jsx(Tooltip, { label: FORMATTER_TYPES_NAMES[member.content.type], children: /* @__PURE__ */ jsx("span", { children: icon }) })
8877
+ accessor: "name",
8878
+ render: ({ contentByLocale }) => {
8879
+ const defaultLocalized = contentByLocale.find((item) => item.locale === localeDefault8);
8880
+ const othersLocalized = contentByLocale.filter((item) => item.locale !== localeDefault8);
8881
+ const nameTooltipContent = othersLocalized.reduce((acc, item) => {
8882
+ const extraName = `${item.locale.toUpperCase()}: ${item.name}`;
8883
+ const newLine2 = acc !== "" ? "\n" : "";
8884
+ return `${acc}${newLine2}${extraName}`;
8885
+ }, "");
8886
+ return /* @__PURE__ */ jsxs(Group, { children: [
8887
+ /* @__PURE__ */ jsx("span", { children: defaultLocalized.name }),
8888
+ othersLocalized.length > 0 && /* @__PURE__ */ jsx(Tooltip, { label: nameTooltipContent, children: /* @__PURE__ */ jsxs(Avatar, { size: "sm", color: "pink", radius: "xl", children: [
8889
+ "+",
8890
+ othersLocalized.length
8891
+ ] }) })
8825
8892
  ] });
8826
8893
  }
8827
8894
  },
8828
8895
  {
8829
- title: "Description",
8830
- accessor: "content.description"
8831
- },
8832
- {
8833
- title: "Demo",
8834
- accessor: "content.testValue",
8835
- render: (member) => {
8836
- let input = member.content.testValue;
8837
- if (member.content.inputType === FORMATTER_INPUT_TYPES.STRING) {
8838
- input = `"${input}"`;
8839
- }
8840
- let output = member.result;
8841
- if (typeof member.result === "string") {
8842
- output = `"${output}"`;
8843
- }
8844
- return /* @__PURE__ */ jsxs(Group, { spacing: 2, children: [
8845
- /* @__PURE__ */ jsx(Tooltip, { label: `Input Type: ${FORMATTER_INPUT_TYPES_NAMES[member.content.inputType]}`, children: /* @__PURE__ */ jsx(Code, { color: "blue", children: input }) }),
8846
- /* @__PURE__ */ jsx(IconArrowRightCircle, {}),
8847
- /* @__PURE__ */ jsx(Code, { color: member.result && `${member.result}`.includes("ERROR") ? "red" : "teal", children: output })
8896
+ title: `Keywords ${localeDefault8.toUpperCase()}`,
8897
+ accessor: "keywords",
8898
+ render: ({ contentByLocale }) => {
8899
+ const defaultLocalized = contentByLocale.find((item) => item.locale === localeDefault8);
8900
+ const othersLocalized = contentByLocale.filter((item) => item.locale !== localeDefault8);
8901
+ const keywords = (defaultLocalized.keywords ? defaultLocalized.keywords : []).map((k) => /* @__PURE__ */ jsx(Badge, { children: k }, k));
8902
+ const keywordsTooltipContent = othersLocalized.reduce((acc, item) => {
8903
+ const otherKeywords = item.keywords || [];
8904
+ const extraKeywords = `${item.locale.toUpperCase()}: ${otherKeywords.length > 0 ? otherKeywords.join(",") : "None"}`;
8905
+ const newLine2 = acc !== "" ? "\n" : "";
8906
+ return `${acc}${newLine2}${extraKeywords}`;
8907
+ }, "");
8908
+ return /* @__PURE__ */ jsxs(Group, { children: [
8909
+ /* @__PURE__ */ jsx("span", { children: keywords.length > 0 ? keywords : /* @__PURE__ */ jsx(Badge, { color: "gray", children: "None" }) }),
8910
+ othersLocalized.length > 0 && /* @__PURE__ */ jsx(Tooltip, { label: keywordsTooltipContent, children: /* @__PURE__ */ jsxs(Avatar, { size: "sm", color: "pink", radius: "xl", children: [
8911
+ "+",
8912
+ othersLocalized.length
8913
+ ] }) })
8848
8914
  ] });
8849
8915
  }
8850
8916
  },
8851
8917
  {
8852
- title: "Usage",
8853
- accessor: "usageCount",
8854
- textAlignment: "right",
8855
- sortable: true
8918
+ title: "Image",
8919
+ accessor: "image",
8920
+ render: (member) => /* @__PURE__ */ jsx(ImagePreview_default, { member, height: "50px" })
8856
8921
  },
8857
8922
  {
8858
8923
  title: "",
8859
8924
  accessor: "actions",
8860
- render: (member) => /* @__PURE__ */ jsxs(Group, { position: "center", spacing: "xs", noWrap: true, children: [
8861
- /* @__PURE__ */ jsx(ActionIcon, { onClick: () => onClickEdit(member), children: /* @__PURE__ */ jsx(IconPencil, { size: 20 }) }, "edit"),
8862
- /* @__PURE__ */ jsx(
8863
- EntityDeleteButton,
8864
- {
8865
- id: member.id,
8866
- type: "formatter",
8867
- disabled: member.usageCount > 0,
8868
- warning: `Delete formatter called "${member.name}" (id:${member.id}) ? It is not used in any block.`
8869
- }
8870
- )
8871
- ] })
8925
+ render: (member) => /* @__PURE__ */ jsx(ActionIcon, { onClick: () => onClickEdit(member), children: /* @__PURE__ */ jsx(IconPencil, { size: 20 }) }, "edit")
8872
8926
  }
8873
8927
  ];
8874
8928
  return /* @__PURE__ */ jsx(
@@ -8880,40 +8934,68 @@ function FormattersTable({ filters, onClickEdit }) {
8880
8934
  columns: cols,
8881
8935
  sortStatus,
8882
8936
  onSortStatusChange: (s) => setSortStatus(s),
8883
- idAccessor: "id",
8937
+ idAccessor: "content_id",
8884
8938
  withBorder: true
8885
8939
  }
8886
8940
  );
8887
8941
  }
8888
- function FormatterEditor() {
8942
+ function MetadataEditor() {
8889
8943
  const [editingId, setEditingId] = useState(null);
8944
+ const [results, setResults] = useState([]);
8945
+ const [loading, setLoading] = useState(true);
8890
8946
  const [internalFilters, setInternalFilters] = useState(true);
8947
+ const [internalSort, setInternalSort] = useState();
8891
8948
  const onChangeFilters = (filters) => {
8892
8949
  setInternalFilters(filters);
8893
8950
  };
8894
- const onCreateFormatter = (id) => {
8895
- setEditingId(id);
8951
+ const onChangeSort = (sort) => {
8952
+ setInternalSort(sort);
8896
8953
  };
8897
- const setEditFormatter = (formatter) => {
8898
- if (formatter) {
8899
- setEditingId(formatter.id);
8900
- }
8954
+ const setEditMember = (member) => {
8955
+ setEditingId(member.content_id);
8901
8956
  };
8902
8957
  const onEditClose = () => {
8903
8958
  setEditingId(null);
8959
+ doSearch();
8960
+ };
8961
+ const doSearch = async () => {
8962
+ setLoading(true);
8963
+ const getParams = Object.keys({ ...internalFilters }).reduce((acc, key) => {
8964
+ if (internalFilters[key] !== "all")
8965
+ acc[key] = internalFilters[key];
8966
+ return acc;
8967
+ }, {});
8968
+ const entityRecords = await axios.get(
8969
+ "/api/cms/search/members",
8970
+ {
8971
+ params: {
8972
+ ...getParams,
8973
+ locale: "all",
8974
+ includes: true,
8975
+ sort: internalSort ? internalSort.columnAccessor : "id",
8976
+ direction: internalSort ? internalSort.direction : "asc"
8977
+ }
8978
+ }
8979
+ ).then((response) => response.data && response.data.data && response.data.data.results ? response.data.data.results : []);
8980
+ setResults(entityRecords);
8981
+ setLoading(false);
8904
8982
  };
8983
+ useEffect(() => {
8984
+ doSearch();
8985
+ }, [internalFilters, internalSort]);
8905
8986
  return /* @__PURE__ */ jsxs(Container, { fluid: true, children: [
8906
8987
  /* @__PURE__ */ jsxs(Grid, { children: [
8907
8988
  /* @__PURE__ */ jsxs(Grid.Col, { span: 2, children: [
8908
- /* @__PURE__ */ jsx(Text, { ml: "xl", my: "sm", weight: "bold", children: "Formatters" }),
8909
- /* @__PURE__ */ jsx(FilterSidebar2, { onChange: onChangeFilters, onEditCreation: onCreateFormatter, loading: false })
8989
+ /* @__PURE__ */ jsx(Text, { ml: "xl", my: "sm", weight: "bold", children: "Metadata" }),
8990
+ /* @__PURE__ */ jsx(FilterSidebar2, { onChange: onChangeFilters, loading })
8910
8991
  ] }),
8911
- /* @__PURE__ */ jsxs(Grid.Col, { span: 10, style: { height: "100vh" }, children: [
8992
+ /* @__PURE__ */ jsxs(Grid.Col, { span: 10, style: { height: "100vh", position: "relative" }, children: [
8993
+ /* @__PURE__ */ jsx(LoadingOverlay, { visible: loading, overlayBlur: 2, overlayOpacity: 50 }),
8912
8994
  /* @__PURE__ */ jsx(Space, { h: "xs" }),
8913
- /* @__PURE__ */ jsx(FormattersTable, { filters: internalFilters, onClickEdit: setEditFormatter })
8995
+ /* @__PURE__ */ jsx(MembersTable, { members: results, onClickEdit: setEditMember, onSort: onChangeSort })
8914
8996
  ] })
8915
8997
  ] }),
8916
- editingId && /* @__PURE__ */ jsx(FormatterForm, { formatterId: editingId, onEditEnd: onEditClose })
8998
+ editingId && /* @__PURE__ */ jsx(MemberForm, { memberId: editingId, onEditEnd: onEditClose })
8917
8999
  ] });
8918
9000
  }
8919
9001
 
@@ -8926,6 +9008,7 @@ init_store2();
8926
9008
  function ReportCard(props) {
8927
9009
  const { for: report } = props;
8928
9010
  const theme = useMantineTheme();
9011
+ const location = useManagerLocation();
8929
9012
  const secondaryColor = theme.colorScheme === "dark" ? theme.colors.dark[1] : theme.colors.gray[7];
8930
9013
  return /* @__PURE__ */ jsxs(Card, { shadow: "xs", p: "lg", withBorder: true, style: { margin: theme.spacing.sm }, children: [
8931
9014
  /* @__PURE__ */ jsxs(Group, { position: "apart", children: [
@@ -8939,7 +9022,7 @@ function ReportCard(props) {
8939
9022
  /* @__PURE__ */ jsx(Text, { size: "sm", style: { color: secondaryColor, lineHeight: 2 }, children: "Description of report will go here" }),
8940
9023
  /* @__PURE__ */ jsx(Space, { w: "xs" }),
8941
9024
  /* @__PURE__ */ jsxs(Group, { position: "apart", children: [
8942
- /* @__PURE__ */ jsx(Button, { component: Link, to: `/reports/${report.id}`, leftIcon: /* @__PURE__ */ jsx(IconPencil, {}), compact: true, children: "Edit" }),
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" }) }),
8943
9026
  /* @__PURE__ */ jsx(EntityDeleteButton, { type: "report", id: report.id })
8944
9027
  ] })
8945
9028
  ] });
@@ -8986,122 +9069,112 @@ function ReportPicker() {
8986
9069
  )
8987
9070
  ] });
8988
9071
  }
8989
-
8990
- // views/BespokeCMS.tsx
8991
- init_ResourceProvider();
8992
- function MainLink({ item }) {
8993
- const { pathname } = useLocation();
8994
- return /* @__PURE__ */ jsx(Link, { to: item.href, children: /* @__PURE__ */ jsx(
8995
- UnstyledButton,
8996
- {
8997
- mt: "xs",
8998
- sx: (theme) => ({
8999
- border: "1px solid white",
9000
- borderColor: pathname === item.href ? "#ccc" : "transparent",
9001
- backgroundColor: "transparent",
9002
- display: "block",
9003
- width: "100%",
9004
- padding: theme.spacing.xs,
9005
- borderRadius: theme.radius.sm,
9006
- "&:hover": {
9007
- backgroundColor: "#f9f9f9"
9008
- }
9009
- }),
9010
- children: /* @__PURE__ */ jsxs(Group, { children: [
9011
- /* @__PURE__ */ jsx(ThemeIcon, { color: item.color, variant: "light", children: item.icon }),
9012
- /* @__PURE__ */ jsx(Text, { size: "sm", children: item.label })
9013
- ] })
9014
- }
9015
- ) }, item.label);
9016
- }
9017
- function MenuBtn({ opened, setOpened }) {
9018
- const { pathname } = useLocation();
9019
- const hasHeader = pathname.indexOf("/reports/") === 0;
9020
- return /* @__PURE__ */ jsx(
9021
- Burger,
9022
- {
9023
- style: {
9024
- position: "fixed",
9025
- top: hasHeader && opened ? 60 : 10,
9026
- left: opened ? 215 : 10,
9027
- zIndex: 101
9028
- },
9029
- opened,
9030
- onClick: () => setOpened((o) => !o),
9031
- size: "sm"
9032
- }
9033
- );
9034
- }
9035
- var options = [
9036
- {
9037
- icon: /* @__PURE__ */ jsx(IconBoxMargin, { size: 16 }),
9038
- color: "blue",
9039
- label: "Reports Builder",
9040
- href: "/reports"
9041
- },
9042
- {
9043
- icon: /* @__PURE__ */ jsx(IconTable, { size: 16 }),
9044
- color: "teal",
9045
- label: "Metadata Editor",
9046
- href: "/metadata"
9047
- },
9048
- {
9049
- icon: /* @__PURE__ */ jsx(IconMathFunction, { size: 16 }),
9050
- color: "violet",
9051
- label: "Formatters List",
9052
- href: "/formatters"
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
+ ] });
9053
9087
  }
9054
- ];
9055
- function BespokeCMS() {
9056
- const [opened, setOpened] = useState(true);
9057
- const links = options.map((link) => /* @__PURE__ */ jsx(MainLink, { item: link }, link.label));
9058
- return /* @__PURE__ */ jsx(MantineProvider, { children: /* @__PURE__ */ jsx(ResourceProvider, { children: /* @__PURE__ */ jsx(NotificationsProvider, { position: "bottom-center", children: /* @__PURE__ */ jsx(DialogProvider, { children: /* @__PURE__ */ jsx("div", { suppressHydrationWarning: true, children: typeof window !== "undefined" && /* @__PURE__ */ jsx(HashRouter, { children: /* @__PURE__ */ jsx(
9059
- AppShell,
9060
- {
9061
- fixed: true,
9062
- padding: 0,
9063
- navbar: /* @__PURE__ */ jsxs(Fragment, { children: [
9064
- /* @__PURE__ */ jsx(MenuBtn, { opened, setOpened }),
9065
- /* @__PURE__ */ jsxs(
9066
- Navbar,
9067
- {
9068
- p: "md",
9069
- width: { base: 250, xs: opened ? 250 : 0 },
9070
- hiddenBreakpoint: 1e5,
9071
- hidden: !opened,
9072
- children: [
9073
- /* @__PURE__ */ jsx(Navbar.Section, { style: { position: "relative" }, children: /* @__PURE__ */ jsx(Link, { to: "/reports", children: /* @__PURE__ */ jsx(
9074
- UnstyledButton,
9075
- {
9076
- sx: (theme) => ({
9077
- display: "block",
9078
- width: "100%",
9079
- borderRadius: theme.radius.sm,
9080
- padding: theme.spacing.xs
9081
- }),
9082
- children: /* @__PURE__ */ jsxs(Group, { children: [
9083
- /* @__PURE__ */ jsx(IconCircleDashed, { size: 30, color: "black" }),
9084
- /* @__PURE__ */ jsxs(Text, { size: "lg", children: [
9085
- "Bespoke",
9086
- /* @__PURE__ */ jsx("small", { children: /* @__PURE__ */ jsx("sub", { children: " beta" }) })
9087
- ] })
9088
- ] })
9089
- }
9090
- ) }) }),
9091
- /* @__PURE__ */ jsx(Navbar.Section, { grow: true, children: links })
9092
- ]
9088
+ }
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"
9093
9123
  }
9094
- )
9095
- ] }),
9096
- children: /* @__PURE__ */ jsxs(Routes, { children: [
9097
- /* @__PURE__ */ jsx(Route, { path: "/reports", element: /* @__PURE__ */ jsx(ReportPicker, {}) }),
9098
- /* @__PURE__ */ jsx(Route, { path: "/reports/:id", element: /* @__PURE__ */ jsx(ReportEditor2, {}) }),
9099
- /* @__PURE__ */ jsx(Route, { path: "/metadata", element: /* @__PURE__ */ jsx(MetadataEditor, {}) }),
9100
- /* @__PURE__ */ jsx(Route, { path: "/formatters", element: /* @__PURE__ */ jsx(FormatterEditor, {}) }),
9101
- /* @__PURE__ */ jsx(Route, { path: "*", element: /* @__PURE__ */ jsx(ReportPicker, {}) })
9102
- ] })
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,
9139
+ zIndex: 101
9140
+ },
9141
+ opened,
9142
+ onClick: toggleOpened,
9143
+ size: "sm"
9144
+ }
9145
+ ),
9146
+ /* @__PURE__ */ jsxs(
9147
+ Navbar,
9148
+ {
9149
+ p: "md",
9150
+ width: { base: 250, xs: opened ? 250 : 0 },
9151
+ hiddenBreakpoint: 1e5,
9152
+ hidden: !opened,
9153
+ 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 })
9156
+ ]
9157
+ }
9158
+ )
9159
+ ] });
9160
+ }, [location, opened]);
9161
+ 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 });
9172
+ }
9173
+ return /* @__PURE__ */ jsx(ReportPicker, {});
9103
9174
  }
9104
- ) }) }) }) }) }) });
9175
+ return null;
9176
+ }, [location]);
9177
+ return /* @__PURE__ */ jsx(AppShell, { fixed: true, padding: 0, navbar, children: route });
9105
9178
  }
9106
9179
 
9107
9180
  // views/BespokeRenderer.tsx
@@ -9110,4 +9183,4 @@ function BespokeRenderer() {
9110
9183
  return /* @__PURE__ */ jsx("div", { children: "Report COMING SOON" });
9111
9184
  }
9112
9185
 
9113
- export { BespokeCMS, Explore_default as BespokeExplore, ExploreModal_default as BespokeExploreModal, BespokeRenderer, Report_default as BespokeReport, Search_default as BespokeSearch, DialogProvider, actions_exports as actions, storeWrapper, useAppDispatch as useBespokeDispatch, useAppSelector as useBespokeSelector, useDialog };
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 };