@overmap-ai/core 1.0.48-fix-agent-errors.4 → 1.0.48-form-submission-view.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";
@@ -3551,6 +3651,14 @@ const selectUserForm = (formId2) => (state) => {
3551
3651
  return state.userFormReducer.userForms[formId2];
3552
3652
  };
3553
3653
  const selectSubmissionMapping = (state) => state.userFormReducer.submissions;
3654
+ const selectUserFormSubmission = restructureCreateSelectorWithArgs(
3655
+ createSelector(
3656
+ [selectSubmissionMapping, (_state, submissionId) => submissionId],
3657
+ (submissions, submissionId) => {
3658
+ return submissions[submissionId];
3659
+ }
3660
+ )
3661
+ );
3554
3662
  const selectSubmissions = createSelector([selectSubmissionMapping], (submissions) => Object.values(submissions));
3555
3663
  const selectRevisionMapping = (state) => state.userFormReducer.revisions;
3556
3664
  const selectRevisions = createSelector([selectRevisionMapping], (revisions) => Object.values(revisions));
@@ -4251,10 +4359,20 @@ function runMiddleware(action) {
4251
4359
  var _a2;
4252
4360
  return (_a2 = allMiddleware[0]) == null ? void 0 : _a2.run(action);
4253
4361
  }
4254
- const discardStatuses = [400, 409, 403, 404];
4362
+ const discardStatuses = [400, 409, 403, 404, 405, 500];
4255
4363
  const statusMessages = {
4256
4364
  403: { title: "Forbidden", description: "You are not authorized to perform this action.", severity: "danger" },
4257
- 404: { title: "Not found", description: "The requested resource was not found.", severity: "danger" }
4365
+ 404: { title: "Not found", description: "The requested resource was not found.", severity: "danger" },
4366
+ 405: {
4367
+ title: "Not supported",
4368
+ description: "It's not you. It's us. Sorry for the inconvenience.",
4369
+ severity: "danger"
4370
+ },
4371
+ 500: {
4372
+ title: "Server error",
4373
+ description: "Our server seems to be experiencing problems at the moment. We have been alerted and will fix the problem as soon as possible.",
4374
+ severity: "danger"
4375
+ }
4258
4376
  };
4259
4377
  function discard(reason, action, retries = 0) {
4260
4378
  var _a2;
@@ -4427,7 +4545,7 @@ class AttachmentService extends BaseApiService {
4427
4545
  }
4428
4546
  // Attachments aren't models, so we use the OptimisticGenericResult type instead
4429
4547
  async addIssueAttachment(attachmentPayload) {
4430
- const { description: description2, issue, file_sha1, offline_id } = attachmentPayload;
4548
+ const { issue, file_sha1, offline_id } = attachmentPayload;
4431
4549
  if (!attachmentPayload.file.objectURL) {
4432
4550
  throw new Error("Expected attachmentPayload.file.objectURL to be defined.");
4433
4551
  }
@@ -4435,7 +4553,9 @@ class AttachmentService extends BaseApiService {
4435
4553
  ...attachmentPayload,
4436
4554
  file: attachmentPayload.file.objectURL,
4437
4555
  file_name: attachmentPayload.file.name,
4438
- file_type: attachmentPayload.file.type
4556
+ file_type: attachmentPayload.file.type,
4557
+ submitted_at: (/* @__PURE__ */ new Date()).toISOString(),
4558
+ created_by: this.client.store.getState().userReducer.currentUser.id
4439
4559
  };
4440
4560
  await this.client.files.addCache(attachmentPayload.file, file_sha1);
4441
4561
  this.client.store.dispatch(addIssueAttachment(offlineAttachment));
@@ -4447,10 +4567,7 @@ class AttachmentService extends BaseApiService {
4447
4567
  blocks: [offline_id, issue],
4448
4568
  blockers: [file_sha1],
4449
4569
  payload: {
4450
- offline_id,
4451
- issue,
4452
- description: description2 ?? "",
4453
- submitted_at: (/* @__PURE__ */ new Date()).getTime() / 1e3,
4570
+ ...offlineAttachment,
4454
4571
  ...fileProps
4455
4572
  }
4456
4573
  });
@@ -4461,7 +4578,7 @@ class AttachmentService extends BaseApiService {
4461
4578
  return [offlineAttachment, promise];
4462
4579
  }
4463
4580
  async addComponentAttachment(attachmentPayload) {
4464
- const { description: description2, component, file_sha1, offline_id } = attachmentPayload;
4581
+ const { component, file_sha1, offline_id } = attachmentPayload;
4465
4582
  if (!attachmentPayload.file.objectURL) {
4466
4583
  throw new Error("Expected attachmentPayload.file.objectURL to be defined.");
4467
4584
  }
@@ -4469,7 +4586,9 @@ class AttachmentService extends BaseApiService {
4469
4586
  ...attachmentPayload,
4470
4587
  file: attachmentPayload.file.objectURL,
4471
4588
  file_name: attachmentPayload.file.name,
4472
- file_type: attachmentPayload.file.type
4589
+ file_type: attachmentPayload.file.type,
4590
+ submitted_at: (/* @__PURE__ */ new Date()).toISOString(),
4591
+ created_by: this.client.store.getState().userReducer.currentUser.id
4473
4592
  };
4474
4593
  await this.client.files.addCache(attachmentPayload.file, file_sha1);
4475
4594
  this.client.store.dispatch(addComponentAttachment(offlineAttachment));
@@ -4481,10 +4600,7 @@ class AttachmentService extends BaseApiService {
4481
4600
  blocks: [offline_id, component],
4482
4601
  blockers: [file_sha1],
4483
4602
  payload: {
4484
- offline_id,
4485
- component,
4486
- description: description2 ?? "",
4487
- submitted_at: (/* @__PURE__ */ new Date()).getTime() / 1e3,
4603
+ ...offlineAttachment,
4488
4604
  ...fileProps
4489
4605
  }
4490
4606
  });
@@ -4495,7 +4611,7 @@ class AttachmentService extends BaseApiService {
4495
4611
  return [offlineAttachment, promise];
4496
4612
  }
4497
4613
  async addComponentTypeAttachment(attachmentPayload) {
4498
- const { description: description2, component_type, file_sha1, offline_id } = attachmentPayload;
4614
+ const { component_type, file_sha1, offline_id } = attachmentPayload;
4499
4615
  if (!attachmentPayload.file.objectURL) {
4500
4616
  throw new Error("Expected attachmentPayload.file.objectURL to be defined.");
4501
4617
  }
@@ -4503,7 +4619,9 @@ class AttachmentService extends BaseApiService {
4503
4619
  ...attachmentPayload,
4504
4620
  file: attachmentPayload.file.objectURL,
4505
4621
  file_name: attachmentPayload.file.name,
4506
- file_type: attachmentPayload.file.type
4622
+ file_type: attachmentPayload.file.type,
4623
+ submitted_at: (/* @__PURE__ */ new Date()).toISOString(),
4624
+ created_by: this.client.store.getState().userReducer.currentUser.id
4507
4625
  };
4508
4626
  await this.client.files.addCache(attachmentPayload.file, file_sha1);
4509
4627
  this.client.store.dispatch(addComponentTypeAttachment(offlineAttachment));
@@ -4515,10 +4633,7 @@ class AttachmentService extends BaseApiService {
4515
4633
  blocks: [offline_id, component_type],
4516
4634
  blockers: [file_sha1],
4517
4635
  payload: {
4518
- offline_id,
4519
- component_type,
4520
- description: description2 ?? "",
4521
- submitted_at: (/* @__PURE__ */ new Date()).getTime() / 1e3,
4636
+ ...offlineAttachment,
4522
4637
  ...fileProps
4523
4638
  }
4524
4639
  });
@@ -4537,7 +4652,9 @@ class AttachmentService extends BaseApiService {
4537
4652
  ...attachmentPayload,
4538
4653
  file: attachmentPayload.file.objectURL,
4539
4654
  file_name: attachmentPayload.file.name,
4540
- file_type: attachmentPayload.file.type
4655
+ file_type: attachmentPayload.file.type,
4656
+ submitted_at: (/* @__PURE__ */ new Date()).toISOString(),
4657
+ created_by: this.client.store.getState().userReducer.currentUser.id
4541
4658
  };
4542
4659
  await this.client.files.addCache(attachmentPayload.file, file_sha1);
4543
4660
  this.client.store.dispatch(addProjectAttachment(offlineAttachment));
@@ -4577,7 +4694,9 @@ class AttachmentService extends BaseApiService {
4577
4694
  file_name: file2.name,
4578
4695
  file_type: file2.type,
4579
4696
  issue: issueId,
4580
- file_sha1: hash
4697
+ file_sha1: hash,
4698
+ submitted_at: (/* @__PURE__ */ new Date()).toISOString(),
4699
+ created_by: this.client.store.getState().userReducer.currentUser.id
4581
4700
  });
4582
4701
  return this.addIssueAttachment(attachment);
4583
4702
  };
@@ -4596,7 +4715,9 @@ class AttachmentService extends BaseApiService {
4596
4715
  file_name: file2.name,
4597
4716
  file_type: file2.type,
4598
4717
  component: componentId,
4599
- file_sha1: hash
4718
+ file_sha1: hash,
4719
+ submitted_at: (/* @__PURE__ */ new Date()).toISOString(),
4720
+ created_by: this.client.store.getState().userReducer.currentUser.id
4600
4721
  });
4601
4722
  return this.addComponentAttachment(attachment);
4602
4723
  };
@@ -4615,7 +4736,9 @@ class AttachmentService extends BaseApiService {
4615
4736
  file_name: file2.name,
4616
4737
  file_type: file2.type,
4617
4738
  component_type: componentTypeId,
4618
- file_sha1: hash
4739
+ file_sha1: hash,
4740
+ submitted_at: (/* @__PURE__ */ new Date()).toISOString(),
4741
+ created_by: this.client.store.getState().userReducer.currentUser.id
4619
4742
  });
4620
4743
  return this.addComponentTypeAttachment(attachment);
4621
4744
  };
@@ -4634,7 +4757,9 @@ class AttachmentService extends BaseApiService {
4634
4757
  file_name: file2.name,
4635
4758
  file_type: file2.type,
4636
4759
  project: projectId,
4637
- file_sha1: hash
4760
+ file_sha1: hash,
4761
+ submitted_at: (/* @__PURE__ */ new Date()).toISOString(),
4762
+ created_by: this.client.store.getState().userReducer.currentUser.id
4638
4763
  });
4639
4764
  return this.addProjectAttachment(attachment);
4640
4765
  };
@@ -5711,49 +5836,35 @@ class ComponentTypeService extends BaseApiService {
5711
5836
  }
5712
5837
  }
5713
5838
  class IssueCommentService extends BaseApiService {
5839
+ // Omit author and submitted_at since these will always be set internally
5714
5840
  add(comment) {
5715
- const offlinePayload = offline(comment);
5716
- const submittedAt = (/* @__PURE__ */ new Date()).toISOString();
5717
5841
  const { store } = this.client;
5718
- const offlineComment = {
5719
- ...offlinePayload,
5842
+ const offlineComment = offline({
5843
+ ...comment,
5720
5844
  author: store.getState().userReducer.currentUser.id,
5721
- created_at: submittedAt
5722
- };
5723
- store.dispatch(addOrReplaceIssueComment(offlineComment));
5845
+ submitted_at: (/* @__PURE__ */ new Date()).toISOString()
5846
+ });
5847
+ store.dispatch(addIssueComment(offlineComment));
5724
5848
  const promise = this.enqueueRequest({
5725
5849
  description: `${truncate(comment.content, 80)}`,
5726
5850
  method: HttpMethod.POST,
5727
5851
  url: `/issues/${comment.issue}/comment/`,
5728
- payload: { ...offlinePayload, submitted_at: submittedAt },
5852
+ payload: offlineComment,
5729
5853
  blockers: [comment.issue],
5730
- blocks: [offlinePayload.offline_id]
5854
+ blocks: [offlineComment.offline_id]
5855
+ });
5856
+ promise.catch(() => {
5857
+ store.dispatch(removeIssueComment(offlineComment.offline_id));
5731
5858
  });
5732
5859
  return [offlineComment, promise];
5733
5860
  }
5734
- async refreshStore() {
5861
+ update(comment) {
5735
5862
  const { store } = this.client;
5736
- const result = await this.enqueueRequest({
5737
- description: "Get comments",
5738
- method: HttpMethod.GET,
5739
- // TODO: Choose between /issues/comments/in-project/${projectId}/ and /projects/${projectId}/issue-comments/
5740
- url: `/projects/${store.getState().projectReducer.activeProjectId}/comments/`,
5741
- blockers: [],
5742
- blocks: []
5743
- });
5744
- let filteredResult = result.filter(onlyUniqueOfflineIds);
5745
- filteredResult = filteredResult.map((comment) => {
5746
- return { ...comment };
5747
- });
5748
- if (result.length !== filteredResult.length) {
5749
- console.error(
5750
- `Received duplicate comments from the API (new length ${filteredResult.length}); filtered in browser.`
5751
- );
5863
+ const commentToUpdate = store.getState().issueReducer.comments[comment.offline_id];
5864
+ if (!commentToUpdate) {
5865
+ throw new Error(`Comment with offline_id ${comment.offline_id} not found in store`);
5752
5866
  }
5753
- store.dispatch(setIssueComments(filteredResult));
5754
- }
5755
- update(comment) {
5756
- this.client.store.dispatch(addOrReplaceIssueComment(comment));
5867
+ store.dispatch(setIssueComment(comment));
5757
5868
  const promise = this.enqueueRequest({
5758
5869
  description: `Edit comment: ${truncate(comment.content, 80)}`,
5759
5870
  method: HttpMethod.PATCH,
@@ -5762,17 +5873,62 @@ class IssueCommentService extends BaseApiService {
5762
5873
  blockers: [comment.issue],
5763
5874
  blocks: [comment.offline_id]
5764
5875
  });
5876
+ promise.catch(() => {
5877
+ store.dispatch(setIssueComment(commentToUpdate));
5878
+ });
5765
5879
  return [comment, promise];
5766
5880
  }
5767
5881
  remove(offline_id) {
5882
+ const commentToRemove = this.client.store.getState().issueReducer.comments[offline_id];
5883
+ if (!commentToRemove) {
5884
+ throw new Error(`Comment with offline_id ${offline_id} not found in store`);
5885
+ }
5768
5886
  this.client.store.dispatch(removeIssueComment(offline_id));
5769
- return this.enqueueRequest({
5887
+ const promise = this.enqueueRequest({
5770
5888
  description: "Delete comment",
5771
5889
  method: HttpMethod.DELETE,
5772
5890
  url: `/issues/comments/${offline_id}/`,
5773
5891
  blockers: [offline_id],
5774
5892
  blocks: []
5775
5893
  });
5894
+ promise.catch(() => {
5895
+ this.client.store.dispatch(addIssueComment(commentToRemove));
5896
+ });
5897
+ return promise;
5898
+ }
5899
+ async refreshStore() {
5900
+ const { store } = this.client;
5901
+ const result = await this.enqueueRequest({
5902
+ description: "Get comments",
5903
+ method: HttpMethod.GET,
5904
+ // TODO: Choose between /issues/comments/in-project/${projectId}/ and /projects/${projectId}/issue-comments/
5905
+ url: `/projects/${store.getState().projectReducer.activeProjectId}/comments/`,
5906
+ blockers: [],
5907
+ blocks: []
5908
+ });
5909
+ store.dispatch(setIssueComments(result));
5910
+ }
5911
+ }
5912
+ class IssueUpdateService extends BaseApiService {
5913
+ async refreshStore() {
5914
+ const { store } = this.client;
5915
+ const result = await this.enqueueRequest({
5916
+ description: "Get issue updates",
5917
+ method: HttpMethod.GET,
5918
+ url: `/projects/${store.getState().projectReducer.activeProjectId}/issues/updates/`,
5919
+ blockers: [],
5920
+ blocks: []
5921
+ });
5922
+ let filteredResult = result.filter(onlyUniqueOfflineIds);
5923
+ filteredResult = filteredResult.map((comment) => {
5924
+ return { ...comment };
5925
+ });
5926
+ if (result.length !== filteredResult.length) {
5927
+ console.error(
5928
+ `Received duplicate comments from the API (new length ${filteredResult.length}); filtered in browser.`
5929
+ );
5930
+ }
5931
+ store.dispatch(setIssueUpdates(filteredResult));
5776
5932
  }
5777
5933
  }
5778
5934
  class IssueService extends BaseApiService {
@@ -5853,7 +6009,83 @@ class IssueService extends BaseApiService {
5853
6009
  return [offlineIssues, promise];
5854
6010
  }
5855
6011
  update(issue) {
6012
+ const state = this.client.store.getState();
6013
+ const issueToBeUpdated = state.issueReducer.issues[issue.offline_id];
6014
+ if (!issueToBeUpdated) {
6015
+ throw new Error(
6016
+ `Attempting to update an issue with offline_id ${issue.offline_id} that doesn't exist in the store`
6017
+ );
6018
+ }
5856
6019
  this.client.store.dispatch(updateIssue(issue));
6020
+ const changes = {};
6021
+ for (const issueUpdateChange of [
6022
+ IssueUpdateChange.TITLE,
6023
+ IssueUpdateChange.DESCRIPTION,
6024
+ IssueUpdateChange.STATUS,
6025
+ IssueUpdateChange.CATEGORY,
6026
+ IssueUpdateChange.PRIORITY,
6027
+ IssueUpdateChange.ASSIGNED_TO,
6028
+ IssueUpdateChange.DUE_DATE
6029
+ ]) {
6030
+ if (issueUpdateChange in issue && issue[issueUpdateChange] !== issueToBeUpdated[issueUpdateChange]) {
6031
+ switch (issueUpdateChange) {
6032
+ case "category": {
6033
+ let categoryOrNull = null;
6034
+ const categoryIdOrNull = issue[issueUpdateChange];
6035
+ if (categoryIdOrNull) {
6036
+ categoryOrNull = state.categoryReducer.categories[categoryIdOrNull] ?? null;
6037
+ if (!categoryOrNull)
6038
+ throw new Error(
6039
+ `Trying to update issue category to ${categoryIdOrNull} which does not exist in store`
6040
+ );
6041
+ }
6042
+ changes[issueUpdateChange] = categoryOrNull ? {
6043
+ name: categoryOrNull.name,
6044
+ color: categoryOrNull.color,
6045
+ offline_id: categoryOrNull.offline_id
6046
+ } : null;
6047
+ break;
6048
+ }
6049
+ case "assigned_to": {
6050
+ let userOrNull = null;
6051
+ const userIdOrNull = issue[issueUpdateChange];
6052
+ if (userIdOrNull) {
6053
+ userOrNull = state.userReducer.users[userIdOrNull] ?? null;
6054
+ if (!userOrNull)
6055
+ throw new Error(
6056
+ `Trying to update issue assigned_to to ${userIdOrNull} which does not exist in store`
6057
+ );
6058
+ }
6059
+ changes[issueUpdateChange] = userOrNull ? {
6060
+ full_name: userOrNull.username,
6061
+ id: userOrNull.id
6062
+ } : null;
6063
+ break;
6064
+ }
6065
+ case "description":
6066
+ changes[issueUpdateChange] = issue[issueUpdateChange] ?? null;
6067
+ break;
6068
+ case "title":
6069
+ changes[issueUpdateChange] = issue[issueUpdateChange] ?? null;
6070
+ break;
6071
+ case "priority":
6072
+ changes[issueUpdateChange] = issue[issueUpdateChange];
6073
+ break;
6074
+ case "status":
6075
+ changes[issueUpdateChange] = issue[issueUpdateChange];
6076
+ break;
6077
+ case "due_date":
6078
+ changes[issueUpdateChange] = issue[issueUpdateChange] ? issue[issueUpdateChange] : null;
6079
+ }
6080
+ }
6081
+ }
6082
+ const offlineIssueUpdate = offline({
6083
+ created_by: state.userReducer.currentUser.id,
6084
+ submitted_at: (/* @__PURE__ */ new Date()).toISOString(),
6085
+ issue: issueToBeUpdated.offline_id,
6086
+ changes
6087
+ });
6088
+ this.client.store.dispatch(addIssueUpdate(offlineIssueUpdate));
5857
6089
  const promise = this.enqueueRequest({
5858
6090
  description: "Edit issue",
5859
6091
  method: HttpMethod.PATCH,
@@ -5862,23 +6094,30 @@ class IssueService extends BaseApiService {
5862
6094
  blockers: [issue.offline_id],
5863
6095
  blocks: [issue.offline_id]
5864
6096
  });
6097
+ promise.catch(() => {
6098
+ this.client.store.dispatch(updateIssue(issueToBeUpdated));
6099
+ this.client.store.dispatch(removeIssueUpdate(offlineIssueUpdate.offline_id));
6100
+ });
5865
6101
  const fullIssue = this.client.store.getState().issueReducer.issues[issue.offline_id];
5866
6102
  return [fullIssue, promise];
5867
6103
  }
5868
6104
  async remove(id) {
5869
6105
  const { store } = this.client;
5870
6106
  const state = store.getState();
6107
+ const dispatch = store.dispatch;
5871
6108
  const backup = state.issueReducer.issues[id];
5872
6109
  if (!backup) {
5873
6110
  throw new Error(`No issue with id ${id} found in the store`);
5874
6111
  }
5875
6112
  const attachments = Object.values(state.issueReducer.attachments).filter((a) => a.issue === id);
5876
6113
  const attachmentsOfIssue = selectAttachmentsOfIssue(id)(state);
5877
- this.client.store.dispatch(removeIssue(id));
5878
- store.dispatch(addActiveProjectIssuesCount(-1));
5879
- if (attachmentsOfIssue.length > 0) {
5880
- this.client.store.dispatch(removeAttachmentsOfIssue(id));
5881
- }
6114
+ const updatesOfIssue = selectIssueUpdatesOfIssue(id)(state);
6115
+ dispatch(removeIssue(id));
6116
+ dispatch(addActiveProjectIssuesCount(-1));
6117
+ if (attachmentsOfIssue.length > 0)
6118
+ dispatch(removeAttachmentsOfIssue(id));
6119
+ if (updatesOfIssue.length > 0)
6120
+ dispatch(removeIssueUpdates(updatesOfIssue.map(({ offline_id }) => offline_id)));
5882
6121
  try {
5883
6122
  return await this.enqueueRequest({
5884
6123
  description: "Delete issue",
@@ -5888,9 +6127,10 @@ class IssueService extends BaseApiService {
5888
6127
  blocks: []
5889
6128
  });
5890
6129
  } catch (e) {
5891
- this.client.store.dispatch(addIssue(backup));
5892
- this.client.store.dispatch(addIssueAttachments(attachments));
5893
- store.dispatch(addActiveProjectIssuesCount(1));
6130
+ dispatch(addIssue(backup));
6131
+ dispatch(addIssueAttachments(attachments));
6132
+ dispatch(addIssueUpdates(updatesOfIssue));
6133
+ dispatch(addActiveProjectIssuesCount(1));
5894
6134
  throw e;
5895
6135
  }
5896
6136
  }
@@ -6072,6 +6312,7 @@ class MainService extends BaseApiService {
6072
6312
  store.dispatch(setProjectAttachments(project_attachments));
6073
6313
  });
6074
6314
  void this.client.documents.refreshStore();
6315
+ void this.client.issueUpdates.refreshStore();
6075
6316
  }
6076
6317
  store.dispatch(setIsFetchingInitialData(false));
6077
6318
  if (overwrite) {
@@ -7499,7 +7740,7 @@ class AgentService extends BaseApiService {
7499
7740
  * @param request The message to prompt the agent with.
7500
7741
  * @param conversationId If continuing an existing message, the UUID of that conversation.
7501
7742
  */
7502
- prompt(request2, conversationId) {
7743
+ async prompt(request2, conversationId) {
7503
7744
  const activeProjectId = this.client.store.getState().projectReducer.activeProjectId;
7504
7745
  return this.enqueueRequest({
7505
7746
  description: "Prompt agent",
@@ -7514,6 +7755,16 @@ class AgentService extends BaseApiService {
7514
7755
  queryParams: conversationId ? { conversation_id: conversationId } : {}
7515
7756
  });
7516
7757
  }
7758
+ async rate(responseId, rating) {
7759
+ return this.enqueueRequest({
7760
+ description: "Rate agent response",
7761
+ method: HttpMethod.PUT,
7762
+ url: `/agents/responses/${responseId}/rate/`,
7763
+ payload: { rating },
7764
+ blockers: ["rate"],
7765
+ blocks: ["rate"]
7766
+ });
7767
+ }
7517
7768
  }
7518
7769
  class OvermapSDK {
7519
7770
  constructor(apiUrl, store) {
@@ -7529,6 +7780,7 @@ class OvermapSDK {
7529
7780
  __publicField(this, "organizationAccess", new OrganizationAccessService(this));
7530
7781
  __publicField(this, "issues", new IssueService(this));
7531
7782
  __publicField(this, "issueComments", new IssueCommentService(this));
7783
+ __publicField(this, "issueUpdates", new IssueUpdateService(this));
7532
7784
  __publicField(this, "workspaces", new WorkspaceService(this));
7533
7785
  __publicField(this, "main", new MainService(this));
7534
7786
  __publicField(this, "components", new ComponentService(this));
@@ -13002,52 +13254,54 @@ const styles$4 = {
13002
13254
  Footer,
13003
13255
  Loading
13004
13256
  };
13005
- const ImageCard = memo((props) => {
13006
- const { file, alt, error: error2, size, rightSlot, className, truncateLength, ...rest } = props;
13007
- const fileCardRef = useRef(null);
13008
- const imageInsetRef = useRef(null);
13009
- const fileCardSize = useSize(fileCardRef);
13010
- useLayoutEffect(() => {
13011
- if (!imageInsetRef.current || !fileCardSize)
13012
- return;
13013
- imageInsetRef.current.style.height = `${fileCardSize.height * 4}px`;
13014
- }, [fileCardSize]);
13015
- const fileName2 = useMemo(() => {
13016
- if (!file)
13017
- return;
13018
- return truncateLength !== void 0 ? truncate(file.name, truncateLength) : file.name;
13019
- }, [file, truncateLength]);
13020
- return /* @__PURE__ */ jsxs(
13021
- Flex,
13022
- {
13023
- className: classNames$1(className, styles$4.ImageCard),
13024
- width: "100%",
13025
- direction: "column",
13026
- position: "relative",
13027
- height: "max-content",
13028
- gap: "0",
13029
- ...rest,
13030
- children: [
13031
- !file && !error2 && /* @__PURE__ */ jsx(Flex, { width: "100%", height: "100%", align: "center", justify: "center", position: "absolute", children: /* @__PURE__ */ jsx(Spinner, {}) }),
13032
- /* @__PURE__ */ jsx(Inset, { className: styles$4.ImageInset, ref: imageInsetRef, clip: "padding-box", side: "y", pb: "0", children: file && !error2 && /* @__PURE__ */ jsx("img", { className: styles$4.Image, src: URL.createObjectURL(file), alt: alt ?? file.name }) }),
13033
- /* @__PURE__ */ jsx(
13034
- OvermapItem,
13035
- {
13036
- className: classNames$1(styles$4.Footer, {
13037
- [styles$4.Loading]: !file
13038
- }),
13039
- size,
13040
- ref: fileCardRef,
13041
- leftSlot: error2 ? /* @__PURE__ */ jsx(RiIcon, { icon: "RiFileWarningLine" }) : file && /* @__PURE__ */ jsx(FileIcon, { fileType: file.type }),
13042
- rightSlot,
13043
- children: error2 ?? fileName2
13044
- }
13045
- )
13046
- ]
13047
- }
13048
- );
13049
- });
13050
- ImageCard.displayName = "ImageCard";
13257
+ const ImageCard = memo(
13258
+ forwardRef((props, forwardedRef) => {
13259
+ const { file, alt, error: error2, size, rightSlot, className, truncateLength, ...rest } = props;
13260
+ const fileCardRef = useRef(null);
13261
+ const imageInsetRef = useRef(null);
13262
+ const fileCardSize = useSize(fileCardRef);
13263
+ useLayoutEffect(() => {
13264
+ if (!imageInsetRef.current || !fileCardSize)
13265
+ return;
13266
+ imageInsetRef.current.style.height = `${fileCardSize.height * 4}px`;
13267
+ }, [fileCardSize]);
13268
+ const fileName2 = useMemo(() => {
13269
+ if (!file)
13270
+ return;
13271
+ return truncateLength !== void 0 ? truncate(file.name, truncateLength) : file.name;
13272
+ }, [file, truncateLength]);
13273
+ return /* @__PURE__ */ jsxs(
13274
+ Flex,
13275
+ {
13276
+ className: classNames$1(className, styles$4.ImageCard),
13277
+ width: "100%",
13278
+ direction: "column",
13279
+ position: "relative",
13280
+ height: "max-content",
13281
+ gap: "0",
13282
+ ref: forwardedRef,
13283
+ ...rest,
13284
+ children: [
13285
+ !file && !error2 && /* @__PURE__ */ jsx(Flex, { width: "100%", height: "100%", align: "center", justify: "center", position: "absolute", children: /* @__PURE__ */ jsx(Spinner, {}) }),
13286
+ /* @__PURE__ */ jsx(Inset, { className: styles$4.ImageInset, ref: imageInsetRef, clip: "padding-box", side: "y", pb: "0", children: file && !error2 && /* @__PURE__ */ jsx("img", { className: styles$4.Image, src: URL.createObjectURL(file), alt: alt ?? file.name }) }),
13287
+ /* @__PURE__ */ jsx(
13288
+ OvermapItem,
13289
+ {
13290
+ className: classNames$1(styles$4.Footer, {
13291
+ [styles$4.Loading]: !file
13292
+ }),
13293
+ size,
13294
+ ref: fileCardRef,
13295
+ leftSlot: error2 ? /* @__PURE__ */ jsx(RiIcon, { icon: "RiFileWarningLine" }) : file && /* @__PURE__ */ jsx(FileIcon, { fileType: file.type }),
13296
+ rightSlot,
13297
+ children: error2 ?? fileName2
13298
+ }
13299
+ )
13300
+ ]
13301
+ }
13302
+ );
13303
+ })
13304
+ );
13051
13305
  const UploadInput = memo((props) => {
13052
13306
  var _a2;
13053
13307
  const [{ inputId, labelId, size, severity, helpText, showInputOnly, field, fieldProps }, rest] = useFormikInput(props);
@@ -14184,17 +14438,15 @@ const FieldActions = memo((props) => {
14184
14438
  Action.key
14185
14439
  )) }),
14186
14440
  /* @__PURE__ */ jsx(Box, { display: forMobile(true, "block"), children: /* @__PURE__ */ jsx(
14187
- DropdownItemMenu,
14441
+ OvermapDropdownMenu,
14188
14442
  {
14189
14443
  trigger: /* @__PURE__ */ jsx(IconButton, { variant: "ghost", "aria-label": "Actions menu", children: /* @__PURE__ */ jsx(RiIcon, { icon: "RiMore2Line" }) }),
14190
14444
  items: actions.map((Action) => {
14191
14445
  var _a2;
14192
14446
  return {
14193
- content: /* @__PURE__ */ jsxs(Flex$1, { gap: "2", align: "center", children: [
14194
- /* @__PURE__ */ jsx(Action.Icon, {}),
14195
- Action.text
14196
- ] }, Action.key),
14197
- onSelect: (_a2 = Action.buttonProps) == null ? void 0 : _a2.onClick
14447
+ leftSlot: /* @__PURE__ */ jsx(Action.Icon, {}),
14448
+ children: Action.text,
14449
+ onClick: (_a2 = Action.buttonProps) == null ? void 0 : _a2.onClick
14198
14450
  };
14199
14451
  })
14200
14452
  }
@@ -14252,10 +14504,8 @@ const useFieldTypeItems = (onSelect = () => null) => {
14252
14504
  const field = FieldTypeToClsMapping[identifier];
14253
14505
  const Icon = field.Icon;
14254
14506
  return {
14255
- content: /* @__PURE__ */ jsxs(Flex$1, { align: "center", gap: "2", children: [
14256
- /* @__PURE__ */ jsx(Icon, {}),
14257
- /* @__PURE__ */ jsx(Text$1, { children: field.fieldTypeName })
14258
- ] }, identifier),
14507
+ children: field.fieldTypeName,
14508
+ leftSlot: /* @__PURE__ */ jsx(Icon, {}),
14259
14509
  value: identifier,
14260
14510
  onSelect: () => {
14261
14511
  onSelect(identifier);
@@ -14459,7 +14709,7 @@ const FieldBuilder = memo((props) => {
14459
14709
  }
14460
14710
  ),
14461
14711
  /* @__PURE__ */ jsxs(Flex$1, { align: "center", gap: "3", children: [
14462
- /* @__PURE__ */ jsx(Badge, { className: styles.typeBadge, children: (_f = fieldTypeItems.flat().find((item) => item.value === type)) == null ? void 0 : _f.content }),
14712
+ /* @__PURE__ */ jsx(Badge, { className: styles.typeBadge, children: (_f = fieldTypeItems.flat().find((item) => item.value === type)) == null ? void 0 : _f.children }),
14463
14713
  showPopoverInputs && /* @__PURE__ */ jsx(FieldSettingsPopover, { popoverInputs, hasError: popoverHasErrors })
14464
14714
  ] }),
14465
14715
  resolvedImage && /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -14820,7 +15070,7 @@ const FieldSectionWithActions = memo((props) => {
14820
15070
  )),
14821
15071
  droppableProvided.placeholder,
14822
15072
  /* @__PURE__ */ jsx(
14823
- DropdownItemMenu,
15073
+ OvermapDropdownMenu,
14824
15074
  {
14825
15075
  trigger: /* @__PURE__ */ jsxs(Button, { type: "button", variant: "soft", children: [
14826
15076
  /* @__PURE__ */ jsx(RiIcon, { icon: "RiAddLine" }),
@@ -15256,6 +15506,8 @@ export {
15256
15506
  IssuePriority,
15257
15507
  IssueService,
15258
15508
  IssueStatus,
15509
+ IssueUpdateChange,
15510
+ IssueUpdateService,
15259
15511
  LicenseLevel,
15260
15512
  LicenseService,
15261
15513
  LicenseStatus,
@@ -15319,6 +15571,10 @@ export {
15319
15571
  addIssue,
15320
15572
  addIssueAttachment,
15321
15573
  addIssueAttachments,
15574
+ addIssueComment,
15575
+ addIssueComments,
15576
+ addIssueUpdate,
15577
+ addIssueUpdates,
15322
15578
  addLicenses,
15323
15579
  addOrReplaceCategories,
15324
15580
  addOrReplaceIssueComment,
@@ -15478,6 +15734,9 @@ export {
15478
15734
  removeIssue,
15479
15735
  removeIssueAttachment,
15480
15736
  removeIssueComment,
15737
+ removeIssueComments,
15738
+ removeIssueUpdate,
15739
+ removeIssueUpdates,
15481
15740
  removeOrganizationAccess,
15482
15741
  removeProjectAccess,
15483
15742
  removeProjectAccessesOfProject,
@@ -15579,6 +15838,8 @@ export {
15579
15838
  selectIssueAttachmentMapping,
15580
15839
  selectIssueAttachments,
15581
15840
  selectIssueMapping,
15841
+ selectIssueUpdateMapping,
15842
+ selectIssueUpdatesOfIssue,
15582
15843
  selectIssues,
15583
15844
  selectLatestFormRevision,
15584
15845
  selectLatestRetryTime,
@@ -15630,6 +15891,7 @@ export {
15630
15891
  selectSortedOrganizationUsers,
15631
15892
  selectSortedProjectUsers,
15632
15893
  selectSortedProjects,
15894
+ selectStage,
15633
15895
  selectStageFormIdsFromStageIds,
15634
15896
  selectStageMapping,
15635
15897
  selectStages,
@@ -15645,6 +15907,7 @@ export {
15645
15907
  selectUser,
15646
15908
  selectUserForm,
15647
15909
  selectUserFormMapping,
15910
+ selectUserFormSubmission,
15648
15911
  selectUsersAsMapping,
15649
15912
  selectVisibleStatuses,
15650
15913
  selectVisibleUserIds,
@@ -15674,7 +15937,9 @@ export {
15674
15937
  setIsImportingProjectFile,
15675
15938
  setIsLoading,
15676
15939
  setIssueAttachments,
15940
+ setIssueComment,
15677
15941
  setIssueComments,
15942
+ setIssueUpdates,
15678
15943
  setIssues,
15679
15944
  setLicenses,
15680
15945
  setLoggedIn,