@procore/saved-views 1.0.1-estimatingFork.3 → 1.1.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3982,7 +3982,7 @@ var require_lodash = __commonJS({
3982
3982
  if (typeof func != "function") {
3983
3983
  throw new TypeError2(FUNC_ERROR_TEXT);
3984
3984
  }
3985
- return setTimeout2(function() {
3985
+ return setTimeout(function() {
3986
3986
  func.apply(undefined2, args);
3987
3987
  }, wait);
3988
3988
  }
@@ -5813,7 +5813,7 @@ var require_lodash = __commonJS({
5813
5813
  return object2[key];
5814
5814
  }
5815
5815
  var setData = shortOut(baseSetData);
5816
- var setTimeout2 = ctxSetTimeout || function(func, wait) {
5816
+ var setTimeout = ctxSetTimeout || function(func, wait) {
5817
5817
  return root.setTimeout(func, wait);
5818
5818
  };
5819
5819
  var setToString = shortOut(baseSetToString);
@@ -6605,7 +6605,7 @@ var require_lodash = __commonJS({
6605
6605
  }
6606
6606
  function leadingEdge(time) {
6607
6607
  lastInvokeTime = time;
6608
- timerId = setTimeout2(timerExpired, wait);
6608
+ timerId = setTimeout(timerExpired, wait);
6609
6609
  return leading ? invokeFunc(time) : result2;
6610
6610
  }
6611
6611
  function remainingWait(time) {
@@ -6621,7 +6621,7 @@ var require_lodash = __commonJS({
6621
6621
  if (shouldInvoke(time)) {
6622
6622
  return trailingEdge(time);
6623
6623
  }
6624
- timerId = setTimeout2(timerExpired, remainingWait(time));
6624
+ timerId = setTimeout(timerExpired, remainingWait(time));
6625
6625
  }
6626
6626
  function trailingEdge(time) {
6627
6627
  timerId = undefined2;
@@ -6652,12 +6652,12 @@ var require_lodash = __commonJS({
6652
6652
  }
6653
6653
  if (maxing) {
6654
6654
  clearTimeout(timerId);
6655
- timerId = setTimeout2(timerExpired, wait);
6655
+ timerId = setTimeout(timerExpired, wait);
6656
6656
  return invokeFunc(lastCallTime);
6657
6657
  }
6658
6658
  }
6659
6659
  if (timerId === undefined2) {
6660
- timerId = setTimeout2(timerExpired, wait);
6660
+ timerId = setTimeout(timerExpired, wait);
6661
6661
  }
6662
6662
  return result2;
6663
6663
  }
@@ -11385,7 +11385,7 @@ var SavedViewCollectionMenuItem = (props) => {
11385
11385
  loading: props.isUpdateProcessing
11386
11386
  },
11387
11387
  i18n.t("savedViews.actions.update")
11388
- )), (props.enableSharingViews ?? true) && props.item.view_level !== "default" && props.selected && !props.canUpdate && props.item.id !== "temporary" && /* @__PURE__ */ React15.createElement("div", null, /* @__PURE__ */ React15.createElement(
11388
+ )), props.item.view_level !== "default" && props.selected && !props.canUpdate && props.item.id !== "temporary" && /* @__PURE__ */ React15.createElement("div", null, /* @__PURE__ */ React15.createElement(
11389
11389
  Button2,
11390
11390
  {
11391
11391
  onClick: copyShareLink,
@@ -11433,12 +11433,199 @@ var ExpandedPanel = styled_components_esm_default(Panel)`
11433
11433
  `;
11434
11434
 
11435
11435
  // src/components/panels/PanelContent.tsx
11436
- import { Flex as Flex3, useI18nContext as useI18nContext4 } from "@procore/core-react";
11436
+ import { Flex as Flex3, useI18nContext as useI18nContext5 } from "@procore/core-react";
11437
11437
  import { useToastAlertContext as useToastAlertContext2 } from "@procore/toast-alert";
11438
11438
  import React17 from "react";
11439
11439
 
11440
+ // node_modules/@procore/core-http/dist/modern/index.js
11441
+ function getCSRFToken() {
11442
+ const token = document.cookie.match("(^|;)\\s*csrf_token\\s*=\\s*([^;]+)");
11443
+ return token ? decodeURIComponent(token.pop() || "") : "";
11444
+ }
11445
+ function getCSRFHeader() {
11446
+ const csrfToken = getCSRFToken();
11447
+ return csrfToken ? { "X-CSRF-TOKEN": csrfToken } : {};
11448
+ }
11449
+ function removeLeadingSlash(url) {
11450
+ return url.startsWith("/") ? url.substring(1, url.length) : url;
11451
+ }
11452
+ function removeTrailingSlash(url) {
11453
+ return url.endsWith("/") ? url.substring(0, url.length - 1) : url;
11454
+ }
11455
+ function applyBaseUrl(url, baseUrl) {
11456
+ return `${removeTrailingSlash(baseUrl)}/${removeLeadingSlash(url)}`;
11457
+ }
11458
+ function getOptions({ headers, ...options }) {
11459
+ const opts = {
11460
+ credentials: "same-origin",
11461
+ headers: {
11462
+ ...getCSRFHeader(),
11463
+ ...headers
11464
+ },
11465
+ mode: "same-origin",
11466
+ ...options
11467
+ };
11468
+ return opts;
11469
+ }
11470
+ function getUrl(url, baseUrl) {
11471
+ return baseUrl ? applyBaseUrl(url, baseUrl) : url;
11472
+ }
11473
+ function request(url, { baseUrl, ...options } = {}) {
11474
+ return fetch(getUrl(url, baseUrl), getOptions(options));
11475
+ }
11476
+ function requestJSON(url, requestParams = {}) {
11477
+ return request(url, requestParams).then(
11478
+ (response) => response.json()
11479
+ );
11480
+ }
11481
+
11482
+ // src/utils/api/queries.ts
11483
+ import { useQuery } from "@tanstack/react-query";
11484
+
11485
+ // src/utils/api/queriesHandler.ts
11486
+ import { useMutation, useQueryClient } from "@tanstack/react-query";
11487
+ import { useI18nContext as useI18nContext3 } from "@procore/core-react";
11488
+ var getBasePath = (companyId, projectId) => {
11489
+ if (projectId) {
11490
+ return `/rest/v2.0/companies/${companyId}/projects/${projectId}/saved_views`;
11491
+ }
11492
+ return `/rest/v2.0/companies/${companyId}/saved_views`;
11493
+ };
11494
+ var useApiRequest = (props, method, mutationKey) => {
11495
+ const { projectId, companyId, domain, tableName } = props;
11496
+ const queryClient2 = useQueryClient();
11497
+ const { locale: locale2 } = useI18nContext3();
11498
+ const basePath = getBasePath(companyId, projectId);
11499
+ const queryKeyContext = projectId ?? companyId;
11500
+ return useMutation({
11501
+ mutationKey,
11502
+ mutationFn: async (savedView) => {
11503
+ let url = "";
11504
+ if (method === "DELETE" || method === "PUT") {
11505
+ url = `${basePath}/${savedView.share_token}?permissions_domain=${domain}`;
11506
+ } else {
11507
+ url = `${basePath}?table_name=${tableName}&permissions_domain=${domain}`;
11508
+ }
11509
+ const response = await requestJSON(url, {
11510
+ method,
11511
+ body: JSON.stringify(savedView),
11512
+ headers: {
11513
+ "Content-Type": "application/json",
11514
+ "Accept-Language": locale2
11515
+ }
11516
+ });
11517
+ if (response.error) {
11518
+ throw response.error;
11519
+ }
11520
+ return response.data;
11521
+ },
11522
+ onSuccess: (savedView) => {
11523
+ if (method === "DELETE" || method === "POST") {
11524
+ queryClient2.invalidateQueries({
11525
+ queryKey: ["savedViews", domain, tableName, queryKeyContext]
11526
+ });
11527
+ return;
11528
+ } else {
11529
+ const oldData = queryClient2.getQueryData([
11530
+ "savedViews",
11531
+ domain,
11532
+ tableName,
11533
+ queryKeyContext
11534
+ ]);
11535
+ const oldView = oldData?.find(
11536
+ (item) => item.share_token === savedView.share_token
11537
+ );
11538
+ if (oldView?.name !== savedView.name) {
11539
+ queryClient2.invalidateQueries({
11540
+ queryKey: ["savedViews", domain, tableName, queryKeyContext]
11541
+ });
11542
+ return;
11543
+ }
11544
+ }
11545
+ queryClient2.setQueryData(
11546
+ ["savedViews", domain, tableName, queryKeyContext],
11547
+ (oldData) => {
11548
+ if (!oldData)
11549
+ return [savedView];
11550
+ return oldData.map(
11551
+ (item) => item.share_token === savedView.share_token ? savedView : item
11552
+ );
11553
+ }
11554
+ );
11555
+ }
11556
+ });
11557
+ };
11558
+
11440
11559
  // src/utils/constants/viewLevels.ts
11441
- var VIEW_LEVELS = ["company", "project", "personal"];
11560
+ var PROJECT_LEVEL_TOOL_VIEW_LEVELS = [
11561
+ "company",
11562
+ "project",
11563
+ "personal"
11564
+ ];
11565
+ var COMPANY_LEVEL_TOOL_VIEW_LEVELS = ["company", "personal"];
11566
+ var getViewLevels = (isProjectLevelTool) => isProjectLevelTool ? PROJECT_LEVEL_TOOL_VIEW_LEVELS : COMPANY_LEVEL_TOOL_VIEW_LEVELS;
11567
+
11568
+ // src/utils/api/queries.ts
11569
+ var PAGE_SIZE = 50 * PROJECT_LEVEL_TOOL_VIEW_LEVELS.length;
11570
+ var getBasePath2 = (companyId, projectId) => {
11571
+ if (projectId) {
11572
+ return `/rest/v2.0/companies/${companyId}/projects/${projectId}/saved_views`;
11573
+ }
11574
+ return `/rest/v2.0/companies/${companyId}/saved_views`;
11575
+ };
11576
+ var useSavedViewsQuery = (props) => {
11577
+ const { projectId, companyId, domain, tableName } = props;
11578
+ const basePath = getBasePath2(companyId, projectId);
11579
+ const url = `${basePath}?table_name=${tableName}&permissions_domain=${domain}`;
11580
+ return useQuery({
11581
+ queryKey: ["savedViews", domain, tableName, projectId ?? companyId],
11582
+ queryFn: async () => {
11583
+ const getUrl2 = `${url}&per_page=${PAGE_SIZE}`;
11584
+ const response = await requestJSON(getUrl2);
11585
+ return response.data;
11586
+ }
11587
+ });
11588
+ };
11589
+ var useSavedViewsPermissions = (props) => {
11590
+ const { projectId, companyId, domain } = props;
11591
+ const basePath = getBasePath2(companyId, projectId);
11592
+ const url = `${basePath}/permissions?permissions_domain=${domain}`;
11593
+ return useQuery({
11594
+ queryKey: ["savedViewsConfig", domain, projectId ?? companyId],
11595
+ queryFn: async () => {
11596
+ const response = await requestJSON(url);
11597
+ return response.data;
11598
+ }
11599
+ });
11600
+ };
11601
+ var useCreateSavedView = (props) => useApiRequest(props, "POST", [
11602
+ "createSavedView",
11603
+ props.domain,
11604
+ props.tableName
11605
+ ]);
11606
+ var useUpdateSavedView = (props) => useApiRequest(props, "PUT", [
11607
+ "updateSavedView",
11608
+ props.domain,
11609
+ props.tableName
11610
+ ]);
11611
+ var useDeleteSavedView = (props) => useApiRequest(props, "DELETE", [
11612
+ "deleteSavedView",
11613
+ props.domain,
11614
+ props.tableName
11615
+ ]);
11616
+ var useFetchSavedViewById = (savedViewToken, queryInput, enabled = true) => {
11617
+ const { projectId, companyId, domain } = queryInput;
11618
+ const basePath = getBasePath2(companyId, projectId);
11619
+ return useQuery({
11620
+ enabled: enabled && Boolean(savedViewToken),
11621
+ queryKey: ["savedView", savedViewToken, projectId ?? companyId],
11622
+ queryFn: async () => {
11623
+ const url = `${basePath}/${savedViewToken}?permissions_domain=${domain}`;
11624
+ const response = await requestJSON(url);
11625
+ return response.data;
11626
+ }
11627
+ });
11628
+ };
11442
11629
 
11443
11630
  // src/components/panels/PanelContentUtils.ts
11444
11631
  var import_lodash = __toESM(require_lodash());
@@ -11513,12 +11700,10 @@ var getColumnIdentifier = (col) => {
11513
11700
  };
11514
11701
  var updateTableConfig = (view, tableApi, provider) => {
11515
11702
  if (provider === "smart-grid") {
11516
- setTimeout(() => {
11517
- setSmartGridConfig(
11518
- tableApi,
11519
- view.table_config
11520
- );
11521
- }, 0);
11703
+ setSmartGridConfig(
11704
+ tableApi,
11705
+ view.table_config
11706
+ );
11522
11707
  } else {
11523
11708
  const dataTableApi = tableApi;
11524
11709
  const tableConfig = view.table_config;
@@ -11590,20 +11775,13 @@ var cleanObject = (table_config, provider) => {
11590
11775
  var normalizeForComparison = (config) => {
11591
11776
  if (!config?.columnState)
11592
11777
  return config;
11593
- const filteredColumnState = config.columnState.filter(
11594
- (col) => {
11595
- const colId = getColumnIdentifier(col);
11596
- return colId !== "drag_handle" && colId !== "ag-Grid-AutoColumn";
11597
- }
11598
- );
11599
11778
  return {
11600
- ...import_lodash.default.omit(config, ["enableRowGrouping", "enableColumnGrouping"]),
11601
- columnState: filteredColumnState.map((col) => {
11602
- const res = import_lodash.default.omit(col, ["aggFunc"]);
11779
+ ...config,
11780
+ columnState: config.columnState.map((col) => {
11603
11781
  if (col.flex) {
11604
- return import_lodash.default.omit(res, ["width", "flex"]);
11782
+ return import_lodash.default.omit(col, ["width", "flex"]);
11605
11783
  }
11606
- return res;
11784
+ return col;
11607
11785
  })
11608
11786
  };
11609
11787
  };
@@ -11616,17 +11794,10 @@ var isEqual = (viewTableConfig, tableConfig, defaultViewConfig, provider) => {
11616
11794
  );
11617
11795
  const normalizedViewConfig = normalizeForComparison(syncedViewTableConfig);
11618
11796
  const normalizedCurrentConfig = normalizeForComparison(tableConfig);
11619
- const cleanedViewConfig = cleanObject(normalizedViewConfig, provider);
11620
- const cleanedCurrentConfig = cleanObject(normalizedCurrentConfig, provider);
11621
- const isEqual2 = import_lodash.default.isEqual(cleanedViewConfig, cleanedCurrentConfig);
11622
- if (!isEqual2) {
11623
- console.log(
11624
- "[SavedViews]: Showing update button - view config, current config",
11625
- cleanedViewConfig,
11626
- cleanedCurrentConfig
11627
- );
11628
- }
11629
- return isEqual2;
11797
+ return import_lodash.default.isEqual(
11798
+ cleanObject(normalizedViewConfig, provider),
11799
+ cleanObject(normalizedCurrentConfig, provider)
11800
+ );
11630
11801
  };
11631
11802
  var hasPermissionForViewLevel = (viewLevel, permissions) => {
11632
11803
  switch (viewLevel) {
@@ -11643,9 +11814,10 @@ var hasPermissionForViewLevel = (viewLevel, permissions) => {
11643
11814
 
11644
11815
  // src/components/panels/useGroups.ts
11645
11816
  import { useState as useState2 } from "react";
11646
- var useGroups = () => {
11817
+ var useGroups = (isProjectLevelTool) => {
11818
+ const viewLevels = getViewLevels(isProjectLevelTool);
11647
11819
  const [groups, setGroups] = useState2(
11648
- Object.fromEntries(VIEW_LEVELS.map((level) => [level, true]))
11820
+ Object.fromEntries(viewLevels.map((level) => [level, true]))
11649
11821
  );
11650
11822
  const toggleGroup = (group) => {
11651
11823
  setGroups((groups2) => ({ ...groups2, [group]: !groups2[group] }));
@@ -11659,7 +11831,7 @@ import {
11659
11831
  Flex as Flex2,
11660
11832
  spacing,
11661
11833
  Typography,
11662
- useI18nContext as useI18nContext3
11834
+ useI18nContext as useI18nContext4
11663
11835
  } from "@procore/core-react";
11664
11836
  import React16 from "react";
11665
11837
  var groupIcon = (group) => {
@@ -11679,7 +11851,7 @@ var Header = styled_components_esm_default(Flex2)`
11679
11851
  }
11680
11852
  `;
11681
11853
  var ViewLevelHeader = ({ expanded, toggleGroup, group }) => {
11682
- const I18n = useI18nContext3();
11854
+ const I18n = useI18nContext4();
11683
11855
  return /* @__PURE__ */ React16.createElement(
11684
11856
  Header,
11685
11857
  {
@@ -11736,9 +11908,9 @@ var Panel2 = styled_components_esm_default(DetailPage.Card)`
11736
11908
  var PanelContent = (props) => {
11737
11909
  const { queryInput, selectedSavedView, tableConfig } = props;
11738
11910
  const { showToast } = useToastAlertContext2();
11739
- const I18n = useI18nContext4();
11740
- const { data: savedViewsFromQuery, error: savedViewsError } = props.backend.useSavedViewsQuery(props.queryInput);
11741
- const updateMutation = props.backend.useUpdateSavedView(queryInput);
11911
+ const I18n = useI18nContext5();
11912
+ const { data: savedViewsFromQuery, error: savedViewsError } = useSavedViewsQuery(props.queryInput);
11913
+ const updateMutation = useUpdateSavedView(queryInput);
11742
11914
  const { mutate: updateSavedView } = updateMutation;
11743
11915
  const isUpdateLoading = "isPending" in updateMutation ? updateMutation.isPending : updateMutation.isLoading ?? false;
11744
11916
  const savedViews = props.savedViews ?? savedViewsFromQuery;
@@ -11749,14 +11921,14 @@ var PanelContent = (props) => {
11749
11921
  errorToastRef.current = savedViewsError;
11750
11922
  }
11751
11923
  }, [savedViewsError, showToast, I18n]);
11752
- const { data: permissions } = props.backend.useSavedViewsPermissions(
11753
- props.queryInput
11754
- );
11924
+ const { data: permissions } = useSavedViewsPermissions(props.queryInput);
11755
11925
  const selectedRowRef = useScrollToRef(savedViews);
11756
- const { groups, toggleGroup } = useGroups();
11757
11926
  const isTemporarySelected = selectedSavedView?.id === "temporary";
11758
11927
  const temporaryView = savedViews?.find((view) => view.id === "temporary");
11759
11928
  const presetViews = props.presetViews || [props.defaultView];
11929
+ const isProjectLevelTool = !!queryInput.projectId;
11930
+ const viewLevels = getViewLevels(isProjectLevelTool);
11931
+ const { groups, toggleGroup } = useGroups(isProjectLevelTool);
11760
11932
  const onUpdate = (data) => {
11761
11933
  const newSavedView = {
11762
11934
  ...data,
@@ -11785,8 +11957,7 @@ var PanelContent = (props) => {
11785
11957
  {
11786
11958
  item: temporaryView,
11787
11959
  selected: isTemporarySelected,
11788
- onClearTemporary: props.onClearTemporary,
11789
- enableSharingViews: false
11960
+ onClearTemporary: props.onClearTemporary
11790
11961
  }
11791
11962
  )
11792
11963
  ), presetViews.map((presetView) => {
@@ -11803,12 +11974,11 @@ var PanelContent = (props) => {
11803
11974
  SavedViewCollectionMenuItem,
11804
11975
  {
11805
11976
  item: presetView,
11806
- selected: isSelected,
11807
- enableSharingViews: false
11977
+ selected: isSelected
11808
11978
  }
11809
11979
  )
11810
11980
  );
11811
- }), VIEW_LEVELS.map((level) => {
11981
+ }), viewLevels.map((level) => {
11812
11982
  const isExpanded = groups[level];
11813
11983
  const views = isExpanded && savedViews ? savedViews.filter(
11814
11984
  (view) => view.view_level === level && view.id !== "temporary"
@@ -11852,8 +12022,7 @@ var PanelContent = (props) => {
11852
12022
  isUpdateProcessing: isUpdateLoading,
11853
12023
  onEdit: () => props.openModal("update" /* UPDATE */),
11854
12024
  onDelete: props.onDelete,
11855
- permissions,
11856
- enableSharingViews: false
12025
+ permissions
11857
12026
  }
11858
12027
  )
11859
12028
  );
@@ -11870,7 +12039,7 @@ import {
11870
12039
  Tooltip,
11871
12040
  useI18nContext as useI18nContext11
11872
12041
  } from "@procore/core-react";
11873
- import React22, { useState as useState4, useEffect as useEffect4, useCallback as useCallback3 } from "react";
12042
+ import React22, { useState as useState4, useEffect as useEffect3, useCallback as useCallback3 } from "react";
11874
12043
  import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
11875
12044
  import { useToastAlertContext as useToastAlertContext3, ToastAlertProvider } from "@procore/toast-alert";
11876
12045
 
@@ -11880,7 +12049,7 @@ import {
11880
12049
  ConfirmModal,
11881
12050
  Modal,
11882
12051
  P as P2,
11883
- useI18nContext as useI18nContext5
12052
+ useI18nContext as useI18nContext6
11884
12053
  } from "@procore/core-react";
11885
12054
  import React18 from "react";
11886
12055
  var SavedViewsDeleteConfirmationModalShared = ({
@@ -11888,7 +12057,7 @@ var SavedViewsDeleteConfirmationModalShared = ({
11888
12057
  onDelete,
11889
12058
  open
11890
12059
  }) => {
11891
- const i18n = useI18nContext5();
12060
+ const i18n = useI18nContext6();
11892
12061
  return /* @__PURE__ */ React18.createElement(
11893
12062
  ConfirmModal,
11894
12063
  {
@@ -11919,7 +12088,7 @@ import {
11919
12088
  Modal as Modal2,
11920
12089
  spacing as spacing3,
11921
12090
  Typography as Typography2,
11922
- useI18nContext as useI18nContext6
12091
+ useI18nContext as useI18nContext7
11923
12092
  } from "@procore/core-react";
11924
12093
  import * as React19 from "react";
11925
12094
 
@@ -14216,10 +14385,11 @@ var TupleSchema = class extends Schema {
14216
14385
  create$1.prototype = TupleSchema.prototype;
14217
14386
 
14218
14387
  // src/components/modals/form-modal/FormModalBaseUtils.ts
14219
- var getViewLevelOptions = (permissions, i18n) => {
14388
+ var getViewLevelOptions = (permissions, i18n, isProjectLevelTool = true) => {
14220
14389
  const options = ["personal"];
14221
- if (permissions?.can_create_project_saved_views)
14390
+ if (isProjectLevelTool && permissions?.can_create_project_saved_views) {
14222
14391
  options.push("project");
14392
+ }
14223
14393
  if (permissions?.can_create_company_saved_views)
14224
14394
  options.push("company");
14225
14395
  return options.map((option) => ({
@@ -14249,7 +14419,6 @@ function extractMessage(error, I18n) {
14249
14419
  }
14250
14420
 
14251
14421
  // src/components/modals/form-modal/FormModalBase.tsx
14252
- var { useEffect: useEffect2, useRef: useRef2 } = React19;
14253
14422
  var ScrollContainer = styled_components_esm_default("div")`
14254
14423
  overflow: auto;
14255
14424
  `;
@@ -14266,38 +14435,22 @@ var FormModalBase = ({
14266
14435
  defaultView,
14267
14436
  selectedSavedView,
14268
14437
  setOpenEditCreateModal,
14269
- onSelect,
14270
- backend
14438
+ onSelect
14271
14439
  }) => {
14272
- const I18n = useI18nContext6();
14440
+ const I18n = useI18nContext7();
14273
14441
  const NAME_MAX_LENGTH = 150;
14274
- const originalBodyWidth = useRef2("");
14275
- useEffect2(() => {
14276
- if (open) {
14277
- originalBodyWidth.current = document.body.style.width || "";
14278
- document.body.style.width = "100%";
14279
- } else {
14280
- document.body.style.width = originalBodyWidth.current;
14281
- }
14282
- return () => {
14283
- if (originalBodyWidth.current !== void 0) {
14284
- document.body.style.width = originalBodyWidth.current;
14285
- }
14286
- };
14287
- }, [open]);
14288
- const { useCreateSavedView: useCreateSavedView2, useUpdateSavedView: useUpdateSavedView2, useSavedViewsPermissions: useSavedViewsPermissions2 } = backend;
14289
14442
  const {
14290
14443
  mutate: createSavedView,
14291
14444
  isPending: isCreating,
14292
14445
  error: createError,
14293
14446
  reset: resetCreateMutation
14294
- } = useCreateSavedView2(queryInput);
14447
+ } = useCreateSavedView(queryInput);
14295
14448
  const {
14296
14449
  mutate: updateSavedView,
14297
14450
  isPending: isUpdating,
14298
14451
  error: updateError,
14299
14452
  reset: resetUpdateMutation
14300
- } = useUpdateSavedView2(queryInput);
14453
+ } = useUpdateSavedView(queryInput);
14301
14454
  const resetMutations = () => {
14302
14455
  resetCreateMutation();
14303
14456
  resetUpdateMutation();
@@ -14306,7 +14459,7 @@ var FormModalBase = ({
14306
14459
  resetMutations();
14307
14460
  onCancel();
14308
14461
  };
14309
- const { data: permissions } = useSavedViewsPermissions2(queryInput);
14462
+ const { data: permissions } = useSavedViewsPermissions(queryInput);
14310
14463
  const isLoading = isCreating || isUpdating;
14311
14464
  const errors = extractMessage(createError || updateError, I18n);
14312
14465
  const handleOnSubmit = (data) => {
@@ -14337,7 +14490,12 @@ var FormModalBase = ({
14337
14490
  });
14338
14491
  }
14339
14492
  };
14340
- const viewLevelOptions = getViewLevelOptions(permissions, I18n);
14493
+ const isProjectLevelTool = !!queryInput.projectId;
14494
+ const viewLevelOptions = getViewLevelOptions(
14495
+ permissions,
14496
+ I18n,
14497
+ isProjectLevelTool
14498
+ );
14341
14499
  return /* @__PURE__ */ React19.createElement(
14342
14500
  Modal2,
14343
14501
  {
@@ -14417,7 +14575,6 @@ var FormModalBase = ({
14417
14575
  Form.Select,
14418
14576
  {
14419
14577
  name: "view_level",
14420
- qa: { label: "view-level" },
14421
14578
  options: viewLevelOptions,
14422
14579
  label: I18n.t("savedViews.modal.fields.viewLevel"),
14423
14580
  colWidth: 12,
@@ -14441,7 +14598,7 @@ var FormModalBase = ({
14441
14598
  };
14442
14599
 
14443
14600
  // src/components/modals/form-modal/FormModal.tsx
14444
- import { useI18nContext as useI18nContext7 } from "@procore/core-react";
14601
+ import { useI18nContext as useI18nContext8 } from "@procore/core-react";
14445
14602
  var FormModal = ({
14446
14603
  open,
14447
14604
  mode,
@@ -14452,10 +14609,9 @@ var FormModal = ({
14452
14609
  selectedSavedView,
14453
14610
  setOpenEditCreateModal,
14454
14611
  onSelect,
14455
- defaultView,
14456
- backend
14612
+ defaultView
14457
14613
  }) => {
14458
- const i18n = useI18nContext7();
14614
+ const i18n = useI18nContext8();
14459
14615
  return /* @__PURE__ */ React20.createElement(
14460
14616
  FormModalBase,
14461
14617
  {
@@ -14471,8 +14627,7 @@ var FormModal = ({
14471
14627
  selectedSavedView,
14472
14628
  setOpenEditCreateModal,
14473
14629
  onSelect,
14474
- defaultView,
14475
- backend
14630
+ defaultView
14476
14631
  }
14477
14632
  );
14478
14633
  };
@@ -14489,7 +14644,7 @@ import {
14489
14644
  P as P3,
14490
14645
  spacing as spacing4,
14491
14646
  Typography as Typography3,
14492
- useI18nContext as useI18nContext8
14647
+ useI18nContext as useI18nContext9
14493
14648
  } from "@procore/core-react";
14494
14649
  import * as React21 from "react";
14495
14650
  var SharedViewFormModal = ({
@@ -14502,7 +14657,7 @@ var SharedViewFormModal = ({
14502
14657
  isCreating,
14503
14658
  resetCreateError
14504
14659
  }) => {
14505
- const I18n = useI18nContext8();
14660
+ const I18n = useI18nContext9();
14506
14661
  const NAME_MAX_LENGTH = 150;
14507
14662
  const errors = extractMessage(createError, I18n);
14508
14663
  const handleNameChange = () => {
@@ -14632,9 +14787,9 @@ var SharedViewFormModal = ({
14632
14787
  };
14633
14788
 
14634
14789
  // src/utils/hooks/useViewSelection.ts
14635
- import { useState as useState3, useCallback as useCallback2, useEffect as useEffect3, useRef as useRef3, useMemo } from "react";
14790
+ import { useState as useState3, useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useMemo } from "react";
14636
14791
  import { useSearchParams } from "react-router-dom";
14637
- import { useI18nContext as useI18nContext9 } from "@procore/core-react";
14792
+ import { useI18nContext as useI18nContext10 } from "@procore/core-react";
14638
14793
 
14639
14794
  // src/utils/viewStorage.ts
14640
14795
  var ViewStorage = {
@@ -14697,11 +14852,12 @@ var restoreUrlParameter = (currentParam, previousParam, setSearchParams) => {
14697
14852
  }
14698
14853
  };
14699
14854
  var useViewSelection = (config, savedViews, presetViews, openSharedViewModal) => {
14700
- const I18n = useI18nContext9();
14701
- const storageKey = `savedView_${config.domain}_${config.tableName}_${config.companyId}_${config.projectId}_${config.userId}`;
14855
+ const I18n = useI18nContext10();
14856
+ const projectIdSegment = config.projectId ?? "company";
14857
+ const storageKey = `savedView_${config.domain}_${config.tableName}_${config.companyId}_${projectIdSegment}_${config.userId}`;
14702
14858
  const temporaryStorageKey = `${storageKey}-temporary`;
14703
14859
  const [searchParams, setSearchParams] = useSearchParams();
14704
- const previousSavedViewParamRef = useRef3(null);
14860
+ const previousSavedViewParamRef = useRef2(null);
14705
14861
  const [selectedSavedView, setSelectedSavedView] = useState3(() => {
14706
14862
  const stored = ViewStorage.load(storageKey, config.defaultView);
14707
14863
  return stored ?? config.defaultView;
@@ -14772,7 +14928,7 @@ var useViewSelection = (config, savedViews, presetViews, openSharedViewModal) =>
14772
14928
  },
14773
14929
  [isViewAlreadySelected, openSharedViewModal, allViews, selectView]
14774
14930
  );
14775
- useEffect3(() => {
14931
+ useEffect2(() => {
14776
14932
  const savedViewId = searchParams.get("saved-view");
14777
14933
  restoreUrlParameter(
14778
14934
  savedViewId,
@@ -14796,177 +14952,6 @@ var useViewSelection = (config, savedViews, presetViews, openSharedViewModal) =>
14796
14952
  };
14797
14953
  };
14798
14954
 
14799
- // node_modules/@procore/core-http/dist/modern/index.js
14800
- function getCSRFToken() {
14801
- const token = document.cookie.match("(^|;)\\s*csrf_token\\s*=\\s*([^;]+)");
14802
- return token ? decodeURIComponent(token.pop() || "") : "";
14803
- }
14804
- function getCSRFHeader() {
14805
- const csrfToken = getCSRFToken();
14806
- return csrfToken ? { "X-CSRF-TOKEN": csrfToken } : {};
14807
- }
14808
- function removeLeadingSlash(url) {
14809
- return url.startsWith("/") ? url.substring(1, url.length) : url;
14810
- }
14811
- function removeTrailingSlash(url) {
14812
- return url.endsWith("/") ? url.substring(0, url.length - 1) : url;
14813
- }
14814
- function applyBaseUrl(url, baseUrl) {
14815
- return `${removeTrailingSlash(baseUrl)}/${removeLeadingSlash(url)}`;
14816
- }
14817
- function getOptions({ headers, ...options }) {
14818
- const opts = {
14819
- credentials: "same-origin",
14820
- headers: {
14821
- ...getCSRFHeader(),
14822
- ...headers
14823
- },
14824
- mode: "same-origin",
14825
- ...options
14826
- };
14827
- return opts;
14828
- }
14829
- function getUrl(url, baseUrl) {
14830
- return baseUrl ? applyBaseUrl(url, baseUrl) : url;
14831
- }
14832
- function request(url, { baseUrl, ...options } = {}) {
14833
- return fetch(getUrl(url, baseUrl), getOptions(options));
14834
- }
14835
- function requestJSON(url, requestParams = {}) {
14836
- return request(url, requestParams).then(
14837
- (response) => response.json()
14838
- );
14839
- }
14840
-
14841
- // src/utils/api/queries.ts
14842
- import { useQuery } from "@tanstack/react-query";
14843
-
14844
- // src/utils/api/queriesHandler.ts
14845
- import { useMutation, useQueryClient } from "@tanstack/react-query";
14846
- import { useI18nContext as useI18nContext10 } from "@procore/core-react";
14847
- var useApiRequest = (props, method, mutationKey) => {
14848
- const { projectId, companyId, domain, tableName } = props;
14849
- const queryClient2 = useQueryClient();
14850
- const { locale: locale2 } = useI18nContext10();
14851
- return useMutation({
14852
- mutationKey,
14853
- mutationFn: async (savedView) => {
14854
- let url = "";
14855
- if (method === "DELETE" || method === "PUT") {
14856
- url = `/rest/v2.0/companies/${companyId}/projects/${projectId}/saved_views/${savedView.share_token}?permissions_domain=${domain}`;
14857
- } else {
14858
- url = `/rest/v2.0/companies/${companyId}/projects/${projectId}/saved_views?table_name=${tableName}&permissions_domain=${domain}`;
14859
- }
14860
- const response = await requestJSON(url, {
14861
- method,
14862
- body: JSON.stringify(savedView),
14863
- headers: {
14864
- "Content-Type": "application/json",
14865
- "Accept-Language": locale2
14866
- }
14867
- });
14868
- if (response.error) {
14869
- throw response.error;
14870
- }
14871
- return response.data;
14872
- },
14873
- onSuccess: (savedView) => {
14874
- if (method === "DELETE" || method === "POST") {
14875
- queryClient2.invalidateQueries({
14876
- queryKey: ["savedViews", domain, tableName]
14877
- });
14878
- return;
14879
- } else {
14880
- const oldData = queryClient2.getQueryData([
14881
- "savedViews",
14882
- domain,
14883
- tableName
14884
- ]);
14885
- const oldView = oldData?.find(
14886
- (item) => item.share_token === savedView.share_token
14887
- );
14888
- if (oldView?.name !== savedView.name) {
14889
- queryClient2.invalidateQueries({
14890
- queryKey: ["savedViews", domain, tableName]
14891
- });
14892
- return;
14893
- }
14894
- }
14895
- queryClient2.setQueryData(
14896
- ["savedViews", domain, tableName],
14897
- (oldData) => {
14898
- if (!oldData)
14899
- return [savedView];
14900
- return oldData.map(
14901
- (item) => item.share_token === savedView.share_token ? savedView : item
14902
- );
14903
- }
14904
- );
14905
- }
14906
- });
14907
- };
14908
-
14909
- // src/utils/api/queries.ts
14910
- var PAGE_SIZE = 50 * VIEW_LEVELS.length;
14911
- var useSavedViewsQuery = (props) => {
14912
- const { projectId, companyId, domain, tableName } = props;
14913
- const url = `/rest/v2.0/companies/${companyId}/projects/${projectId}/saved_views?table_name=${tableName}&permissions_domain=${domain}`;
14914
- return useQuery({
14915
- queryKey: ["savedViews", domain, tableName],
14916
- queryFn: async () => {
14917
- const getUrl2 = `${url}&per_page=${PAGE_SIZE}`;
14918
- const response = await requestJSON(getUrl2);
14919
- return response.data;
14920
- }
14921
- });
14922
- };
14923
- var useSavedViewsPermissions = (props) => {
14924
- const { projectId, companyId, domain } = props;
14925
- const url = `/rest/v2.0/companies/${companyId}/projects/${projectId}/saved_views/permissions?permissions_domain=${domain}`;
14926
- return useQuery({
14927
- queryKey: ["savedViewsConfig", domain],
14928
- queryFn: async () => {
14929
- const response = await requestJSON(url);
14930
- return response.data;
14931
- }
14932
- });
14933
- };
14934
- var useCreateSavedView = (props) => useApiRequest(props, "POST", [
14935
- "createSavedView",
14936
- props.domain,
14937
- props.tableName
14938
- ]);
14939
- var useUpdateSavedView = (props) => useApiRequest(props, "PUT", [
14940
- "updateSavedView",
14941
- props.domain,
14942
- props.tableName
14943
- ]);
14944
- var useDeleteSavedView = (props) => useApiRequest(props, "DELETE", [
14945
- "deleteSavedView",
14946
- props.domain,
14947
- props.tableName
14948
- ]);
14949
- var useFetchSavedViewById = (savedViewToken, queryInput, enabled = true) => {
14950
- const { projectId, companyId } = queryInput;
14951
- return useQuery({
14952
- enabled: enabled && Boolean(savedViewToken),
14953
- queryKey: ["savedView", savedViewToken],
14954
- queryFn: async () => {
14955
- const url = `/rest/v2.0/companies/${companyId}/projects/${projectId}/saved_views/${savedViewToken}`;
14956
- const response = await requestJSON(url);
14957
- return response.data;
14958
- }
14959
- });
14960
- };
14961
- var createQueries = (customBackend) => ({
14962
- useSavedViewsQuery: customBackend?.useSavedViewsQuery ?? useSavedViewsQuery,
14963
- useSavedViewsPermissions: customBackend?.useSavedViewsPermissions ?? useSavedViewsPermissions,
14964
- useCreateSavedView: customBackend?.useCreateSavedView ?? useCreateSavedView,
14965
- useUpdateSavedView: customBackend?.useUpdateSavedView ?? useUpdateSavedView,
14966
- useDeleteSavedView: customBackend?.useDeleteSavedView ?? useDeleteSavedView,
14967
- useFetchSavedViewById: customBackend?.useFetchSavedViewById ?? useFetchSavedViewById
14968
- });
14969
-
14970
14955
  // src/components/saved-views/SavedViews.tsx
14971
14956
  var StyledPanel = styled_components_esm_default.div`
14972
14957
  border: ${({ provider }) => provider === "data-table" ? "1px solid #d6dadc" : "none"};
@@ -14974,15 +14959,14 @@ var StyledPanel = styled_components_esm_default.div`
14974
14959
  var queryClient = new QueryClient();
14975
14960
  var SavedViewsContent = (props) => {
14976
14961
  const { projectId, companyId } = props;
14977
- const backend = createQueries(props.backend);
14978
14962
  const queryInput = {
14979
14963
  domain: props.domain,
14980
14964
  tableName: props.tableName,
14981
- projectId,
14965
+ projectId: projectId ?? void 0,
14982
14966
  companyId
14983
14967
  };
14984
- const { data: savedViews } = backend.useSavedViewsQuery(queryInput);
14985
- const { mutate: deleteSavedView } = backend.useDeleteSavedView(queryInput);
14968
+ const { data: savedViews } = useSavedViewsQuery(queryInput);
14969
+ const { mutate: deleteSavedView } = useDeleteSavedView(queryInput);
14986
14970
  const { showToast } = useToastAlertContext3();
14987
14971
  const i18n = useI18nContext11();
14988
14972
  const [activeModal, setActiveModal] = useState4(null);
@@ -15010,7 +14994,7 @@ var SavedViewsContent = (props) => {
15010
14994
  domain: props.domain,
15011
14995
  tableName: props.tableName,
15012
14996
  userId: props.userId,
15013
- projectId,
14997
+ projectId: projectId ?? void 0,
15014
14998
  companyId,
15015
14999
  defaultView: props.defaultView,
15016
15000
  onSelect: props.onSelect
@@ -15019,7 +15003,7 @@ var SavedViewsContent = (props) => {
15019
15003
  props.presetViews,
15020
15004
  openSharedViewModal
15021
15005
  );
15022
- const { data: fetchedView, isError: fetchError } = backend.useFetchSavedViewById(
15006
+ const { data: fetchedView, isError: fetchError } = useFetchSavedViewById(
15023
15007
  modalData?.viewId ?? null,
15024
15008
  queryInput,
15025
15009
  Boolean(modalData?.viewId)
@@ -15029,8 +15013,8 @@ var SavedViewsContent = (props) => {
15029
15013
  isPending: isCreating,
15030
15014
  error: createError,
15031
15015
  reset: resetCreateError
15032
- } = backend.useCreateSavedView(queryInput);
15033
- useEffect4(() => {
15016
+ } = useCreateSavedView(queryInput);
15017
+ useEffect3(() => {
15034
15018
  if (fetchError) {
15035
15019
  showToast.error(i18n.t("savedViews.errors.notFound"));
15036
15020
  selectView(selectedView ?? props.defaultView);
@@ -15116,8 +15100,7 @@ var SavedViewsContent = (props) => {
15116
15100
  savedViews: allViews,
15117
15101
  provider: props.provider,
15118
15102
  userId: props.userId,
15119
- onClearTemporary: clearTemporaryView,
15120
- backend
15103
+ onClearTemporary: clearTemporaryView
15121
15104
  }
15122
15105
  ))
15123
15106
  ), (isModalOpen("create" /* CREATE */) || isModalOpen("update" /* UPDATE */)) && /* @__PURE__ */ React22.createElement(
@@ -15132,8 +15115,7 @@ var SavedViewsContent = (props) => {
15132
15115
  selectedSavedView: selectedView,
15133
15116
  onSelect: selectView,
15134
15117
  setOpenEditCreateModal: closeModal,
15135
- defaultView: props.defaultView,
15136
- backend
15118
+ defaultView: props.defaultView
15137
15119
  }
15138
15120
  ), selectedView && isModalOpen("delete" /* DELETE */) && /* @__PURE__ */ React22.createElement(
15139
15121
  SavedViewsDeleteConfirmationModalShared,
@@ -15178,37 +15160,27 @@ var DEFAULT_COLUMN_STATE = {
15178
15160
  rowGroupIndex: null,
15179
15161
  flex: null
15180
15162
  };
15181
- var isColGroupDef = (colDef) => {
15182
- return "children" in colDef && Array.isArray(colDef.children);
15183
- };
15163
+ var flattenColumnDefs = (defs) => defs.flatMap((d) => "children" in d ? flattenColumnDefs(d.children) : [d]);
15184
15164
  var getColumnStateFromDefs = (columnDefs) => {
15185
- return columnDefs.flatMap((colDef) => {
15186
- if (isColGroupDef(colDef)) {
15187
- return getColumnStateFromDefs(colDef.children);
15188
- }
15189
- return getColumnStateFromSingleDef(colDef);
15190
- }).filter(
15191
- (col) => col !== null
15192
- );
15193
- };
15194
- var getColumnStateFromSingleDef = (colDef) => {
15195
- const field = colDef.field ?? colDef.colId;
15196
- if (!field)
15197
- return null;
15198
- return {
15199
- colId: field,
15200
- hide: colDef.hide ?? false,
15201
- pinned: colDef.pinned ?? null,
15202
- width: colDef.width ?? colDef.minWidth ?? DEFAULT_COLUMN_STATE.width,
15203
- sort: null,
15204
- sortIndex: null,
15205
- pivot: false,
15206
- pivotIndex: null,
15207
- aggFunc: null,
15208
- rowGroup: false,
15209
- rowGroupIndex: null,
15210
- flex: colDef.flex ?? null
15211
- };
15165
+ return flattenColumnDefs(columnDefs).map((colDef) => {
15166
+ const field = colDef.field ?? colDef.colId;
15167
+ if (!field)
15168
+ return null;
15169
+ return {
15170
+ colId: field,
15171
+ hide: colDef.hide ?? false,
15172
+ pinned: colDef.pinned ?? null,
15173
+ width: colDef.width ?? colDef.minWidth ?? DEFAULT_COLUMN_STATE.width,
15174
+ sort: null,
15175
+ sortIndex: null,
15176
+ pivot: false,
15177
+ pivotIndex: null,
15178
+ aggFunc: null,
15179
+ rowGroup: false,
15180
+ rowGroupIndex: null,
15181
+ flex: colDef.flex ?? null
15182
+ };
15183
+ }).filter((col) => col !== null);
15212
15184
  };
15213
15185
  var extractDefaultView = (gridApi, receivedConfig) => {
15214
15186
  const columnDefs = gridApi.getColumnDefs() ?? [];
@@ -15235,7 +15207,7 @@ var useNormalizedDefaultViews = (defaultViews, gridApi) => {
15235
15207
  };
15236
15208
 
15237
15209
  // src/components/adapters/smart-grid/useSmartGridConfig.ts
15238
- import { useState as useState5, useEffect as useEffect5, useRef as useRef4 } from "react";
15210
+ import { useState as useState5, useEffect as useEffect4 } from "react";
15239
15211
  var GRID_STATE_EVENTS = [
15240
15212
  "sortChanged",
15241
15213
  "filterOpened",
@@ -15254,14 +15226,10 @@ var useSmartGridConfig = (gridApi) => {
15254
15226
  const [config, setConfig] = useState5(
15255
15227
  () => getSmartGridConfig(gridApi)
15256
15228
  );
15257
- const eventListenersDisabledRef = useRef4(false);
15258
- useEffect5(() => {
15229
+ useEffect4(() => {
15259
15230
  if (!gridApi)
15260
15231
  return;
15261
15232
  const updateConfig = () => {
15262
- if (eventListenersDisabledRef.current) {
15263
- return;
15264
- }
15265
15233
  setConfig(getSmartGridConfig(gridApi));
15266
15234
  };
15267
15235
  GRID_STATE_EVENTS.forEach((event) => {
@@ -15273,24 +15241,13 @@ var useSmartGridConfig = (gridApi) => {
15273
15241
  });
15274
15242
  };
15275
15243
  }, [gridApi]);
15276
- const disableEventListeners = () => {
15277
- eventListenersDisabledRef.current = true;
15278
- };
15279
- const enableEventListeners = () => {
15280
- eventListenersDisabledRef.current = false;
15281
- };
15282
- return { config, setConfig, disableEventListeners, enableEventListeners };
15244
+ return { config, setConfig };
15283
15245
  };
15284
15246
 
15285
15247
  // src/components/adapters/smart-grid/SmartGridSavedViews.tsx
15286
15248
  var SmartGridSavedViews = (props) => {
15287
15249
  const { gridApi, userId, projectId, companyId } = props;
15288
- const {
15289
- config: tableConfig,
15290
- setConfig: setTableConfig,
15291
- disableEventListeners,
15292
- enableEventListeners
15293
- } = useSmartGridConfig(gridApi);
15250
+ const { config: tableConfig, setConfig: setTableConfig } = useSmartGridConfig(gridApi);
15294
15251
  const presetViews = useNormalizedDefaultViews(props.defaultViews, gridApi);
15295
15252
  const defaultView = presetViews.find((view) => view.id === "default") ?? presetViews[0];
15296
15253
  const onSelect = useCallback4(
@@ -15298,29 +15255,20 @@ var SmartGridSavedViews = (props) => {
15298
15255
  if (!gridApi)
15299
15256
  return item;
15300
15257
  const isPresetView = item.view_level === "default";
15301
- const newConfig = item.table_config;
15302
- const transformedConfig = props.transformSettings?.(newConfig) ?? newConfig;
15303
- disableEventListeners();
15304
15258
  if (isPresetView) {
15305
15259
  updateTableConfig(item, gridApi, "smart-grid");
15306
- setTableConfig(transformedConfig);
15307
- setTimeout(() => {
15308
- enableEventListeners();
15309
- }, 0);
15260
+ setTableConfig(item.table_config);
15310
15261
  return item;
15311
15262
  }
15312
15263
  const updatedView = {
15313
15264
  ...item,
15314
15265
  table_config: customAndConfigSync(
15315
- transformedConfig,
15266
+ item.table_config,
15316
15267
  tableConfig
15317
15268
  )
15318
15269
  };
15319
15270
  updateTableConfig(updatedView, gridApi, "smart-grid");
15320
15271
  setTableConfig(updatedView.table_config);
15321
- setTimeout(() => {
15322
- enableEventListeners();
15323
- }, 0);
15324
15272
  return updatedView;
15325
15273
  },
15326
15274
  [gridApi, tableConfig, setTableConfig]
@@ -15337,8 +15285,7 @@ var SmartGridSavedViews = (props) => {
15337
15285
  defaultView,
15338
15286
  presetViews,
15339
15287
  tableName: props.tableName,
15340
- tableConfig,
15341
- backend: props.backend
15288
+ tableConfig
15342
15289
  }
15343
15290
  );
15344
15291
  };