@digital-ai/dot-components 3.21.0 → 3.23.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.
package/index.esm.js CHANGED
@@ -4221,6 +4221,22 @@ const DotClickAwayListener = ({
4221
4221
  });
4222
4222
  };
4223
4223
 
4224
+ const useKeyPress = (key, callback, dependencies) => {
4225
+ useEffect(() => {
4226
+ if (!key) return;
4227
+ const handleKeyPress = event => {
4228
+ const element = event.target;
4229
+ if (event.key === key && !['INPUT', 'TEXTAREA'].includes(element.nodeName) && !element.isContentEditable) {
4230
+ callback();
4231
+ }
4232
+ };
4233
+ window.addEventListener('keydown', handleKeyPress);
4234
+ return () => {
4235
+ window.removeEventListener('keydown', handleKeyPress);
4236
+ };
4237
+ }, [key, ...dependencies]);
4238
+ };
4239
+
4224
4240
  const DotList = ({
4225
4241
  ariaLabel,
4226
4242
  ariaRole = 'list',
@@ -4231,6 +4247,7 @@ const DotList = ({
4231
4247
  dense = false,
4232
4248
  disablePadding = false,
4233
4249
  items = [],
4250
+ keyForNestedReset,
4234
4251
  menuPlacement = 'right-start',
4235
4252
  nestedDrawerLeftSpacing = 240,
4236
4253
  nestedListCloseOnClickAway = true,
@@ -4251,6 +4268,7 @@ const DotList = ({
4251
4268
  const handleHrefClick = index => () => {
4252
4269
  updateSelectedListItem(index);
4253
4270
  };
4271
+ useKeyPress(keyForNestedReset, handleClickAway, []);
4254
4272
  const list = jsxs(StyledList, {
4255
4273
  "aria-label": ariaLabel,
4256
4274
  classes: {
@@ -5902,19 +5920,7 @@ const DotSidebar = ({
5902
5920
  onCollapseChange && onCollapseChange(isOpen);
5903
5921
  setIsOpen(!isOpen);
5904
5922
  };
5905
- useEffect(() => {
5906
- if (!collapsable) return;
5907
- const handleKeyPress = event => {
5908
- const element = event.target;
5909
- if (event.key === collapseKey && !['INPUT', 'TEXTAREA'].includes(element.nodeName) && !element.isContentEditable) {
5910
- toggleNavCollapseState();
5911
- }
5912
- };
5913
- window.addEventListener('keydown', handleKeyPress);
5914
- return () => {
5915
- window.removeEventListener('keydown', handleKeyPress);
5916
- };
5917
- }, [isOpen, collapsable]);
5923
+ useKeyPress(collapsable && collapseKey, toggleNavCollapseState, [isOpen, collapsable]);
5918
5924
  const sidebarClasses = useStylesWithRootClass('side-nav', openClass);
5919
5925
  const rootClasses = useStylesWithRootClass(rootClassName$$, openClass, className);
5920
5926
  return jsxs(StyledSidebar, {
@@ -5958,6 +5964,7 @@ const DotSidebar = ({
5958
5964
  dense: true,
5959
5965
  disablePadding: true,
5960
5966
  items: navItems,
5967
+ keyForNestedReset: collapseKey,
5961
5968
  nestedDrawerLeftSpacing: sidebarWidth,
5962
5969
  nestedListType: nestedListType,
5963
5970
  width: "100%"
@@ -9027,7 +9034,7 @@ class DashboardsService {
9027
9034
  * @param sort Sort ordering to apply to the query.
9028
9035
  * @param filter List of filters (each filter is a separate query param, and they are OR'ed).
9029
9036
  *
9030
- * * **Filterable field names**: author_fullname, author_id, bi_type, categories, created_dt, description, external_embedding_id, external_id, id, is_ootb_dashboard, lifecycle_state, name, target_apps, updated_by_fullname, updated_by_id, updated_dt
9037
+ * * **Filterable field names**: author_fullname, author_id, bi_type, categories, created_dt, description, external_embedding_id, external_id, id, is_ootb_dashboard, lifecycle_state, modified_parent_id, name, target_apps, updated_by_fullname, updated_by_id, updated_dt
9031
9038
  * * **Searchable field names**: author_fullname, categories, description, name, updated_by_fullname
9032
9039
  *
9033
9040
  * @param favorite Boolean flag to only return dashboards marked as favorites.
@@ -9104,19 +9111,24 @@ class DashboardsService {
9104
9111
  * Get one Dashboard definition.
9105
9112
  * Return a single Dashboard definition, selected by ID.
9106
9113
  * @param dashboardId
9114
+ * @param viewModified Return the dashboard with modified changes if they exist for this user.
9107
9115
  * @returns DashboardView OK
9108
9116
  * @returns Error Default error response
9109
9117
  * @throws ApiError
9110
9118
  */
9111
- static getDashboardsService1(dashboardId) {
9119
+ static getDashboardsService1(dashboardId, viewModified = false) {
9112
9120
  return __awaiter(this, void 0, void 0, function* () {
9113
9121
  const result = yield request({
9114
9122
  method: 'GET',
9115
9123
  path: `/metadata/bi/dashboards/${dashboardId}`,
9124
+ query: {
9125
+ view_modified: viewModified
9126
+ },
9116
9127
  errors: {
9117
9128
  400: `An unknown parameter was specified.`,
9118
9129
  401: `Could not resolve a valid Tenant from the provided API Token.`,
9119
- 404: `The dashboard could not be found.`
9130
+ 404: `The dashboard could not be found.`,
9131
+ 422: `Unprocessable Entity`
9120
9132
  }
9121
9133
  });
9122
9134
  return result.body;
@@ -9200,6 +9212,58 @@ class DashboardsService {
9200
9212
  return result.body;
9201
9213
  });
9202
9214
  }
9215
+ /**
9216
+ * Publish modifications to a dashboard
9217
+ * Publish modifications to a dashboard
9218
+ * @param dashboardId
9219
+ * @param requestBody
9220
+ * @param isSync Sync the dashboard with BI
9221
+ * @returns DashboardView Dashboard published.
9222
+ * @returns Error Default error response
9223
+ * @throws ApiError
9224
+ */
9225
+ static postDashboardsService1(dashboardId, requestBody, isSync = null) {
9226
+ return __awaiter(this, void 0, void 0, function* () {
9227
+ const result = yield request({
9228
+ method: 'POST',
9229
+ path: `/metadata/bi/dashboards/${dashboardId}/publish`,
9230
+ query: {
9231
+ is_sync: isSync
9232
+ },
9233
+ body: requestBody,
9234
+ mediaType: 'application/json',
9235
+ errors: {
9236
+ 400: `The provided data is not valid.`,
9237
+ 401: `Could not resolve a valid Tenant from the provided API Token.`,
9238
+ 404: `The dashboard could not be found.`,
9239
+ 422: `Unprocessable Entity`
9240
+ }
9241
+ });
9242
+ return result.body;
9243
+ });
9244
+ }
9245
+ /**
9246
+ * Revert modifications to a dashboard
9247
+ * Revert modifications to a dashboard
9248
+ * @param dashboardId
9249
+ * @returns DashboardView Dashboard changes reverted.
9250
+ * @returns Error Default error response
9251
+ * @throws ApiError
9252
+ */
9253
+ static postDashboardsService2(dashboardId) {
9254
+ return __awaiter(this, void 0, void 0, function* () {
9255
+ const result = yield request({
9256
+ method: 'POST',
9257
+ path: `/metadata/bi/dashboards/${dashboardId}/revert`,
9258
+ errors: {
9259
+ 400: `The provided data is not valid.`,
9260
+ 401: `Could not resolve a valid Tenant from the provided API Token.`,
9261
+ 404: `The dashboard could not be found.`
9262
+ }
9263
+ });
9264
+ return result.body;
9265
+ });
9266
+ }
9203
9267
  /**
9204
9268
  * Create a copy of existing dashboard
9205
9269
  * Create a copy of existing dashboard
@@ -9209,7 +9273,7 @@ class DashboardsService {
9209
9273
  * @returns Error Default error response
9210
9274
  * @throws ApiError
9211
9275
  */
9212
- static postDashboardsService1(dashboardId, requestBody) {
9276
+ static postDashboardsService3(dashboardId, requestBody) {
9213
9277
  return __awaiter(this, void 0, void 0, function* () {
9214
9278
  const result = yield request({
9215
9279
  method: 'POST',
@@ -9233,7 +9297,7 @@ class DashboardsService {
9233
9297
  * @returns Error Default error response
9234
9298
  * @throws ApiError
9235
9299
  */
9236
- static postDashboardsService2(dashboardId) {
9300
+ static postDashboardsService4(dashboardId) {
9237
9301
  return __awaiter(this, void 0, void 0, function* () {
9238
9302
  const result = yield request({
9239
9303
  method: 'POST',
@@ -9421,15 +9485,35 @@ class HelpContentService {
9421
9485
  }
9422
9486
 
9423
9487
  function DotDashboardStatusPill({
9424
- status
9488
+ dashboard,
9489
+ currentUser
9425
9490
  }) {
9426
- if (status === DashboardView.lifecycle_state.DRAFT) {
9491
+ if (dashboard.lifecycle_state === DashboardView.lifecycle_state.DRAFT) {
9427
9492
  return jsx(DotPill, {
9428
9493
  label: "Draft",
9429
9494
  size: "small",
9430
9495
  variant: "outlined",
9431
9496
  status: "in-progress"
9432
9497
  });
9498
+ } else if (dashboard.lifecycle_state === DashboardView.lifecycle_state.PUBLISHED && dashboard.is_being_modified && currentUser) {
9499
+ if (dashboard.modified_author_id === currentUser.id) {
9500
+ return jsx(DotPill, {
9501
+ label: "Edit in progress",
9502
+ size: "small",
9503
+ variant: "outlined",
9504
+ status: "success"
9505
+ });
9506
+ } else {
9507
+ return jsx(DotPill, {
9508
+ label: "Published",
9509
+ icon: jsx(DotIcon, {
9510
+ iconId: "lock"
9511
+ }),
9512
+ size: "small",
9513
+ variant: "outlined",
9514
+ status: "success"
9515
+ });
9516
+ }
9433
9517
  } else {
9434
9518
  return jsx(DotPill, {
9435
9519
  status: "success",
@@ -9459,7 +9543,7 @@ const dashboard1 = {
9459
9543
  lifecycle_state: DashboardView.lifecycle_state.DRAFT,
9460
9544
  application_instances: [],
9461
9545
  categories: [],
9462
- bi_type: DashboardView.bi_type.SUPERSET,
9546
+ bi_type: DashboardView.bi_type.MICROSTRATEGY,
9463
9547
  is_ootb_dashboard: false,
9464
9548
  thumbnail: '',
9465
9549
  external_object: {
@@ -9469,7 +9553,8 @@ const dashboard1 = {
9469
9553
  help_content_id: helpContent.id,
9470
9554
  filter_configuration: [],
9471
9555
  favorite: false,
9472
- updated_dt: '2023-11-22T06:42:07.952000+00:00'
9556
+ updated_dt: '2023-11-22T06:42:07.952000+00:00',
9557
+ is_being_modified: false
9473
9558
  };
9474
9559
  const dashboard2 = {
9475
9560
  author_fullname: 'Dashboard2 Author',
@@ -9571,6 +9656,36 @@ const dashboard4 = {
9571
9656
  favorite: false,
9572
9657
  updated_dt: '2023-11-21T04:42:07.951000+00:00'
9573
9658
  });
9659
+ // Dashboard with in-progress edits
9660
+ ({
9661
+ author_fullname: 'Dashboard6 Author',
9662
+ author_id: 'id6a',
9663
+ target_apps: ['AGILITY'],
9664
+ external_id: '6',
9665
+ name: 'Test dashboard 6',
9666
+ description: '',
9667
+ dashboard_url: 'https://test.com',
9668
+ server_url: 'https://test.com',
9669
+ external_embedding_id: null,
9670
+ created_dt: '2023-11-22T06:42:07.872000+00:00',
9671
+ id: 'id6',
9672
+ lifecycle_state: DashboardView.lifecycle_state.PUBLISHED,
9673
+ application_instances: [],
9674
+ categories: [],
9675
+ bi_type: DashboardView.bi_type.MICROSTRATEGY,
9676
+ is_ootb_dashboard: false,
9677
+ thumbnail: '',
9678
+ external_object: {
9679
+ page_count: 3
9680
+ },
9681
+ embed_config: {},
9682
+ help_content_id: helpContent.id,
9683
+ filter_configuration: [],
9684
+ favorite: false,
9685
+ updated_dt: '2023-11-22T06:42:07.952000+00:00',
9686
+ is_being_modified: true,
9687
+ modified_author_id: 'id123'
9688
+ });
9574
9689
  const getCategoriesMock = {
9575
9690
  categories: {
9576
9691
  AGILITY: ['Scrum Master', 'DevOps Manager', 'Release Train Engineer', 'Agility Analytics'],
@@ -9782,7 +9897,7 @@ const DotMetadataApiProvider = ({
9782
9897
  },
9783
9898
  duplicateDashboard: (dashboardId, dashboard) => {
9784
9899
  setAccountIdHeader(accountId);
9785
- return cancelablePromise(DashboardsService.postDashboardsService1(dashboardId, dashboard)).then(response => {
9900
+ return cancelablePromise(DashboardsService.postDashboardsService3(dashboardId, dashboard)).then(response => {
9786
9901
  setDashboardsError(null);
9787
9902
  setMetadata(null);
9788
9903
  if (dashboards) {
@@ -9797,10 +9912,33 @@ const DotMetadataApiProvider = ({
9797
9912
  return null;
9798
9913
  });
9799
9914
  },
9915
+ publishDashboardChanges: (dashboardId, dashboard, isSync) => __awaiter(void 0, void 0, void 0, function* () {
9916
+ setAccountIdHeader(accountId);
9917
+ return cancelablePromise(DashboardsService.postDashboardsService1(dashboardId, dashboard, isSync)).then(response => {
9918
+ setDashboardsError(null);
9919
+ setDashboards(orig => handleUpdateDashboard(orig, response));
9920
+ return response;
9921
+ }).catch(error => setDashboardsError(error));
9922
+ }),
9923
+ revertDashboardChanges: dashboardId => __awaiter(void 0, void 0, void 0, function* () {
9924
+ setAccountIdHeader(accountId);
9925
+ return cancelablePromise(DashboardsService.postDashboardsService2(dashboardId)).then(response => {
9926
+ setDashboardsError(null);
9927
+ setDashboards(orig => handleUpdateDashboard(orig, response));
9928
+ return response;
9929
+ }).catch(error => setDashboardsError(error));
9930
+ }),
9931
+ getDashboard: (dashboardId, viewModified) => {
9932
+ setAccountIdHeader(accountOverrideId);
9933
+ return cancelablePromise(viewModified === undefined ? DashboardsService.getDashboardsService1(dashboardId) : DashboardsService.getDashboardsService1(dashboardId, viewModified)).then(response => {
9934
+ setDashboardsError(null);
9935
+ return response;
9936
+ }).catch(error => setDashboardsError(error));
9937
+ },
9800
9938
  favoriteDashboard: (dashboardId, favoriteValue) => {
9801
9939
  setAccountIdHeader(accountId);
9802
9940
  if (favoriteValue) {
9803
- return cancelablePromise(DashboardsService.postDashboardsService2(dashboardId)).then(response => {
9941
+ return cancelablePromise(DashboardsService.postDashboardsService4(dashboardId)).then(response => {
9804
9942
  setDashboardsError(null);
9805
9943
  setDashboards(orig => handleUpdateDashboard(orig, response));
9806
9944
  return response;
@@ -10303,7 +10441,8 @@ const DotDashboardDetailsView = ({
10303
10441
  onFavorite,
10304
10442
  open,
10305
10443
  yOffset = 56,
10306
- zIndex = 990
10444
+ zIndex = 990,
10445
+ currentUser
10307
10446
  }) => {
10308
10447
  var _a, _b;
10309
10448
  const rootClasses = useStylesWithRootClass(rootClassName$E, className);
@@ -10388,7 +10527,8 @@ const DotDashboardDetailsView = ({
10388
10527
  className: "dashboard-details-status",
10389
10528
  label: "Status",
10390
10529
  children: jsx(DotDashboardStatusPill, {
10391
- status: dashboard.lifecycle_state
10530
+ dashboard: dashboard,
10531
+ currentUser: currentUser ? currentUser : null
10392
10532
  })
10393
10533
  }), jsx(DashboardDetailsField, {
10394
10534
  className: "dashboard-details-category",
@@ -10432,9 +10572,10 @@ const DotDashboardDetails = _a => {
10432
10572
  var {
10433
10573
  dashboard,
10434
10574
  onClose,
10435
- onFavorite
10575
+ onFavorite,
10576
+ currentUser
10436
10577
  } = _a,
10437
- commonProps = __rest(_a, ["dashboard", "onClose", "onFavorite"]);
10578
+ commonProps = __rest(_a, ["dashboard", "onClose", "onFavorite", "currentUser"]);
10438
10579
  const [_openedDashboard, _setOpenedDashboard] = useState(dashboard);
10439
10580
  const metadataApiContext = useDotMetadataApiContext();
10440
10581
  const {
@@ -10474,7 +10615,8 @@ const DotDashboardDetails = _a => {
10474
10615
  dashboard: openedDashboard,
10475
10616
  open: true,
10476
10617
  onFavorite: favoriteHandler,
10477
- onClose: closeHandler
10618
+ onClose: closeHandler,
10619
+ currentUser: currentUser ? currentUser : null
10478
10620
  }));
10479
10621
  }
10480
10622
  return null;
@@ -10568,7 +10710,14 @@ const DotInputSelect = ({
10568
10710
  return typeof option === 'string' ? option : option.option;
10569
10711
  };
10570
10712
  const getValue = option => {
10571
- return typeof option === 'string' ? option : (option === null || option === void 0 ? void 0 : option.value) || option.option;
10713
+ if (typeof option === 'string') {
10714
+ return option;
10715
+ }
10716
+ // Allow empty string as a value. This is useful when the user wants to select an empty option.
10717
+ if ((option === null || option === void 0 ? void 0 : option.value) === '') {
10718
+ return option.value;
10719
+ }
10720
+ return (option === null || option === void 0 ? void 0 : option.value) || option.option;
10572
10721
  };
10573
10722
  return jsxs("div", {
10574
10723
  className: className,
@@ -11129,7 +11278,8 @@ function ActivePublishMessage({
11129
11278
  published,
11130
11279
  publishing,
11131
11280
  onChange,
11132
- requiresApplication
11281
+ requiresApplication,
11282
+ editInProgress
11133
11283
  }) {
11134
11284
  const [selectedApp, setSelectedApp] = useState(applicationList.length > 0 ? applicationList[0] : null);
11135
11285
  const [selectedCategories, setSelectedCategories] = useState([]);
@@ -11213,11 +11363,11 @@ function ActivePublishMessage({
11213
11363
  children: ["To view your newly published dashboard in", ' ', jsxs("strong", {
11214
11364
  children: ["\"", selectedApp.name, "\""]
11215
11365
  }), ", go to the", ' ', jsx("strong", {
11216
- children: "'Digital.ai Analytics'"
11366
+ children: "'Analytics'"
11217
11367
  }), " section and explore."]
11218
11368
  }) : jsxs(DotTypography, {
11219
11369
  children: ["To view your newly published dashboard, go to the", ' ', jsx("strong", {
11220
- children: "'Digital.ai Analytics'"
11370
+ children: "'Analytics'"
11221
11371
  }), " section and explore."]
11222
11372
  })]
11223
11373
  });
@@ -11296,9 +11446,9 @@ function ActivePublishMessage({
11296
11446
  value: selectedCategories,
11297
11447
  filterSelectedOptions: true
11298
11448
  }), requiresApplication && jsxs(DotTypography, {
11299
- children: ["You are about to publish ", jsxs("strong", {
11449
+ children: ["You are about to", ' ', editInProgress ? 'apply changes to ' : 'publish ', ' ', jsxs("strong", {
11300
11450
  children: ["\"", dashboardName, "\""]
11301
- }), ". Click", ' ', "the publish button to promote it to the selected instance."]
11451
+ }), ".", editInProgress ? ' Do you want to proceed with publishing these updates?' : ' Click the publish button to promote it to the selected instance.']
11302
11452
  })]
11303
11453
  })
11304
11454
  });
@@ -11354,7 +11504,8 @@ function DotDashboardPublishConfirm({
11354
11504
  dashboardToPublish,
11355
11505
  dashboardToUnpublish,
11356
11506
  onClose,
11357
- onStatusChanged
11507
+ onStatusChanged,
11508
+ currentUser
11358
11509
  }) {
11359
11510
  const [publishing, setPublishing] = useState(null);
11360
11511
  const [unpublishing, setUnpublishing] = useState(null);
@@ -11369,7 +11520,8 @@ function DotDashboardPublishConfirm({
11369
11520
  });
11370
11521
  const {
11371
11522
  updateDashboard,
11372
- dashboardsError
11523
+ dashboardsError,
11524
+ publishDashboardChanges
11373
11525
  } = useDotMetadataApiContext();
11374
11526
  const {
11375
11527
  cancelablePromise
@@ -11394,20 +11546,26 @@ function DotDashboardPublishConfirm({
11394
11546
  setUnpublished(false);
11395
11547
  }
11396
11548
  }, [dashboardToUnpublish]);
11549
+ const editInProgressByCurrentUser = dashboard => {
11550
+ return currentUser && dashboard.is_being_modified && dashboard.modified_author_id === currentUser.id;
11551
+ };
11397
11552
  const handlePublish = useCallback(() => {
11398
11553
  const patchDashboard = {
11399
11554
  lifecycle_state: DashboardView.lifecycle_state.PUBLISHED,
11400
11555
  application_instances: publishAppInstance ? [publishAppInstance.id] : [],
11401
11556
  categories: publishCategories
11402
11557
  };
11403
- setPublishing(cancelablePromise(updateDashboard(dashboardToPublish.id, patchDashboard).then(updatedDashboard => {
11558
+ // If we're publishing a dashboard that is being modified by the current user, we need to use the
11559
+ // publishDashboardChanges method to commit the changes to the published dashboard.
11560
+ const publishMethod = editInProgressByCurrentUser(dashboardToPublish) ? publishDashboardChanges : updateDashboard;
11561
+ setPublishing(cancelablePromise(publishMethod(dashboardToPublish.id, patchDashboard).then(updatedDashboard => {
11404
11562
  setPublished(true);
11405
11563
  onStatusChanged(updatedDashboard);
11406
11564
  enqueueMessage('Dashboard has been published!', 'success');
11407
11565
  }).catch(() => {
11408
11566
  setPublishing(null);
11409
11567
  })));
11410
- }, [dashboardToPublish, publishAppInstance, publishCategories, updateDashboard]);
11568
+ }, [dashboardToPublish, publishAppInstance, publishCategories, updateDashboard, publishDashboardChanges]);
11411
11569
  const handleCancelPublish = useCallback(() => {
11412
11570
  if (publishing !== null) {
11413
11571
  publishing.cancel();
@@ -11441,17 +11599,26 @@ function DotDashboardPublishConfirm({
11441
11599
  }
11442
11600
  return !(requiresCategories && publishCategories.length === 0);
11443
11601
  }, [publishAppInstance, publishCategories, requiresApplication, requiresCategories]);
11602
+ const getPublishButtonLabel = dashboard => {
11603
+ if (published) {
11604
+ return 'Got it';
11605
+ } else if (editInProgressByCurrentUser(dashboard)) {
11606
+ return 'Yes, publish changes';
11607
+ } else {
11608
+ return 'Publish';
11609
+ }
11610
+ };
11444
11611
  return jsxs(Fragment, {
11445
11612
  children: [dashboardToPublish !== null && jsx(DotDialog, {
11446
11613
  cancelButtonVisible: !published,
11447
11614
  closeOnSubmit: false,
11448
11615
  hasPrimaryAction: publishing === null || published,
11449
11616
  open: dashboardToPublish !== null,
11450
- title: "Publish to application",
11617
+ title: editInProgressByCurrentUser(dashboardToPublish) ? 'Publish changes' : 'Publish to application',
11451
11618
  submitButtonProps: {
11452
11619
  'data-testid': 'publish-confirm-button',
11453
11620
  disabled: !canSubmit(),
11454
- label: published ? 'Got it' : 'Publish'
11621
+ label: getPublishButtonLabel(dashboardToPublish)
11455
11622
  },
11456
11623
  onCancel: handleCancelPublish,
11457
11624
  onSubmit: published ? handleCancelPublish : handlePublish,
@@ -11462,7 +11629,8 @@ function DotDashboardPublishConfirm({
11462
11629
  published: published,
11463
11630
  publishing: publishing !== null,
11464
11631
  onChange: handlePublishChange,
11465
- requiresApplication: requiresApplication
11632
+ requiresApplication: requiresApplication,
11633
+ editInProgress: editInProgressByCurrentUser(dashboardToPublish)
11466
11634
  })
11467
11635
  }), dashboardToUnpublish !== null && jsx(DotDialog, {
11468
11636
  cancelButtonVisible: !unpublished,
@@ -11517,11 +11685,14 @@ const StyledDotMenu = styled(DotMenu)`
11517
11685
 
11518
11686
  function DotDashboardOptionsMenu({
11519
11687
  dashboard,
11688
+ currentUser,
11520
11689
  isEdit = false,
11521
11690
  menuPlacement,
11522
11691
  onStartDelete,
11523
11692
  onStartDuplicate,
11524
11693
  onStartStatusChange,
11694
+ onStartRevert,
11695
+ onViewOriginal,
11525
11696
  onViewMode,
11526
11697
  onDetails
11527
11698
  }) {
@@ -11540,8 +11711,10 @@ function DotDashboardOptionsMenu({
11540
11711
  onViewMode(dashboard, 'edit');
11541
11712
  }, [dashboard, onViewMode]);
11542
11713
  const isDraft = dashboard.lifecycle_state === DashboardView.lifecycle_state.DRAFT;
11714
+ const isPublished = dashboard.lifecycle_state === DashboardView.lifecycle_state.PUBLISHED;
11715
+ const dashboardLocked = isPublished && currentUser && dashboard.is_being_modified && dashboard.modified_author_id !== currentUser.id;
11543
11716
  const menuItems = [];
11544
- if (!isEdit && onViewMode && !dashboard.is_ootb_dashboard) {
11717
+ if (!isEdit && onViewMode && !dashboard.is_ootb_dashboard && !dashboardLocked) {
11545
11718
  menuItems.push({
11546
11719
  children: jsxs(DotLink, {
11547
11720
  ariaLabel: "Edit dashboard",
@@ -11616,6 +11789,62 @@ function DotDashboardOptionsMenu({
11616
11789
  onclick: handleDeleteClick
11617
11790
  });
11618
11791
  }
11792
+ // If this is a published dashboard, with in-progress changes, and the user is the author of the changes,
11793
+ // show the republish, revert, and view original options.
11794
+ if (isEdit && isPublished && currentUser && dashboard.is_being_modified && dashboard.modified_author_id === currentUser.id) {
11795
+ const handlePublishChangesClick = () => {
11796
+ handleMenuClose();
11797
+ onStartStatusChange && onStartStatusChange(dashboard, DashboardView.lifecycle_state.PUBLISHED);
11798
+ };
11799
+ const handleRevertChangesClick = () => {
11800
+ handleMenuClose();
11801
+ onStartRevert && onStartRevert(dashboard);
11802
+ };
11803
+ const handleViewOriginalClick = () => {
11804
+ handleMenuClose();
11805
+ // Move to view mode and display the original dashboard without the changes
11806
+ onViewOriginal && onViewOriginal(dashboard);
11807
+ };
11808
+ menuItems.push({
11809
+ children: jsxs(DotLink, {
11810
+ ariaLabel: "Publish changes",
11811
+ color: "inherit",
11812
+ onClick: handlePublishChangesClick,
11813
+ underline: "none",
11814
+ children: [jsx(DotIcon, {
11815
+ iconId: "send-airplane"
11816
+ }), " Publish changes"]
11817
+ }),
11818
+ key: 'republish',
11819
+ onclick: handlePublishChangesClick
11820
+ });
11821
+ menuItems.push({
11822
+ children: jsxs(DotLink, {
11823
+ ariaLabel: "Revert to original",
11824
+ color: "inherit",
11825
+ onClick: handleRevertChangesClick,
11826
+ underline: "none",
11827
+ children: [jsx(DotIcon, {
11828
+ iconId: "undo"
11829
+ }), " Revert to original"]
11830
+ }),
11831
+ key: 'revert',
11832
+ onclick: handleRevertChangesClick
11833
+ });
11834
+ menuItems.push({
11835
+ children: jsxs(DotLink, {
11836
+ ariaLabel: "Open published dashboard",
11837
+ color: "inherit",
11838
+ onClick: handleViewOriginalClick,
11839
+ underline: "none",
11840
+ children: [jsx(DotIcon, {
11841
+ iconId: "visibility-on"
11842
+ }), " Open published dashboard"]
11843
+ }),
11844
+ key: 'original',
11845
+ onclick: handleViewOriginalClick
11846
+ });
11847
+ }
11619
11848
  if (onDetails) {
11620
11849
  if (menuItems.length > 0) {
11621
11850
  menuItems.push({
@@ -11749,23 +11978,49 @@ function CloseButton({
11749
11978
  onClick: () => onClose(dashboard)
11750
11979
  }) : null;
11751
11980
  }
11981
+ function FullscreenButton({
11982
+ dashboard,
11983
+ isFullscreen = false,
11984
+ onToggleFullscreen
11985
+ }) {
11986
+ if (!onToggleFullscreen) {
11987
+ return null;
11988
+ }
11989
+ return isFullscreen ? jsx(DotIconButton, {
11990
+ iconId: "fullscreen-exit",
11991
+ "data-testid": 'dashboard-exit-fullscreen-button',
11992
+ tooltip: "Exit fullscreen mode",
11993
+ onClick: () => onToggleFullscreen(false)
11994
+ }) : jsx(DotIconButton, {
11995
+ iconId: "fullscreen-enter",
11996
+ "data-testid": 'dashboard-enter-fullscreen-button',
11997
+ tooltip: "Show dashboard in fullscreen mode",
11998
+ onClick: () => onToggleFullscreen(true)
11999
+ });
12000
+ }
11752
12001
  function DotDashboardActions({
11753
12002
  applications,
11754
12003
  dashboard,
11755
12004
  isEdit = false,
11756
12005
  canEdit = false,
12006
+ isFullscreen = false,
11757
12007
  onClose,
11758
12008
  onFavorite,
11759
12009
  onStatusChanged,
11760
12010
  onDeleted,
11761
12011
  onDuplicated,
11762
- onViewMode
12012
+ onToggleFullscreen,
12013
+ onViewMode,
12014
+ onViewOriginal,
12015
+ onRevertChanges,
12016
+ currentUser
11763
12017
  }) {
11764
12018
  // NOTE: useState functions need to stay at the top of the file so that
11765
12019
  // they are evaluated before any context imports. If they are evaluated
11766
12020
  // after they can cause React state issues when rendering.
11767
12021
  const [dashboardToCopy, setDashboardToCopy] = useState(null);
11768
12022
  const [dashboardToDelete, setDashboardToDelete] = useState(null);
12023
+ const [dashboardToRevert, setDashboardToRevert] = useState(null);
11769
12024
  const [dashboardToPublish, setDashboardToPublish] = useState(null);
11770
12025
  const [dashboardToUnpublish, setDashboardToUnpublish] = useState(null);
11771
12026
  const [appCategories, setAppCategories] = useState(null);
@@ -11773,7 +12028,9 @@ function DotDashboardActions({
11773
12028
  deleteDashboard,
11774
12029
  duplicateDashboard,
11775
12030
  getCategories,
11776
- setOpenedDashboardDetails
12031
+ setOpenedDashboardDetails,
12032
+ revertDashboardChanges,
12033
+ getDashboard
11777
12034
  } = useDotMetadataApiContext();
11778
12035
  const {
11779
12036
  cancelablePromise
@@ -11790,6 +12047,8 @@ function DotDashboardActions({
11790
12047
  setDashboardToPublish(null);
11791
12048
  setDashboardToUnpublish(null);
11792
12049
  onStatusChanged && onStatusChanged(publishedDashboard);
12050
+ // Move to View Mode after publishing
12051
+ onViewMode && onViewMode(publishedDashboard, 'view');
11793
12052
  }, [onStatusChanged]);
11794
12053
  const handlePublishConfirmClose = useCallback(() => {
11795
12054
  setDashboardToPublish(null);
@@ -11825,9 +12084,32 @@ function DotDashboardActions({
11825
12084
  }
11826
12085
  });
11827
12086
  }, [dashboard, onDuplicated]);
12087
+ const handleRevertSubmit = useCallback(() => __awaiter(this, void 0, void 0, function* () {
12088
+ const dashboardId = dashboardToRevert.id;
12089
+ return cancelablePromise(revertDashboardChanges(dashboardId)).then(result => {
12090
+ setDashboardToRevert(null);
12091
+ onRevertChanges && onRevertChanges(result);
12092
+ onStatusChanged && onStatusChanged(result);
12093
+ onViewMode && onViewMode(result, 'view');
12094
+ });
12095
+ }), [dashboardToRevert, onRevertChanges, onStatusChanged]);
12096
+ const handleStartViewOriginal = useCallback(() => __awaiter(this, void 0, void 0, function* () {
12097
+ const dashboardId = dashboard.id;
12098
+ return cancelablePromise(getDashboard(dashboardId, false)).then(result => {
12099
+ onStatusChanged && onStatusChanged(result);
12100
+ onViewMode && onViewMode(result, 'view');
12101
+ onViewOriginal && onViewOriginal(result);
12102
+ });
12103
+ }), [dashboard, onViewOriginal, onStatusChanged]);
11828
12104
  const handleStartDelete = useCallback(dashboardToDel => {
11829
12105
  setDashboardToDelete(dashboardToDel);
11830
12106
  }, []);
12107
+ const handleStartRevert = useCallback(dashboard => {
12108
+ setDashboardToRevert(dashboard);
12109
+ }, []);
12110
+ const handleRevertClose = useCallback(() => {
12111
+ setDashboardToRevert(null);
12112
+ }, []);
11831
12113
  const handleStartDuplicate = useCallback(dashboardToDupe => {
11832
12114
  setDashboardToCopy(dashboardToDupe);
11833
12115
  }, []);
@@ -11835,6 +12117,8 @@ function DotDashboardActions({
11835
12117
  const handleStartStatusChangeIfConfig = onStatusChanged ? handleStartStatusChange : undefined;
11836
12118
  const handleStartDeleteIfConfig = onDeleted ? handleStartDelete : undefined;
11837
12119
  const handleViewModeIfConfig = onViewMode && canEdit ? onViewMode : undefined;
12120
+ const handleRevertChangesIfConfig = onRevertChanges ? handleStartRevert : undefined;
12121
+ const handleViewOriginalIfConfig = onViewOriginal ? handleStartViewOriginal : undefined;
11838
12122
  return jsxs(StyledDashboardActions, {
11839
12123
  "data-testid": "dot-dashboard-actions",
11840
12124
  children: [jsx(DotDashboardPublishConfirm, {
@@ -11842,7 +12126,8 @@ function DotDashboardActions({
11842
12126
  dashboardToPublish: dashboardToPublish,
11843
12127
  dashboardToUnpublish: dashboardToUnpublish,
11844
12128
  onClose: handlePublishConfirmClose,
11845
- onStatusChanged: handlePublishConfirm
12129
+ onStatusChanged: handlePublishConfirm,
12130
+ currentUser: currentUser ? currentUser : null
11846
12131
  }), appCategories && dashboardToCopy && jsx(DotDashboardDialog, {
11847
12132
  title: "Duplicate dashboard",
11848
12133
  availableCategories: appCategories,
@@ -11856,6 +12141,14 @@ function DotDashboardActions({
11856
12141
  record: dashboardToDelete.name,
11857
12142
  impact: dashboardToDelete.lifecycle_state === DashboardView.lifecycle_state.PUBLISHED ? 'high' : 'medium',
11858
12143
  open: true
12144
+ }), dashboardToRevert && jsx(DotImpactDialog, {
12145
+ onCancel: handleRevertClose,
12146
+ onSubmit: handleRevertSubmit,
12147
+ record: dashboardToRevert.name,
12148
+ impact: "medium",
12149
+ action: "revert",
12150
+ actionIcon: "undo",
12151
+ open: true
11859
12152
  }), jsx(HelpButton, {
11860
12153
  dashboard: dashboard
11861
12154
  }), onFavorite && jsx(FavoriteButton, {
@@ -11865,11 +12158,18 @@ function DotDashboardActions({
11865
12158
  dashboard: dashboard,
11866
12159
  isEdit: isEdit,
11867
12160
  menuPlacement: "bottom-start",
12161
+ onViewOriginal: handleViewOriginalIfConfig,
11868
12162
  onViewMode: handleViewModeIfConfig,
11869
12163
  onStartStatusChange: handleStartStatusChangeIfConfig,
11870
12164
  onStartDuplicate: handleStartDuplicateIfConfig,
11871
12165
  onStartDelete: handleStartDeleteIfConfig,
11872
- onDetails: () => setOpenedDashboardDetails(dashboard)
12166
+ onStartRevert: handleRevertChangesIfConfig,
12167
+ onDetails: () => setOpenedDashboardDetails(dashboard),
12168
+ currentUser: currentUser ? currentUser : null
12169
+ }), onToggleFullscreen && jsx(FullscreenButton, {
12170
+ dashboard: dashboard,
12171
+ isFullscreen: isFullscreen,
12172
+ onToggleFullscreen: onToggleFullscreen
11873
12173
  }), onClose && jsx(CloseButton, {
11874
12174
  dashboard: dashboard,
11875
12175
  onClose: onClose
@@ -11887,6 +12187,22 @@ const StyledDashboardHeader = styled(DotActionToolbar)`
11887
12187
  padding: ${theme.spacing(0, 2)};
11888
12188
  `}
11889
12189
  `;
12190
+ const StyledDashboardBanner = styled.div`
12191
+ ${({
12192
+ theme
12193
+ }) => css`
12194
+ padding: ${theme.spacing(1, 2)};
12195
+ align-items: stretch;
12196
+ .MuiAlert-message {
12197
+ flex: 5;
12198
+ }
12199
+ .dot-typography {
12200
+ display: flex;
12201
+ justify-content: space-between;
12202
+ align-items: center;
12203
+ }
12204
+ `}
12205
+ `;
11890
12206
  const StyledDashboardHeaderTitleSection = styled.div`
11891
12207
  ${({
11892
12208
  theme
@@ -11894,22 +12210,105 @@ const StyledDashboardHeaderTitleSection = styled.div`
11894
12210
  display: flex;
11895
12211
  align-items: center;
11896
12212
  gap: ${theme.spacing(2)};
12213
+
12214
+ h2 {
12215
+ display: flex;
12216
+ align-items: center;
12217
+ gap: ${theme.spacing(0.5)};
12218
+ }
12219
+
12220
+ .dashboard-header-back-button .dot-icon {
12221
+ font-size: 20px;
12222
+ }
11897
12223
  `}
11898
12224
  `;
11899
12225
 
12226
+ function DotDashboardBanner({
12227
+ bannerSeverity,
12228
+ bannerText,
12229
+ buttonText,
12230
+ buttonAction
12231
+ }) {
12232
+ return jsx(StyledDashboardBanner, {
12233
+ children: jsxs(DotAlertBanner, {
12234
+ severity: bannerSeverity,
12235
+ "data-testid": "dashboard-banner",
12236
+ children: [bannerText, buttonText && jsx(DotButton, {
12237
+ type: "outlined",
12238
+ onClick: buttonAction,
12239
+ "data-testid": "dashboard-banner-button",
12240
+ children: buttonText
12241
+ })]
12242
+ })
12243
+ });
12244
+ }
12245
+ function getDashboardBanner(dashboard, isEdit, onExitEditMode, onPublishChanges, currentUser, modifiedAuthorUser) {
12246
+ if (isEdit) {
12247
+ if (dashboard.is_being_modified) {
12248
+ if (dashboard.modified_author_id === currentUser.id) {
12249
+ // We're in Edit mode for a published dashboard, and the dashboard is being modified by the current user.
12250
+ return jsx(DotDashboardBanner, {
12251
+ bannerSeverity: "warning",
12252
+ bannerText: jsx(DotTypography, {
12253
+ children: "The Dashboard is currently in edit mode and only you can make changes. Publish the changes for other users to see."
12254
+ }),
12255
+ buttonText: "Publish changes",
12256
+ buttonAction: () => {
12257
+ onPublishChanges(dashboard);
12258
+ }
12259
+ });
12260
+ }
12261
+ } else {
12262
+ // We're in Edit mode for a published dashboard, but there are no staged changes saved yet.
12263
+ return jsx(DotDashboardBanner, {
12264
+ bannerSeverity: "info",
12265
+ bannerText: jsx(DotTypography, {
12266
+ children: "You are currently in Edit Mode. Any changes you make will not be visible to others until you publish the updated dashboard."
12267
+ }),
12268
+ buttonText: "Exit edit mode",
12269
+ buttonAction: () => {
12270
+ onExitEditMode(dashboard);
12271
+ }
12272
+ });
12273
+ }
12274
+ } else {
12275
+ if (currentUser && dashboard.is_being_modified && dashboard.modified_author_id !== currentUser.id && modifiedAuthorUser) {
12276
+ // We're in View mode for a published dashboard that is being edited by someone else.
12277
+ const bannerText = jsx(DotTypography, {
12278
+ children: jsxs("div", {
12279
+ children: ["This Dashboard is currently being edited by", ' ', jsxs("strong", {
12280
+ children: [modifiedAuthorUser.given_name, " ", modifiedAuthorUser.family_name]
12281
+ }), ". You will be able to see the updated dashboard once the changes are published."]
12282
+ })
12283
+ });
12284
+ return jsx(DotDashboardBanner, {
12285
+ bannerSeverity: "info",
12286
+ bannerText: bannerText
12287
+ });
12288
+ }
12289
+ }
12290
+ }
11900
12291
  function DotDashboardHeader({
11901
12292
  accountId,
11902
12293
  dashboard,
12294
+ currentUser,
11903
12295
  isEdit = false,
11904
12296
  canEdit = false,
12297
+ isFullscreen = false,
12298
+ onBack,
11905
12299
  onClose,
11906
12300
  onFavorite,
11907
12301
  onStatusChanged,
11908
12302
  onDeleted,
11909
12303
  onDuplicated,
12304
+ onRevertChanges,
12305
+ onViewOriginal,
12306
+ onToggleFullscreen,
11910
12307
  onViewMode,
11911
- showStatus = false
12308
+ showStatus = false,
12309
+ modifiedAuthorUser = null
11912
12310
  }) {
12311
+ const [dashboardToPublish, setDashboardToPublish] = useState(null);
11913
12312
  const {
11914
12313
  applications,
11915
12314
  applicationsError,
@@ -11920,32 +12319,68 @@ function DotDashboardHeader({
11920
12319
  metadataLoading,
11921
12320
  dashboardsError
11922
12321
  } = useDotMetadataApiContext();
12322
+ const handleStartPublishChanges = useCallback(publishDashboard => {
12323
+ setDashboardToPublish(publishDashboard);
12324
+ }, []);
12325
+ const handlePublishConfirmClose = useCallback(() => {
12326
+ setDashboardToPublish(null);
12327
+ }, []);
12328
+ const handlePublishConfirm = useCallback(publishedDashboard => {
12329
+ setDashboardToPublish(null);
12330
+ onStatusChanged && onStatusChanged(publishedDashboard);
12331
+ // Navigate to View Mode after publishing changes
12332
+ onViewMode && onViewMode(publishedDashboard, 'view');
12333
+ }, [onStatusChanged]);
11923
12334
  useEnqueueErrorMessage(!applicationsLoading && applicationsError);
11924
12335
  useEnqueueErrorMessage(!metadataLoading && dashboardsError);
11925
12336
  useEffect(() => {
11926
12337
  loadApplications(accountId);
11927
12338
  }, []);
11928
- return jsxs(StyledDashboardHeader, {
11929
- children: [jsxs(StyledDashboardHeaderTitleSection, {
11930
- children: [jsx(DotTypography, {
11931
- component: "h2",
11932
- variant: "h2",
11933
- children: dashboard === null || dashboard === void 0 ? void 0 : dashboard.name
11934
- }), showStatus && jsx(DotDashboardStatusPill, {
11935
- status: dashboard.lifecycle_state
12339
+ const backButton = onBack && jsx(DotIconButton, {
12340
+ className: "dashboard-header-back-button",
12341
+ "data-testid": "dashboard-header-back-button",
12342
+ iconId: "back-solid",
12343
+ onClick: () => onBack(dashboard.id)
12344
+ });
12345
+ const onExitEditMode = dashboardEdit => {
12346
+ onViewMode && onViewMode(dashboardEdit, 'view');
12347
+ };
12348
+ return jsxs(Fragment, {
12349
+ children: [jsx(DotDashboardPublishConfirm, {
12350
+ applicationList: applications,
12351
+ dashboardToPublish: dashboardToPublish,
12352
+ dashboardToUnpublish: null,
12353
+ onClose: handlePublishConfirmClose,
12354
+ onStatusChanged: handlePublishConfirm,
12355
+ currentUser: currentUser ? currentUser : null
12356
+ }), jsxs(StyledDashboardHeader, {
12357
+ children: [jsxs(StyledDashboardHeaderTitleSection, {
12358
+ children: [jsxs(DotTypography, {
12359
+ component: "h2",
12360
+ variant: "h2",
12361
+ children: [backButton, dashboard === null || dashboard === void 0 ? void 0 : dashboard.name]
12362
+ }), showStatus && dashboard && jsx(DotDashboardStatusPill, {
12363
+ dashboard: dashboard,
12364
+ currentUser: currentUser ? currentUser : null
12365
+ })]
12366
+ }), jsx(DotDashboardActions, {
12367
+ applications: applications,
12368
+ canEdit: canEdit,
12369
+ dashboard: dashboard,
12370
+ isEdit: isEdit,
12371
+ isFullscreen: isFullscreen,
12372
+ onClose: onClose,
12373
+ onFavorite: onFavorite,
12374
+ onRevertChanges: onRevertChanges,
12375
+ onStatusChanged: onStatusChanged,
12376
+ onDeleted: onDeleted,
12377
+ onDuplicated: onDuplicated,
12378
+ onToggleFullscreen: onToggleFullscreen,
12379
+ onViewMode: onViewMode,
12380
+ onViewOriginal: onViewOriginal,
12381
+ currentUser: currentUser ? currentUser : null
11936
12382
  })]
11937
- }), jsx(DotDashboardActions, {
11938
- applications: applications,
11939
- canEdit: canEdit,
11940
- dashboard: dashboard,
11941
- isEdit: isEdit,
11942
- onClose: onClose,
11943
- onFavorite: onFavorite,
11944
- onStatusChanged: onStatusChanged,
11945
- onDeleted: onDeleted,
11946
- onDuplicated: onDuplicated,
11947
- onViewMode: onViewMode
11948
- })]
12383
+ }), dashboard && dashboard.lifecycle_state === 'PUBLISHED' && getDashboardBanner(dashboard, isEdit, onExitEditMode, handleStartPublishChanges, currentUser ? currentUser : null, modifiedAuthorUser ? modifiedAuthorUser : null)]
11949
12384
  });
11950
12385
  }
11951
12386
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@digital-ai/dot-components",
3
- "version": "3.21.0",
3
+ "version": "3.23.0",
4
4
  "private": false,
5
5
  "license": "SEE LICENSE IN <LICENSE.md>",
6
6
  "contributors": [
@@ -1,18 +1,23 @@
1
- import { ApplicationModel } from '../../core-api/openapi';
1
+ import { ApplicationModel, UserModel } from '../../core-api/openapi';
2
2
  import { DashboardView } from '../metadata-api/openapi/';
3
3
  interface DashboardActionsCommonProps {
4
4
  canEdit?: boolean;
5
+ currentUser?: UserModel;
5
6
  dashboard: DashboardView;
6
7
  isEdit?: boolean;
8
+ isFullscreen?: boolean;
7
9
  onClose?: (dashboard: DashboardView) => void;
8
10
  onDeleted?: (id: string, result: boolean) => void;
9
11
  onDuplicated?: (dashboard: DashboardView, isDone?: boolean) => void;
10
12
  onFavorite?: (id: string, value: boolean) => void;
13
+ onRevertChanges?: (dashboard: DashboardView) => void;
11
14
  onStatusChanged?: (dashboard: DashboardView) => void;
15
+ onToggleFullscreen?: (newValue: boolean) => void;
12
16
  onViewMode?: (dashboard: DashboardView, mode: string) => void;
17
+ onViewOriginal?: (dashboard: DashboardView) => void;
13
18
  }
14
19
  interface DashboardActionsProps extends DashboardActionsCommonProps {
15
20
  applications: ApplicationModel[];
16
21
  }
17
- declare function DotDashboardActions({ applications, dashboard, isEdit, canEdit, onClose, onFavorite, onStatusChanged, onDeleted, onDuplicated, onViewMode, }: DashboardActionsProps): import("react/jsx-runtime").JSX.Element;
18
- export { DashboardActionsCommonProps, DotDashboardActions };
22
+ declare function DotDashboardActions({ applications, dashboard, isEdit, canEdit, isFullscreen, onClose, onFavorite, onStatusChanged, onDeleted, onDuplicated, onToggleFullscreen, onViewMode, onViewOriginal, onRevertChanges, currentUser, }: DashboardActionsProps): import("react/jsx-runtime").JSX.Element;
23
+ export { DashboardActionsCommonProps, DotDashboardActions, DashboardActionsProps, };
@@ -1,14 +1,18 @@
1
1
  import { PopperPlacement } from '../../menu/Menu';
2
2
  import { DashboardView } from '../metadata-api/openapi';
3
+ import { UserModel } from '../../core-api/openapi';
3
4
  interface DashboardOptionsMenuProps {
5
+ currentUser?: UserModel;
4
6
  dashboard: DashboardView;
5
7
  isEdit?: boolean;
6
8
  menuPlacement?: PopperPlacement;
7
9
  onDetails?: (dashboard: DashboardView) => void;
8
10
  onStartDelete?: (dashboard: DashboardView) => void;
9
11
  onStartDuplicate?: (dashboard: DashboardView) => void;
12
+ onStartRevert?: (dashboard: DashboardView) => void;
10
13
  onStartStatusChange?: (dashboard: DashboardView, status: DashboardView.lifecycle_state) => void;
11
14
  onViewMode?: (dashboard: DashboardView, mode: string) => void;
15
+ onViewOriginal?: (dashboard: DashboardView) => void;
12
16
  }
13
- export declare function DotDashboardOptionsMenu({ dashboard, isEdit, menuPlacement, onStartDelete, onStartDuplicate, onStartStatusChange, onViewMode, onDetails, }: DashboardOptionsMenuProps): import("react/jsx-runtime").JSX.Element;
17
+ export declare function DotDashboardOptionsMenu({ dashboard, currentUser, isEdit, menuPlacement, onStartDelete, onStartDuplicate, onStartStatusChange, onStartRevert, onViewOriginal, onViewMode, onDetails, }: DashboardOptionsMenuProps): import("react/jsx-runtime").JSX.Element;
14
18
  export {};
@@ -1,8 +1,10 @@
1
- import { ApplicationModel } from '../../core-api/openapi';
1
+ import { ApplicationModel, UserModel } from '../../core-api/openapi';
2
2
  import { DashboardView } from '../metadata-api/openapi';
3
3
  interface DashboardPublishConfirmProps {
4
4
  /** List of applications the dashboard can be published to **/
5
5
  applicationList: ApplicationModel[];
6
+ /** Current User, used to determine if they have in-progress edits. **/
7
+ currentUser?: UserModel;
6
8
  /** Dashboard to confirm publishing for **/
7
9
  dashboardToPublish: DashboardView;
8
10
  /** Dashboard to confirm unpublishing for **/
@@ -12,5 +14,5 @@ interface DashboardPublishConfirmProps {
12
14
  /** When a dashboard is updated, this function is called with the updated dashboard **/
13
15
  onStatusChanged: (updatedDashboard: DashboardView) => void;
14
16
  }
15
- export declare function DotDashboardPublishConfirm({ applicationList, dashboardToPublish, dashboardToUnpublish, onClose, onStatusChanged, }: Readonly<DashboardPublishConfirmProps>): import("react/jsx-runtime").JSX.Element;
17
+ export declare function DotDashboardPublishConfirm({ applicationList, dashboardToPublish, dashboardToUnpublish, onClose, onStatusChanged, currentUser, }: Readonly<DashboardPublishConfirmProps>): import("react/jsx-runtime").JSX.Element;
16
18
  export {};
@@ -1,6 +1,7 @@
1
1
  import { MouseEvent } from 'react';
2
2
  import { CommonProps } from '../../CommonProps';
3
3
  import { DashboardView } from '../metadata-api/openapi';
4
+ import { UserModel } from '../../core-api/openapi';
4
5
  interface CommonDashboardDetailsProps extends CommonProps {
5
6
  onClose?: (event: MouseEvent | KeyboardEvent) => void;
6
7
  onFavorite?: (dashboardId: string, favoriteValue: boolean) => void;
@@ -8,12 +9,14 @@ interface CommonDashboardDetailsProps extends CommonProps {
8
9
  zIndex?: number;
9
10
  }
10
11
  interface DashboardDetailsViewProps extends CommonDashboardDetailsProps {
12
+ currentUser?: UserModel;
11
13
  dashboard: DashboardView;
12
14
  open: boolean;
13
15
  }
14
- export declare const DotDashboardDetailsView: ({ className, dashboard, onClose, onFavorite, open, yOffset, zIndex, }: DashboardDetailsViewProps) => import("react/jsx-runtime").JSX.Element;
16
+ export declare const DotDashboardDetailsView: ({ className, dashboard, onClose, onFavorite, open, yOffset, zIndex, currentUser, }: DashboardDetailsViewProps) => import("react/jsx-runtime").JSX.Element;
15
17
  export interface DashboardDetailsProps extends CommonDashboardDetailsProps {
18
+ currentUser?: UserModel;
16
19
  dashboard?: DashboardView;
17
20
  }
18
- export declare const DotDashboardDetails: ({ dashboard, onClose, onFavorite, ...commonProps }: DashboardDetailsProps) => import("react/jsx-runtime").JSX.Element;
21
+ export declare const DotDashboardDetails: ({ dashboard, onClose, onFavorite, currentUser, ...commonProps }: DashboardDetailsProps) => import("react/jsx-runtime").JSX.Element;
19
22
  export {};
@@ -1,5 +1,8 @@
1
+ import { DashboardView } from '../metadata-api/openapi';
2
+ import { UserModel } from '../../core-api/openapi';
1
3
  interface DashboardStatusPillProps {
2
- status: string;
4
+ currentUser?: UserModel;
5
+ dashboard: DashboardView;
3
6
  }
4
- declare function DotDashboardStatusPill({ status, }: Readonly<DashboardStatusPillProps>): import("react/jsx-runtime").JSX.Element;
7
+ declare function DotDashboardStatusPill({ dashboard, currentUser, }: Readonly<DashboardStatusPillProps>): import("react/jsx-runtime").JSX.Element;
5
8
  export { DashboardStatusPillProps, DotDashboardStatusPill };
@@ -1,7 +1,10 @@
1
+ import { UserModel } from '../../core-api/openapi';
1
2
  import { DashboardActionsCommonProps } from '../dashboard-actions/DashboardActions';
2
3
  interface DashboardHeaderProps extends DashboardActionsCommonProps {
3
4
  accountId?: string;
5
+ modifiedAuthorUser?: UserModel;
6
+ onBack?: (dashboardId?: string) => void;
4
7
  showStatus?: boolean;
5
8
  }
6
- declare function DotDashboardHeader({ accountId, dashboard, isEdit, canEdit, onClose, onFavorite, onStatusChanged, onDeleted, onDuplicated, onViewMode, showStatus, }: DashboardHeaderProps): import("react/jsx-runtime").JSX.Element;
9
+ declare function DotDashboardHeader({ accountId, dashboard, currentUser, isEdit, canEdit, isFullscreen, onBack, onClose, onFavorite, onStatusChanged, onDeleted, onDuplicated, onRevertChanges, onViewOriginal, onToggleFullscreen, onViewMode, showStatus, modifiedAuthorUser, }: DashboardHeaderProps): import("react/jsx-runtime").JSX.Element;
7
10
  export { DashboardHeaderProps, DotDashboardHeader };
@@ -1,2 +1,3 @@
1
1
  export declare const StyledDashboardHeader: import("styled-components").StyledComponent<({ ariaLabel, children, className, "data-testid": dataTestId, variant, }: import("../../action-toolbar/ActionToolbar").DotActionBarProps) => import("react/jsx-runtime").JSX.Element, any, {}, never>;
2
+ export declare const StyledDashboardBanner: import("styled-components").StyledComponent<"div", any, {}, never>;
2
3
  export declare const StyledDashboardHeaderTitleSection: import("styled-components").StyledComponent<"div", any, {}, never>;
@@ -14,10 +14,13 @@ interface DotMetadataApiContextProps {
14
14
  favoriteDashboard: (dashboardId: string, favoriteValue: boolean) => Promise<DashboardView>;
15
15
  getAuthors: (appType: string, publishedOnly?: boolean) => Promise<Author[]>;
16
16
  getCategories: (appType: string, publishedOnly?: boolean) => Promise<string[]>;
17
+ getDashboard: (dashboardId: string, viewModified?: boolean) => Promise<DashboardView>;
17
18
  getDashboardHelpContent: (helpContentId: string) => Promise<string>;
18
19
  metadataLoading: boolean;
19
20
  openedDashboardDetails: DashboardView;
20
21
  platformConsoleUrl: string;
22
+ publishDashboardChanges: (dashboardId: string, dashboard: DashboardPatchBody, isSync?: boolean) => Promise<DashboardView>;
23
+ revertDashboardChanges: (dashboardId: string) => Promise<DashboardView>;
21
24
  searchDashboards: (search: DashboardFilters) => Promise<DashboardView[]>;
22
25
  setOpenedDashboardDetails: (dashboard: DashboardView) => void;
23
26
  setOverrideAccountId: (accountId: string) => void;
@@ -99,6 +99,14 @@ export type DashboardView = {
99
99
  * Timestamp of the latest modification
100
100
  */
101
101
  updated_dt?: string | null;
102
+ /**
103
+ * Whether a published dashboard is currently being modified.
104
+ */
105
+ is_being_modified?: boolean | null;
106
+ /**
107
+ * The platform id of the author currently modifying the dashboard.
108
+ */
109
+ modified_author_id?: string | null;
102
110
  };
103
111
  export declare namespace DashboardView {
104
112
  /**
@@ -15,7 +15,7 @@ export declare class DashboardsService {
15
15
  * @param sort Sort ordering to apply to the query.
16
16
  * @param filter List of filters (each filter is a separate query param, and they are OR'ed).
17
17
  *
18
- * * **Filterable field names**: author_fullname, author_id, bi_type, categories, created_dt, description, external_embedding_id, external_id, id, is_ootb_dashboard, lifecycle_state, name, target_apps, updated_by_fullname, updated_by_id, updated_dt
18
+ * * **Filterable field names**: author_fullname, author_id, bi_type, categories, created_dt, description, external_embedding_id, external_id, id, is_ootb_dashboard, lifecycle_state, modified_parent_id, name, target_apps, updated_by_fullname, updated_by_id, updated_dt
19
19
  * * **Searchable field names**: author_fullname, categories, description, name, updated_by_fullname
20
20
  *
21
21
  * @param favorite Boolean flag to only return dashboards marked as favorites.
@@ -47,11 +47,12 @@ export declare class DashboardsService {
47
47
  * Get one Dashboard definition.
48
48
  * Return a single Dashboard definition, selected by ID.
49
49
  * @param dashboardId
50
+ * @param viewModified Return the dashboard with modified changes if they exist for this user.
50
51
  * @returns DashboardView OK
51
52
  * @returns Error Default error response
52
53
  * @throws ApiError
53
54
  */
54
- static getDashboardsService1(dashboardId: string): Promise<DashboardView | Error>;
55
+ static getDashboardsService1(dashboardId: string, viewModified?: boolean): Promise<DashboardView | Error>;
55
56
  /**
56
57
  * Sets a single Dashboard definition.
57
58
  * Sets a Dashboard definition, selected by ID.
@@ -82,6 +83,26 @@ export declare class DashboardsService {
82
83
  * @throws ApiError
83
84
  */
84
85
  static deleteDashboardsService(dashboardId: string): Promise<Error>;
86
+ /**
87
+ * Publish modifications to a dashboard
88
+ * Publish modifications to a dashboard
89
+ * @param dashboardId
90
+ * @param requestBody
91
+ * @param isSync Sync the dashboard with BI
92
+ * @returns DashboardView Dashboard published.
93
+ * @returns Error Default error response
94
+ * @throws ApiError
95
+ */
96
+ static postDashboardsService1(dashboardId: string, requestBody: DashboardPatchBody, isSync?: boolean): Promise<DashboardView | Error>;
97
+ /**
98
+ * Revert modifications to a dashboard
99
+ * Revert modifications to a dashboard
100
+ * @param dashboardId
101
+ * @returns DashboardView Dashboard changes reverted.
102
+ * @returns Error Default error response
103
+ * @throws ApiError
104
+ */
105
+ static postDashboardsService2(dashboardId: string): Promise<DashboardView | Error>;
85
106
  /**
86
107
  * Create a copy of existing dashboard
87
108
  * Create a copy of existing dashboard
@@ -91,7 +112,7 @@ export declare class DashboardsService {
91
112
  * @returns Error Default error response
92
113
  * @throws ApiError
93
114
  */
94
- static postDashboardsService1(dashboardId: string, requestBody?: DashboardCopyBody): Promise<DashboardView | Error>;
115
+ static postDashboardsService3(dashboardId: string, requestBody?: DashboardCopyBody): Promise<DashboardView | Error>;
95
116
  /**
96
117
  * Favorite a dashboard
97
118
  * Favorite a dashboard
@@ -100,7 +121,7 @@ export declare class DashboardsService {
100
121
  * @returns Error Default error response
101
122
  * @throws ApiError
102
123
  */
103
- static postDashboardsService2(dashboardId: string): Promise<DashboardView | Error>;
124
+ static postDashboardsService4(dashboardId: string): Promise<DashboardView | Error>;
104
125
  /**
105
126
  * Un-favorite a dashboard
106
127
  * Un-favorite a dashboard
@@ -10,3 +10,4 @@ export type { ApplicationModelWrapper } from './models/ApplicationModelWrapper';
10
10
  export type { PaginationModel } from './models/PaginationModel';
11
11
  export { AccountsService } from './services/AccountsService';
12
12
  export { ApplicationsService } from './services/ApplicationsService';
13
+ export type { UserModel } from './models/UserModel';
@@ -0,0 +1,70 @@
1
+ export type UserModel = {
2
+ /**
3
+ * UUID Account Identifier
4
+ */
5
+ account_id: string;
6
+ /**
7
+ * The user creation date in ISO format
8
+ */
9
+ created_date: string;
10
+ /**
11
+ * Email address
12
+ */
13
+ email?: string;
14
+ /**
15
+ * Whether the user is enabled or not
16
+ */
17
+ enabled: boolean;
18
+ /**
19
+ * A message when problems arise retrieving details of a user
20
+ */
21
+ readonly error?: string;
22
+ /**
23
+ * A numerical code when problems occur retrieving details of a user
24
+ */
25
+ readonly error_code?: number;
26
+ /**
27
+ * User's last name
28
+ */
29
+ family_name?: string;
30
+ /**
31
+ * User's first name
32
+ */
33
+ given_name?: string;
34
+ /**
35
+ * List of Group names
36
+ */
37
+ groups?: Array<string>;
38
+ /**
39
+ * UUID User Identifier
40
+ */
41
+ id: string;
42
+ /**
43
+ * The date of the user's most recent login in ISO format
44
+ */
45
+ last_login_date: string;
46
+ /**
47
+ * Whether the user is created via API
48
+ */
49
+ readonly local: boolean;
50
+ /**
51
+ * Whether the user is locked out or not
52
+ */
53
+ readonly locked?: boolean;
54
+ /**
55
+ * The date that the user was last modified on in ISO format
56
+ */
57
+ modified_date: string;
58
+ /**
59
+ * Whether the user is invited and not yet logged in
60
+ */
61
+ pending: boolean;
62
+ /**
63
+ * List of Role names
64
+ */
65
+ roles?: Array<string>;
66
+ /**
67
+ * Username
68
+ */
69
+ username: string;
70
+ };
@@ -1,2 +1,2 @@
1
1
  import { ListProps } from './utils/models';
2
- export declare const DotList: ({ ariaLabel, ariaRole, children, className, component, "data-testid": dataTestId, dense, disablePadding, items, menuPlacement, nestedDrawerLeftSpacing, nestedListCloseOnClickAway, nestedListType, width, }: ListProps) => import("react/jsx-runtime").JSX.Element;
2
+ export declare const DotList: ({ ariaLabel, ariaRole, children, className, component, "data-testid": dataTestId, dense, disablePadding, items, keyForNestedReset, menuPlacement, nestedDrawerLeftSpacing, nestedListCloseOnClickAway, nestedListType, width, }: ListProps) => import("react/jsx-runtime").JSX.Element;
@@ -17,6 +17,8 @@ export interface ListProps extends CommonProps {
17
17
  disablePadding?: boolean;
18
18
  /** Array of list items displayed */
19
19
  items?: Array<ListItemProps>;
20
+ /** If defined, list selection will reset when key is pressed */
21
+ keyForNestedReset?: string;
20
22
  /** If nested list type is 'menu', determines the placement of the menu */
21
23
  menuPlacement?: PopperPlacement;
22
24
  /** If nested type is 'drawer', determines the width of the left spacing */
@@ -0,0 +1 @@
1
+ export declare const useKeyPress: (key: string, callback: () => void, dependencies: unknown[]) => void;