@gooddata/sdk-ui-catalog 11.46.0-alpha.3 → 11.46.0-alpha.5

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 (110) hide show
  1. package/esm/AnalyticsCatalogDetail.d.ts.map +1 -1
  2. package/esm/AnalyticsCatalogDetail.js +2 -1
  3. package/esm/catalog/Catalog.d.ts.map +1 -1
  4. package/esm/catalog/Catalog.js +5 -2
  5. package/esm/catalogDetail/CatalogDetailActionBar.d.ts +6 -4
  6. package/esm/catalogDetail/CatalogDetailActionBar.d.ts.map +1 -1
  7. package/esm/catalogDetail/CatalogDetailActionBar.js +5 -6
  8. package/esm/catalogDetail/CatalogDetailActions.d.ts +2 -1
  9. package/esm/catalogDetail/CatalogDetailActions.d.ts.map +1 -1
  10. package/esm/catalogDetail/CatalogDetailActions.js +6 -2
  11. package/esm/catalogDetail/CatalogDetailContent.d.ts.map +1 -1
  12. package/esm/catalogDetail/CatalogDetailContent.js +3 -1
  13. package/esm/catalogItem/query.d.ts +7 -2
  14. package/esm/catalogItem/query.d.ts.map +1 -1
  15. package/esm/catalogItem/query.js +16 -1
  16. package/esm/header/CreateObjectButton.d.ts +2 -1
  17. package/esm/header/CreateObjectButton.d.ts.map +1 -1
  18. package/esm/header/CreateObjectButton.js +42 -38
  19. package/esm/localization/bundles/en-US.localization-bundle.d.ts +112 -0
  20. package/esm/localization/bundles/en-US.localization-bundle.d.ts.map +1 -1
  21. package/esm/localization/bundles/en-US.localization-bundle.js +121 -9
  22. package/esm/main/Main.d.ts.map +1 -1
  23. package/esm/main/Main.js +2 -3
  24. package/esm/metric/MetricCreateDialog.d.ts +11 -0
  25. package/esm/metric/MetricCreateDialog.d.ts.map +1 -0
  26. package/esm/metric/MetricCreateDialog.js +55 -0
  27. package/esm/metric/MetricDeleteDialog.d.ts +9 -0
  28. package/esm/metric/MetricDeleteDialog.d.ts.map +1 -0
  29. package/esm/metric/MetricDeleteDialog.js +68 -0
  30. package/esm/metric/MetricDetailActions.d.ts +23 -0
  31. package/esm/metric/MetricDetailActions.d.ts.map +1 -0
  32. package/esm/metric/MetricDetailActions.js +101 -0
  33. package/esm/metric/MetricDialog.d.ts +14 -0
  34. package/esm/metric/MetricDialog.d.ts.map +1 -0
  35. package/esm/metric/MetricDialog.js +100 -0
  36. package/esm/metric/MetricEditDialog.d.ts +11 -0
  37. package/esm/metric/MetricEditDialog.d.ts.map +1 -0
  38. package/esm/metric/MetricEditDialog.js +51 -0
  39. package/esm/metric/MetricMutationContext.d.ts +16 -0
  40. package/esm/metric/MetricMutationContext.d.ts.map +1 -0
  41. package/esm/metric/MetricMutationContext.js +32 -0
  42. package/esm/metric/MetricYamlEditor.d.ts +8 -0
  43. package/esm/metric/MetricYamlEditor.d.ts.map +1 -0
  44. package/esm/metric/MetricYamlEditor.js +20 -0
  45. package/esm/metric/gate.d.ts +9 -0
  46. package/esm/metric/gate.d.ts.map +1 -0
  47. package/esm/metric/gate.js +16 -0
  48. package/esm/metric/metricCompletions.d.ts +7 -0
  49. package/esm/metric/metricCompletions.d.ts.map +1 -0
  50. package/esm/metric/metricCompletions.js +59 -0
  51. package/esm/metric/metricConverter.d.ts +49 -0
  52. package/esm/metric/metricConverter.d.ts.map +1 -0
  53. package/esm/metric/metricConverter.js +101 -0
  54. package/esm/metric/metricCopy.d.ts +4 -0
  55. package/esm/metric/metricCopy.d.ts.map +1 -0
  56. package/esm/metric/metricCopy.js +60 -0
  57. package/esm/metric/metricMutationPort.d.ts +18 -0
  58. package/esm/metric/metricMutationPort.d.ts.map +1 -0
  59. package/esm/metric/metricMutationPort.js +39 -0
  60. package/esm/metric/metricSchema.d.ts +29 -0
  61. package/esm/metric/metricSchema.d.ts.map +1 -0
  62. package/esm/metric/metricSchema.js +30 -0
  63. package/esm/metric/metricSerialization.d.ts +10 -0
  64. package/esm/metric/metricSerialization.d.ts.map +1 -0
  65. package/esm/metric/metricSerialization.js +33 -0
  66. package/esm/metric/metricValidation.d.ts +15 -0
  67. package/esm/metric/metricValidation.d.ts.map +1 -0
  68. package/esm/metric/metricValidation.js +47 -0
  69. package/esm/metric/metricYamlLinter.d.ts +7 -0
  70. package/esm/metric/metricYamlLinter.d.ts.map +1 -0
  71. package/esm/metric/metricYamlLinter.js +13 -0
  72. package/esm/metric/tests/metricMutationPort.test.utils.d.ts +7 -0
  73. package/esm/metric/tests/metricMutationPort.test.utils.d.ts.map +1 -0
  74. package/esm/metric/tests/metricMutationPort.test.utils.js +43 -0
  75. package/esm/parameter/ParameterDialog.d.ts.map +1 -1
  76. package/esm/parameter/ParameterDialog.js +19 -12
  77. package/esm/parameter/ParameterYamlEditor.d.ts +3 -1
  78. package/esm/parameter/ParameterYamlEditor.d.ts.map +1 -1
  79. package/esm/parameter/ParameterYamlEditor.js +4 -3
  80. package/esm/parameter/gate.d.ts +5 -0
  81. package/esm/parameter/gate.d.ts.map +1 -1
  82. package/esm/parameter/gate.js +8 -0
  83. package/esm/parameter/parameterCompletions.d.ts +9 -3
  84. package/esm/parameter/parameterCompletions.d.ts.map +1 -1
  85. package/esm/parameter/parameterCompletions.js +68 -43
  86. package/esm/parameter/parameterSchema.d.ts +23 -3
  87. package/esm/parameter/parameterSchema.d.ts.map +1 -1
  88. package/esm/parameter/parameterSchema.js +32 -10
  89. package/esm/parameter/parameterValidation.d.ts +4 -1
  90. package/esm/parameter/parameterValidation.d.ts.map +1 -1
  91. package/esm/parameter/parameterValidation.js +22 -9
  92. package/esm/parameter/yamlLinter.d.ts +1 -4
  93. package/esm/parameter/yamlLinter.d.ts.map +1 -1
  94. package/esm/parameter/yamlLinter.js +3 -24
  95. package/esm/utils/backendError.d.ts +11 -0
  96. package/esm/utils/backendError.d.ts.map +1 -0
  97. package/esm/utils/backendError.js +21 -0
  98. package/esm/utils/yamlSyntaxLinter.d.ts +11 -0
  99. package/esm/utils/yamlSyntaxLinter.d.ts.map +1 -0
  100. package/esm/utils/yamlSyntaxLinter.js +31 -0
  101. package/package.json +15 -14
  102. package/styles/css/{parameter.css → codeDialog.css} +26 -18
  103. package/styles/css/codeDialog.css.map +1 -0
  104. package/styles/css/detail.css +25 -17
  105. package/styles/css/detail.css.map +1 -1
  106. package/styles/css/main.css +25 -17
  107. package/styles/css/main.css.map +1 -1
  108. package/styles/scss/{parameter.scss → codeDialog.scss} +17 -8
  109. package/styles/scss/detail.scss +1 -1
  110. package/styles/css/parameter.css.map +0 -1
@@ -670,7 +670,7 @@ export const en_US = {
670
670
  "crowdinContext": "Section header label above the YAML editor in the parameter dialog."
671
671
  },
672
672
  "analyticsCatalog.parameter.dialog.sectionHeader.tooltip": {
673
- "text": "Create a YAML statement that defines a numeric parameter.",
673
+ "text": "Create a YAML statement that defines a parameter.",
674
674
  "crowdinContext": "Tooltip shown for the help icon next to the parameter definition section header."
675
675
  },
676
676
  "analyticsCatalog.parameter.dialog.create.submit": {
@@ -710,20 +710,20 @@ export const en_US = {
710
710
  "crowdinContext": "Error shown when the parameter YAML does not follow the expected object structure."
711
711
  },
712
712
  "analyticsCatalog.parameter.validation.unsupportedType": {
713
- "text": "Only NUMBER parameters are supported.",
714
- "crowdinContext": "Error shown when the parameter YAML declares an unsupported parameter type."
713
+ "text": "Only {enabledTypes} parameters are supported.",
714
+ "crowdinContext": "Error shown when the parameter YAML declares an unsupported parameter type. {enabledTypes} is a comma-separated list of the supported type names (e.g. 'NUMBER' or 'NUMBER and STRING')."
715
715
  },
716
716
  "analyticsCatalog.parameter.validation.invalidDefaultValue": {
717
- "text": "Default value must be a number.",
718
- "crowdinContext": "Error shown when the parameter YAML contains a non-numeric defaultValue."
717
+ "text": "Default value must be {type, select, STRING {a string} other {a number}}.",
718
+ "crowdinContext": "Error shown when the parameter YAML contains a defaultValue whose kind does not match the declared parameter type. {type} is the declared type: STRING expects a string, otherwise a number."
719
719
  },
720
720
  "analyticsCatalog.parameter.validation.invalidConstraints": {
721
- "text": "Parameter constraints must use numeric min and max values.",
722
- "crowdinContext": "Error shown when the parameter YAML contains invalid constraint values."
721
+ "text": "Invalid constraints: {type, select, STRING {STRING parameters allow non-negative integer minLength and maxLength} other {NUMBER parameters allow numeric min and max}}.",
722
+ "crowdinContext": "Error shown when the parameter YAML contains invalid constraint values. {type} is the declared type, selecting the constraint keys valid for that type."
723
723
  },
724
724
  "analyticsCatalog.parameter.validation.invalidConstraintRange": {
725
- "text": "Constraint min cannot be greater than max.",
726
- "crowdinContext": "Error shown when the parameter YAML declares an invalid numeric range."
725
+ "text": "Constraint {type, select, STRING {minLength cannot be greater than maxLength} other {min cannot be greater than max}}.",
726
+ "crowdinContext": "Error shown when the parameter YAML declares an invalid constraint range. {type} is the declared type, selecting the constraint key names for that type."
727
727
  },
728
728
  "analyticsCatalog.parameter.validation.invalidTags": {
729
729
  "text": "Tags must be a list of strings.",
@@ -761,6 +761,118 @@ export const en_US = {
761
761
  "text": "Parameter could not be deleted.",
762
762
  "crowdinContext": "Error toast shown when parameter deletion fails."
763
763
  },
764
+ "analyticsCatalog.metric.dialog.create.title": {
765
+ "text": "Create metric",
766
+ "crowdinContext": "Title of the dialog for creating a new metric as code."
767
+ },
768
+ "analyticsCatalog.metric.dialog.edit.title": {
769
+ "text": "Edit metric",
770
+ "crowdinContext": "Title of the dialog for editing a metric as code."
771
+ },
772
+ "analyticsCatalog.metric.dialog.sectionHeader": {
773
+ "text": "Metric definition",
774
+ "crowdinContext": "Header above the as-code editor in the metric dialog."
775
+ },
776
+ "analyticsCatalog.metric.dialog.sectionHeader.tooltip": {
777
+ "text": "Define the metric in YAML, including its MAQL expression and format.",
778
+ "crowdinContext": "Tooltip explaining the as-code metric editor."
779
+ },
780
+ "analyticsCatalog.metric.dialog.create.submit": {
781
+ "text": "Create",
782
+ "crowdinContext": "Submit button of the create metric dialog."
783
+ },
784
+ "analyticsCatalog.metric.dialog.edit.submit": {
785
+ "text": "Save",
786
+ "crowdinContext": "Submit button of the edit metric dialog."
787
+ },
788
+ "analyticsCatalog.metric.actions.openInEditor": {
789
+ "text": "Open in metric editor",
790
+ "crowdinContext": "Detail-panel menu action that opens the metric in the standalone metric editor (as opposed to the inline as-code editor)."
791
+ },
792
+ "analyticsCatalog.metric.dialog.edit.duplicate": {
793
+ "text": "Duplicate",
794
+ "crowdinContext": "Button that duplicates the metric being edited into a new metric."
795
+ },
796
+ "analyticsCatalog.metric.dialog.edit.idImmutable": {
797
+ "text": "The metric id cannot be changed. Use Duplicate to create a metric with a different id.",
798
+ "crowdinContext": "Validation error shown when the user changes the id while editing a metric."
799
+ },
800
+ "analyticsCatalog.metric.dialog.cancel": {
801
+ "text": "Cancel",
802
+ "crowdinContext": "Cancel button of the metric dialogs."
803
+ },
804
+ "analyticsCatalog.metric.dialog.help": {
805
+ "text": "How to write a metric",
806
+ "crowdinContext": "Help link in the metric as-code dialog."
807
+ },
808
+ "analyticsCatalog.metric.dialog.submit.error": {
809
+ "text": "The metric could not be saved.",
810
+ "crowdinContext": "Generic error shown when saving a metric fails."
811
+ },
812
+ "analyticsCatalog.metric.validation.empty": {
813
+ "text": "The metric definition cannot be empty.",
814
+ "crowdinContext": "Validation error shown when the metric editor is empty."
815
+ },
816
+ "analyticsCatalog.metric.validation.syntax": {
817
+ "text": "The metric definition is not valid YAML.",
818
+ "crowdinContext": "Validation error shown when the metric YAML has a syntax error."
819
+ },
820
+ "analyticsCatalog.metric.validation.invalidStructure": {
821
+ "text": "The metric definition has an invalid structure.",
822
+ "crowdinContext": "Validation error shown when the metric YAML does not match the expected structure."
823
+ },
824
+ "analyticsCatalog.metric.validation.missingMaql": {
825
+ "text": "The metric must define a MAQL expression.",
826
+ "crowdinContext": "Validation error shown when the metric YAML is missing the maql field."
827
+ },
828
+ "analyticsCatalog.metric.validation.invalidTags": {
829
+ "text": "Tags must be a list of text values.",
830
+ "crowdinContext": "Validation error shown when the metric tags are not a list of strings."
831
+ },
832
+ "analyticsCatalog.dialog.error.label": {
833
+ "text": "Error:",
834
+ "crowdinContext": "Bold label prefixing the error message in the as-code editor dialogs (metrics, parameters)."
835
+ },
836
+ "analyticsCatalog.metric.create.defaultTitle": {
837
+ "text": "My metric",
838
+ "crowdinContext": "Default placeholder title seeded into the new-metric as-code editor before the user types their own."
839
+ },
840
+ "analyticsCatalog.metric.create.success": {
841
+ "text": "Metric created.",
842
+ "crowdinContext": "Toast shown after a metric is created successfully from the Analytics Catalog."
843
+ },
844
+ "analyticsCatalog.metric.update.success": {
845
+ "text": "Metric saved.",
846
+ "crowdinContext": "Toast shown after a metric is updated successfully from the Analytics Catalog."
847
+ },
848
+ "analyticsCatalog.metric.load.error": {
849
+ "text": "The metric could not be loaded.",
850
+ "crowdinContext": "Error toast shown when loading a metric for editing or duplication fails."
851
+ },
852
+ "analyticsCatalog.metric.dialog.delete.title": {
853
+ "text": "Delete metric",
854
+ "crowdinContext": "Title of the confirmation dialog for deleting a metric."
855
+ },
856
+ "analyticsCatalog.metric.dialog.delete.body": {
857
+ "text": "Are you sure you want to delete <b>{name}</b>?",
858
+ "crowdinContext": "Body of the confirmation dialog for deleting a metric. {name} is the metric title."
859
+ },
860
+ "analyticsCatalog.metric.dialog.delete.usageWarning": {
861
+ "text": "This metric is used by {count, plural, one {# object} other {# objects}}. Deleting it may break them.",
862
+ "crowdinContext": "Warning shown in the metric delete dialog when the metric is referenced by other objects."
863
+ },
864
+ "analyticsCatalog.metric.dialog.delete.submit": {
865
+ "text": "Delete",
866
+ "crowdinContext": "Submit button of the delete metric confirmation dialog."
867
+ },
868
+ "analyticsCatalog.metric.delete.success": {
869
+ "text": "Metric deleted.",
870
+ "crowdinContext": "Toast shown after a metric is deleted successfully from the Analytics Catalog."
871
+ },
872
+ "analyticsCatalog.metric.delete.error": {
873
+ "text": "Metric could not be deleted.",
874
+ "crowdinContext": "Error toast shown when metric deletion fails."
875
+ },
764
876
  "analyticsCatalog.share.button": {
765
877
  "text": "Share",
766
878
  "crowdinContext": "Button label opening the share dialog from the catalog detail page."
@@ -1 +1 @@
1
- {"version":3,"file":"Main.d.ts","sourceRoot":"","sources":["../../src/main/Main.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,OAAO,CAAC;AAIxC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAIpE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAElE,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAelF,KAAK,KAAK,GAAG;IACT,OAAO,EAAE,kBAAkB,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,eAAe,GAAG,YAAY,GAAG,IAAI,CAAC;IAClD,aAAa,EAAE,CAAC,IAAI,EAAE,eAAe,GAAG,YAAY,GAAG,IAAI,KAAK,IAAI,CAAC;IACrE,YAAY,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;IAC3C,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,WAAW,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC3E,uBAAuB,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,eAAe,KAAK,IAAI,CAAC;CAC/E,CAAC;AAQF,wBAAgB,IAAI,CAAC,EACjB,OAAO,EACP,SAAS,EACT,IAAI,EACJ,UAAU,EACV,aAAa,EACb,YAAY,EACZ,aAAa,EACb,WAAW,EACX,uBAAuB,EAC1B,EAAE,KAAK,2CAmEP"}
1
+ {"version":3,"file":"Main.d.ts","sourceRoot":"","sources":["../../src/main/Main.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,OAAO,CAAC;AAIxC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAIpE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAElE,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAclF,KAAK,KAAK,GAAG;IACT,OAAO,EAAE,kBAAkB,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,eAAe,GAAG,YAAY,GAAG,IAAI,CAAC;IAClD,aAAa,EAAE,CAAC,IAAI,EAAE,eAAe,GAAG,YAAY,GAAG,IAAI,KAAK,IAAI,CAAC;IACrE,YAAY,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;IAC3C,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,WAAW,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC3E,uBAAuB,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,eAAe,KAAK,IAAI,CAAC;CAC/E,CAAC;AAQF,wBAAgB,IAAI,CAAC,EACjB,OAAO,EACP,SAAS,EACT,IAAI,EACJ,UAAU,EACV,aAAa,EACb,YAAY,EACZ,aAAa,EACb,WAAW,EACX,uBAAuB,EAC1B,EAAE,KAAK,2CAmEP"}
package/esm/main/Main.js CHANGED
@@ -16,7 +16,6 @@ import { FilterQualityMemo } from "../filter/FilterQuality.js";
16
16
  import { FilterResetButtonMemo } from "../filter/FilterResetButton.js";
17
17
  import { FilterTagsMemo } from "../filter/FilterTags.js";
18
18
  import { FilterVisibilityMemo } from "../filter/FilterVisibility.js";
19
- import { ObjectTypes } from "../objectType/constants.js";
20
19
  import { useIsCatalogQualityEnabled } from "../quality/gate.js";
21
20
  import { Table } from "../table/Table.js";
22
21
  const messages = defineMessages({
@@ -41,13 +40,13 @@ export function Main({ backend, workspace, open, openedItem, setItemOpened, onOp
41
40
  onCatalogItemNavigation?.(event, ref);
42
41
  }, onCatalogItemCreate: (item) => {
43
42
  setItemOpened(item);
44
- refetchObjectType(ObjectTypes.PARAMETER);
43
+ refetchObjectType(item.type);
45
44
  }, onCatalogItemUpdate: (item) => {
46
45
  setItemOpened(item);
47
46
  updateItem(item);
48
47
  }, onCatalogItemDelete: (ref) => {
49
48
  removeItem(ref);
50
- refetchObjectType(ObjectTypes.PARAMETER);
49
+ refetchObjectType(ref.type);
51
50
  onCloseDetail();
52
51
  }, onCatalogItemUpdateError: (err) => {
53
52
  addError(messages.updateFailed, {
@@ -0,0 +1,11 @@
1
+ import type { IMeasureMetadataObjectDefinition } from "@gooddata/sdk-model";
2
+ import type { ICatalogItemMeasure } from "../catalogItem/types.js";
3
+ type Props = {
4
+ sourceDefinition?: IMeasureMetadataObjectDefinition;
5
+ isLoading?: boolean;
6
+ onClose: () => void;
7
+ onCreated?: (item: ICatalogItemMeasure) => void;
8
+ };
9
+ export declare function MetricCreateDialog({ sourceDefinition, isLoading, onClose, onCreated }: Props): import("react/jsx-runtime").JSX.Element;
10
+ export {};
11
+ //# sourceMappingURL=MetricCreateDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MetricCreateDialog.d.ts","sourceRoot":"","sources":["../../src/metric/MetricCreateDialog.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,gCAAgC,EAAE,MAAM,qBAAqB,CAAC;AAG5E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAYnE,KAAK,KAAK,GAAG;IACT,gBAAgB,CAAC,EAAE,gCAAgC,CAAC;IACpD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,mBAAmB,KAAK,IAAI,CAAC;CACnD,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,EAAE,gBAAgB,EAAE,SAAiB,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,KAAK,2CA0DpG"}
@@ -0,0 +1,55 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // (C) 2026 GoodData Corporation
3
+ import { useCallback, useMemo } from "react";
4
+ import { defineMessages, useIntl } from "react-intl";
5
+ import { useToastMessage } from "@gooddata/sdk-ui-kit";
6
+ import { definitionToMetricYaml, mergeCopiedMetricDefinition } from "./metricConverter.js";
7
+ import { createCopiedMetric, isDuplicateIdError } from "./metricCopy.js";
8
+ import { MetricDialog } from "./MetricDialog.js";
9
+ import { useMetricMutation } from "./MetricMutationContext.js";
10
+ const messages = defineMessages({
11
+ metricCreateSuccess: { id: "analyticsCatalog.metric.create.success" },
12
+ metricCreateDefaultTitle: { id: "analyticsCatalog.metric.create.defaultTitle" },
13
+ });
14
+ export function MetricCreateDialog({ sourceDefinition, isLoading = false, onClose, onCreated }) {
15
+ const intl = useIntl();
16
+ const { addSuccess } = useToastMessage();
17
+ const mutation = useMetricMutation();
18
+ // The single source of truth for a duplicate's copy semantics (title bump, id derivation, and
19
+ // which author-owned fields carry over). Undefined for a blank create.
20
+ const copiedMetric = useMemo(() => (sourceDefinition ? createCopiedMetric(sourceDefinition) : undefined), [sourceDefinition]);
21
+ const initialMetric = useMemo(() => {
22
+ if (!copiedMetric) {
23
+ return {
24
+ type: "metric",
25
+ title: intl.formatMessage(messages.metricCreateDefaultTitle),
26
+ maql: "SELECT 1",
27
+ };
28
+ }
29
+ return definitionToMetricYaml(copiedMetric);
30
+ }, [copiedMetric, intl]);
31
+ const copiedId = copiedMetric ? initialMetric.id : undefined;
32
+ const handleSubmit = useCallback(async (metric) => {
33
+ // A duplicate layers the author's YAML edits over the copied source so its non-YAML
34
+ // fields (metricType, isHiddenFromKda) survive the lossy YAML round-trip; a blank create
35
+ // has no source and persists the parsed YAML as-is.
36
+ const definition = copiedMetric ? mergeCopiedMetricDefinition(copiedMetric, metric) : metric;
37
+ let createdMetric;
38
+ try {
39
+ createdMetric = await mutation.create(definition);
40
+ }
41
+ catch (error) {
42
+ if (copiedId !== undefined && definition.id === copiedId && isDuplicateIdError(error)) {
43
+ const { id: _id, ...metricWithoutId } = definition;
44
+ createdMetric = await mutation.create(metricWithoutId);
45
+ }
46
+ else {
47
+ throw error;
48
+ }
49
+ }
50
+ onCreated?.(createdMetric);
51
+ onClose();
52
+ addSuccess(messages.metricCreateSuccess);
53
+ }, [addSuccess, copiedId, copiedMetric, mutation, onClose, onCreated]);
54
+ return (_jsx(MetricDialog, { mode: "create", isLoading: isLoading, initialMetric: initialMetric, onClose: onClose, onSubmit: handleSubmit }));
55
+ }
@@ -0,0 +1,9 @@
1
+ import { type ICatalogItemMeasure } from "../catalogItem/types.js";
2
+ type Props = {
3
+ item: ICatalogItemMeasure;
4
+ onClose: () => void;
5
+ onDeleted: () => void;
6
+ };
7
+ export declare function MetricDeleteDialog({ item, onClose, onDeleted }: Props): import("react/jsx-runtime").JSX.Element;
8
+ export {};
9
+ //# sourceMappingURL=MetricDeleteDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MetricDeleteDialog.d.ts","sourceRoot":"","sources":["../../src/metric/MetricDeleteDialog.tsx"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAcnE,KAAK,KAAK,GAAG;IACT,IAAI,EAAE,mBAAmB,CAAC;IAC1B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,SAAS,EAAE,MAAM,IAAI,CAAC;CACzB,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,KAAK,2CA+ErE"}
@@ -0,0 +1,68 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // (C) 2026 GoodData Corporation
3
+ import { useCallback, useEffect, useState } from "react";
4
+ import { FormattedMessage, defineMessages, useIntl } from "react-intl";
5
+ import { ConfirmDialog, useToastMessage } from "@gooddata/sdk-ui-kit";
6
+ import {} from "../catalogItem/types.js";
7
+ import { useMetricMutation } from "./MetricMutationContext.js";
8
+ const messages = defineMessages({
9
+ title: { id: "analyticsCatalog.metric.dialog.delete.title" },
10
+ body: { id: "analyticsCatalog.metric.dialog.delete.body" },
11
+ usageWarning: { id: "analyticsCatalog.metric.dialog.delete.usageWarning" },
12
+ submit: { id: "analyticsCatalog.metric.dialog.delete.submit" },
13
+ cancel: { id: "analyticsCatalog.metric.dialog.cancel" },
14
+ deleteSuccess: { id: "analyticsCatalog.metric.delete.success" },
15
+ deleteError: { id: "analyticsCatalog.metric.delete.error" },
16
+ });
17
+ export function MetricDeleteDialog({ item, onClose, onDeleted }) {
18
+ const intl = useIntl();
19
+ const { addSuccess, addError } = useToastMessage();
20
+ const mutation = useMetricMutation();
21
+ const [isDeleting, setIsDeleting] = useState(false);
22
+ // undefined until the usage lookup resolves; deletion is withheld until then so the dependent-object
23
+ // warning can surface before the user can confirm.
24
+ const [referencingCount, setReferencingCount] = useState(undefined);
25
+ const displayName = item.title || item.identifier;
26
+ useEffect(() => {
27
+ let cancelled = false;
28
+ mutation
29
+ .getReferencingObjects(item)
30
+ .then((referencing) => {
31
+ if (!cancelled) {
32
+ const count = (referencing.insights?.length ?? 0) + (referencing.measures?.length ?? 0);
33
+ setReferencingCount(count);
34
+ }
35
+ })
36
+ .catch(() => {
37
+ // Usage lookup is advisory; on failure fall back to no warning rather than block deletion.
38
+ if (!cancelled) {
39
+ setReferencingCount(0);
40
+ }
41
+ });
42
+ return () => {
43
+ cancelled = true;
44
+ };
45
+ }, [item, mutation]);
46
+ const handleDelete = useCallback(async () => {
47
+ setIsDeleting(true);
48
+ try {
49
+ await mutation.delete(item);
50
+ onDeleted();
51
+ onClose();
52
+ addSuccess(messages.deleteSuccess);
53
+ }
54
+ catch {
55
+ addError(messages.deleteError);
56
+ setIsDeleting(false);
57
+ }
58
+ }, [addError, addSuccess, item, mutation, onClose, onDeleted]);
59
+ const handleClose = useCallback(() => {
60
+ if (!isDeleting) {
61
+ onClose();
62
+ }
63
+ }, [isDeleting, onClose]);
64
+ return (_jsxs(ConfirmDialog, { headline: intl.formatMessage(messages.title), cancelButtonText: intl.formatMessage(messages.cancel), submitButtonText: intl.formatMessage(messages.submit), isPositive: false, isSubmitDisabled: isDeleting || referencingCount === undefined, isCancelDisabled: isDeleting, showProgressIndicator: isDeleting, onCancel: handleClose, onClose: handleClose, onSubmit: handleDelete, displayCloseButton: !isDeleting, children: [_jsx(FormattedMessage, { ...messages.body, values: {
65
+ name: displayName,
66
+ b: (chunks) => _jsx("b", { children: chunks }),
67
+ } }), referencingCount ? (_jsx("div", { children: _jsx(FormattedMessage, { ...messages.usageWarning, values: { count: referencingCount } }) })) : null] }));
68
+ }
@@ -0,0 +1,23 @@
1
+ import { type MouseEvent } from "react";
2
+ import type { OpenHandlerEvent } from "../catalogDetail/types.js";
3
+ import type { ICatalogItemMeasure, ICatalogItemRef } from "../catalogItem/types.js";
4
+ /**
5
+ * @internal
6
+ */
7
+ export interface IMetricDetailActionsProps {
8
+ item: ICatalogItemMeasure;
9
+ /** When provided, adds an "Open in metric editor" menu action that opens the standalone editor. */
10
+ onOpen?: (event: MouseEvent, openEvent: OpenHandlerEvent) => void;
11
+ canShare?: boolean;
12
+ onShare?: () => void;
13
+ onCatalogItemCreate?: (item: ICatalogItemMeasure) => void;
14
+ onCatalogItemUpdate?: (item: ICatalogItemMeasure) => void;
15
+ onCatalogItemDelete?: (ref: ICatalogItemRef) => void;
16
+ }
17
+ /**
18
+ * Detail-panel actions for a metric: inline edit, duplicate, and delete via the as-code editor,
19
+ * alongside the shared Share button and "Open in metric editor" action.
20
+ * @internal
21
+ */
22
+ export declare function MetricDetailActions({ item, onOpen, canShare, onShare, onCatalogItemCreate, onCatalogItemUpdate, onCatalogItemDelete }: IMetricDetailActionsProps): import("react/jsx-runtime").JSX.Element;
23
+ //# sourceMappingURL=MetricDetailActions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MetricDetailActions.d.ts","sourceRoot":"","sources":["../../src/metric/MetricDetailActions.tsx"],"names":[],"mappings":"AAEA,OAAO,EAEH,KAAK,UAAU,EAMlB,MAAM,OAAO,CAAC;AAWf,OAAO,KAAK,EAAwB,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACxF,OAAO,KAAK,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAqBpF;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACtC,IAAI,EAAE,mBAAmB,CAAC;IAC1B,mGAAmG;IACnG,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAClE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,mBAAmB,CAAC,EAAE,CAAC,IAAI,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAC1D,mBAAmB,CAAC,EAAE,CAAC,IAAI,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAC1D,mBAAmB,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,IAAI,CAAC;CACxD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,EAChC,IAAI,EACJ,MAAM,EACN,QAAQ,EACR,OAAO,EACP,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACtB,EAAE,yBAAyB,2CA6H3B"}
@@ -0,0 +1,101 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // (C) 2026 GoodData Corporation
3
+ import { useCallback, useEffect, useMemo, useRef, useState, } from "react";
4
+ import { defineMessages, useIntl } from "react-intl";
5
+ import { useWorkspaceStrict } from "@gooddata/sdk-ui";
6
+ import { useToastMessage } from "@gooddata/sdk-ui-kit";
7
+ import { catalogDetailActionDelete, catalogDetailActionDuplicate } from "../automation/testIds.js";
8
+ import { CatalogDetailActionBar } from "../catalogDetail/CatalogDetailActionBar.js";
9
+ import { ShareButton } from "../catalogDetail/share/ShareButton.js";
10
+ import { MetricCreateDialog } from "./MetricCreateDialog.js";
11
+ import { MetricDeleteDialog } from "./MetricDeleteDialog.js";
12
+ import { MetricEditDialog } from "./MetricEditDialog.js";
13
+ import { useMetricMutation } from "./MetricMutationContext.js";
14
+ const messages = defineMessages({
15
+ openInEditor: { id: "analyticsCatalog.metric.actions.openInEditor" },
16
+ duplicate: { id: "analyticsCatalog.metric.dialog.edit.duplicate" },
17
+ delete: { id: "analyticsCatalog.metric.dialog.delete.submit" },
18
+ loadError: { id: "analyticsCatalog.metric.load.error" },
19
+ });
20
+ /**
21
+ * Detail-panel actions for a metric: inline edit, duplicate, and delete via the as-code editor,
22
+ * alongside the shared Share button and "Open in metric editor" action.
23
+ * @internal
24
+ */
25
+ export function MetricDetailActions({ item, onOpen, canShare, onShare, onCatalogItemCreate, onCatalogItemUpdate, onCatalogItemDelete, }) {
26
+ const intl = useIntl();
27
+ const workspaceId = useWorkspaceStrict();
28
+ const { addError } = useToastMessage();
29
+ const mutation = useMetricMutation();
30
+ const [dialog, setDialog] = useState(undefined);
31
+ const isMounted = useRef(true);
32
+ useEffect(() => {
33
+ // Reset on (re)mount: StrictMode runs mount -> cleanup -> remount, and without this the ref
34
+ // would stay false after the remount even though the component is mounted.
35
+ isMounted.current = true;
36
+ return () => {
37
+ isMounted.current = false;
38
+ };
39
+ }, []);
40
+ const actions = useMemo(() => {
41
+ const result = [];
42
+ // The standalone metric editor stays reachable alongside the inline Edit; omitted when the
43
+ // host does not provide an open handler, so the menu never shows a dead item.
44
+ if (onOpen) {
45
+ result.push({ id: "open", label: intl.formatMessage(messages.openInEditor) });
46
+ }
47
+ result.push({
48
+ id: "duplicate",
49
+ label: intl.formatMessage(messages.duplicate),
50
+ dataTestId: catalogDetailActionDuplicate,
51
+ }, {
52
+ id: "delete",
53
+ label: intl.formatMessage(messages.delete),
54
+ isDestructive: true,
55
+ dataTestId: catalogDetailActionDelete,
56
+ });
57
+ return result;
58
+ }, [intl, onOpen]);
59
+ const closeDialog = useCallback(() => setDialog(undefined), []);
60
+ const handleEditOpen = useCallback(() => setDialog({ kind: "edit", item }), [item]);
61
+ const handleActionsMenuSelect = useCallback(async (actionId, event) => {
62
+ if (actionId === "open") {
63
+ onOpen?.(event, {
64
+ item,
65
+ workspaceId,
66
+ newTab: event.metaKey || event.ctrlKey,
67
+ preventDefault: event.preventDefault.bind(event),
68
+ });
69
+ return;
70
+ }
71
+ if (actionId === "delete") {
72
+ setDialog({ kind: "delete", item });
73
+ return;
74
+ }
75
+ if (actionId === "duplicate") {
76
+ // The catalog item carries no MAQL, so show the create dialog in its loading state
77
+ // and feed it the copied definition once the full metric is fetched. Only advance
78
+ // while still in this loading state, so closing the dialog or opening Edit/Delete
79
+ // meanwhile cancels the duplicate.
80
+ setDialog({ kind: "duplicate", isLoading: true });
81
+ try {
82
+ const measure = await mutation.load(item);
83
+ if (isMounted.current) {
84
+ setDialog((current) => current?.kind === "duplicate" && current.isLoading
85
+ ? { kind: "duplicate", sourceDefinition: measure, isLoading: false }
86
+ : current);
87
+ }
88
+ }
89
+ catch {
90
+ if (isMounted.current) {
91
+ addError(messages.loadError);
92
+ setDialog((current) => current?.kind === "duplicate" && current.isLoading ? undefined : current);
93
+ }
94
+ }
95
+ }
96
+ }, [addError, item, mutation, onOpen, workspaceId]);
97
+ const handleEditDuplicate = useCallback((sourceDefinition) => {
98
+ setDialog({ kind: "duplicate", sourceDefinition, isLoading: false });
99
+ }, []);
100
+ return (_jsxs(_Fragment, { children: [_jsx(CatalogDetailActionBar, { item: item, workspaceId: workspaceId, actions: actions, leadingActions: canShare && onShare ? _jsx(ShareButton, { onClick: onShare }) : null, onEditClick: handleEditOpen, onActionsMenuSelect: handleActionsMenuSelect }), dialog?.kind === "edit" ? (_jsx(MetricEditDialog, { item: dialog.item, onClose: closeDialog, onSaved: (saved) => onCatalogItemUpdate?.(saved), onDuplicate: handleEditDuplicate })) : null, dialog?.kind === "duplicate" ? (_jsx(MetricCreateDialog, { sourceDefinition: dialog.sourceDefinition, isLoading: dialog.isLoading, onClose: closeDialog, onCreated: (created) => onCatalogItemCreate?.(created) })) : null, dialog?.kind === "delete" ? (_jsx(MetricDeleteDialog, { item: dialog.item, onClose: closeDialog, onDeleted: () => onCatalogItemDelete?.(dialog.item) })) : null] }));
101
+ }
@@ -0,0 +1,14 @@
1
+ import type { IMeasureMetadataObjectDefinition } from "@gooddata/sdk-model";
2
+ import type { MetricYaml } from "./metricConverter.js";
3
+ export type MetricDialogMode = "create" | "edit";
4
+ type Props = {
5
+ mode: MetricDialogMode;
6
+ initialMetric?: MetricYaml;
7
+ isLoading?: boolean;
8
+ onClose: () => void;
9
+ onSubmit: (measure: IMeasureMetadataObjectDefinition) => Promise<void>;
10
+ onDuplicate?: (measure: IMeasureMetadataObjectDefinition) => void;
11
+ };
12
+ export declare function MetricDialog(props: Props): import("react/jsx-runtime").JSX.Element;
13
+ export {};
14
+ //# sourceMappingURL=MetricDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MetricDialog.d.ts","sourceRoot":"","sources":["../../src/metric/MetricDialog.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,gCAAgC,EAAE,MAAM,qBAAqB,CAAC;AAO5E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AA0BvD,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEjD,KAAK,KAAK,GAAG;IACT,IAAI,EAAE,gBAAgB,CAAC;IAGvB,aAAa,CAAC,EAAE,UAAU,CAAC;IAC3B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,CAAC,OAAO,EAAE,gCAAgC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,gCAAgC,KAAK,IAAI,CAAC;CACrE,CAAC;AAEF,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,2CA8KxC"}
@@ -0,0 +1,100 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // (C) 2026 GoodData Corporation
3
+ import { Suspense, lazy, useCallback, useMemo, useRef, useState } from "react";
4
+ import { FormattedMessage, defineMessages, useIntl } from "react-intl";
5
+ import { LoadingComponent } from "@gooddata/sdk-ui";
6
+ import { ConfirmDialog, UiButton, UiIcon, UiLink, UiTooltip } from "@gooddata/sdk-ui-kit";
7
+ import { useIsWhiteLabeled } from "../permission/PermissionsContext.js";
8
+ import { extractBackendErrorDetail } from "../utils/backendError.js";
9
+ import { serializeMetricToYaml } from "./metricSerialization.js";
10
+ import { validateMetricYaml } from "./metricValidation.js";
11
+ const MetricYamlEditor = lazy(() => import("./MetricYamlEditor.js").then((m) => ({ default: m.MetricYamlEditor })));
12
+ const METRIC_DOCS_URL = "https://www.gooddata.ai/docs/cloud/api-and-sdk/vs-code-extension/structures/#metric";
13
+ const messages = defineMessages({
14
+ empty: { id: "analyticsCatalog.metric.validation.empty" },
15
+ syntax: { id: "analyticsCatalog.metric.validation.syntax" },
16
+ invalidStructure: { id: "analyticsCatalog.metric.validation.invalidStructure" },
17
+ idImmutable: { id: "analyticsCatalog.metric.dialog.edit.idImmutable" },
18
+ missingMaql: { id: "analyticsCatalog.metric.validation.missingMaql" },
19
+ invalidTags: { id: "analyticsCatalog.metric.validation.invalidTags" },
20
+ dialogSubmitError: { id: "analyticsCatalog.metric.dialog.submit.error" },
21
+ dialogCreateTitle: { id: "analyticsCatalog.metric.dialog.create.title" },
22
+ dialogEditTitle: { id: "analyticsCatalog.metric.dialog.edit.title" },
23
+ dialogCreateSubmit: { id: "analyticsCatalog.metric.dialog.create.submit" },
24
+ dialogEditSubmit: { id: "analyticsCatalog.metric.dialog.edit.submit" },
25
+ dialogCancel: { id: "analyticsCatalog.metric.dialog.cancel" },
26
+ dialogDuplicate: { id: "analyticsCatalog.metric.dialog.edit.duplicate" },
27
+ });
28
+ export function MetricDialog(props) {
29
+ const { mode, initialMetric, isLoading = false, onClose, onSubmit, onDuplicate } = props;
30
+ const intl = useIntl();
31
+ const isEdit = mode === "edit";
32
+ const isWhiteLabeled = useIsWhiteLabeled();
33
+ const initialYaml = useMemo(() => (initialMetric ? serializeMetricToYaml(initialMetric) : ""), [initialMetric]);
34
+ // The user's edits; `null` until they type. Submit falls back to `initialYaml`, so the editor
35
+ // mounts once with real data and is the single source of truth — no re-seed effect needed.
36
+ const yamlValue = useRef(null);
37
+ const [validationError, setValidationError] = useState(null);
38
+ const [submitError, setSubmitError] = useState(null);
39
+ const [isSubmitting, setIsSubmitting] = useState(false);
40
+ const [isDirty, setIsDirty] = useState(false);
41
+ const headlineMessage = intl.formatMessage(isEdit ? messages.dialogEditTitle : messages.dialogCreateTitle);
42
+ const submitMessage = intl.formatMessage(isEdit ? messages.dialogEditSubmit : messages.dialogCreateSubmit);
43
+ const cancelMessage = intl.formatMessage(messages.dialogCancel);
44
+ const fixedIdentifier = isEdit ? initialMetric?.id : undefined;
45
+ const validate = useCallback((value, allowIdChange = false) => {
46
+ return validateMetricYaml(value, {
47
+ fixedIdentifier: isEdit && !allowIdChange ? fixedIdentifier : undefined,
48
+ });
49
+ }, [fixedIdentifier, isEdit]);
50
+ const handleClose = useCallback(() => {
51
+ if (!isSubmitting) {
52
+ onClose();
53
+ }
54
+ }, [isSubmitting, onClose]);
55
+ const handleChange = useCallback((nextValue) => {
56
+ yamlValue.current = nextValue;
57
+ setIsDirty(true);
58
+ setSubmitError(null);
59
+ setValidationError(null);
60
+ }, []);
61
+ const handleSubmit = useCallback(async () => {
62
+ const result = validate(yamlValue.current ?? initialYaml);
63
+ if (!result.isValid) {
64
+ setValidationError(intl.formatMessage(messages[result.errorCode]));
65
+ return;
66
+ }
67
+ setIsSubmitting(true);
68
+ setSubmitError(null);
69
+ try {
70
+ await onSubmit(result.measure);
71
+ }
72
+ catch (error) {
73
+ setSubmitError(extractBackendErrorDetail(error) ?? intl.formatMessage(messages.dialogSubmitError));
74
+ }
75
+ finally {
76
+ setIsSubmitting(false);
77
+ }
78
+ }, [initialYaml, intl, onSubmit, validate]);
79
+ const handleDuplicate = useCallback(() => {
80
+ if (!onDuplicate) {
81
+ return;
82
+ }
83
+ setSubmitError(null);
84
+ const result = validate(yamlValue.current ?? initialYaml, true);
85
+ if (!result.isValid) {
86
+ setValidationError(intl.formatMessage(messages[result.errorCode]));
87
+ return;
88
+ }
89
+ onDuplicate(result.measure);
90
+ }, [initialYaml, intl, onDuplicate, validate]);
91
+ const footerLeftRenderer = useCallback(() => (_jsxs("div", { className: "gd-ascode-dialog-footer-left", children: [isWhiteLabeled ? null : (_jsxs(UiLink, { variant: "secondary", href: METRIC_DOCS_URL, target: "_blank", rel: "noopener noreferrer", children: [
92
+ _jsx(UiIcon, { type: "question", size: 14, color: "complementary-6" }), _jsx(FormattedMessage, { id: "analyticsCatalog.metric.dialog.help" })
93
+ ] })), isEdit && onDuplicate ? (_jsx(UiButton, { label: intl.formatMessage(messages.dialogDuplicate), variant: "tertiary", isDisabled: isSubmitting || isLoading, onClick: handleDuplicate })) : null] })), [handleDuplicate, intl, isEdit, isLoading, isSubmitting, isWhiteLabeled, onDuplicate]);
94
+ const bodySpinner = (_jsx("div", { className: "gd-ascode-dialog-loading", children: _jsx(LoadingComponent, {}) }));
95
+ return (_jsx(ConfirmDialog, { className: "gd-ascode-dialog", containerClassName: "gd-ascode-dialog-overlay", headline: headlineMessage, cancelButtonText: cancelMessage, submitButtonText: submitMessage, isPositive: true, isSubmitDisabled: isSubmitting || isLoading || (isEdit && !isDirty), isCancelDisabled: isSubmitting, shouldCloseOnEscape: !isSubmitting, onCancel: handleClose, onClose: handleClose, onSubmit: handleSubmit, displayCloseButton: !isSubmitting, footerLeftRenderer: footerLeftRenderer, children: _jsx("div", { className: "gd-ascode-dialog-content", children: _jsx(Suspense, { fallback: bodySpinner, children: isLoading ? (bodySpinner) : (_jsxs(_Fragment, { children: [
96
+ _jsxs("div", { className: "gd-ascode-dialog-section-header", children: [
97
+ _jsx(FormattedMessage, { id: "analyticsCatalog.metric.dialog.sectionHeader" }), _jsx(UiTooltip, { arrowPlacement: "top", optimalPlacement: true, triggerBy: ["hover", "click", "focus"], anchor: _jsx(UiIcon, { type: "question", size: 14, color: "complementary-6" }), content: _jsx(FormattedMessage, { id: "analyticsCatalog.metric.dialog.sectionHeader.tooltip" }) })
98
+ ] }), _jsx("div", { className: "gd-ascode-dialog-editor", children: _jsx(MetricYamlEditor, { initialValue: initialYaml, onChange: handleChange, disabled: isSubmitting }) }), validationError || submitError ? (_jsxs("div", { className: "gd-ascode-dialog-error", children: [
99
+ _jsx("span", { className: "gd-ascode-dialog-error-label", children: _jsx(FormattedMessage, { id: "analyticsCatalog.dialog.error.label" }) }), " ", validationError ?? submitError] })) : null] })) }) }) }));
100
+ }
@@ -0,0 +1,11 @@
1
+ import type { IMeasureMetadataObjectDefinition } from "@gooddata/sdk-model";
2
+ import { type ICatalogItemMeasure } from "../catalogItem/types.js";
3
+ type Props = {
4
+ item: ICatalogItemMeasure;
5
+ onClose: () => void;
6
+ onSaved: (item: ICatalogItemMeasure) => void;
7
+ onDuplicate?: (metric: IMeasureMetadataObjectDefinition) => void;
8
+ };
9
+ export declare function MetricEditDialog({ item, onClose, onSaved, onDuplicate }: Props): import("react/jsx-runtime").JSX.Element;
10
+ export {};
11
+ //# sourceMappingURL=MetricEditDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MetricEditDialog.d.ts","sourceRoot":"","sources":["../../src/metric/MetricEditDialog.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAA0B,gCAAgC,EAAE,MAAM,qBAAqB,CAAC;AAIpG,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAWnE,KAAK,KAAK,GAAG;IACT,IAAI,EAAE,mBAAmB,CAAC;IAC1B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,EAAE,CAAC,IAAI,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAC7C,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,gCAAgC,KAAK,IAAI,CAAC;CACpE,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,KAAK,2CA8D9E"}