@overmap-ai/core 1.0.48-menu-improvements.0 → 1.0.48-tanstack-table.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.
@@ -8,7 +8,7 @@ var _a;
8
8
  import * as React from "react";
9
9
  import React__default, { useState, useEffect, useRef, memo, useMemo, useCallback, createContext, createElement, useContext, forwardRef, Children, isValidElement, cloneElement, Fragment as Fragment$1, useLayoutEffect, useReducer, lazy, Suspense } from "react";
10
10
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
11
- import { unsafeShowToast, AlertDialogProvider, ToastProvider, DefaultTheme, Flex as Flex$1, IconButton, RiIcon, Text as Text$1, useSeverityColor, Checkbox, TextArea, Select, useToast, Badge, MultiSelect, useViewportSize, Overlay, ButtonGroup, Spinner, IconColorUtility, Tooltip, Popover, useSize, ToggleButton, Separator, OvermapItem, Button, ButtonList, divButtonProps, DropdownItemMenu, Input, useAlertDialog } from "@overmap-ai/blocks";
11
+ import { unsafeShowToast, AlertDialogProvider, ToastProvider, DefaultTheme, Flex as Flex$1, IconButton, RiIcon, Text as Text$1, useSeverityColor, Checkbox, TextArea, Select, useToast, Badge, MultiSelect, useViewportSize, Overlay, ButtonGroup, Spinner, IconColorUtility, Tooltip, Popover, useSize, ToggleButton, Separator, OvermapItem, Button, ButtonList, divButtonProps, OvermapDropdownMenu, Input, useAlertDialog } from "@overmap-ai/blocks";
12
12
  import { DepGraph } from "dependency-graph";
13
13
  import { offline as offline$1 } from "@redux-offline/redux-offline";
14
14
  import offlineConfig from "@redux-offline/redux-offline/lib/defaults";
@@ -1807,6 +1807,11 @@ const componentStageSlice = createSlice({
1807
1807
  }
1808
1808
  });
1809
1809
  const selectStageMapping = (state) => state.componentStageReducer.stages;
1810
+ const selectStage = restructureCreateSelectorWithArgs(
1811
+ createSelector([selectStageMapping, (_state, stageId) => stageId], (stageMapping, stageId) => {
1812
+ return stageMapping[stageId];
1813
+ })
1814
+ );
1810
1815
  const selectStages = createSelector(
1811
1816
  [selectStageMapping],
1812
1817
  (stageMapping) => {
@@ -2047,6 +2052,7 @@ const initialState$g = {
2047
2052
  issues: {},
2048
2053
  attachments: {},
2049
2054
  comments: {},
2055
+ updates: {},
2050
2056
  visibleStatuses: [IssueStatus.BACKLOG, IssueStatus.SELECTED],
2051
2057
  visibleUserIds: null,
2052
2058
  recentIssueIds: [],
@@ -2068,6 +2074,16 @@ const issueSlice = createSlice({
2068
2074
  });
2069
2075
  },
2070
2076
  setIssueAttachments: setAttachments,
2077
+ setIssueUpdates: (state, action) => {
2078
+ if (action.payload.filter(onlyUniqueOfflineIds).length !== action.payload.length) {
2079
+ throw new Error("Tried to use setIssues reducer with duplicate ID's");
2080
+ }
2081
+ const newUpdates = {};
2082
+ for (const update of action.payload) {
2083
+ newUpdates[update.offline_id] = update;
2084
+ }
2085
+ state.updates = newUpdates;
2086
+ },
2071
2087
  setActiveIssueId: (state, action) => {
2072
2088
  state.activeIssueId = action.payload;
2073
2089
  },
@@ -2079,6 +2095,17 @@ const issueSlice = createSlice({
2079
2095
  },
2080
2096
  addIssueAttachment: addAttachment,
2081
2097
  addIssueAttachments: addAttachments,
2098
+ addIssueUpdate: (state, action) => {
2099
+ if (action.payload.offline_id in state.updates) {
2100
+ throw new Error(`Tried to add duplicate issue update with offline_id: ${action.payload.offline_id}`);
2101
+ }
2102
+ state.updates[action.payload.offline_id] = action.payload;
2103
+ },
2104
+ addIssueUpdates: (state, action) => {
2105
+ for (const update of action.payload) {
2106
+ state.updates[update.offline_id] = update;
2107
+ }
2108
+ },
2082
2109
  updateIssue: (state, action) => {
2083
2110
  if (action.payload.offline_id in state.issues) {
2084
2111
  state.issues[action.payload.offline_id] = {
@@ -2098,6 +2125,18 @@ const issueSlice = createSlice({
2098
2125
  }
2099
2126
  },
2100
2127
  removeIssueAttachment: removeAttachment,
2128
+ removeIssueUpdate: (state, action) => {
2129
+ if (action.payload in state.updates) {
2130
+ delete state.updates[action.payload];
2131
+ } else {
2132
+ throw new Error(`Failed to remove issue update because offline_id doesn't exist: ${action.payload}`);
2133
+ }
2134
+ },
2135
+ removeIssueUpdates: (state, action) => {
2136
+ for (const updateId of action.payload) {
2137
+ delete state.updates[updateId];
2138
+ }
2139
+ },
2101
2140
  removeAttachmentsOfIssue: (state, action) => {
2102
2141
  const attachments = Object.values(state.attachments).filter((a) => a.issue === action.payload);
2103
2142
  for (const attachment of attachments) {
@@ -2110,20 +2149,55 @@ const issueSlice = createSlice({
2110
2149
  setVisibleUserIds: (state, action) => {
2111
2150
  state.visibleUserIds = [...new Set(action.payload)];
2112
2151
  },
2113
- setIssueComments: (state, action) => {
2152
+ // Comments
2153
+ addIssueComment: (state, action) => {
2154
+ if (action.payload.offline_id in state.comments) {
2155
+ throw new Error(
2156
+ `Tried to add issue comment with offline_id: ${action.payload.offline_id} that already exists`
2157
+ );
2158
+ }
2159
+ state.comments[action.payload.offline_id] = action.payload;
2160
+ },
2161
+ addIssueComments: (state, action) => {
2162
+ for (const comment of action.payload) {
2163
+ if (comment.offline_id in state.comments) {
2164
+ throw new Error(
2165
+ `Tried to add issue comment with offline_id: ${comment.offline_id} that already exists`
2166
+ );
2167
+ }
2168
+ }
2114
2169
  for (const comment of action.payload) {
2115
2170
  state.comments[comment.offline_id] = comment;
2116
2171
  }
2117
2172
  },
2173
+ setIssueComment: (state, action) => {
2174
+ state.comments[action.payload.offline_id] = action.payload;
2175
+ },
2176
+ setIssueComments: (state, action) => {
2177
+ const newComments = {};
2178
+ for (const comment of action.payload) {
2179
+ newComments[comment.offline_id] = comment;
2180
+ }
2181
+ state.comments = newComments;
2182
+ },
2118
2183
  addOrReplaceIssueComment: (state, action) => {
2119
2184
  state.comments[action.payload.offline_id] = action.payload;
2120
2185
  },
2121
2186
  removeIssueComment: (state, action) => {
2122
- if (action.payload in state.comments) {
2123
- delete state.comments[action.payload];
2124
- } else {
2187
+ if (!(action.payload in state.comments)) {
2125
2188
  throw new Error(`Failed to remove issue comment because ID doesn't exist: ${action.payload}`);
2126
2189
  }
2190
+ delete state.comments[action.payload];
2191
+ },
2192
+ removeIssueComments: (state, action) => {
2193
+ for (const commentId of action.payload) {
2194
+ if (!(commentId in state.comments)) {
2195
+ throw new Error(`Failed to remove issue comment because ID doesn't exist: ${commentId}`);
2196
+ }
2197
+ }
2198
+ for (const commentId of action.payload) {
2199
+ delete state.comments[commentId];
2200
+ }
2127
2201
  },
2128
2202
  cleanRecentIssues: (state) => {
2129
2203
  state.recentIssueIds = state.recentIssueIds.filter((recentIssue) => state.issues[recentIssue.offlineId]);
@@ -2154,23 +2228,33 @@ const {
2154
2228
  addIssueAttachment,
2155
2229
  addIssueAttachments,
2156
2230
  addIssue,
2231
+ addIssueUpdate,
2232
+ addIssueUpdates,
2157
2233
  addOrReplaceIssueComment,
2158
2234
  addToRecentIssues,
2159
2235
  cleanRecentIssues,
2160
2236
  removeIssueAttachment,
2161
2237
  removeAttachmentsOfIssue,
2162
2238
  removeIssue,
2163
- removeIssueComment,
2239
+ removeIssueUpdate,
2240
+ removeIssueUpdates,
2164
2241
  removeRecentIssue,
2165
2242
  resetRecentIssues,
2166
2243
  setActiveIssueId,
2167
2244
  setIssueAttachments,
2168
- setIssueComments,
2245
+ setIssueUpdates,
2169
2246
  setIssues,
2170
2247
  setVisibleStatuses,
2171
2248
  setVisibleUserIds,
2172
2249
  updateIssueAttachment,
2173
- updateIssue
2250
+ updateIssue,
2251
+ // Commments
2252
+ addIssueComment,
2253
+ addIssueComments,
2254
+ setIssueComment,
2255
+ setIssueComments,
2256
+ removeIssueComment,
2257
+ removeIssueComments
2174
2258
  } = issueSlice.actions;
2175
2259
  const selectIssueMapping = (state) => state.issueReducer.issues;
2176
2260
  const selectRecentIssueIds = (state) => state.issueReducer.recentIssueIds;
@@ -2241,6 +2325,12 @@ const selectCommentsOfIssue = restructureCreateSelectorWithArgs(
2241
2325
  return Object.values(commentMapping).filter((comment) => comment.issue === issueId);
2242
2326
  })
2243
2327
  );
2328
+ const selectIssueUpdateMapping = (state) => state.issueReducer.updates;
2329
+ const selectIssueUpdatesOfIssue = restructureCreateSelectorWithArgs(
2330
+ createSelector([selectIssueUpdateMapping, (_state, issueId) => issueId], (updates, issueId) => {
2331
+ return Object.values(updates).filter((update) => update.issue === issueId);
2332
+ })
2333
+ );
2244
2334
  const selectAttachmentsOfIssue = restructureCreateSelectorWithArgs(
2245
2335
  createSelector(
2246
2336
  [selectIssueAttachments, (_state, issueId) => issueId],
@@ -2449,6 +2539,16 @@ var OrganizationAccessLevel = /* @__PURE__ */ ((OrganizationAccessLevel2) => {
2449
2539
  OrganizationAccessLevel2[OrganizationAccessLevel2["ADMIN"] = 2] = "ADMIN";
2450
2540
  return OrganizationAccessLevel2;
2451
2541
  })(OrganizationAccessLevel || {});
2542
+ var IssueUpdateChange = /* @__PURE__ */ ((IssueUpdateChange2) => {
2543
+ IssueUpdateChange2["STATUS"] = "status";
2544
+ IssueUpdateChange2["PRIORITY"] = "priority";
2545
+ IssueUpdateChange2["CATEGORY"] = "category";
2546
+ IssueUpdateChange2["DESCRIPTION"] = "description";
2547
+ IssueUpdateChange2["TITLE"] = "title";
2548
+ IssueUpdateChange2["ASSIGNED_TO"] = "assigned_to";
2549
+ IssueUpdateChange2["DUE_DATE"] = "due_date";
2550
+ return IssueUpdateChange2;
2551
+ })(IssueUpdateChange || {});
2452
2552
  var ProjectType = /* @__PURE__ */ ((ProjectType2) => {
2453
2553
  ProjectType2[ProjectType2["PERSONAL"] = 0] = "PERSONAL";
2454
2554
  ProjectType2[ProjectType2["ORGANIZATION"] = 2] = "ORGANIZATION";
@@ -4437,7 +4537,7 @@ class AttachmentService extends BaseApiService {
4437
4537
  }
4438
4538
  // Attachments aren't models, so we use the OptimisticGenericResult type instead
4439
4539
  async addIssueAttachment(attachmentPayload) {
4440
- const { description: description2, issue, file_sha1, offline_id } = attachmentPayload;
4540
+ const { issue, file_sha1, offline_id } = attachmentPayload;
4441
4541
  if (!attachmentPayload.file.objectURL) {
4442
4542
  throw new Error("Expected attachmentPayload.file.objectURL to be defined.");
4443
4543
  }
@@ -4445,7 +4545,9 @@ class AttachmentService extends BaseApiService {
4445
4545
  ...attachmentPayload,
4446
4546
  file: attachmentPayload.file.objectURL,
4447
4547
  file_name: attachmentPayload.file.name,
4448
- file_type: attachmentPayload.file.type
4548
+ file_type: attachmentPayload.file.type,
4549
+ submitted_at: (/* @__PURE__ */ new Date()).toISOString(),
4550
+ created_by: this.client.store.getState().userReducer.currentUser.id
4449
4551
  };
4450
4552
  await this.client.files.addCache(attachmentPayload.file, file_sha1);
4451
4553
  this.client.store.dispatch(addIssueAttachment(offlineAttachment));
@@ -4457,10 +4559,7 @@ class AttachmentService extends BaseApiService {
4457
4559
  blocks: [offline_id, issue],
4458
4560
  blockers: [file_sha1],
4459
4561
  payload: {
4460
- offline_id,
4461
- issue,
4462
- description: description2 ?? "",
4463
- submitted_at: (/* @__PURE__ */ new Date()).getTime() / 1e3,
4562
+ ...offlineAttachment,
4464
4563
  ...fileProps
4465
4564
  }
4466
4565
  });
@@ -4471,7 +4570,7 @@ class AttachmentService extends BaseApiService {
4471
4570
  return [offlineAttachment, promise];
4472
4571
  }
4473
4572
  async addComponentAttachment(attachmentPayload) {
4474
- const { description: description2, component, file_sha1, offline_id } = attachmentPayload;
4573
+ const { component, file_sha1, offline_id } = attachmentPayload;
4475
4574
  if (!attachmentPayload.file.objectURL) {
4476
4575
  throw new Error("Expected attachmentPayload.file.objectURL to be defined.");
4477
4576
  }
@@ -4479,7 +4578,9 @@ class AttachmentService extends BaseApiService {
4479
4578
  ...attachmentPayload,
4480
4579
  file: attachmentPayload.file.objectURL,
4481
4580
  file_name: attachmentPayload.file.name,
4482
- file_type: attachmentPayload.file.type
4581
+ file_type: attachmentPayload.file.type,
4582
+ submitted_at: (/* @__PURE__ */ new Date()).toISOString(),
4583
+ created_by: this.client.store.getState().userReducer.currentUser.id
4483
4584
  };
4484
4585
  await this.client.files.addCache(attachmentPayload.file, file_sha1);
4485
4586
  this.client.store.dispatch(addComponentAttachment(offlineAttachment));
@@ -4491,10 +4592,7 @@ class AttachmentService extends BaseApiService {
4491
4592
  blocks: [offline_id, component],
4492
4593
  blockers: [file_sha1],
4493
4594
  payload: {
4494
- offline_id,
4495
- component,
4496
- description: description2 ?? "",
4497
- submitted_at: (/* @__PURE__ */ new Date()).getTime() / 1e3,
4595
+ ...offlineAttachment,
4498
4596
  ...fileProps
4499
4597
  }
4500
4598
  });
@@ -4505,7 +4603,7 @@ class AttachmentService extends BaseApiService {
4505
4603
  return [offlineAttachment, promise];
4506
4604
  }
4507
4605
  async addComponentTypeAttachment(attachmentPayload) {
4508
- const { description: description2, component_type, file_sha1, offline_id } = attachmentPayload;
4606
+ const { component_type, file_sha1, offline_id } = attachmentPayload;
4509
4607
  if (!attachmentPayload.file.objectURL) {
4510
4608
  throw new Error("Expected attachmentPayload.file.objectURL to be defined.");
4511
4609
  }
@@ -4513,7 +4611,9 @@ class AttachmentService extends BaseApiService {
4513
4611
  ...attachmentPayload,
4514
4612
  file: attachmentPayload.file.objectURL,
4515
4613
  file_name: attachmentPayload.file.name,
4516
- file_type: attachmentPayload.file.type
4614
+ file_type: attachmentPayload.file.type,
4615
+ submitted_at: (/* @__PURE__ */ new Date()).toISOString(),
4616
+ created_by: this.client.store.getState().userReducer.currentUser.id
4517
4617
  };
4518
4618
  await this.client.files.addCache(attachmentPayload.file, file_sha1);
4519
4619
  this.client.store.dispatch(addComponentTypeAttachment(offlineAttachment));
@@ -4525,10 +4625,7 @@ class AttachmentService extends BaseApiService {
4525
4625
  blocks: [offline_id, component_type],
4526
4626
  blockers: [file_sha1],
4527
4627
  payload: {
4528
- offline_id,
4529
- component_type,
4530
- description: description2 ?? "",
4531
- submitted_at: (/* @__PURE__ */ new Date()).getTime() / 1e3,
4628
+ ...offlineAttachment,
4532
4629
  ...fileProps
4533
4630
  }
4534
4631
  });
@@ -4547,7 +4644,9 @@ class AttachmentService extends BaseApiService {
4547
4644
  ...attachmentPayload,
4548
4645
  file: attachmentPayload.file.objectURL,
4549
4646
  file_name: attachmentPayload.file.name,
4550
- file_type: attachmentPayload.file.type
4647
+ file_type: attachmentPayload.file.type,
4648
+ submitted_at: (/* @__PURE__ */ new Date()).toISOString(),
4649
+ created_by: this.client.store.getState().userReducer.currentUser.id
4551
4650
  };
4552
4651
  await this.client.files.addCache(attachmentPayload.file, file_sha1);
4553
4652
  this.client.store.dispatch(addProjectAttachment(offlineAttachment));
@@ -4587,7 +4686,9 @@ class AttachmentService extends BaseApiService {
4587
4686
  file_name: file2.name,
4588
4687
  file_type: file2.type,
4589
4688
  issue: issueId,
4590
- file_sha1: hash
4689
+ file_sha1: hash,
4690
+ submitted_at: (/* @__PURE__ */ new Date()).toISOString(),
4691
+ created_by: this.client.store.getState().userReducer.currentUser.id
4591
4692
  });
4592
4693
  return this.addIssueAttachment(attachment);
4593
4694
  };
@@ -4606,7 +4707,9 @@ class AttachmentService extends BaseApiService {
4606
4707
  file_name: file2.name,
4607
4708
  file_type: file2.type,
4608
4709
  component: componentId,
4609
- file_sha1: hash
4710
+ file_sha1: hash,
4711
+ submitted_at: (/* @__PURE__ */ new Date()).toISOString(),
4712
+ created_by: this.client.store.getState().userReducer.currentUser.id
4610
4713
  });
4611
4714
  return this.addComponentAttachment(attachment);
4612
4715
  };
@@ -4625,7 +4728,9 @@ class AttachmentService extends BaseApiService {
4625
4728
  file_name: file2.name,
4626
4729
  file_type: file2.type,
4627
4730
  component_type: componentTypeId,
4628
- file_sha1: hash
4731
+ file_sha1: hash,
4732
+ submitted_at: (/* @__PURE__ */ new Date()).toISOString(),
4733
+ created_by: this.client.store.getState().userReducer.currentUser.id
4629
4734
  });
4630
4735
  return this.addComponentTypeAttachment(attachment);
4631
4736
  };
@@ -4644,7 +4749,9 @@ class AttachmentService extends BaseApiService {
4644
4749
  file_name: file2.name,
4645
4750
  file_type: file2.type,
4646
4751
  project: projectId,
4647
- file_sha1: hash
4752
+ file_sha1: hash,
4753
+ submitted_at: (/* @__PURE__ */ new Date()).toISOString(),
4754
+ created_by: this.client.store.getState().userReducer.currentUser.id
4648
4755
  });
4649
4756
  return this.addProjectAttachment(attachment);
4650
4757
  };
@@ -5721,49 +5828,35 @@ class ComponentTypeService extends BaseApiService {
5721
5828
  }
5722
5829
  }
5723
5830
  class IssueCommentService extends BaseApiService {
5831
+ // Omit author and submitted_at since these will always be set internally
5724
5832
  add(comment) {
5725
- const offlinePayload = offline(comment);
5726
- const submittedAt = (/* @__PURE__ */ new Date()).toISOString();
5727
5833
  const { store } = this.client;
5728
- const offlineComment = {
5729
- ...offlinePayload,
5834
+ const offlineComment = offline({
5835
+ ...comment,
5730
5836
  author: store.getState().userReducer.currentUser.id,
5731
- created_at: submittedAt
5732
- };
5733
- store.dispatch(addOrReplaceIssueComment(offlineComment));
5837
+ submitted_at: (/* @__PURE__ */ new Date()).toISOString()
5838
+ });
5839
+ store.dispatch(addIssueComment(offlineComment));
5734
5840
  const promise = this.enqueueRequest({
5735
5841
  description: `${truncate(comment.content, 80)}`,
5736
5842
  method: HttpMethod.POST,
5737
5843
  url: `/issues/${comment.issue}/comment/`,
5738
- payload: { ...offlinePayload, submitted_at: submittedAt },
5844
+ payload: offlineComment,
5739
5845
  blockers: [comment.issue],
5740
- blocks: [offlinePayload.offline_id]
5846
+ blocks: [offlineComment.offline_id]
5847
+ });
5848
+ promise.catch(() => {
5849
+ store.dispatch(removeIssueComment(offlineComment.offline_id));
5741
5850
  });
5742
5851
  return [offlineComment, promise];
5743
5852
  }
5744
- async refreshStore() {
5853
+ update(comment) {
5745
5854
  const { store } = this.client;
5746
- const result = await this.enqueueRequest({
5747
- description: "Get comments",
5748
- method: HttpMethod.GET,
5749
- // TODO: Choose between /issues/comments/in-project/${projectId}/ and /projects/${projectId}/issue-comments/
5750
- url: `/projects/${store.getState().projectReducer.activeProjectId}/comments/`,
5751
- blockers: [],
5752
- blocks: []
5753
- });
5754
- let filteredResult = result.filter(onlyUniqueOfflineIds);
5755
- filteredResult = filteredResult.map((comment) => {
5756
- return { ...comment };
5757
- });
5758
- if (result.length !== filteredResult.length) {
5759
- console.error(
5760
- `Received duplicate comments from the API (new length ${filteredResult.length}); filtered in browser.`
5761
- );
5855
+ const commentToUpdate = store.getState().issueReducer.comments[comment.offline_id];
5856
+ if (!commentToUpdate) {
5857
+ throw new Error(`Comment with offline_id ${comment.offline_id} not found in store`);
5762
5858
  }
5763
- store.dispatch(setIssueComments(filteredResult));
5764
- }
5765
- update(comment) {
5766
- this.client.store.dispatch(addOrReplaceIssueComment(comment));
5859
+ store.dispatch(setIssueComment(comment));
5767
5860
  const promise = this.enqueueRequest({
5768
5861
  description: `Edit comment: ${truncate(comment.content, 80)}`,
5769
5862
  method: HttpMethod.PATCH,
@@ -5772,17 +5865,62 @@ class IssueCommentService extends BaseApiService {
5772
5865
  blockers: [comment.issue],
5773
5866
  blocks: [comment.offline_id]
5774
5867
  });
5868
+ promise.catch(() => {
5869
+ store.dispatch(setIssueComment(commentToUpdate));
5870
+ });
5775
5871
  return [comment, promise];
5776
5872
  }
5777
5873
  remove(offline_id) {
5874
+ const commentToRemove = this.client.store.getState().issueReducer.comments[offline_id];
5875
+ if (!commentToRemove) {
5876
+ throw new Error(`Comment with offline_id ${offline_id} not found in store`);
5877
+ }
5778
5878
  this.client.store.dispatch(removeIssueComment(offline_id));
5779
- return this.enqueueRequest({
5879
+ const promise = this.enqueueRequest({
5780
5880
  description: "Delete comment",
5781
5881
  method: HttpMethod.DELETE,
5782
5882
  url: `/issues/comments/${offline_id}/`,
5783
5883
  blockers: [offline_id],
5784
5884
  blocks: []
5785
5885
  });
5886
+ promise.catch(() => {
5887
+ this.client.store.dispatch(addIssueComment(commentToRemove));
5888
+ });
5889
+ return promise;
5890
+ }
5891
+ async refreshStore() {
5892
+ const { store } = this.client;
5893
+ const result = await this.enqueueRequest({
5894
+ description: "Get comments",
5895
+ method: HttpMethod.GET,
5896
+ // TODO: Choose between /issues/comments/in-project/${projectId}/ and /projects/${projectId}/issue-comments/
5897
+ url: `/projects/${store.getState().projectReducer.activeProjectId}/comments/`,
5898
+ blockers: [],
5899
+ blocks: []
5900
+ });
5901
+ store.dispatch(setIssueComments(result));
5902
+ }
5903
+ }
5904
+ class IssueUpdateService extends BaseApiService {
5905
+ async refreshStore() {
5906
+ const { store } = this.client;
5907
+ const result = await this.enqueueRequest({
5908
+ description: "Get issue updates",
5909
+ method: HttpMethod.GET,
5910
+ url: `/projects/${store.getState().projectReducer.activeProjectId}/issues/updates/`,
5911
+ blockers: [],
5912
+ blocks: []
5913
+ });
5914
+ let filteredResult = result.filter(onlyUniqueOfflineIds);
5915
+ filteredResult = filteredResult.map((comment) => {
5916
+ return { ...comment };
5917
+ });
5918
+ if (result.length !== filteredResult.length) {
5919
+ console.error(
5920
+ `Received duplicate comments from the API (new length ${filteredResult.length}); filtered in browser.`
5921
+ );
5922
+ }
5923
+ store.dispatch(setIssueUpdates(filteredResult));
5786
5924
  }
5787
5925
  }
5788
5926
  class IssueService extends BaseApiService {
@@ -5863,7 +6001,83 @@ class IssueService extends BaseApiService {
5863
6001
  return [offlineIssues, promise];
5864
6002
  }
5865
6003
  update(issue) {
6004
+ const state = this.client.store.getState();
6005
+ const issueToBeUpdated = state.issueReducer.issues[issue.offline_id];
6006
+ if (!issueToBeUpdated) {
6007
+ throw new Error(
6008
+ `Attempting to update an issue with offline_id ${issue.offline_id} that doesn't exist in the store`
6009
+ );
6010
+ }
5866
6011
  this.client.store.dispatch(updateIssue(issue));
6012
+ const changes = {};
6013
+ for (const issueUpdateChange of [
6014
+ IssueUpdateChange.TITLE,
6015
+ IssueUpdateChange.DESCRIPTION,
6016
+ IssueUpdateChange.STATUS,
6017
+ IssueUpdateChange.CATEGORY,
6018
+ IssueUpdateChange.PRIORITY,
6019
+ IssueUpdateChange.ASSIGNED_TO,
6020
+ IssueUpdateChange.DUE_DATE
6021
+ ]) {
6022
+ if (issueUpdateChange in issue && issue[issueUpdateChange] !== issueToBeUpdated[issueUpdateChange]) {
6023
+ switch (issueUpdateChange) {
6024
+ case "category": {
6025
+ let categoryOrNull = null;
6026
+ const categoryIdOrNull = issue[issueUpdateChange];
6027
+ if (categoryIdOrNull) {
6028
+ categoryOrNull = state.categoryReducer.categories[categoryIdOrNull] ?? null;
6029
+ if (!categoryOrNull)
6030
+ throw new Error(
6031
+ `Trying to update issue category to ${categoryIdOrNull} which does not exist in store`
6032
+ );
6033
+ }
6034
+ changes[issueUpdateChange] = categoryOrNull ? {
6035
+ name: categoryOrNull.name,
6036
+ color: categoryOrNull.color,
6037
+ offline_id: categoryOrNull.offline_id
6038
+ } : null;
6039
+ break;
6040
+ }
6041
+ case "assigned_to": {
6042
+ let userOrNull = null;
6043
+ const userIdOrNull = issue[issueUpdateChange];
6044
+ if (userIdOrNull) {
6045
+ userOrNull = state.userReducer.users[userIdOrNull] ?? null;
6046
+ if (!userOrNull)
6047
+ throw new Error(
6048
+ `Trying to update issue assigned_to to ${userIdOrNull} which does not exist in store`
6049
+ );
6050
+ }
6051
+ changes[issueUpdateChange] = userOrNull ? {
6052
+ full_name: userOrNull.username,
6053
+ id: userOrNull.id
6054
+ } : null;
6055
+ break;
6056
+ }
6057
+ case "description":
6058
+ changes[issueUpdateChange] = issue[issueUpdateChange] ?? null;
6059
+ break;
6060
+ case "title":
6061
+ changes[issueUpdateChange] = issue[issueUpdateChange] ?? null;
6062
+ break;
6063
+ case "priority":
6064
+ changes[issueUpdateChange] = issue[issueUpdateChange];
6065
+ break;
6066
+ case "status":
6067
+ changes[issueUpdateChange] = issue[issueUpdateChange];
6068
+ break;
6069
+ case "due_date":
6070
+ changes[issueUpdateChange] = issue[issueUpdateChange] ? issue[issueUpdateChange] : null;
6071
+ }
6072
+ }
6073
+ }
6074
+ const offlineIssueUpdate = offline({
6075
+ created_by: state.userReducer.currentUser.id,
6076
+ submitted_at: (/* @__PURE__ */ new Date()).toISOString(),
6077
+ issue: issueToBeUpdated.offline_id,
6078
+ changes
6079
+ });
6080
+ this.client.store.dispatch(addIssueUpdate(offlineIssueUpdate));
5867
6081
  const promise = this.enqueueRequest({
5868
6082
  description: "Edit issue",
5869
6083
  method: HttpMethod.PATCH,
@@ -5872,23 +6086,30 @@ class IssueService extends BaseApiService {
5872
6086
  blockers: [issue.offline_id],
5873
6087
  blocks: [issue.offline_id]
5874
6088
  });
6089
+ promise.catch(() => {
6090
+ this.client.store.dispatch(updateIssue(issueToBeUpdated));
6091
+ this.client.store.dispatch(removeIssueUpdate(offlineIssueUpdate.offline_id));
6092
+ });
5875
6093
  const fullIssue = this.client.store.getState().issueReducer.issues[issue.offline_id];
5876
6094
  return [fullIssue, promise];
5877
6095
  }
5878
6096
  async remove(id) {
5879
6097
  const { store } = this.client;
5880
6098
  const state = store.getState();
6099
+ const dispatch = store.dispatch;
5881
6100
  const backup = state.issueReducer.issues[id];
5882
6101
  if (!backup) {
5883
6102
  throw new Error(`No issue with id ${id} found in the store`);
5884
6103
  }
5885
6104
  const attachments = Object.values(state.issueReducer.attachments).filter((a) => a.issue === id);
5886
6105
  const attachmentsOfIssue = selectAttachmentsOfIssue(id)(state);
5887
- this.client.store.dispatch(removeIssue(id));
5888
- store.dispatch(addActiveProjectIssuesCount(-1));
5889
- if (attachmentsOfIssue.length > 0) {
5890
- this.client.store.dispatch(removeAttachmentsOfIssue(id));
5891
- }
6106
+ const updatesOfIssue = selectIssueUpdatesOfIssue(id)(state);
6107
+ dispatch(removeIssue(id));
6108
+ dispatch(addActiveProjectIssuesCount(-1));
6109
+ if (attachmentsOfIssue.length > 0)
6110
+ dispatch(removeAttachmentsOfIssue(id));
6111
+ if (updatesOfIssue.length > 0)
6112
+ dispatch(removeIssueUpdates(updatesOfIssue.map(({ offline_id }) => offline_id)));
5892
6113
  try {
5893
6114
  return await this.enqueueRequest({
5894
6115
  description: "Delete issue",
@@ -5898,9 +6119,10 @@ class IssueService extends BaseApiService {
5898
6119
  blocks: []
5899
6120
  });
5900
6121
  } catch (e) {
5901
- this.client.store.dispatch(addIssue(backup));
5902
- this.client.store.dispatch(addIssueAttachments(attachments));
5903
- store.dispatch(addActiveProjectIssuesCount(1));
6122
+ dispatch(addIssue(backup));
6123
+ dispatch(addIssueAttachments(attachments));
6124
+ dispatch(addIssueUpdates(updatesOfIssue));
6125
+ dispatch(addActiveProjectIssuesCount(1));
5904
6126
  throw e;
5905
6127
  }
5906
6128
  }
@@ -6082,6 +6304,7 @@ class MainService extends BaseApiService {
6082
6304
  store.dispatch(setProjectAttachments(project_attachments));
6083
6305
  });
6084
6306
  void this.client.documents.refreshStore();
6307
+ void this.client.issueUpdates.refreshStore();
6085
6308
  }
6086
6309
  store.dispatch(setIsFetchingInitialData(false));
6087
6310
  if (overwrite) {
@@ -7549,6 +7772,7 @@ class OvermapSDK {
7549
7772
  __publicField(this, "organizationAccess", new OrganizationAccessService(this));
7550
7773
  __publicField(this, "issues", new IssueService(this));
7551
7774
  __publicField(this, "issueComments", new IssueCommentService(this));
7775
+ __publicField(this, "issueUpdates", new IssueUpdateService(this));
7552
7776
  __publicField(this, "workspaces", new WorkspaceService(this));
7553
7777
  __publicField(this, "main", new MainService(this));
7554
7778
  __publicField(this, "components", new ComponentService(this));
@@ -14206,17 +14430,15 @@ const FieldActions = memo((props) => {
14206
14430
  Action.key
14207
14431
  )) }),
14208
14432
  /* @__PURE__ */ jsx(Box, { display: forMobile(true, "block"), children: /* @__PURE__ */ jsx(
14209
- DropdownItemMenu,
14433
+ OvermapDropdownMenu,
14210
14434
  {
14211
14435
  trigger: /* @__PURE__ */ jsx(IconButton, { variant: "ghost", "aria-label": "Actions menu", children: /* @__PURE__ */ jsx(RiIcon, { icon: "RiMore2Line" }) }),
14212
14436
  items: actions.map((Action) => {
14213
14437
  var _a2;
14214
14438
  return {
14215
- content: /* @__PURE__ */ jsxs(Flex$1, { gap: "2", align: "center", children: [
14216
- /* @__PURE__ */ jsx(Action.Icon, {}),
14217
- Action.text
14218
- ] }, Action.key),
14219
- onSelect: (_a2 = Action.buttonProps) == null ? void 0 : _a2.onClick
14439
+ leftSlot: /* @__PURE__ */ jsx(Action.Icon, {}),
14440
+ children: Action.text,
14441
+ onClick: (_a2 = Action.buttonProps) == null ? void 0 : _a2.onClick
14220
14442
  };
14221
14443
  })
14222
14444
  }
@@ -14274,10 +14496,8 @@ const useFieldTypeItems = (onSelect = () => null) => {
14274
14496
  const field = FieldTypeToClsMapping[identifier];
14275
14497
  const Icon = field.Icon;
14276
14498
  return {
14277
- content: /* @__PURE__ */ jsxs(Flex$1, { align: "center", gap: "2", children: [
14278
- /* @__PURE__ */ jsx(Icon, {}),
14279
- /* @__PURE__ */ jsx(Text$1, { children: field.fieldTypeName })
14280
- ] }, identifier),
14499
+ children: field.fieldTypeName,
14500
+ leftSlot: /* @__PURE__ */ jsx(Icon, {}),
14281
14501
  value: identifier,
14282
14502
  onSelect: () => {
14283
14503
  onSelect(identifier);
@@ -14481,7 +14701,7 @@ const FieldBuilder = memo((props) => {
14481
14701
  }
14482
14702
  ),
14483
14703
  /* @__PURE__ */ jsxs(Flex$1, { align: "center", gap: "3", children: [
14484
- /* @__PURE__ */ jsx(Badge, { className: styles.typeBadge, children: (_f = fieldTypeItems.flat().find((item) => item.value === type)) == null ? void 0 : _f.content }),
14704
+ /* @__PURE__ */ jsx(Badge, { className: styles.typeBadge, children: (_f = fieldTypeItems.flat().find((item) => item.value === type)) == null ? void 0 : _f.children }),
14485
14705
  showPopoverInputs && /* @__PURE__ */ jsx(FieldSettingsPopover, { popoverInputs, hasError: popoverHasErrors })
14486
14706
  ] }),
14487
14707
  resolvedImage && /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -14842,7 +15062,7 @@ const FieldSectionWithActions = memo((props) => {
14842
15062
  )),
14843
15063
  droppableProvided.placeholder,
14844
15064
  /* @__PURE__ */ jsx(
14845
- DropdownItemMenu,
15065
+ OvermapDropdownMenu,
14846
15066
  {
14847
15067
  trigger: /* @__PURE__ */ jsxs(Button, { type: "button", variant: "soft", children: [
14848
15068
  /* @__PURE__ */ jsx(RiIcon, { icon: "RiAddLine" }),
@@ -15278,6 +15498,8 @@ export {
15278
15498
  IssuePriority,
15279
15499
  IssueService,
15280
15500
  IssueStatus,
15501
+ IssueUpdateChange,
15502
+ IssueUpdateService,
15281
15503
  LicenseLevel,
15282
15504
  LicenseService,
15283
15505
  LicenseStatus,
@@ -15341,6 +15563,10 @@ export {
15341
15563
  addIssue,
15342
15564
  addIssueAttachment,
15343
15565
  addIssueAttachments,
15566
+ addIssueComment,
15567
+ addIssueComments,
15568
+ addIssueUpdate,
15569
+ addIssueUpdates,
15344
15570
  addLicenses,
15345
15571
  addOrReplaceCategories,
15346
15572
  addOrReplaceIssueComment,
@@ -15500,6 +15726,9 @@ export {
15500
15726
  removeIssue,
15501
15727
  removeIssueAttachment,
15502
15728
  removeIssueComment,
15729
+ removeIssueComments,
15730
+ removeIssueUpdate,
15731
+ removeIssueUpdates,
15503
15732
  removeOrganizationAccess,
15504
15733
  removeProjectAccess,
15505
15734
  removeProjectAccessesOfProject,
@@ -15601,6 +15830,8 @@ export {
15601
15830
  selectIssueAttachmentMapping,
15602
15831
  selectIssueAttachments,
15603
15832
  selectIssueMapping,
15833
+ selectIssueUpdateMapping,
15834
+ selectIssueUpdatesOfIssue,
15604
15835
  selectIssues,
15605
15836
  selectLatestFormRevision,
15606
15837
  selectLatestRetryTime,
@@ -15652,6 +15883,7 @@ export {
15652
15883
  selectSortedOrganizationUsers,
15653
15884
  selectSortedProjectUsers,
15654
15885
  selectSortedProjects,
15886
+ selectStage,
15655
15887
  selectStageFormIdsFromStageIds,
15656
15888
  selectStageMapping,
15657
15889
  selectStages,
@@ -15696,7 +15928,9 @@ export {
15696
15928
  setIsImportingProjectFile,
15697
15929
  setIsLoading,
15698
15930
  setIssueAttachments,
15931
+ setIssueComment,
15699
15932
  setIssueComments,
15933
+ setIssueUpdates,
15700
15934
  setIssues,
15701
15935
  setLicenses,
15702
15936
  setLoggedIn,