@retailcrm/datalens-ui 0.2.4 → 0.2.6

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 (43) hide show
  1. package/index.css +587 -587
  2. package/package.json +1 -6
  3. package/public/i18n/data.json +2 -1
  4. package/public/i18n/{en.144c7612.js → en.01e3d689.js} +2 -1
  5. package/public/i18n/manifest.json +2 -2
  6. package/public/i18n/{ru.b7e6f7b0.js → ru.a9fbfe3a.js} +2 -1
  7. package/ui/capabilities/capabilities.d.ts +1 -0
  8. package/ui/capabilities/capabilities.js +5 -0
  9. package/ui/components/ActionPanel/ActionPanel.js +2 -1
  10. package/ui/components/AsideHeaderAdapter/AsideHeaderAdapter.js +9 -1
  11. package/ui/components/DashKit/helpers.js +4 -4
  12. package/ui/components/DialogAddSharedEntryFromLink/DialogAddSharedEntryFromLink.js +4 -1
  13. package/ui/components/DialogSharedEntryPermissions/DialogSharedEntryPermissions.js +3 -8
  14. package/ui/components/EntryDialogues/DialogCreateDashboard/DialogCreateDashboard.js +4 -3
  15. package/ui/components/EntryDialogues/DialogCreateEditorChart/DialogCreateEditorChart.js +4 -3
  16. package/ui/components/EntryDialogues/DialogCreateQLChart/DialogCreateQLChart.js +4 -3
  17. package/ui/components/EntryDialogues/DialogCreateWizardChart/DialogCreateWizardChart.js +4 -3
  18. package/ui/components/EntryDialogues/DialogRenameEntry/DialogRenameEntry.js +4 -3
  19. package/ui/components/InaccessibleOnMobileInset/InaccessibleOnMobileInset.js +18 -4
  20. package/ui/components/Navigation/Base/configure.d.ts +0 -20
  21. package/ui/components/Navigation/Base/configure.js +23 -23
  22. package/ui/components/Navigation/Core/NavigationBreadcrumbs/BreadcrumbMenu.d.ts +1 -1
  23. package/ui/components/Navigation/Core/NavigationBreadcrumbs/BreadcrumbMenu.js +32 -2
  24. package/ui/libs/DatalensChartkit/ChartKit/helpers/apply-hc-handlers.js +23 -2
  25. package/ui/libs/DatalensChartkit/menu/MenuItems.js +25 -17
  26. package/ui/libs/DatalensChartkit/modules/data-provider/charts/ui-sandbox.js +23 -2
  27. package/ui/units/connections/components/ConnectorForm/components/Datepicker/Datepicker.d.ts +1 -1
  28. package/ui/units/connections/components/ConnectorForm/components/FileInput/FileInput.d.ts +1 -1
  29. package/ui/units/connections/components/ConnectorForm/components/Input/Input.d.ts +1 -1
  30. package/ui/units/connections/components/ConnectorForm/components/Select/Select.d.ts +1 -1
  31. package/ui/units/dash/components/DashActionPanel/DashActionPanel.d.ts +2 -0
  32. package/ui/units/dash/components/DashActionPanel/DashActionPanel.js +36 -0
  33. package/ui/units/dash/components/DashActionPanel/ViewControls/ViewControls.d.ts +1 -0
  34. package/ui/units/dash/components/DashActionPanel/ViewControls/ViewControls.js +2 -1
  35. package/ui/units/dash/store/utils.js +2 -1
  36. package/ui/units/ql/containers/QL/QLActionPanel/QLActionPanel.js +26 -6
  37. package/ui/units/wizard/actions/widget.js +7 -1
  38. package/ui/units/wizard/containers/Wizard/Wizard.js +5 -3
  39. package/ui/units/workbooks/components/RenameEntryDialog/RenameEntryDialog.js +48 -12
  40. package/ui/units/workbooks/store/actions/index.d.ts +1 -1
  41. package/ui/units/workbooks/store/actions/index.js +1 -7
  42. package/ui/utils/errors/errorByCode.d.ts +1 -0
  43. package/ui/utils/errors/errorByCode.js +97 -0
@@ -12,16 +12,24 @@ import { isEnabledFeature } from "../../../utils/isEnabledFeature.js";
12
12
  import { getExportItem, isExportItemDisabled } from "../components/ChartKitBase/components/Header/components/Menu/Items/Export/Export.js";
13
13
  import getInspectorMenuitem from "../components/ChartKitBase/components/Header/components/Menu/Items/Inspector/Inspector.js";
14
14
  import ChartKitIcon_default from "../components/ChartKitIcon/ChartKitIcon.js";
15
+ import { getRouter, getLocation } from "../../../navigation/router.js";
15
16
  import { Feature } from "../../../../shared/types/feature.js";
16
17
  import { MenuItemsIds } from "../../../../shared/types/menu.js";
17
18
  import { DL, URL_OPTIONS } from "../../../constants/common.js";
18
19
  import { WidgetKind } from "../../../../shared/types/widget.js";
19
20
  import { PREVIEW_ROUTE } from "../../../../shared/constants/entry.js";
20
- import { getLocation } from "../../../navigation/router.js";
21
21
  import { FOCUSED_WIDGET_PARAM_NAME } from "../../../../shared/constants/dash.js";
22
22
  const getExportMenuItem = getExportItem;
23
23
  const getInspectorMenuItem = getInspectorMenuitem;
24
24
  const alertI18n = I18n.keyset("component.chartkit-alerts.view");
25
+ const openInternalUrlInNewTab = (rawUrl) => {
26
+ const url = new URL(rawUrl, window.location.origin);
27
+ getRouter().openTab({
28
+ pathname: url.pathname,
29
+ search: url.search,
30
+ hash: url.hash
31
+ });
32
+ };
25
33
  const getAlertsMenuItem = ({
26
34
  chartsDataProvider,
27
35
  customConfig
@@ -89,7 +97,7 @@ const getNewWindowMenuItem = ({
89
97
  }
90
98
  )
91
99
  );
92
- window.open(link);
100
+ openInternalUrlInNewTab(link.toString());
93
101
  })
94
102
  });
95
103
  const getEditMenuItem = ({
@@ -103,14 +111,15 @@ const getEditMenuItem = ({
103
111
  icon: customConfig?.icon || /* @__PURE__ */ jsx(ChartKitIcon_default, { data: Pencil }),
104
112
  isVisible: () => !DL.IS_MOBILE && (customConfig?.isVisible ? customConfig.isVisible() : true),
105
113
  action: customConfig?.action || (({ loadedData = {}, propsData, chartsDataProvider: dataProvider }) => {
106
- window.open(
107
- (dataProvider || chartsDataProvider)?.getGoAwayLink(
108
- { loadedData, propsData },
109
- {
110
- idPrefix: "/navigate/"
111
- }
112
- )
114
+ const url = (dataProvider || chartsDataProvider)?.getGoAwayLink(
115
+ { loadedData, propsData },
116
+ {
117
+ idPrefix: "/navigate/"
118
+ }
113
119
  );
120
+ if (url) {
121
+ openInternalUrlInNewTab(url);
122
+ }
114
123
  })
115
124
  });
116
125
  const getOpenAsTableMenuItem = ({
@@ -131,15 +140,14 @@ const getOpenAsTableMenuItem = ({
131
140
  return Boolean(!isCriticalError && isExportAllowed && isChart && customIsVisible);
132
141
  },
133
142
  action: customConfig?.action || (({ loadedData, propsData, chartsDataProvider: dataProvider }) => {
134
- window.open(
135
- (dataProvider || chartsDataProvider).getGoAwayLink(
136
- { loadedData, propsData },
137
- {
138
- extraParams: { _chart_type: "table" },
139
- idPrefix: "/preview/"
140
- }
141
- )
143
+ const url = (dataProvider || chartsDataProvider).getGoAwayLink(
144
+ { loadedData, propsData },
145
+ {
146
+ extraParams: { _chart_type: "table" },
147
+ idPrefix: "/preview/"
148
+ }
142
149
  );
150
+ openInternalUrlInNewTab(url);
143
151
  })
144
152
  });
145
153
  const getLinkMenuItem = (customConfig) => ({
@@ -4,6 +4,7 @@ import get from "lodash/get";
4
4
  import merge from "lodash/merge";
5
5
  import set from "lodash/set";
6
6
  import { chartStorage } from "../../../ChartKit/plugins/chart-storage.js";
7
+ import "../../../../../navigation/index.js";
7
8
  import "../../../../../../shared/index.js";
8
9
  import { wrapHtml } from "../../../../../../shared/utils/ui-sandbox.js";
9
10
  import { getRandomCKId } from "../../../ChartKit/helpers/getRandomCKId.js";
@@ -13,11 +14,32 @@ import { generateHtml } from "../../html-generator/index.js";
13
14
  import { getParseHtmlFn } from "../../html-generator/utils.js";
14
15
  import { UiSandboxRuntime } from "./ui-sandbox-runtime.js";
15
16
  import { clearVmProp } from "./utils.js";
17
+ import { getRouter } from "../../../../../navigation/router.js";
16
18
  import { EditorType, LegacyEditorType } from "../../../../../../shared/types/widget.js";
17
19
  import { WRAPPED_FN_KEY } from "../../../../../../shared/constants/ui-sandbox.js";
18
20
  import { WRAPPED_HTML_KEY } from "../../../../../../shared/constants/chartkit-handlers.js";
19
21
  const UI_SANDBOX_TOTAL_TIME_LIMIT = 3e3;
20
22
  const UI_SANDBOX_FN_TIME_LIMIT = 100;
23
+ const openSandboxUrl = (rawUrl, target) => {
24
+ if (typeof window === "undefined") {
25
+ return;
26
+ }
27
+ const windowTarget = target === "_self" ? "_self" : "_blank";
28
+ const href = sanitizeUrl(rawUrl);
29
+ const url = new URL(href, window.location.origin);
30
+ if (url.origin === window.location.origin) {
31
+ getRouter().open(
32
+ {
33
+ pathname: url.pathname,
34
+ search: url.search,
35
+ hash: url.hash
36
+ },
37
+ windowTarget
38
+ );
39
+ return;
40
+ }
41
+ window.open(url.toString(), windowTarget);
42
+ };
21
43
  let uiSandbox;
22
44
  let getInterruptAfterDeadlineHandler;
23
45
  const getUISandbox = async () => {
@@ -196,8 +218,7 @@ async function getUnwrappedFunction(args) {
196
218
  window: {
197
219
  open: function(url, target) {
198
220
  try {
199
- const href = sanitizeUrl(url);
200
- window.open(href, target === "_self" ? "_self" : "_blank");
221
+ openSandboxUrl(url, target);
201
222
  } catch (e) {
202
223
  console.error(e);
203
224
  }
@@ -515,4 +515,4 @@ export declare const Datepicker: ConnectedComponent<{
515
515
  validationErrors?: ValidationError[];
516
516
  width?: BaseControlItem["width"];
517
517
  hintText?: BaseControlItem["hintText"];
518
- }, "form" | "actions" | "innerForm" | "validationErrors">>;
518
+ }, "form" | "actions" | "validationErrors" | "innerForm">>;
@@ -598,5 +598,5 @@ export declare const FileInput: ConnectedComponent<{
598
598
  validationErrors?: ValidationError[];
599
599
  width?: BaseControlItem["width"];
600
600
  hintText?: BaseControlItem["hintText"];
601
- }, "form" | "actions" | "innerForm" | "validationErrors">>;
601
+ }, "form" | "actions" | "validationErrors" | "innerForm">>;
602
602
  export {};
@@ -546,4 +546,4 @@ export declare const Input: ConnectedComponent<{
546
546
  validationErrors?: ValidationError[];
547
547
  width?: BaseControlItem["width"];
548
548
  hintText?: BaseControlItem["hintText"];
549
- }, "form" | "actions" | "innerForm" | "validationErrors">>;
549
+ }, "form" | "actions" | "validationErrors" | "innerForm">>;
@@ -547,4 +547,4 @@ export declare const Select: ConnectedComponent<{
547
547
  validationErrors?: ValidationError[];
548
548
  width?: BaseControlItem["width"];
549
549
  hintText?: BaseControlItem["hintText"];
550
- }, "form" | "actions" | "innerForm" | "validationErrors">>;
550
+ }, "form" | "actions" | "validationErrors" | "innerForm">>;
@@ -55,6 +55,8 @@ declare class DashActionPanel extends React.PureComponent<ActionPanelProps, Acti
55
55
  private getAdditionalEntryItems;
56
56
  private onSelectStateClick;
57
57
  private onValueTableOfContentsClick;
58
+ private isDashboardBreadcrumbEditingAccessible;
59
+ private filterEntryContextMenuItems;
58
60
  private handleGoBack;
59
61
  private handleGoForward;
60
62
  private handleSaveDash;
@@ -8,7 +8,9 @@ import { connect } from "react-redux";
8
8
  import "../../../../../shared/index.js";
9
9
  import { ActionPanelEntryContextMenuQa } from "../../../../../shared/constants/qa/action-panel.js";
10
10
  import "../../../../index.js";
11
+ import { capabilities } from "../../../../capabilities/index.js";
11
12
  import ActionPanel from "../../../../components/ActionPanel/ActionPanel.js";
13
+ import "../../../../components/EntryContextMenu/index.js";
12
14
  import { registry } from "../../../../registry/index.js";
13
15
  import { closeDialog, openDialogConfirm } from "../../../../store/actions/dialog.js";
14
16
  import { goForward, goBack } from "../../../../store/actions/editHistory.js";
@@ -30,13 +32,29 @@ import { EditControls } from "./EditControls/EditControls.js";
30
32
  import { ViewControls } from "./ViewControls/ViewControls.js";
31
33
  import { cancelEditClick } from "./helpers.js";
32
34
  /* empty css */
35
+ import { ENTRY_CONTEXT_MENU_ACTION } from "../../../../components/EntryContextMenu/constants.js";
33
36
  import { EntryDialogName } from "../../../../components/EntryDialogues/EntryDialogues.js";
34
37
  import { EntryDialogResolveStatus } from "../../../../components/EntryDialogues/constants.js";
38
+ import { Capability } from "../../../../capabilities/capabilities.js";
39
+ import { EntryScope } from "../../../../../shared/types/common.js";
35
40
  import { Feature } from "../../../../../shared/types/feature.js";
36
41
  import { DL } from "../../../../constants/common.js";
37
42
  import { ICONS_MENU_DEFAULT_SIZE } from "../../../../libs/DatalensChartkit/menu/constants.js";
38
43
  const b = block("dash-action-panel");
39
44
  const i18n = I18n.keyset("dash.action-panel.view");
45
+ const HIDDEN_DASH_ACTION_PANEL_CONTEXT_MENU_ITEMS = /* @__PURE__ */ new Set([
46
+ ENTRY_CONTEXT_MENU_ACTION.RENAME,
47
+ ENTRY_CONTEXT_MENU_ACTION.DELETE,
48
+ ENTRY_CONTEXT_MENU_ACTION.MOVE,
49
+ ENTRY_CONTEXT_MENU_ACTION.COPY,
50
+ ENTRY_CONTEXT_MENU_ACTION.ACCESS,
51
+ ENTRY_CONTEXT_MENU_ACTION.SHARE,
52
+ ENTRY_CONTEXT_MENU_ACTION.MIGRATE_TO_WORKBOOK
53
+ ]);
54
+ const DASH_BREADCRUMB_EDITING_MENU_ITEMS = /* @__PURE__ */ new Set([
55
+ "edit",
56
+ ENTRY_CONTEXT_MENU_ACTION.RENAME
57
+ ]);
40
58
  const _DashActionPanel = class _DashActionPanel extends React__default.PureComponent {
41
59
  constructor() {
42
60
  super(...arguments);
@@ -133,6 +151,22 @@ const _DashActionPanel = class _DashActionPanel extends React__default.PureCompo
133
151
  this.onValueTableOfContentsClick = () => {
134
152
  this.props.toggleTableOfContent();
135
153
  };
154
+ this.isDashboardBreadcrumbEditingAccessible = () => {
155
+ return capabilities.has(Capability.AccessibleDashboardBreadcrumbEditing);
156
+ };
157
+ this.filterEntryContextMenuItems = ({ items }) => {
158
+ const isDashboardBreadcrumbEditingAccessible = this.isDashboardBreadcrumbEditingAccessible();
159
+ const shouldHideDashEditingItems = this.props.entry?.scope === EntryScope.Dash && !isDashboardBreadcrumbEditingAccessible;
160
+ return items.filter((item) => {
161
+ if (HIDDEN_DASH_ACTION_PANEL_CONTEXT_MENU_ITEMS.has(item.id)) {
162
+ return false;
163
+ }
164
+ if (shouldHideDashEditingItems && DASH_BREADCRUMB_EDITING_MENU_ITEMS.has(item.id)) {
165
+ return false;
166
+ }
167
+ return true;
168
+ });
169
+ };
136
170
  this.handleGoBack = () => {
137
171
  if (this.props.canGoBack) {
138
172
  this.props.goBack({ unitId: DASH_EDIT_HISTORY_UNIT_ID });
@@ -183,6 +217,7 @@ const _DashActionPanel = class _DashActionPanel extends React__default.PureCompo
183
217
  {
184
218
  entry,
185
219
  additionalEntryItems: this.getAdditionalEntryItems(),
220
+ filterEntryContextMenuItems: this.filterEntryContextMenuItems,
186
221
  rightItems: [
187
222
  /* @__PURE__ */ jsx(
188
223
  DashActionPanelAdditionalButtons,
@@ -238,6 +273,7 @@ const _DashActionPanel = class _DashActionPanel extends React__default.PureCompo
238
273
  canEdit: this.props.canEdit,
239
274
  progress: this.props.progress,
240
275
  isLoadingEditMode: this.props.isLoadingEditMode,
276
+ showEditButton: this.isDashboardBreadcrumbEditingAccessible(),
241
277
  onEditClick: this.props.handlerEditClick,
242
278
  onAccessClick: this.openDialogAccess,
243
279
  entryDialoguesRef: this.props.entryDialoguesRef,
@@ -4,6 +4,7 @@ type ViewControlsProps = {
4
4
  canEdit: boolean;
5
5
  progress: boolean;
6
6
  isLoadingEditMode: boolean;
7
+ showEditButton?: boolean;
7
8
  onEditClick: () => void;
8
9
  onAccessClick: () => void;
9
10
  entryDialoguesRef: React.RefObject<EntryDialogues>;
@@ -13,6 +13,7 @@ const ViewControls = (props) => {
13
13
  canEdit,
14
14
  progress,
15
15
  isLoadingEditMode,
16
+ showEditButton = true,
16
17
  onEditClick,
17
18
  onAccessClick,
18
19
  entryDialoguesRef,
@@ -33,7 +34,7 @@ const ViewControls = (props) => {
33
34
  }
34
35
  ),
35
36
  /* @__PURE__ */ jsx(EntryDialogues, { ref: entryDialoguesRef }),
36
- canEdit && !DL.IS_MOBILE && /* @__PURE__ */ jsx(
37
+ showEditButton && canEdit && !DL.IS_MOBILE && /* @__PURE__ */ jsx(
37
38
  Button,
38
39
  {
39
40
  view: "normal",
@@ -8,6 +8,7 @@ import "../../../utils/index.js";
8
8
  import { Mode, DASHKIT_STATE_VERSION } from "../modules/constants.js";
9
9
  import { getLocation } from "../../../navigation/router.js";
10
10
  import { URL_QUERY, DL } from "../../../constants/common.js";
11
+ import { normalizeDestination } from "../../../../shared/modules/entry.js";
11
12
  import Utils from "../../../utils/utils.js";
12
13
  import { EntryScope } from "../../../../shared/types/common.js";
13
14
  const storeI18n = I18n.keyset("dash.store.view");
@@ -17,7 +18,7 @@ const getFakeDashEntry = (workbookId) => {
17
18
  const { counter, id: newTabId } = generateUniqId({ salt, counter: 0, ids: [] });
18
19
  const searchParams = getLocation().params();
19
20
  const searchCurrentPath = searchParams.get(URL_QUERY.CURRENT_PATH);
20
- const path = searchCurrentPath || DL.USER_FOLDER;
21
+ const path = normalizeDestination(searchCurrentPath || DL.USER_FOLDER);
21
22
  const initialKey = `${path}${dashCreateI18n("label_default-name")}`;
22
23
  const data = {
23
24
  tabs: [
@@ -11,10 +11,12 @@ import { isDraftVersion } from "../../../../../utils/revisions.js";
11
11
  import "../../../../../index.js";
12
12
  import ActionPanel from "../../../../../components/ActionPanel/ActionPanel.js";
13
13
  import { ChartSaveControls } from "../../../../../components/ActionPanel/components/ChartSaveControls/ChartSaveControl.js";
14
+ import "../../../../../navigation/index.js";
14
15
  import { registry } from "../../../../../registry/index.js";
15
16
  import { openDialogSaveDraftChartAsActualConfirm } from "../../../../../store/actions/dialog.js";
16
17
  import { resetEditHistoryUnit, addEditHistoryPoint } from "../../../../../store/actions/editHistory.js";
17
- import { reloadRevisionsOnSave } from "../../../../../store/actions/entryContent.js";
18
+ import { setIsRenameWithoutReload, reloadRevisionsOnSave } from "../../../../../store/actions/entryContent.js";
19
+ import { selectIsRenameWithoutReload } from "../../../../../store/selectors/entryContent.js";
18
20
  import DialogSettings from "../../../components/Dialogs/Settings/Settings.js";
19
21
  import { QL_EDIT_HISTORY_UNIT_ID } from "../../../constants/index.js";
20
22
  import { prepareChartDataBeforeSave } from "../../../modules/helpers.js";
@@ -25,6 +27,7 @@ import SvgMonitoring from "../../../../../assets/icons/monitoring.svg.js";
25
27
  /* empty css */
26
28
  import { getValid, getConnection, getExtraSettings, getPreviewData, getEntry } from "../../../store/selectors/ql.js";
27
29
  import { Feature } from "../../../../../../shared/types/feature.js";
30
+ import { useRouter } from "../../../../../navigation/router.js";
28
31
  import Utils from "../../../../../utils/utils.js";
29
32
  import { EntryDialogName } from "../../../../../components/EntryDialogues/EntryDialogues.js";
30
33
  import { EntryDialogResolveStatus } from "../../../../../components/EntryDialogues/constants.js";
@@ -42,6 +45,7 @@ const QLActionPanel = (props) => {
42
45
  const redirectUrl = useSelector(getRedirectUrl);
43
46
  const previewData = useSelector(getPreviewData);
44
47
  const entry = useSelector(getEntry);
48
+ const isRenameWithoutReload = useSelector(selectIsRenameWithoutReload);
45
49
  const canEdit = !(entry && entry.permissions && entry.permissions.edit === false);
46
50
  const isCurrentRevisionActual = entry?.revId && entry?.revId === entry?.publishedId;
47
51
  const isNewChart = typeof entry?.fake !== "undefined" && entry?.fake;
@@ -49,17 +53,18 @@ const QLActionPanel = (props) => {
49
53
  const { QlActionPanelExtension } = registry.ql.components.getAll();
50
54
  const enablePublish = entry && isEnabledFeature(Feature.EnablePublishEntry) && !entry.fake;
51
55
  const dispatch = useDispatch();
56
+ const router = useRouter();
52
57
  const [dialogNoRightsVisible, setDialogNoRightsVisible] = React__default.useState(false);
53
58
  const [dialogSettingsVisible, setDialogSettingsVisible] = React__default.useState(false);
54
59
  const qlState = useSelector((state) => state.ql);
55
60
  const wizardState = useSelector((state) => state.wizard);
56
61
  const handleBeforeunload = React__default.useCallback(
57
62
  (event) => {
58
- if (!entryNotChanged) {
63
+ if (!entryNotChanged && !isRenameWithoutReload) {
59
64
  event.returnValue = true;
60
65
  }
61
66
  },
62
- [entryNotChanged]
67
+ [entryNotChanged, isRenameWithoutReload]
63
68
  );
64
69
  React__default.useEffect(() => {
65
70
  window.removeEventListener("beforeunload", handleBeforeunload);
@@ -91,12 +96,21 @@ const QLActionPanel = (props) => {
91
96
  icon: /* @__PURE__ */ jsx(Icon, { data: SvgMonitoring, width: 16, height: 16 }),
92
97
  id: "sql-to-monitoring",
93
98
  action: () => {
94
- window.open(redirectUrl, "_blank");
99
+ const url = new URL(redirectUrl, window.location.origin);
100
+ if (url.origin === window.location.origin) {
101
+ router.openTab({
102
+ pathname: url.pathname,
103
+ search: url.search,
104
+ hash: url.hash
105
+ });
106
+ return;
107
+ }
108
+ window.open(url.toString(), "_blank");
95
109
  }
96
110
  });
97
111
  }
98
112
  return items;
99
- }, [redirectUrl]);
113
+ }, [redirectUrl, router]);
100
114
  const defaultChartName = React__default.useMemo(() => {
101
115
  if (connection === null) {
102
116
  return "";
@@ -127,7 +141,12 @@ const QLActionPanel = (props) => {
127
141
  if (!result || !result.data || result.status === EntryDialogResolveStatus.Close) {
128
142
  return;
129
143
  }
130
- window.history.replaceState({}, document.title, `/ql/${result.data.entryId}`);
144
+ dispatch(setIsRenameWithoutReload(true));
145
+ try {
146
+ router.replace({ pathname: `/ql/${result.data.entryId}` });
147
+ } finally {
148
+ dispatch(setIsRenameWithoutReload(false));
149
+ }
131
150
  result.data.data = {
132
151
  shared: JSON.parse(result.data.data.shared)
133
152
  };
@@ -152,6 +171,7 @@ const QLActionPanel = (props) => {
152
171
  entryDialoguesRef,
153
172
  defaultChartName,
154
173
  qlState,
174
+ router,
155
175
  wizardState
156
176
  ]
157
177
  );
@@ -2,6 +2,7 @@ import { WIZARD_DATASET_ID_PARAMETER_KEY } from "../../../constants/misc.js";
2
2
  import { CHART_SETTINGS } from "../../../constants/visualizations/index.js";
3
3
  import "../../../navigation/index.js";
4
4
  import { saveWidget } from "../../../store/actions/chartWidget.js";
5
+ import { setIsRenameWithoutReload } from "../../../store/actions/entryContent.js";
5
6
  import { updateClientChartsConfig } from "./preview.js";
6
7
  import { mapClientConfigToChartsConfig } from "../utils/mappers/mapClientToChartsConfig.js";
7
8
  import { removeUrlParameters } from "../utils/wizard.js";
@@ -32,7 +33,12 @@ function receiveWidgetAndPrepareMetadata({
32
33
  pathname += entryId;
33
34
  }
34
35
  if (!location.pathname.includes(entryId)) {
35
- router.replace({ pathname });
36
+ dispatch(setIsRenameWithoutReload(true));
37
+ try {
38
+ router.replace({ pathname });
39
+ } finally {
40
+ dispatch(setIsRenameWithoutReload(false));
41
+ }
36
42
  }
37
43
  }
38
44
  if (error) {
@@ -23,6 +23,7 @@ import { registry } from "../../../../registry/index.js";
23
23
  import { openDialogSaveDraftChartAsActualConfirm } from "../../../../store/actions/dialog.js";
24
24
  import { addEditHistoryPoint, resetEditHistoryUnit, initEditHistoryUnit } from "../../../../store/actions/editHistory.js";
25
25
  import { reloadRevisionsOnSave, cleanRevisions, setRevisionsMode } from "../../../../store/actions/entryContent.js";
26
+ import { selectIsRenameWithoutReload } from "../../../../store/selectors/entryContent.js";
26
27
  import { RevisionsMode } from "../../../../store/typings/entryContent.js";
27
28
  import "../../../../utils/index.js";
28
29
  import { isDraft, isEditMode } from "../../../dash/store/selectors/dashTypedSelectors.js";
@@ -67,9 +68,9 @@ class Wizard extends React__default.Component {
67
68
  super(props);
68
69
  this.chartKitRef = React__default.createRef();
69
70
  this.unloadConfirmation = (event) => {
70
- const { previewHash, widgetHash, initialPreviewHash } = this.props;
71
+ const { previewHash, widgetHash, initialPreviewHash, isRenameWithoutReload } = this.props;
71
72
  const widgetChanged = previewHash !== widgetHash && previewHash !== initialPreviewHash;
72
- if (widgetChanged) {
73
+ if (widgetChanged && !isRenameWithoutReload) {
73
74
  const message = i18n("wizard", "toast_unsaved");
74
75
  (event || window.event).returnValue = message;
75
76
  return message;
@@ -478,7 +479,8 @@ const mapStateToProps = (state, ownProps) => {
478
479
  isParentDashWasChanged: isDraft(state) && isEditMode(state),
479
480
  initialPreviewHash: selectInitialPreviewHash(state),
480
481
  wizardState: state.wizard,
481
- description: selectPreviewDescription(state)
482
+ description: selectPreviewDescription(state),
483
+ isRenameWithoutReload: selectIsRenameWithoutReload(state)
482
484
  };
483
485
  };
484
486
  const mapDispatchToProps = (dispatch) => {
@@ -4,7 +4,9 @@ import { Dialog, TextInput } from "@gravity-ui/uikit";
4
4
  import block from "bem-cn-lite";
5
5
  import { I18n } from "../../../../../i18n/index.js";
6
6
  import { useDispatch, useSelector } from "react-redux";
7
+ import { getEntryNameInputError } from "../../../../utils/errors/errorByCode.js";
7
8
  import DialogManager from "../../../../components/DialogManager/DialogManager.js";
9
+ import { showToast } from "../../../../store/actions/toaster.js";
8
10
  import { renameEntry } from "../../store/actions/index.js";
9
11
  import { selectRenameEntryIsLoading } from "../../store/selectors/index.js";
10
12
  /* empty css */
@@ -15,21 +17,54 @@ const RenameEntryDialog = React__default.memo(({ open, data, onClose }) => {
15
17
  const dispatch = useDispatch();
16
18
  const isLoading = useSelector(selectRenameEntryIsLoading);
17
19
  const [newNameValue, setNewNameValue] = React__default.useState(data.name);
20
+ const [inputError, setInputError] = React__default.useState(false);
18
21
  const textInputControlRef = React__default.useRef(null);
19
- const handleApply = React__default.useCallback(() => {
20
- dispatch(
21
- renameEntry({
22
- entryId: data.entryId,
23
- name: newNameValue,
24
- updateInline: true
25
- })
26
- ).then(() => {
27
- onClose();
28
- });
22
+ React__default.useEffect(() => {
23
+ if (open) {
24
+ setNewNameValue(data.name);
25
+ setInputError(false);
26
+ }
27
+ }, [data.name, open]);
28
+ const handleApply = React__default.useCallback(async () => {
29
+ const name = newNameValue.trim();
30
+ try {
31
+ const renamedEntry = await dispatch(
32
+ renameEntry({
33
+ entryId: data.entryId,
34
+ name,
35
+ updateInline: true
36
+ })
37
+ );
38
+ if (renamedEntry) {
39
+ onClose();
40
+ }
41
+ } catch (error) {
42
+ const errorMessage = getEntryNameInputError(
43
+ error,
44
+ i18n("label_entry-name-already-exists")
45
+ );
46
+ if (errorMessage) {
47
+ setInputError(errorMessage);
48
+ return;
49
+ }
50
+ dispatch(
51
+ showToast({
52
+ title: error.message,
53
+ name: "RenameEntryDialogFailed",
54
+ error,
55
+ withReport: true
56
+ })
57
+ );
58
+ }
29
59
  }, [data.entryId, dispatch, newNameValue, onClose]);
60
+ const handleInputUpdate = React__default.useCallback((value) => {
61
+ setNewNameValue(value);
62
+ setInputError(false);
63
+ }, []);
30
64
  const propsButtonApply = React__default.useMemo(() => {
65
+ const normalizedValue = newNameValue.trim();
31
66
  return {
32
- disabled: !newNameValue || newNameValue === data.name
67
+ disabled: !normalizedValue || normalizedValue === data.name
33
68
  };
34
69
  }, [data.name, newNameValue]);
35
70
  return /* @__PURE__ */ jsxs(
@@ -49,7 +84,8 @@ const RenameEntryDialog = React__default.memo(({ open, data, onClose }) => {
49
84
  {
50
85
  value: newNameValue,
51
86
  controlRef: textInputControlRef,
52
- onUpdate: setNewNameValue
87
+ onUpdate: handleInputUpdate,
88
+ error: inputError
53
89
  }
54
90
  )
55
91
  ] }),
@@ -164,7 +164,7 @@ export declare const renameEntry: ({ entryId, name, updateInline, }: {
164
164
  entryId: string;
165
165
  name: string;
166
166
  updateInline: boolean;
167
- }) => (dispatch: WorkbooksDispatch) => CancellablePromise<RenameEntryResponse | null>;
167
+ }) => (dispatch: WorkbooksDispatch) => CancellablePromise<RenameEntryResponse>;
168
168
  type ChangeFavoriteEntryLoadingAction = {
169
169
  type: typeof CHANGE_FAVORITE_ENTRY_LOADING;
170
170
  };
@@ -319,18 +319,12 @@ const renameEntry = ({
319
319
  }).catch((error) => {
320
320
  if (!getSdk().sdk.isCancel(error)) {
321
321
  logger.logError("workbooks/renameEntry failed", error);
322
- dispatch(
323
- showToast({
324
- title: error.message,
325
- error
326
- })
327
- );
328
322
  }
329
323
  dispatch({
330
324
  type: RENAME_ENTRY_FAILED,
331
325
  error
332
326
  });
333
- return null;
327
+ throw error;
334
328
  });
335
329
  };
336
330
  };
@@ -14,4 +14,5 @@ interface EntryIsLockedError extends Error {
14
14
  export declare function isEntryIsLockedError(error: DataLensApiError): error is EntryIsLockedError;
15
15
  export declare function getLoginOrIdFromLockedError(error: EntryIsLockedError): string;
16
16
  export declare function isEntryAlreadyExists(error: DataLensApiError): boolean;
17
+ export declare function getEntryNameInputError(error: DataLensApiError, duplicateNameErrorText: string): string | null;
17
18
  export {};