@overmap-ai/core 1.0.51-add-submitted-at-to-form-revisions.1 → 1.0.51-attachment-creation-flows.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/forms/renderer/FormSubmissionBrowser/FormSubmissionBrowser.d.ts +5 -5
  2. package/dist/forms/renderer/FormSubmissionViewer/FormSubmissionViewer.d.ts +3 -3
  3. package/dist/overmap-core.js +1231 -500
  4. package/dist/overmap-core.js.map +1 -1
  5. package/dist/overmap-core.umd.cjs +1231 -500
  6. package/dist/overmap-core.umd.cjs.map +1 -1
  7. package/dist/sdk/sdk.d.ts +6 -1
  8. package/dist/sdk/services/BaseAttachmentService.d.ts +31 -0
  9. package/dist/sdk/services/ComponentAttachmentService.d.ts +10 -0
  10. package/dist/sdk/services/ComponentTypeAttachmentService.d.ts +10 -0
  11. package/dist/sdk/services/DocumentAttachmentService.d.ts +10 -0
  12. package/dist/sdk/services/IssueAttachmentService.d.ts +10 -0
  13. package/dist/sdk/services/ProjectAttachmentService.d.ts +10 -0
  14. package/dist/sdk/services/UserFormSubmissionService.d.ts +9 -2
  15. package/dist/sdk/services/index.d.ts +5 -0
  16. package/dist/store/slices/categorySlice.d.ts +3 -1
  17. package/dist/store/slices/componentSlice.d.ts +15 -7
  18. package/dist/store/slices/componentTypeSlice.d.ts +16 -8
  19. package/dist/store/slices/documentSlice.d.ts +13 -3
  20. package/dist/store/slices/formRevisionSlice.d.ts +65 -0
  21. package/dist/store/slices/formSlice.d.ts +110 -0
  22. package/dist/store/slices/formSubmissionSlice.d.ts +47 -0
  23. package/dist/store/slices/index.d.ts +3 -1
  24. package/dist/store/slices/issueSlice.d.ts +36 -22
  25. package/dist/store/slices/projectFileSlice.d.ts +3 -1
  26. package/dist/store/slices/projectSlice.d.ts +12 -3
  27. package/dist/store/slices/utils.d.ts +4 -2
  28. package/dist/store/slices/workspaceSlice.d.ts +3 -1
  29. package/dist/store/store.d.ts +10 -4
  30. package/dist/typings/files.d.ts +11 -1
  31. package/dist/typings/models/attachments.d.ts +15 -11
  32. package/dist/typings/models/base.d.ts +7 -0
  33. package/dist/typings/models/forms.d.ts +6 -11
  34. package/dist/typings/models/projects.d.ts +4 -5
  35. package/dist/utils/file.d.ts +2 -0
  36. package/dist/utils/forms.d.ts +2 -0
  37. package/package.json +1 -1
  38. package/dist/store/slices/userFormSlice.d.ts +0 -145
@@ -677,15 +677,15 @@ const wrapMigration = (migrator) => (state) => {
677
677
  };
678
678
  const migrations = [initialVersioning, signOut, signOut, createOutboxState];
679
679
  const manifest = Object.fromEntries(migrations.map((migration2, i) => [i, wrapMigration(migration2)]));
680
- const initialState$n = {
680
+ const initialState$p = {
681
681
  accessToken: "",
682
682
  refreshToken: "",
683
683
  isLoggedIn: false
684
684
  };
685
685
  const authSlice = createSlice({
686
686
  name: "auth",
687
- initialState: initialState$n,
688
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$n)),
687
+ initialState: initialState$p,
688
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$p)),
689
689
  reducers: {
690
690
  setTokens: (state, action) => {
691
691
  state.accessToken = action.payload.accessToken;
@@ -850,6 +850,19 @@ function downloadInMemoryFile(filename, text) {
850
850
  element.click();
851
851
  document.body.removeChild(element);
852
852
  }
853
+ const constructUploadedFilePayloads = async (files) => {
854
+ const filePayloads = {};
855
+ for (const file of files) {
856
+ const sha1 = await hashFile(file);
857
+ filePayloads[sha1] = {
858
+ sha1,
859
+ extension: file.name.split(".").pop() || "",
860
+ file_type: file.type,
861
+ size: file.size
862
+ };
863
+ }
864
+ return Object.values(filePayloads);
865
+ };
853
866
  const fileToBlob = async (dataUrl) => {
854
867
  return (await fetch(dataUrl)).blob();
855
868
  };
@@ -1416,7 +1429,7 @@ const getLocalRelativeDateString = memoize((date, min, max) => {
1416
1429
  return getLocalDateString(date);
1417
1430
  return relative.format(days, "days");
1418
1431
  });
1419
- const initialState$m = {
1432
+ const initialState$o = {
1420
1433
  categories: {},
1421
1434
  usedCategoryColors: [],
1422
1435
  categoryVisibility: {
@@ -1426,8 +1439,8 @@ const initialState$m = {
1426
1439
  };
1427
1440
  const categorySlice = createSlice({
1428
1441
  name: "categories",
1429
- initialState: initialState$m,
1430
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$m)),
1442
+ initialState: initialState$o,
1443
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$o)),
1431
1444
  reducers: {
1432
1445
  setCategories: (state, action) => {
1433
1446
  if (!Array.isArray(action.payload))
@@ -1560,6 +1573,9 @@ const selectHiddenCategoryCount = (state) => {
1560
1573
  return hiddenCategoryCount;
1561
1574
  };
1562
1575
  const categoryReducer = categorySlice.reducer;
1576
+ function setAttachment(state, action) {
1577
+ state.attachments[action.payload.offline_id] = action.payload;
1578
+ }
1563
1579
  function setAttachments(state, action) {
1564
1580
  state.attachments = {};
1565
1581
  for (const attachment of action.payload) {
@@ -1584,6 +1600,15 @@ function updateAttachment(state, action) {
1584
1600
  throw new Error(`Attachment ${action.payload.offline_id} does not exist.`);
1585
1601
  }
1586
1602
  }
1603
+ function updateAttachments(state, action) {
1604
+ for (const attachment of action.payload) {
1605
+ if (attachment.offline_id in state.attachments) {
1606
+ state.attachments[attachment.offline_id] = attachment;
1607
+ } else {
1608
+ throw new Error(`Attachment ${attachment.offline_id} does not exist.`);
1609
+ }
1610
+ }
1611
+ }
1587
1612
  function removeAttachment(state, action) {
1588
1613
  if (action.payload in state.attachments) {
1589
1614
  delete state.attachments[action.payload];
@@ -1596,14 +1621,14 @@ function removeAttachments(state, action) {
1596
1621
  delete state.attachments[attachmentId];
1597
1622
  }
1598
1623
  }
1599
- const initialState$l = {
1624
+ const initialState$n = {
1600
1625
  components: {},
1601
1626
  attachments: {}
1602
1627
  };
1603
1628
  const componentSlice = createSlice({
1604
1629
  name: "components",
1605
- initialState: initialState$l,
1606
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$l)),
1630
+ initialState: initialState$n,
1631
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$n)),
1607
1632
  reducers: {
1608
1633
  addComponent: (state, action) => {
1609
1634
  state.components[action.payload.offline_id] = action.payload;
@@ -1617,12 +1642,6 @@ const componentSlice = createSlice({
1617
1642
  state.components = toOfflineIdRecord(action.payload);
1618
1643
  prevComponents = null;
1619
1644
  },
1620
- setComponentAttachments: setAttachments,
1621
- addComponentAttachment: addAttachment,
1622
- addComponentAttachments: addAttachments,
1623
- updateComponentAttachment: updateAttachment,
1624
- removeComponentAttachment: removeAttachment,
1625
- removeComponentAttachments: removeAttachments,
1626
1645
  updateComponent: (state, action) => {
1627
1646
  if (action.payload.offline_id in state.components) {
1628
1647
  state.components[action.payload.offline_id] = action.payload;
@@ -1647,9 +1666,35 @@ const componentSlice = createSlice({
1647
1666
  }
1648
1667
  }
1649
1668
  prevComponents = null;
1650
- }
1669
+ },
1670
+ // Attachments
1671
+ setComponentAttachment: setAttachment,
1672
+ setComponentAttachments: setAttachments,
1673
+ addComponentAttachment: addAttachment,
1674
+ addComponentAttachments: addAttachments,
1675
+ updateComponentAttachment: updateAttachment,
1676
+ updateComponentAttachments: updateAttachments,
1677
+ removeComponentAttachment: removeAttachment,
1678
+ removeComponentAttachments: removeAttachments
1651
1679
  }
1652
1680
  });
1681
+ const {
1682
+ addComponent,
1683
+ updateComponent,
1684
+ removeComponent,
1685
+ addComponentsInBatches,
1686
+ setComponents,
1687
+ removeAllComponentsOfType,
1688
+ // Attachments
1689
+ setComponentAttachment,
1690
+ setComponentAttachments,
1691
+ addComponentAttachment,
1692
+ addComponentAttachments,
1693
+ updateComponentAttachment,
1694
+ updateComponentAttachments,
1695
+ removeComponentAttachment,
1696
+ removeComponentAttachments
1697
+ } = componentSlice.actions;
1653
1698
  let prevComponents = null;
1654
1699
  const selectComponents = (state) => {
1655
1700
  if (!prevComponents) {
@@ -1744,28 +1789,14 @@ const selectAttachmentsOfComponentByType = restructureCreateSelectorWithArgs(
1744
1789
  }
1745
1790
  )
1746
1791
  );
1747
- const {
1748
- addComponent,
1749
- updateComponent,
1750
- removeComponent,
1751
- addComponentsInBatches,
1752
- setComponents,
1753
- setComponentAttachments,
1754
- addComponentAttachment,
1755
- addComponentAttachments,
1756
- updateComponentAttachment,
1757
- removeComponentAttachment,
1758
- removeComponentAttachments,
1759
- removeAllComponentsOfType
1760
- } = componentSlice.actions;
1761
1792
  const componentReducer = componentSlice.reducer;
1762
- const initialState$k = {
1793
+ const initialState$m = {
1763
1794
  completionsByComponentId: {}
1764
1795
  };
1765
1796
  const componentStageCompletionSlice = createSlice({
1766
1797
  name: "componentStageCompletions",
1767
- initialState: initialState$k,
1768
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$k)),
1798
+ initialState: initialState$m,
1799
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$m)),
1769
1800
  reducers: {
1770
1801
  addStageCompletion: (state, action) => {
1771
1802
  let stageToCompletionDateMapping = state.completionsByComponentId[action.payload.component];
@@ -1816,13 +1847,13 @@ const selectCompletedStageIdsForComponent = (component) => (state) => {
1816
1847
  return Object.keys(state.componentStageCompletionReducer.completionsByComponentId[component.offline_id] ?? {});
1817
1848
  };
1818
1849
  const componentStageCompletionReducer = componentStageCompletionSlice.reducer;
1819
- const initialState$j = {
1850
+ const initialState$l = {
1820
1851
  stages: {}
1821
1852
  };
1822
1853
  const componentStageSlice = createSlice({
1823
1854
  name: "componentStages",
1824
- initialState: initialState$j,
1825
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$j)),
1855
+ initialState: initialState$l,
1856
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$l)),
1826
1857
  reducers: {
1827
1858
  addStages: (state, action) => {
1828
1859
  Object.assign(state.stages, toOfflineIdRecord(action.payload));
@@ -1932,15 +1963,15 @@ const selectStageFormIdsFromStageIds = restructureCreateSelectorWithArgs(
1932
1963
  );
1933
1964
  const { addStages, updateStages, removeStages, linkStageToForm, unlinkStageToForm } = componentStageSlice.actions;
1934
1965
  const componentStageReducer = componentStageSlice.reducer;
1935
- const initialState$i = {
1966
+ const initialState$k = {
1936
1967
  componentTypes: {},
1937
1968
  hiddenComponentTypeIds: {},
1938
1969
  attachments: {}
1939
1970
  };
1940
1971
  const componentTypeSlice = createSlice({
1941
1972
  name: "componentTypes",
1942
- initialState: initialState$i,
1943
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$i)),
1973
+ initialState: initialState$k,
1974
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$k)),
1944
1975
  reducers: {
1945
1976
  addComponentType: (state, action) => {
1946
1977
  state.componentTypes[action.payload.offline_id] = action.payload;
@@ -1948,20 +1979,38 @@ const componentTypeSlice = createSlice({
1948
1979
  setComponentTypes: (state, action) => {
1949
1980
  state.componentTypes = toOfflineIdRecord(action.payload);
1950
1981
  },
1951
- setComponentTypeAttachments: setAttachments,
1952
- addComponentTypeAttachment: addAttachment,
1953
- addComponentTypeAttachments: addAttachments,
1954
- updateComponentTypeAttachment: updateAttachment,
1955
- removeComponentTypeAttachment: removeAttachment,
1956
- removeComponentTypeAttachments: removeAttachments,
1957
1982
  toggleComponentTypeVisibility: (state, action) => {
1958
1983
  state.hiddenComponentTypeIds[action.payload] = !state.hiddenComponentTypeIds[action.payload];
1959
1984
  },
1960
1985
  deleteComponentType: (state, action) => {
1961
1986
  delete state.componentTypes[action.payload];
1962
- }
1987
+ },
1988
+ // Attachments
1989
+ setComponentTypeAttachment: setAttachment,
1990
+ setComponentTypeAttachments: setAttachments,
1991
+ addComponentTypeAttachment: addAttachment,
1992
+ addComponentTypeAttachments: addAttachments,
1993
+ updateComponentTypeAttachment: updateAttachment,
1994
+ updateComponentTypeAttachments: updateAttachments,
1995
+ removeComponentTypeAttachment: removeAttachment,
1996
+ removeComponentTypeAttachments: removeAttachments
1963
1997
  }
1964
1998
  });
1999
+ const {
2000
+ addComponentType,
2001
+ setComponentTypes,
2002
+ toggleComponentTypeVisibility,
2003
+ deleteComponentType,
2004
+ // Attachmet
2005
+ setComponentTypeAttachment,
2006
+ setComponentTypeAttachments,
2007
+ addComponentTypeAttachment,
2008
+ addComponentTypeAttachments,
2009
+ updateComponentTypeAttachment,
2010
+ updateComponentTypeAttachments,
2011
+ removeComponentTypeAttachment,
2012
+ removeComponentTypeAttachments
2013
+ } = componentTypeSlice.actions;
1965
2014
  const selectComponentTypesMapping = (state) => state.componentTypeReducer.componentTypes;
1966
2015
  const selectComponentTypes = createSelector(
1967
2016
  [selectComponentTypesMapping],
@@ -2038,26 +2087,14 @@ const selectAttachmentsOfComponentTypeByType = restructureCreateSelectorWithArgs
2038
2087
  }
2039
2088
  )
2040
2089
  );
2041
- const {
2042
- addComponentType,
2043
- setComponentTypes,
2044
- setComponentTypeAttachments,
2045
- addComponentTypeAttachment,
2046
- addComponentTypeAttachments,
2047
- updateComponentTypeAttachment,
2048
- removeComponentTypeAttachment,
2049
- removeComponentTypeAttachments,
2050
- toggleComponentTypeVisibility,
2051
- deleteComponentType
2052
- } = componentTypeSlice.actions;
2053
2090
  const componentTypeReducer = componentTypeSlice.reducer;
2054
- const initialState$h = {
2091
+ const initialState$j = {
2055
2092
  workspaces: {},
2056
2093
  activeWorkspaceId: null
2057
2094
  };
2058
2095
  const workspaceSlice = createSlice({
2059
2096
  name: "workspace",
2060
- initialState: initialState$h,
2097
+ initialState: initialState$j,
2061
2098
  // The `reducers` field lets us define reducers and generate associated actions
2062
2099
  reducers: {
2063
2100
  setWorkspaces: (state, action) => {
@@ -2114,7 +2151,7 @@ const selectPermittedWorkspaceIds = createSelector(
2114
2151
  );
2115
2152
  const workspaceReducer = workspaceSlice.reducer;
2116
2153
  const maxRecentIssues = 10;
2117
- const initialState$g = {
2154
+ const initialState$i = {
2118
2155
  issues: {},
2119
2156
  attachments: {},
2120
2157
  comments: {},
@@ -2126,9 +2163,9 @@ const initialState$g = {
2126
2163
  };
2127
2164
  const issueSlice = createSlice({
2128
2165
  name: "issues",
2129
- initialState: initialState$g,
2166
+ initialState: initialState$i,
2130
2167
  extraReducers: (builder) => builder.addCase("RESET", (state) => {
2131
- Object.assign(state, initialState$g);
2168
+ Object.assign(state, initialState$i);
2132
2169
  }),
2133
2170
  reducers: {
2134
2171
  setIssues: (state, action) => {
@@ -2139,7 +2176,6 @@ const issueSlice = createSlice({
2139
2176
  state.issues[issue.offline_id] = issue;
2140
2177
  });
2141
2178
  },
2142
- setIssueAttachments: setAttachments,
2143
2179
  setIssueUpdates: (state, action) => {
2144
2180
  if (action.payload.filter(onlyUniqueOfflineIds).length !== action.payload.length) {
2145
2181
  throw new Error("Tried to use setIssues reducer with duplicate ID's");
@@ -2159,8 +2195,6 @@ const issueSlice = createSlice({
2159
2195
  }
2160
2196
  state.issues[action.payload.offline_id] = action.payload;
2161
2197
  },
2162
- addIssueAttachment: addAttachment,
2163
- addIssueAttachments: addAttachments,
2164
2198
  addIssueUpdate: (state, action) => {
2165
2199
  if (action.payload.offline_id in state.updates) {
2166
2200
  throw new Error(`Tried to add duplicate issue update with offline_id: ${action.payload.offline_id}`);
@@ -2182,7 +2216,6 @@ const issueSlice = createSlice({
2182
2216
  throw new Error(`Tried to update issue with ID that doesn't exist: ${action.payload.offline_id}`);
2183
2217
  }
2184
2218
  },
2185
- updateIssueAttachment: updateAttachment,
2186
2219
  removeIssue: (state, action) => {
2187
2220
  if (action.payload in state.issues) {
2188
2221
  delete state.issues[action.payload];
@@ -2190,7 +2223,6 @@ const issueSlice = createSlice({
2190
2223
  throw new Error(`Failed to remove issue because ID doesn't exist: ${action.payload}`);
2191
2224
  }
2192
2225
  },
2193
- removeIssueAttachment: removeAttachment,
2194
2226
  removeIssueUpdate: (state, action) => {
2195
2227
  if (action.payload in state.updates) {
2196
2228
  delete state.updates[action.payload];
@@ -2287,19 +2319,25 @@ const issueSlice = createSlice({
2287
2319
  if (indexToRemove !== -1) {
2288
2320
  state.recentIssueIds.splice(indexToRemove, 1);
2289
2321
  }
2290
- }
2322
+ },
2323
+ // Attachments
2324
+ setIssueAttachment: setAttachment,
2325
+ setIssueAttachments: setAttachments,
2326
+ addIssueAttachment: addAttachment,
2327
+ addIssueAttachments: addAttachments,
2328
+ updateIssueAttachment: updateAttachment,
2329
+ updateIssueAttachments: updateAttachments,
2330
+ removeIssueAttachment: removeAttachment,
2331
+ removeIssueAttachments: removeAttachments
2291
2332
  }
2292
2333
  });
2293
2334
  const {
2294
- addIssueAttachment,
2295
- addIssueAttachments,
2296
2335
  addIssue,
2297
2336
  addIssueUpdate,
2298
2337
  addIssueUpdates,
2299
2338
  addOrReplaceIssueComment,
2300
2339
  addToRecentIssues,
2301
2340
  cleanRecentIssues,
2302
- removeIssueAttachment,
2303
2341
  removeAttachmentsOfIssue,
2304
2342
  removeIssue,
2305
2343
  removeIssueUpdate,
@@ -2307,13 +2345,20 @@ const {
2307
2345
  removeRecentIssue,
2308
2346
  resetRecentIssues,
2309
2347
  setActiveIssueId,
2310
- setIssueAttachments,
2311
2348
  setIssueUpdates,
2312
2349
  setIssues,
2313
2350
  setVisibleStatuses,
2314
2351
  setVisibleUserIds,
2315
- updateIssueAttachment,
2316
2352
  updateIssue,
2353
+ // Attachments
2354
+ setIssueAttachment,
2355
+ setIssueAttachments,
2356
+ addIssueAttachment,
2357
+ addIssueAttachments,
2358
+ updateIssueAttachment,
2359
+ updateIssueAttachments,
2360
+ removeIssueAttachment,
2361
+ removeIssueAttachments,
2317
2362
  // Commments
2318
2363
  addIssueComment,
2319
2364
  addIssueComments,
@@ -2536,15 +2581,15 @@ const selectRecentIssuesAsSearchResults = createSelector(
2536
2581
  }
2537
2582
  );
2538
2583
  const issueReducer = issueSlice.reducer;
2539
- const initialState$f = {
2584
+ const initialState$h = {
2540
2585
  s3Urls: {}
2541
2586
  };
2542
2587
  const msPerHour = 1e3 * 60 * 60;
2543
2588
  const msPerWeek = msPerHour * 24 * 7;
2544
2589
  const fileSlice = createSlice({
2545
2590
  name: "file",
2546
- initialState: initialState$f,
2547
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$f)),
2591
+ initialState: initialState$h,
2592
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$h)),
2548
2593
  reducers: {
2549
2594
  setUploadUrl: (state, action) => {
2550
2595
  const { url, fields, sha1 } = action.payload;
@@ -2571,7 +2616,7 @@ const selectUploadUrl = (sha1) => (state) => {
2571
2616
  return url;
2572
2617
  };
2573
2618
  const fileReducer = fileSlice.reducer;
2574
- const initialState$e = {
2619
+ const initialState$g = {
2575
2620
  // TODO: Change first MapStyle.SATELLITE to MaptStyle.None when project creation map is fixed
2576
2621
  mapStyle: MapStyle.SATELLITE,
2577
2622
  showTooltips: false,
@@ -2579,8 +2624,8 @@ const initialState$e = {
2579
2624
  };
2580
2625
  const mapSlice = createSlice({
2581
2626
  name: "map",
2582
- initialState: initialState$e,
2583
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$e)),
2627
+ initialState: initialState$g,
2628
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$g)),
2584
2629
  reducers: {
2585
2630
  setMapStyle: (state, action) => {
2586
2631
  state.mapStyle = action.payload;
@@ -2608,6 +2653,14 @@ var OrganizationAccessLevel = /* @__PURE__ */ ((OrganizationAccessLevel2) => {
2608
2653
  OrganizationAccessLevel2[OrganizationAccessLevel2["ADMIN"] = 2] = "ADMIN";
2609
2654
  return OrganizationAccessLevel2;
2610
2655
  })(OrganizationAccessLevel || {});
2656
+ var AttachmentModel = /* @__PURE__ */ ((AttachmentModel2) => {
2657
+ AttachmentModel2["Issue"] = "issue";
2658
+ AttachmentModel2["Component"] = "component";
2659
+ AttachmentModel2["ComponentType"] = "component_type";
2660
+ AttachmentModel2["Project"] = "project";
2661
+ AttachmentModel2["Document"] = "document";
2662
+ return AttachmentModel2;
2663
+ })(AttachmentModel || {});
2611
2664
  var IssueUpdateChange = /* @__PURE__ */ ((IssueUpdateChange2) => {
2612
2665
  IssueUpdateChange2["STATUS"] = "status";
2613
2666
  IssueUpdateChange2["PRIORITY"] = "priority";
@@ -2649,7 +2702,7 @@ var LicenseStatus = /* @__PURE__ */ ((LicenseStatus2) => {
2649
2702
  LicenseStatus2[LicenseStatus2["PAST_DUE"] = 8] = "PAST_DUE";
2650
2703
  return LicenseStatus2;
2651
2704
  })(LicenseStatus || {});
2652
- const initialState$d = {
2705
+ const initialState$f = {
2653
2706
  users: {},
2654
2707
  currentUser: {
2655
2708
  id: 0,
@@ -2660,8 +2713,8 @@ const initialState$d = {
2660
2713
  };
2661
2714
  const userSlice = createSlice({
2662
2715
  name: "users",
2663
- initialState: initialState$d,
2664
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$d)),
2716
+ initialState: initialState$f,
2717
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$f)),
2665
2718
  reducers: {
2666
2719
  setUsers: (state, action) => {
2667
2720
  const usersMapping = {};
@@ -2723,13 +2776,13 @@ const selectUser = (userId) => (state) => {
2723
2776
  const selectUsersAsMapping = (state) => state.userReducer.users;
2724
2777
  const selectFavouriteProjects = (state) => state.userReducer.currentUser.profile.favourite_project_ids;
2725
2778
  const userReducer = userSlice.reducer;
2726
- const initialState$c = {
2779
+ const initialState$e = {
2727
2780
  organizationAccesses: {}
2728
2781
  };
2729
2782
  const organizationAccessSlice = createSlice({
2730
2783
  name: "organizationAccess",
2731
- initialState: initialState$c,
2732
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$c)),
2784
+ initialState: initialState$e,
2785
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$e)),
2733
2786
  reducers: {
2734
2787
  setOrganizationAccesses: (state, action) => {
2735
2788
  if (!Array.isArray(action.payload))
@@ -2792,13 +2845,13 @@ const selectOrganizationAccessUserMapping = (state) => {
2792
2845
  return organizationAccesses;
2793
2846
  };
2794
2847
  const organizationAccessReducer = organizationAccessSlice.reducer;
2795
- const initialState$b = {
2848
+ const initialState$d = {
2796
2849
  licenses: {}
2797
2850
  };
2798
2851
  const licenseSlice = createSlice({
2799
2852
  name: "license",
2800
- initialState: initialState$b,
2801
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$b)),
2853
+ initialState: initialState$d,
2854
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$d)),
2802
2855
  reducers: {
2803
2856
  setLicenses: (state, action) => {
2804
2857
  if (!Array.isArray(action.payload))
@@ -2843,13 +2896,13 @@ const selectLicensesForProjectsMapping = createSelector(
2843
2896
  (licenses) => Object.values(licenses).filter((license) => license.project).reduce((accum, license) => ({ ...accum, [license.project]: license }), {})
2844
2897
  );
2845
2898
  const licenseReducer = licenseSlice.reducer;
2846
- const initialState$a = {
2899
+ const initialState$c = {
2847
2900
  projectAccesses: {}
2848
2901
  };
2849
2902
  const projectAccessSlice = createSlice({
2850
2903
  name: "projectAccess",
2851
- initialState: initialState$a,
2852
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$a)),
2904
+ initialState: initialState$c,
2905
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$c)),
2853
2906
  reducers: {
2854
2907
  setProjectAccesses: (state, action) => {
2855
2908
  if (!Array.isArray(action.payload))
@@ -2917,7 +2970,7 @@ const selectProjectAccessUserMapping = (state) => {
2917
2970
  return projectAccesses;
2918
2971
  };
2919
2972
  const projectAccessReducer = projectAccessSlice.reducer;
2920
- const initialState$9 = {
2973
+ const initialState$b = {
2921
2974
  projects: {},
2922
2975
  activeProjectId: null,
2923
2976
  recentProjectIds: [],
@@ -2927,7 +2980,7 @@ const initialState$9 = {
2927
2980
  };
2928
2981
  const projectSlice = createSlice({
2929
2982
  name: "projects",
2930
- initialState: initialState$9,
2983
+ initialState: initialState$b,
2931
2984
  reducers: {
2932
2985
  setProjects: (state, action) => {
2933
2986
  const projectsMap = {};
@@ -2995,11 +3048,13 @@ const projectSlice = createSlice({
2995
3048
  throw new Error("Update form submissions count: no active project");
2996
3049
  }
2997
3050
  },
2998
- // Attachment related
3051
+ // Attachments
3052
+ setProjectAttachment: setAttachment,
2999
3053
  setProjectAttachments: setAttachments,
3000
3054
  addProjectAttachment: addAttachment,
3001
3055
  addProjectAttachments: addAttachments,
3002
3056
  updateProjectAttachment: updateAttachment,
3057
+ updateProjectAttachments: updateAttachments,
3003
3058
  removeProjectAttachment: removeAttachment,
3004
3059
  removeProjectAttachments: removeAttachments
3005
3060
  }
@@ -3014,11 +3069,13 @@ const {
3014
3069
  acceptProjectInvite,
3015
3070
  addActiveProjectIssuesCount,
3016
3071
  addActiveProjectFormSubmissionsCount,
3017
- // Attachment related
3072
+ // Attachments
3073
+ setProjectAttachment,
3018
3074
  setProjectAttachments,
3019
3075
  addProjectAttachment,
3020
3076
  addProjectAttachments,
3021
3077
  updateProjectAttachment,
3078
+ updateProjectAttachments,
3022
3079
  removeProjectAttachment,
3023
3080
  removeProjectAttachments
3024
3081
  } = projectSlice.actions;
@@ -3089,6 +3146,9 @@ const selectAllProjectAttachments = createSelector(
3089
3146
  [selectProjectAttachmentMapping],
3090
3147
  (mapping) => Object.values(mapping)
3091
3148
  );
3149
+ const selectProjectAttachment = (attachmentId) => (state) => {
3150
+ return state.projectReducer.attachments[attachmentId];
3151
+ };
3092
3152
  const selectAttachmentsOfProject = restructureCreateSelectorWithArgs(
3093
3153
  createSelector(
3094
3154
  [selectAllProjectAttachments, (_state, projectId) => projectId],
@@ -3114,14 +3174,14 @@ const selectAttachmentsOfProjectByType = restructureCreateSelectorWithArgs(
3114
3174
  }
3115
3175
  )
3116
3176
  );
3117
- const initialState$8 = {
3177
+ const initialState$a = {
3118
3178
  organizations: {},
3119
3179
  activeOrganizationId: null
3120
3180
  };
3121
3181
  const organizationSlice = createSlice({
3122
3182
  name: "organizations",
3123
- initialState: initialState$8,
3124
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$8)),
3183
+ initialState: initialState$a,
3184
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$a)),
3125
3185
  reducers: {
3126
3186
  setOrganizations: (state, action) => {
3127
3187
  for (const org of action.payload) {
@@ -3240,14 +3300,14 @@ const createOfflineAction = (request2, baseUrl) => {
3240
3300
  }
3241
3301
  };
3242
3302
  };
3243
- const initialState$7 = {
3303
+ const initialState$9 = {
3244
3304
  deletedRequests: [],
3245
3305
  latestRetryTime: 0
3246
3306
  };
3247
3307
  const outboxSlice = createSlice({
3248
3308
  name: "outbox",
3249
- initialState: initialState$7,
3250
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$7)),
3309
+ initialState: initialState$9,
3310
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$9)),
3251
3311
  reducers: {
3252
3312
  // enqueueActions is a reducer that does nothing but enqueue API request to the Redux Offline outbox
3253
3313
  // Whenever an issue is being created, a reducer addIssue() is responsible for adding it to the offline store
@@ -3279,7 +3339,7 @@ const selectDeletedRequests = (state) => state.outboxReducer.deletedRequests;
3279
3339
  const selectLatestRetryTime = (state) => state.outboxReducer.latestRetryTime;
3280
3340
  const { enqueueRequest, markForDeletion, markAsDeleted, _setLatestRetryTime } = outboxSlice.actions;
3281
3341
  const outboxReducer = outboxSlice.reducer;
3282
- const initialState$6 = {
3342
+ const initialState$8 = {
3283
3343
  projectFiles: {},
3284
3344
  activeProjectFileId: null,
3285
3345
  isImportingProjectFile: false,
@@ -3287,8 +3347,8 @@ const initialState$6 = {
3287
3347
  };
3288
3348
  const projectFileSlice = createSlice({
3289
3349
  name: "projectFiles",
3290
- initialState: initialState$6,
3291
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$6)),
3350
+ initialState: initialState$8,
3351
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$8)),
3292
3352
  reducers: {
3293
3353
  addOrReplaceProjectFiles: (state, action) => {
3294
3354
  for (let fileObj of action.payload) {
@@ -3389,12 +3449,12 @@ const selectProjectFiles = createSelector(
3389
3449
  const selectActiveProjectFileId = (state) => state.projectFileReducer.activeProjectFileId;
3390
3450
  const selectIsImportingProjectFile = (state) => state.projectFileReducer.isImportingProjectFile;
3391
3451
  const projectFileReducer = projectFileSlice.reducer;
3392
- const initialState$5 = {
3452
+ const initialState$7 = {
3393
3453
  isRehydrated: false
3394
3454
  };
3395
3455
  const rehydratedSlice = createSlice({
3396
3456
  name: "rehydrated",
3397
- initialState: initialState$5,
3457
+ initialState: initialState$7,
3398
3458
  // The `reducers` field lets us define reducers and generate associated actions
3399
3459
  reducers: {
3400
3460
  setRehydrated: (state, action) => {
@@ -3404,7 +3464,7 @@ const rehydratedSlice = createSlice({
3404
3464
  });
3405
3465
  const selectRehydrated = (state) => state.rehydratedReducer.isRehydrated;
3406
3466
  const rehydratedReducer = rehydratedSlice.reducer;
3407
- const initialState$4 = {
3467
+ const initialState$6 = {
3408
3468
  useIssueTemplate: false,
3409
3469
  placementMode: false,
3410
3470
  enableClustering: false,
@@ -3421,8 +3481,8 @@ const initialState$4 = {
3421
3481
  };
3422
3482
  const settingSlice = createSlice({
3423
3483
  name: "settings",
3424
- initialState: initialState$4,
3425
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$4)),
3484
+ initialState: initialState$6,
3485
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$6)),
3426
3486
  reducers: {
3427
3487
  setEnableDuplicateIssues: (state, action) => {
3428
3488
  state.useIssueTemplate = action.payload;
@@ -3468,146 +3528,231 @@ const selectAppearance = (state) => state.settingReducer.appearance;
3468
3528
  const settingReducer = settingSlice.reducer;
3469
3529
  const selectIsFetchingInitialData = (state) => state.settingReducer.isFetchingInitialData;
3470
3530
  const selectIsLoading = (state) => state.settingReducer.isLoading;
3471
- const LATEST_REVISION_CACHE = {};
3472
- function considerCachingRevision(revision, formId2, preferPending = false) {
3473
- var _a2;
3474
- if (!revision) {
3475
- if (!formId2) {
3476
- throw new Error("If revision is null, formId is required.");
3477
- }
3478
- const currentLatestRevision = getLatestRevisionFromCache(formId2);
3479
- if (currentLatestRevision)
3480
- return;
3481
- LATEST_REVISION_CACHE[formId2] = null;
3482
- return;
3483
- }
3484
- if (revision.revision === "Pending") {
3485
- if (preferPending) {
3486
- LATEST_REVISION_CACHE[revision.form] = revision;
3487
- }
3488
- return;
3489
- }
3490
- const cachedRevision = (_a2 = LATEST_REVISION_CACHE[revision.form]) == null ? void 0 : _a2.revision;
3491
- if (revision.revision > (typeof cachedRevision === "number" ? cachedRevision : -1)) {
3492
- LATEST_REVISION_CACHE[revision.form] = revision;
3531
+ const formRevisionSortFn = (formRevisionA, formRevisionB) => {
3532
+ const revisionA = formRevisionA.revision;
3533
+ const revisionB = formRevisionB.revision;
3534
+ if (revisionA === "Pending" && revisionB === "Pending") {
3535
+ return formRevisionA.submitted_at < formRevisionB.submitted_at ? -1 : 1;
3536
+ } else if (revisionA === "Pending") {
3537
+ return 1;
3538
+ } else if (revisionB === "Pending") {
3539
+ return -1;
3540
+ } else {
3541
+ return revisionA < revisionB ? -1 : 1;
3493
3542
  }
3494
- }
3495
- function getLatestRevisionFromCache(formId2) {
3496
- return LATEST_REVISION_CACHE[formId2];
3497
- }
3498
- const initialState$3 = {
3499
- userForms: {},
3500
- revisions: {},
3501
- submissions: {},
3502
- submissionAttachments: {},
3503
- revisionAttachments: {}
3504
- };
3505
- const userFormSlice = createSlice({
3506
- name: "userForms",
3507
- initialState: initialState$3,
3508
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$3)),
3543
+ };
3544
+ const initialState$5 = {
3545
+ formRevisions: {},
3546
+ attachments: {}
3547
+ };
3548
+ const formRevisionsSlice = createSlice({
3549
+ name: "formRevisions",
3550
+ initialState: initialState$5,
3551
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$5)),
3509
3552
  reducers: {
3510
- setUserForms: (state, action) => {
3511
- state.userForms = {};
3512
- action.payload.forEach((userForm) => {
3513
- state.userForms[userForm.offline_id] = userForm;
3514
- });
3515
- },
3516
- addUserForm: (state, action) => {
3517
- state.userForms[action.payload.offline_id] = action.payload;
3518
- },
3519
- addUserForms: (state, action) => {
3520
- action.payload.forEach((userForm) => {
3521
- state.userForms[userForm.offline_id] = userForm;
3522
- });
3523
- },
3524
- addUserFormRevisions: (state, action) => {
3525
- action.payload.forEach((userFormRevision) => {
3526
- state.revisions[userFormRevision.offline_id] = userFormRevision;
3527
- considerCachingRevision(userFormRevision);
3528
- });
3553
+ // revision related actions
3554
+ setFormRevision: (state, action) => {
3555
+ state.formRevisions[action.payload.offline_id] = action.payload;
3529
3556
  },
3530
- addUserFormRevision: (state, action) => {
3531
- state.revisions[action.payload.offline_id] = action.payload;
3532
- considerCachingRevision(action.payload);
3557
+ setFormRevisions: (state, action) => {
3558
+ state.formRevisions = {};
3559
+ for (const revision of action.payload) {
3560
+ state.formRevisions[revision.offline_id] = revision;
3561
+ }
3533
3562
  },
3534
- deleteUserFormRevision: (state, action) => {
3535
- delete state.revisions[action.payload];
3536
- delete LATEST_REVISION_CACHE[action.payload];
3563
+ addFormRevision: (state, action) => {
3564
+ if (state.formRevisions[action.payload.offline_id] !== void 0) {
3565
+ throw new Error(`Revision with offline_id ${action.payload.offline_id} already exists`);
3566
+ }
3567
+ state.formRevisions[action.payload.offline_id] = action.payload;
3537
3568
  },
3538
- deleteUserFormRevisions: (state, action) => {
3569
+ addFormRevisions: (state, action) => {
3539
3570
  for (const userFormRevision of action.payload) {
3540
- delete state.revisions[userFormRevision.offline_id];
3541
- delete LATEST_REVISION_CACHE[userFormRevision.offline_id];
3571
+ if (state.formRevisions[userFormRevision.offline_id] !== void 0) {
3572
+ throw new Error(`Revision with offline_id ${userFormRevision.offline_id} already exists`);
3573
+ }
3542
3574
  }
3543
- },
3544
- updateOrCreateUserFormSubmission: (state, action) => {
3545
- state.submissions[action.payload.offline_id] = action.payload;
3546
- },
3547
- addUserFormSubmissionAttachment: (state, action) => {
3548
- const submissionId = action.payload.submission;
3549
- const submissionAttachments = state.submissionAttachments[submissionId];
3550
- if (submissionAttachments) {
3551
- submissionAttachments.push(action.payload);
3552
- } else {
3553
- state.submissionAttachments[submissionId] = [action.payload];
3575
+ for (const userFormRevision of action.payload) {
3576
+ state.formRevisions[userFormRevision.offline_id] = userFormRevision;
3554
3577
  }
3555
3578
  },
3556
- addUserFormRevisionAttachment: (state, action) => {
3557
- const revisionId = action.payload.revision;
3558
- const revisionAttachments = state.revisionAttachments[revisionId];
3559
- if (revisionAttachments) {
3560
- revisionAttachments.push(action.payload);
3561
- } else {
3562
- state.revisionAttachments[revisionId] = [action.payload];
3579
+ // UserFormRevisions do not get updated
3580
+ deleteFormRevision: (state, action) => {
3581
+ if (state.formRevisions[action.payload] === void 0) {
3582
+ throw new Error(`Revision with offline_id ${action.payload} does not exist`);
3563
3583
  }
3584
+ delete state.formRevisions[action.payload];
3564
3585
  },
3565
- setUserFormSubmissionAttachments: (state, action) => {
3566
- state.submissionAttachments = {};
3567
- for (const attachment of action.payload) {
3568
- const submissionId = attachment.submission;
3569
- const submissionAttachments = state.submissionAttachments[submissionId];
3570
- if (submissionAttachments) {
3571
- submissionAttachments.push(attachment);
3572
- } else {
3573
- state.submissionAttachments[submissionId] = [attachment];
3586
+ deleteFormRevisions: (state, action) => {
3587
+ for (const offlineId of action.payload) {
3588
+ if (state.formRevisions[offlineId] === void 0) {
3589
+ throw new Error(`Revision with offline_id ${offlineId} does not exist`);
3574
3590
  }
3575
3591
  }
3592
+ for (const offlineId of action.payload) {
3593
+ delete state.formRevisions[offlineId];
3594
+ }
3576
3595
  },
3577
- setUserFormRevisionAttachments: (state, action) => {
3578
- state.revisionAttachments = {};
3596
+ // attachment related actions
3597
+ setFormRevisionAttachments: (state, action) => {
3598
+ state.attachments = {};
3579
3599
  for (const attachment of action.payload) {
3580
- const revisionId = attachment.revision;
3581
- const revisionAttachments = state.revisionAttachments[revisionId];
3582
- if (revisionAttachments) {
3583
- revisionAttachments.push(attachment);
3584
- } else {
3585
- state.revisionAttachments[revisionId] = [attachment];
3586
- }
3600
+ state.attachments[attachment.offline_id] = attachment;
3587
3601
  }
3588
3602
  },
3589
- deleteUserFormSubmission: (state, action) => {
3590
- delete state.submissions[action.payload];
3603
+ addFormRevisionAttachment: (state, action) => {
3604
+ if (state.attachments[action.payload.offline_id] !== void 0) {
3605
+ throw new Error(`Attachment with offline_id ${action.payload.offline_id} already exists`);
3606
+ }
3607
+ state.attachments[action.payload.offline_id] = action.payload;
3591
3608
  },
3592
- deleteUserFormSubmissions: (state, action) => {
3593
- for (const userFormSubmission of action.payload) {
3594
- delete state.submissions[userFormSubmission.offline_id];
3609
+ addFormRevisionAttachments: (state, action) => {
3610
+ for (const attachment of action.payload) {
3611
+ if (state.attachments[attachment.offline_id] !== void 0) {
3612
+ throw new Error(`Attachment with offline_id ${attachment.offline_id} already exists`);
3613
+ }
3614
+ }
3615
+ for (const attachment of action.payload) {
3616
+ state.attachments[attachment.offline_id] = attachment;
3595
3617
  }
3596
3618
  },
3597
- addUserFormSubmissions: (state, action) => {
3598
- for (const submission of action.payload) {
3599
- state.submissions[submission.offline_id] = submission;
3619
+ deleteFormRevisionAttachment: (state, action) => {
3620
+ if (state.attachments[action.payload] === void 0) {
3621
+ throw new Error(`Attachment with offline_id ${action.payload} does not exist`);
3600
3622
  }
3623
+ delete state.attachments[action.payload];
3601
3624
  },
3602
- setUserFormSubmissions: (state, action) => {
3603
- state.submissions = {};
3604
- action.payload.forEach((submission) => {
3605
- state.submissions[submission.offline_id] = submission;
3625
+ deleteFormRevisionAttachments: (state, action) => {
3626
+ for (const offlineId of action.payload) {
3627
+ if (state.attachments[offlineId] === void 0) {
3628
+ throw new Error(`Attachment with offline_id ${offlineId} does not exist`);
3629
+ }
3630
+ }
3631
+ for (const offlineId of action.payload) {
3632
+ delete state.attachments[offlineId];
3633
+ }
3634
+ }
3635
+ }
3636
+ });
3637
+ const {
3638
+ setFormRevision,
3639
+ setFormRevisions,
3640
+ addFormRevision,
3641
+ addFormRevisions,
3642
+ deleteFormRevision,
3643
+ deleteFormRevisions,
3644
+ setFormRevisionAttachments,
3645
+ addFormRevisionAttachment,
3646
+ addFormRevisionAttachments,
3647
+ deleteFormRevisionAttachment,
3648
+ deleteFormRevisionAttachments
3649
+ } = formRevisionsSlice.actions;
3650
+ const selectFormRevisionMapping = (state) => state.formRevisionReducer.formRevisions;
3651
+ const selectFormRevisions = createSelector(
3652
+ [selectFormRevisionMapping],
3653
+ (formRevisions) => Object.values(formRevisions)
3654
+ );
3655
+ const selectFormRevision = (formRevisionId) => (state) => {
3656
+ return state.formRevisionReducer.formRevisions[formRevisionId];
3657
+ };
3658
+ const _selectLatestFormRevision = (formRevisions, formId2) => {
3659
+ let ret = null;
3660
+ for (const candidate of Object.values(formRevisions)) {
3661
+ if (candidate.form === formId2 && (!ret || ret.revision < candidate.revision)) {
3662
+ ret = candidate;
3663
+ }
3664
+ }
3665
+ if (!ret) {
3666
+ throw new Error("No form revision found for form " + formId2);
3667
+ }
3668
+ return ret;
3669
+ };
3670
+ const selectLatestFormRevisionOfForm = restructureCreateSelectorWithArgs(
3671
+ createSelector([selectFormRevisions, (_state, formId2) => formId2], (revisions, formId2) => {
3672
+ return revisions.filter((revision) => revision.form === formId2).sort(formRevisionSortFn).pop();
3673
+ })
3674
+ );
3675
+ const selectFormRevisionsOfForm = restructureCreateSelectorWithArgs(
3676
+ createSelector([selectFormRevisions, (_state, formId2) => formId2], (revisions, formId2) => {
3677
+ return revisions.filter((revision) => {
3678
+ return revision.form === formId2;
3679
+ });
3680
+ })
3681
+ );
3682
+ const selectLatestFormRevisionsOfComponentTypes = restructureCreateSelectorWithArgs(
3683
+ createSelector(
3684
+ [
3685
+ (state) => state.formReducer.forms,
3686
+ selectFormRevisionMapping,
3687
+ (_state, componentTypeIds) => componentTypeIds
3688
+ ],
3689
+ (userForms, revisions, componentTypeIds) => {
3690
+ const componentTypeIdsSet = new Set(componentTypeIds);
3691
+ const formsOfComponentTypes = {};
3692
+ const ret = {};
3693
+ for (const form of Object.values(userForms)) {
3694
+ if (form.component_type && componentTypeIdsSet.has(form.component_type)) {
3695
+ formsOfComponentTypes[form.offline_id] = form;
3696
+ }
3697
+ }
3698
+ for (const revision of Object.values(revisions)) {
3699
+ const form = formsOfComponentTypes[revision.form];
3700
+ if (!form || !form.component_type || ret[form.component_type] && formRevisionSortFn(ret[form.component_type], revision) > 0)
3701
+ continue;
3702
+ ret[form.component_type] = revision;
3703
+ }
3704
+ return ret;
3705
+ }
3706
+ )
3707
+ );
3708
+ const selectLatestFormRevisionByForm = createSelector([selectFormRevisionMapping], (revisions) => {
3709
+ const latestRevisions = {};
3710
+ for (const revision of Object.values(revisions)) {
3711
+ const formId2 = revision.form;
3712
+ const currentLatestRevision = latestRevisions[formId2];
3713
+ if (!currentLatestRevision || currentLatestRevision.revision < revision.revision) {
3714
+ latestRevisions[formId2] = revision;
3715
+ }
3716
+ }
3717
+ return latestRevisions;
3718
+ });
3719
+ const selectUserFormRevisionAttachmentsMapping = (state) => {
3720
+ return state.formRevisionReducer.attachments;
3721
+ };
3722
+ const selectAttachmentsOfFormRevision = restructureCreateSelectorWithArgs(
3723
+ createSelector(
3724
+ [selectUserFormRevisionAttachmentsMapping, (_state, revisionId) => revisionId],
3725
+ (attachments, revisionId) => {
3726
+ return Object.values(attachments).filter((attachment) => attachment.revision === revisionId);
3727
+ }
3728
+ )
3729
+ );
3730
+ const formRevisionReducer = formRevisionsSlice.reducer;
3731
+ const initialState$4 = {
3732
+ forms: {}
3733
+ };
3734
+ const formSlice = createSlice({
3735
+ name: "forms",
3736
+ initialState: initialState$4,
3737
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$4)),
3738
+ reducers: {
3739
+ setForms: (state, action) => {
3740
+ state.forms = {};
3741
+ action.payload.forEach((userForm) => {
3742
+ state.forms[userForm.offline_id] = userForm;
3606
3743
  });
3607
3744
  },
3745
+ addForm: (state, action) => {
3746
+ state.forms[action.payload.offline_id] = action.payload;
3747
+ },
3748
+ addForms: (state, action) => {
3749
+ for (const userForm of action.payload) {
3750
+ state.forms[userForm.offline_id] = userForm;
3751
+ }
3752
+ },
3608
3753
  favoriteForm: (state, action) => {
3609
3754
  const { formId: formId2 } = action.payload;
3610
- const form = state.userForms[formId2];
3755
+ const form = state.forms[formId2];
3611
3756
  if (!form) {
3612
3757
  throw new Error("No form exists with the id " + formId2);
3613
3758
  }
@@ -3615,48 +3760,23 @@ const userFormSlice = createSlice({
3615
3760
  },
3616
3761
  unfavoriteForm: (state, action) => {
3617
3762
  const { formId: formId2 } = action.payload;
3618
- const form = state.userForms[formId2];
3763
+ const form = state.forms[formId2];
3619
3764
  if (!form) {
3620
3765
  throw new Error("No form exists with the id " + formId2);
3621
3766
  }
3622
3767
  form.favorite = false;
3623
3768
  },
3624
- deleteUserForm: (state, action) => {
3625
- delete state.userForms[action.payload];
3769
+ deleteForm: (state, action) => {
3770
+ delete state.forms[action.payload];
3626
3771
  }
3627
3772
  }
3628
3773
  });
3629
- const {
3630
- addUserForm,
3631
- addUserForms,
3632
- addUserFormRevisions,
3633
- updateOrCreateUserFormSubmission,
3634
- addUserFormSubmissions,
3635
- deleteUserFormSubmission,
3636
- deleteUserFormSubmissions,
3637
- favoriteForm,
3638
- unfavoriteForm,
3639
- deleteUserForm,
3640
- deleteUserFormRevision,
3641
- deleteUserFormRevisions,
3642
- setUserFormSubmissions,
3643
- addUserFormRevision,
3644
- addUserFormSubmissionAttachment,
3645
- addUserFormRevisionAttachment,
3646
- setUserFormSubmissionAttachments,
3647
- setUserFormRevisionAttachments
3648
- } = userFormSlice.actions;
3649
- const selectSubmissionAttachments = (submissionId) => (state) => {
3650
- return state.userFormReducer.submissionAttachments[submissionId] || [];
3651
- };
3652
- const selectRevisionAttachments = (revisionId) => (state) => {
3653
- return state.userFormReducer.revisionAttachments[revisionId] || [];
3654
- };
3655
- const selectFilteredUserForms = restructureCreateSelectorWithArgs(
3774
+ const { setForms, addForm, addForms, favoriteForm, unfavoriteForm, deleteForm } = formSlice.actions;
3775
+ const selectFilteredForms = restructureCreateSelectorWithArgs(
3656
3776
  createSelector(
3657
3777
  [
3658
- (state) => state.userFormReducer.userForms,
3659
- (state) => state.userFormReducer.revisions,
3778
+ (state) => state.formReducer.forms,
3779
+ (state) => state.formRevisionReducer.formRevisions,
3660
3780
  (_state, search) => search
3661
3781
  ],
3662
3782
  (userForms, revisions, search) => {
@@ -3683,70 +3803,195 @@ const selectFilteredUserForms = restructureCreateSelectorWithArgs(
3683
3803
  break;
3684
3804
  }
3685
3805
  }
3686
- const maxRegularMatches = maxResults - favoriteMatches.length;
3687
- return [...favoriteMatches, ...regularMatches.slice(0, maxRegularMatches)];
3806
+ const maxRegularMatches = maxResults - favoriteMatches.length;
3807
+ return [...favoriteMatches, ...regularMatches.slice(0, maxRegularMatches)];
3808
+ },
3809
+ // as the argument is an object, we check the first level of properties for equality
3810
+ { memoizeOptions: { equalityCheck: shallowEqual$1 } }
3811
+ )
3812
+ );
3813
+ const selectForm = (formId2) => (state) => {
3814
+ return state.formReducer.forms[formId2];
3815
+ };
3816
+ const selectFormMapping = (state) => {
3817
+ return state.formReducer.forms;
3818
+ };
3819
+ const selectFormOfComponentType = restructureCreateSelectorWithArgs(
3820
+ createSelector(
3821
+ [selectFormMapping, (_state, componentTypeId) => componentTypeId],
3822
+ (userForms, componentTypeId) => {
3823
+ return Object.values(userForms).find((userForm) => userForm.component_type === componentTypeId);
3824
+ }
3825
+ )
3826
+ );
3827
+ const selectFormsCount = createSelector([selectFormMapping], (userForms) => {
3828
+ return Object.keys(userForms).length;
3829
+ });
3830
+ const selectGeneralFormCount = createSelector([selectFormMapping], (userForms) => {
3831
+ return Object.values(userForms).filter((form) => !form.component_type).length;
3832
+ });
3833
+ const formReducer = formSlice.reducer;
3834
+ const initialState$3 = {
3835
+ formSubmissions: {},
3836
+ attachments: {}
3837
+ };
3838
+ const formSubmissionSlice = createSlice({
3839
+ name: "formSubmissions",
3840
+ initialState: initialState$3,
3841
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$3)),
3842
+ reducers: {
3843
+ setFormSubmission: (state, action) => {
3844
+ state.formSubmissions[action.payload.offline_id] = action.payload;
3845
+ },
3846
+ setFormSubmissions: (state, action) => {
3847
+ state.formSubmissions = {};
3848
+ for (const submission of action.payload) {
3849
+ state.formSubmissions[submission.offline_id] = submission;
3850
+ }
3851
+ },
3852
+ addFormSubmission: (state, action) => {
3853
+ if (action.payload.offline_id in state.formSubmissions) {
3854
+ throw new Error(`Submission with offline_id ${action.payload.offline_id} already exists`);
3855
+ }
3856
+ state.formSubmissions[action.payload.offline_id] = action.payload;
3857
+ },
3858
+ addFormSubmissions: (state, action) => {
3859
+ for (const submission of action.payload) {
3860
+ if (state.formSubmissions[submission.offline_id] !== void 0) {
3861
+ throw new Error(`Submission with offline_id ${submission.offline_id} already exists`);
3862
+ }
3863
+ }
3864
+ for (const submission of action.payload) {
3865
+ state.formSubmissions[submission.offline_id] = submission;
3866
+ }
3867
+ },
3868
+ updateFormSubmission: (state, action) => {
3869
+ if (state.formSubmissions[action.payload.offline_id] === void 0) {
3870
+ throw new Error(`Submission with offline_id ${action.payload.offline_id} does not exist`);
3871
+ }
3872
+ state.formSubmissions[action.payload.offline_id] = action.payload;
3873
+ },
3874
+ updateFormSubmissions: (state, action) => {
3875
+ for (const submission of action.payload) {
3876
+ if (state.formSubmissions[submission.offline_id] === void 0) {
3877
+ throw new Error(`Submission with offline_id ${submission.offline_id} does not exist`);
3878
+ }
3879
+ }
3880
+ for (const submission of action.payload) {
3881
+ state.formSubmissions[submission.offline_id] = submission;
3882
+ }
3883
+ },
3884
+ deleteFormSubmission: (state, action) => {
3885
+ if (state.formSubmissions[action.payload] === void 0) {
3886
+ throw new Error(`Submission with offline_id ${action.payload} does not exist`);
3887
+ }
3888
+ delete state.formSubmissions[action.payload];
3889
+ },
3890
+ deleteFormSubmissions: (state, action) => {
3891
+ for (const offlineId of action.payload) {
3892
+ if (state.formSubmissions[offlineId] === void 0) {
3893
+ throw new Error(`Submission with offline_id ${offlineId} does not exist`);
3894
+ }
3895
+ delete state.formSubmissions[offlineId];
3896
+ }
3897
+ for (const offlineId of action.payload) {
3898
+ delete state.formSubmissions[offlineId];
3899
+ }
3900
+ },
3901
+ // Attachments
3902
+ addFormSubmissionAttachment: (state, action) => {
3903
+ if (state.attachments[action.payload.offline_id] !== void 0) {
3904
+ throw new Error(`Attachment with offline_id ${action.payload.offline_id} already exists`);
3905
+ }
3906
+ state.attachments[action.payload.offline_id] = action.payload;
3907
+ },
3908
+ addFormSubmissionAttachments: (state, action) => {
3909
+ for (const attachment of action.payload) {
3910
+ if (state.attachments[attachment.offline_id] !== void 0) {
3911
+ throw new Error(`Attachment with offline_id ${attachment.offline_id} already exists`);
3912
+ }
3913
+ }
3914
+ for (const attachment of action.payload) {
3915
+ state.attachments[attachment.offline_id] = attachment;
3916
+ }
3917
+ },
3918
+ // We only need a multi set for attachments because they are not updated, only added and deleted
3919
+ setFormSubmissionAttachments: (state, action) => {
3920
+ state.attachments = {};
3921
+ for (const attachment of action.payload) {
3922
+ state.attachments[attachment.offline_id] = attachment;
3923
+ }
3924
+ },
3925
+ updateFormSubmissionAttachments: (state, action) => {
3926
+ for (const attachment of action.payload) {
3927
+ if (state.attachments[attachment.offline_id] === void 0) {
3928
+ throw new Error(`Attachment with offline_id ${attachment.offline_id} does not exist`);
3929
+ }
3930
+ }
3931
+ for (const attachment of action.payload) {
3932
+ state.attachments[attachment.offline_id] = attachment;
3933
+ }
3688
3934
  },
3689
- // as the argument is an object, we check the first level of properties for equality
3690
- { memoizeOptions: { equalityCheck: shallowEqual$1 } }
3691
- )
3692
- );
3693
- const selectFormRevision = (revisionId) => (state) => {
3694
- return state.userFormReducer.revisions[revisionId];
3695
- };
3696
- const _selectLatestFormRevision = (revisions, formId2) => {
3697
- let ret = null;
3698
- for (const candidate of Object.values(revisions)) {
3699
- if (candidate.form === formId2 && (!ret || ret.revision < candidate.revision)) {
3700
- ret = candidate;
3935
+ // The delete actions for UserFormSubmissionAttachments are not used in the app, but are included for completeness
3936
+ // Could be used if editing a submission is ever supported, will be applicable for supporting tip tap content in submissions
3937
+ deleteFormSubmissionAttachment: (state, action) => {
3938
+ if (state.attachments[action.payload] === void 0) {
3939
+ throw new Error(`Attachment with offline_id ${action.payload} does not exist`);
3940
+ }
3941
+ delete state.attachments[action.payload];
3942
+ },
3943
+ deleteFormSubmissionAttachments: (state, action) => {
3944
+ for (const offlineId of action.payload) {
3945
+ if (state.attachments[offlineId] === void 0) {
3946
+ throw new Error(`Attachment with offline_id ${offlineId} does not exist`);
3947
+ }
3948
+ delete state.attachments[offlineId];
3949
+ }
3701
3950
  }
3702
3951
  }
3703
- if (!ret) {
3704
- throw new Error("No revision found for form " + formId2);
3705
- }
3706
- return ret;
3952
+ });
3953
+ const {
3954
+ setFormSubmission,
3955
+ setFormSubmissions,
3956
+ addFormSubmission,
3957
+ addFormSubmissions,
3958
+ updateFormSubmission,
3959
+ updateFormSubmissions,
3960
+ deleteFormSubmission,
3961
+ deleteFormSubmissions,
3962
+ addFormSubmissionAttachment,
3963
+ addFormSubmissionAttachments,
3964
+ setFormSubmissionAttachments,
3965
+ updateFormSubmissionAttachments,
3966
+ deleteFormSubmissionAttachment,
3967
+ deleteFormSubmissionAttachments
3968
+ } = formSubmissionSlice.actions;
3969
+ const selectFormSubmissionsMapping = (state) => {
3970
+ return state.formSubmissionReducer.formSubmissions;
3707
3971
  };
3708
- const selectLatestFormRevision = restructureCreateSelectorWithArgs(
3709
- createSelector(
3710
- [(state) => state.userFormReducer.revisions, (_state, formId2) => formId2],
3711
- (revisions, formId2) => {
3712
- if (!formId2) {
3713
- throw new Error("formId is required");
3714
- }
3715
- return _selectLatestFormRevision(revisions, formId2);
3716
- }
3717
- )
3718
- );
3719
- const selectUserForm = (formId2) => (state) => {
3720
- return state.userFormReducer.userForms[formId2];
3721
- };
3722
- const selectSubmissionMapping = (state) => state.userFormReducer.submissions;
3723
- const selectUserFormSubmission = (submissionId) => (state) => {
3724
- return state.userFormReducer.submissions[submissionId];
3725
- };
3726
- const selectSubmissions = createSelector([selectSubmissionMapping], (submissions) => Object.values(submissions));
3727
- const selectRevisionMapping = (state) => state.userFormReducer.revisions;
3728
- const selectRevisions = createSelector([selectRevisionMapping], (revisions) => Object.values(revisions));
3729
- const selectRevisionsForForm = restructureCreateSelectorWithArgs(
3730
- createSelector([selectRevisions, (_state, formId2) => formId2], (revisions, formId2) => {
3731
- return revisions.filter((revision) => {
3732
- return revision.form === formId2;
3733
- });
3734
- })
3972
+ const selectFormSubmissions = createSelector(
3973
+ [selectFormSubmissionsMapping],
3974
+ (submissions) => {
3975
+ return Object.values(submissions);
3976
+ }
3735
3977
  );
3736
- const selectSubmissionsForForm = restructureCreateSelectorWithArgs(
3978
+ const selectFormSubmission = (submissionId) => (state) => {
3979
+ return state.formSubmissionReducer.formSubmissions[submissionId];
3980
+ };
3981
+ const selectFormSubmissionsOfForm = restructureCreateSelectorWithArgs(
3737
3982
  createSelector(
3738
- [selectSubmissions, selectRevisionMapping, (_state, formId2) => formId2],
3983
+ [selectFormSubmissions, selectFormRevisionMapping, (_state, formId2) => formId2],
3739
3984
  (submissions, revisionMapping, formId2) => {
3740
- return Object.values(submissions).filter((submission) => {
3985
+ return submissions.filter((submission) => {
3741
3986
  const revision = revisionMapping[submission.form_revision];
3742
3987
  return (revision == null ? void 0 : revision.form) === formId2;
3743
3988
  });
3744
3989
  }
3745
3990
  )
3746
3991
  );
3747
- const selectSubmissionsForIssue = restructureCreateSelectorWithArgs(
3992
+ const selectFormSubmissionsOfIssue = restructureCreateSelectorWithArgs(
3748
3993
  createSelector(
3749
- [(state) => state.userFormReducer.submissions, (_state, issueId) => issueId],
3994
+ [selectFormSubmissions, (_state, issueId) => issueId],
3750
3995
  (submissions, issueId) => {
3751
3996
  return Object.values(submissions).filter((submission) => {
3752
3997
  return submission.issue === issueId;
@@ -3754,9 +3999,9 @@ const selectSubmissionsForIssue = restructureCreateSelectorWithArgs(
3754
3999
  }
3755
4000
  )
3756
4001
  );
3757
- const selectSubmissionsForComponent = restructureCreateSelectorWithArgs(
4002
+ const selectFormSubmissionsOfComponent = restructureCreateSelectorWithArgs(
3758
4003
  createSelector(
3759
- [selectSubmissions, (_state, componentId) => componentId],
4004
+ [selectFormSubmissions, (_state, componentId) => componentId],
3760
4005
  (submissions, componentId) => {
3761
4006
  return submissions.filter((submission) => {
3762
4007
  return submission.component === componentId;
@@ -3764,8 +4009,8 @@ const selectSubmissionsForComponent = restructureCreateSelectorWithArgs(
3764
4009
  }
3765
4010
  )
3766
4011
  );
3767
- const selectComponentSubmissionMapping = createSelector(
3768
- [selectSubmissionMapping, selectComponentsMapping],
4012
+ const selectFormSubmissionsByComponents = createSelector(
4013
+ [selectFormSubmissionsMapping, selectComponentsMapping],
3769
4014
  (submissions, components) => {
3770
4015
  var _a2;
3771
4016
  const componentSubmissionMapping = {};
@@ -3781,54 +4026,18 @@ const selectComponentSubmissionMapping = createSelector(
3781
4026
  return componentSubmissionMapping;
3782
4027
  }
3783
4028
  );
3784
- const selectUserFormMapping = (state) => {
3785
- return state.userFormReducer.userForms;
4029
+ const selectFormSubmissionAttachmentsMapping = (state) => {
4030
+ return state.formSubmissionReducer.attachments;
3786
4031
  };
3787
- const selectComponentTypeForm = restructureCreateSelectorWithArgs(
3788
- createSelector(
3789
- [selectUserFormMapping, (_state, componentTypeId) => componentTypeId],
3790
- (userForms, componentTypeId) => {
3791
- return Object.values(userForms).find((userForm) => userForm.component_type === componentTypeId);
3792
- }
3793
- )
3794
- );
3795
- const selectLatestRevisionsFromComponentTypeIds = restructureCreateSelectorWithArgs(
4032
+ const selectAttachmentsOfFormSubmission = restructureCreateSelectorWithArgs(
3796
4033
  createSelector(
3797
- [
3798
- selectUserFormMapping,
3799
- selectRevisionMapping,
3800
- (_state, componentTypeIds) => componentTypeIds
3801
- ],
3802
- (userForms, revisions, componentTypeIds) => {
3803
- const componentTypeIdsSet = new Set(componentTypeIds);
3804
- const ret = {};
3805
- for (const form of Object.values(userForms)) {
3806
- if (form.component_type && componentTypeIdsSet.has(form.component_type)) {
3807
- ret[form.component_type] = _selectLatestFormRevision(revisions, form.offline_id);
3808
- }
3809
- }
3810
- return ret;
4034
+ [selectFormSubmissionAttachmentsMapping, (_state, submissionId) => submissionId],
4035
+ (attachmentsMapping, submissionId) => {
4036
+ return Object.values(attachmentsMapping).filter((attachment) => attachment.submission === submissionId);
3811
4037
  }
3812
4038
  )
3813
4039
  );
3814
- const selectLatestRevisionByFormId = createSelector([selectRevisionMapping], (revisions) => {
3815
- const latestRevisions = {};
3816
- for (const revision of Object.values(revisions)) {
3817
- const formId2 = revision.form;
3818
- const currentLatestRevision = latestRevisions[formId2];
3819
- if (!currentLatestRevision || currentLatestRevision.revision < revision.revision) {
3820
- latestRevisions[formId2] = revision;
3821
- }
3822
- }
3823
- return latestRevisions;
3824
- });
3825
- const selectNumberOfUserForms = createSelector([selectUserFormMapping], (userForms) => {
3826
- return Object.keys(userForms).length;
3827
- });
3828
- const selectNumberOfGeneralUserForms = createSelector([selectUserFormMapping], (userForms) => {
3829
- return Object.values(userForms).filter((form) => !form.component_type).length;
3830
- });
3831
- const userFormReducer = userFormSlice.reducer;
4040
+ const formSubmissionReducer = formSubmissionSlice.reducer;
3832
4041
  const initialState$2 = {
3833
4042
  emailDomains: {}
3834
4043
  };
@@ -4001,10 +4210,13 @@ const documentSlice = createSlice({
4001
4210
  delete state.documents[documentId];
4002
4211
  }
4003
4212
  },
4213
+ // Attachments
4214
+ setDocumentAttachment: setAttachment,
4004
4215
  setDocumentAttachments: setAttachments,
4005
4216
  addDocumentAttachment: addAttachment,
4006
4217
  addDocumentAttachments: addAttachments,
4007
4218
  updateDocumentAttachment: updateAttachment,
4219
+ updateDocumentAttachments: updateAttachments,
4008
4220
  removeDocumentAttachment: removeAttachment,
4009
4221
  removeDocumentAttachments: removeAttachments
4010
4222
  }
@@ -4015,10 +4227,13 @@ const {
4015
4227
  updateDocuments,
4016
4228
  moveDocument,
4017
4229
  removeDocuments,
4230
+ // Attachments
4231
+ setDocumentAttachment,
4018
4232
  setDocumentAttachments,
4019
4233
  addDocumentAttachment,
4020
4234
  addDocumentAttachments,
4021
4235
  updateDocumentAttachment,
4236
+ updateDocumentAttachments,
4022
4237
  removeDocumentAttachment,
4023
4238
  removeDocumentAttachments
4024
4239
  } = documentSlice.actions;
@@ -4126,7 +4341,9 @@ const overmapReducers = {
4126
4341
  projectFileReducer,
4127
4342
  rehydratedReducer,
4128
4343
  settingReducer,
4129
- userFormReducer,
4344
+ formReducer,
4345
+ formRevisionReducer,
4346
+ formSubmissionReducer,
4130
4347
  userReducer,
4131
4348
  workspaceReducer,
4132
4349
  emailDomainsReducer,
@@ -4179,9 +4396,7 @@ function handleWorkspaceRemoval(draft, action) {
4179
4396
  throw new Error(`Failed to update index_workspace of issue ${issue.offline_id} to main workspace`);
4180
4397
  }
4181
4398
  }
4182
- const indexedForms = Object.values(draft.userFormReducer.userForms).filter(
4183
- (form) => form.index_workspace === workspaceId
4184
- );
4399
+ const indexedForms = Object.values(draft.formReducer.forms).filter((form) => form.index_workspace === workspaceId);
4185
4400
  for (const form of indexedForms) {
4186
4401
  form.index_workspace = mainWorkspace.offline_id;
4187
4402
  }
@@ -5888,6 +6103,221 @@ class ComponentStageService extends BaseApiService {
5888
6103
  store.dispatch(addStages(result));
5889
6104
  }
5890
6105
  }
6106
+ const AttachmentModelMeta = {
6107
+ [AttachmentModel.Issue]: {
6108
+ name: "issue",
6109
+ attachUrlPrefix: "/issues",
6110
+ deleteUrlPrefix: "/issues",
6111
+ fetchUrlPostfix: "/issue-attachments"
6112
+ },
6113
+ [AttachmentModel.Component]: {
6114
+ name: "component",
6115
+ attachUrlPrefix: "/components",
6116
+ deleteUrlPrefix: "/components",
6117
+ fetchUrlPostfix: "/component-attachments"
6118
+ },
6119
+ [AttachmentModel.ComponentType]: {
6120
+ name: "component type",
6121
+ attachUrlPrefix: "/components/types",
6122
+ deleteUrlPrefix: "/components/types",
6123
+ fetchUrlPostfix: "/component-type-attachments"
6124
+ },
6125
+ [AttachmentModel.Project]: {
6126
+ name: "component project",
6127
+ attachUrlPrefix: "/projects",
6128
+ deleteUrlPrefix: "/projects",
6129
+ fetchUrlPostfix: "/attachments"
6130
+ },
6131
+ [AttachmentModel.Document]: {
6132
+ name: "document",
6133
+ attachUrlPrefix: "/documents",
6134
+ deleteUrlPrefix: "/documents",
6135
+ fetchUrlPostfix: "/document-attachments"
6136
+ }
6137
+ };
6138
+ class BaseAttachmentService extends BaseApiService {
6139
+ getNumberOfAttachmentsWithSha1(sha1) {
6140
+ const {
6141
+ issueReducer: issueReducer2,
6142
+ componentReducer: componentReducer2,
6143
+ componentTypeReducer: componentTypeReducer2,
6144
+ documentsReducer: documentsReducer2,
6145
+ projectReducer: projectReducer2,
6146
+ formSubmissionReducer: formSubmissionReducer2,
6147
+ formRevisionReducer: formRevisionReducer2
6148
+ } = this.client.store.getState();
6149
+ const objectsWithSha1 = [].concat(
6150
+ Object.values(issueReducer2.attachments),
6151
+ Object.values(componentReducer2.attachments),
6152
+ Object.values(componentTypeReducer2.attachments),
6153
+ Object.values(documentsReducer2.attachments),
6154
+ Object.values(projectReducer2.attachments),
6155
+ Object.values(formRevisionReducer2.attachments),
6156
+ Object.values(formSubmissionReducer2.attachments)
6157
+ );
6158
+ return objectsWithSha1.filter((object) => object.file_sha1 === sha1).length;
6159
+ }
6160
+ processPresignedUrls(presignedUrls) {
6161
+ for (const [sha1, presignedUrl] of Object.entries(presignedUrls)) {
6162
+ void this.enqueueRequest({
6163
+ url: presignedUrl.url,
6164
+ description: "Upload file to S3",
6165
+ method: HttpMethod.POST,
6166
+ isExternalUrl: true,
6167
+ isAuthNeeded: false,
6168
+ attachmentHash: sha1,
6169
+ // TODO: can we use the sha1 as the blocker?
6170
+ blockers: [`s3-${presignedUrl.fields.key}`],
6171
+ blocks: [sha1],
6172
+ s3url: presignedUrl
6173
+ });
6174
+ }
6175
+ }
6176
+ // Note that currently the fetching of attachments for all models dependds on the active projectId. This may change in the future. And
6177
+ // so for some attachment model services, this method will have to be overridden.
6178
+ async getAttachments(actions) {
6179
+ const { store } = this.client;
6180
+ const activeProjectId = store.getState().projectReducer.activeProjectId;
6181
+ const meta = AttachmentModelMeta[this.attachmentModel];
6182
+ const result = await this.enqueueRequest({
6183
+ description: `Get ${meta.name} attachments`,
6184
+ method: HttpMethod.GET,
6185
+ url: `/projects/${activeProjectId}${meta.fetchUrlPostfix}/`,
6186
+ blocks: [],
6187
+ blockers: []
6188
+ });
6189
+ store.dispatch(actions.setAttachments(result));
6190
+ }
6191
+ async attachFiles(files, modelId, buildOfflineAttachment, actions) {
6192
+ const { store } = this.client;
6193
+ const currentUser = store.getState().userReducer.currentUser;
6194
+ const submittedAt = (/* @__PURE__ */ new Date()).toISOString();
6195
+ const offlineAttachments = [];
6196
+ const attachmentPayloads = [];
6197
+ const filePayloads = {};
6198
+ for (const file of files) {
6199
+ const sha1 = await hashFile(file);
6200
+ if (!(sha1 in filePayloads)) {
6201
+ filePayloads[sha1] = {
6202
+ sha1,
6203
+ file_type: file.type,
6204
+ extension: file.name.split(".").pop(),
6205
+ size: file.size
6206
+ };
6207
+ await this.client.files.addCache(file, sha1);
6208
+ }
6209
+ const offlineAttachment = buildOfflineAttachment({
6210
+ file,
6211
+ sha1,
6212
+ submittedAt,
6213
+ createdBy: currentUser.id,
6214
+ description: "",
6215
+ modelId
6216
+ });
6217
+ offlineAttachments.push(offlineAttachment);
6218
+ attachmentPayloads.push({
6219
+ offline_id: offlineAttachment.offline_id,
6220
+ name: offlineAttachment.file_name,
6221
+ sha1: offlineAttachment.file_sha1,
6222
+ description: offlineAttachment.description
6223
+ });
6224
+ }
6225
+ store.dispatch(actions.addAttachments(offlineAttachments));
6226
+ const meta = AttachmentModelMeta[this.attachmentModel];
6227
+ const promise = this.enqueueRequest({
6228
+ description: `Attach files to ${meta.name}`,
6229
+ method: HttpMethod.POST,
6230
+ url: `${meta.attachUrlPrefix}/${modelId}/attach/`,
6231
+ payload: {
6232
+ submitted_at: submittedAt,
6233
+ attachments: attachmentPayloads,
6234
+ files: Object.values(filePayloads)
6235
+ },
6236
+ blocks: offlineAttachments.map((attachment) => attachment.offline_id),
6237
+ blockers: offlineAttachments.map((attachment) => attachment.file_sha1)
6238
+ });
6239
+ promise.then(({ attachments, presigned_urls }) => {
6240
+ store.dispatch(actions.updateAttachments(attachments));
6241
+ this.processPresignedUrls(presigned_urls);
6242
+ }).catch(() => {
6243
+ store.dispatch(actions.removeAttachments(offlineAttachments.map((attachment) => attachment.offline_id)));
6244
+ });
6245
+ return [offlineAttachments, promise.then(({ attachments }) => attachments)];
6246
+ }
6247
+ async deleteAttachment(attachmendId, actions, selectors) {
6248
+ const { store } = this.client;
6249
+ const attachment = selectors.selectAttachment(attachmendId)(store.getState());
6250
+ if (!attachment) {
6251
+ throw new Error(
6252
+ `Attempting to delete attachment with offline_id ${attachmendId} that does not exist in the store`
6253
+ );
6254
+ }
6255
+ store.dispatch(actions.removeAttachment(attachment.offline_id));
6256
+ const meta = AttachmentModelMeta[this.attachmentModel];
6257
+ const promise = this.enqueueRequest({
6258
+ description: "Delete attachment",
6259
+ method: HttpMethod.DELETE,
6260
+ url: `${meta.deleteUrlPrefix}/attachments/${attachmendId}/`,
6261
+ blockers: [attachmendId],
6262
+ blocks: []
6263
+ });
6264
+ promise.then(() => {
6265
+ if (this.getNumberOfAttachmentsWithSha1(attachment.file_sha1) === 0) {
6266
+ void this.client.files.removeCache(attachment.file_sha1);
6267
+ }
6268
+ }).catch(() => {
6269
+ store.dispatch(actions.setAttachment(attachment));
6270
+ });
6271
+ return promise;
6272
+ }
6273
+ }
6274
+ class ComponentAttachmentService extends BaseAttachmentService {
6275
+ constructor() {
6276
+ super(...arguments);
6277
+ __publicField(this, "attachmentModel", AttachmentModel.Component);
6278
+ }
6279
+ buildOfflineAttachment(data) {
6280
+ return offline({
6281
+ file: URL.createObjectURL(data.file),
6282
+ file_sha1: data.sha1,
6283
+ created_by: data.createdBy,
6284
+ file_name: data.file.name,
6285
+ file_type: data.file.type,
6286
+ submitted_at: data.submittedAt,
6287
+ description: data.description,
6288
+ component: data.modelId
6289
+ });
6290
+ }
6291
+ async attachFilesToComponent(files, componentId) {
6292
+ return this.attachFiles(
6293
+ files,
6294
+ componentId,
6295
+ this.buildOfflineAttachment.bind(this),
6296
+ {
6297
+ addAttachments: addComponentAttachments,
6298
+ updateAttachments: updateComponentAttachments,
6299
+ removeAttachments: removeComponentAttachments
6300
+ }
6301
+ );
6302
+ }
6303
+ deleteComponentAttachment(attachmentId) {
6304
+ return this.deleteAttachment(
6305
+ attachmentId,
6306
+ {
6307
+ setAttachment: setComponentAttachment,
6308
+ removeAttachment: removeComponentAttachment
6309
+ },
6310
+ {
6311
+ selectAttachment: selectComponentAttachment
6312
+ }
6313
+ );
6314
+ }
6315
+ async refreshStore() {
6316
+ return this.getAttachments({
6317
+ setAttachments: setComponentAttachments
6318
+ });
6319
+ }
6320
+ }
5891
6321
  class ComponentTypeService extends BaseApiService {
5892
6322
  add(componentType) {
5893
6323
  const offlineComponentType = offline(componentType);
@@ -5960,6 +6390,53 @@ class ComponentTypeService extends BaseApiService {
5960
6390
  store.dispatch(setComponentTypes(result));
5961
6391
  }
5962
6392
  }
6393
+ class ComponentTypeAttachmentService extends BaseAttachmentService {
6394
+ constructor() {
6395
+ super(...arguments);
6396
+ __publicField(this, "attachmentModel", AttachmentModel.ComponentType);
6397
+ }
6398
+ buildOfflineAttachment(data) {
6399
+ return offline({
6400
+ file: URL.createObjectURL(data.file),
6401
+ file_sha1: data.sha1,
6402
+ created_by: data.createdBy,
6403
+ file_name: data.file.name,
6404
+ file_type: data.file.type,
6405
+ submitted_at: data.submittedAt,
6406
+ description: data.description,
6407
+ component_type: data.modelId
6408
+ });
6409
+ }
6410
+ async attachFilesToComponentType(files, componentTypeId) {
6411
+ return this.attachFiles(
6412
+ files,
6413
+ componentTypeId,
6414
+ this.buildOfflineAttachment.bind(this),
6415
+ {
6416
+ addAttachments: addComponentTypeAttachments,
6417
+ updateAttachments: updateComponentTypeAttachments,
6418
+ removeAttachments: removeComponentTypeAttachments
6419
+ }
6420
+ );
6421
+ }
6422
+ deleteComponentTypeAttachment(attachmentId) {
6423
+ return this.deleteAttachment(
6424
+ attachmentId,
6425
+ {
6426
+ setAttachment: setComponentTypeAttachment,
6427
+ removeAttachment: removeComponentTypeAttachment
6428
+ },
6429
+ {
6430
+ selectAttachment: selectComponentTypeAttachment
6431
+ }
6432
+ );
6433
+ }
6434
+ async refreshStore() {
6435
+ return this.getAttachments({
6436
+ setAttachments: setComponentTypeAttachments
6437
+ });
6438
+ }
6439
+ }
5963
6440
  class IssueCommentService extends BaseApiService {
5964
6441
  // Omit author and submitted_at since these will always be set internally
5965
6442
  add(comment) {
@@ -6056,6 +6533,48 @@ class IssueUpdateService extends BaseApiService {
6056
6533
  store.dispatch(setIssueUpdates(filteredResult));
6057
6534
  }
6058
6535
  }
6536
+ class IssueAttachmentService extends BaseAttachmentService {
6537
+ constructor() {
6538
+ super(...arguments);
6539
+ __publicField(this, "attachmentModel", AttachmentModel.Issue);
6540
+ }
6541
+ buildOfflineAttachment(data) {
6542
+ return offline({
6543
+ file: URL.createObjectURL(data.file),
6544
+ file_sha1: data.sha1,
6545
+ created_by: data.createdBy,
6546
+ file_name: data.file.name,
6547
+ file_type: data.file.type,
6548
+ submitted_at: data.submittedAt,
6549
+ description: data.description,
6550
+ issue: data.modelId
6551
+ });
6552
+ }
6553
+ async attachFilesToIssue(files, issueId) {
6554
+ return this.attachFiles(files, issueId, this.buildOfflineAttachment.bind(this), {
6555
+ addAttachments: addIssueAttachments,
6556
+ updateAttachments: updateIssueAttachments,
6557
+ removeAttachments: removeIssueAttachments
6558
+ });
6559
+ }
6560
+ deleteIssueAttachment(attachmentId) {
6561
+ return this.deleteAttachment(
6562
+ attachmentId,
6563
+ {
6564
+ setAttachment: setIssueAttachment,
6565
+ removeAttachment: removeIssueAttachment
6566
+ },
6567
+ {
6568
+ selectAttachment: selectIssueAttachment
6569
+ }
6570
+ );
6571
+ }
6572
+ async refreshStore() {
6573
+ return this.getAttachments({
6574
+ setAttachments: setIssueAttachments
6575
+ });
6576
+ }
6577
+ }
6059
6578
  class IssueService extends BaseApiService {
6060
6579
  // Basic CRUD functions
6061
6580
  // TODO: Once all models are represented in `Created<TModel>`, use `Created` in `OptimisticModelResult`, so we don't
@@ -6401,6 +6920,7 @@ class MainService extends BaseApiService {
6401
6920
  const usersResult = await usersResultPromise;
6402
6921
  await projectAccessRefreshPromise;
6403
6922
  store.dispatch(addUsers(usersResult));
6923
+ void this.client.projectAttachments.refreshStore();
6404
6924
  }
6405
6925
  let currentWorkspaceId;
6406
6926
  const oldWorkspaceId = this.client.store.getState().workspaceReducer.activeWorkspaceId;
@@ -6413,38 +6933,30 @@ class MainService extends BaseApiService {
6413
6933
  store.dispatch(setActiveWorkspaceId(currentWorkspaceId));
6414
6934
  void this.client.categories.refreshStore().then(() => {
6415
6935
  void this.client.issues.refreshStore().then(() => {
6936
+ void this.client.issueAttachments.refreshStore().then();
6416
6937
  void this.client.issueComments.refreshStore().then();
6938
+ void this.client.issueUpdates.refreshStore().then();
6417
6939
  });
6418
6940
  });
6419
6941
  void this.client.projectFiles.refreshStore().then();
6420
6942
  void this.client.componentTypes.refreshStore().then(() => {
6421
- void this.client.componentStages.refreshStore().then(() => {
6422
- void this.client.components.refreshStore(overwrite).then();
6943
+ void this.client.componentTypeAttachments.refreshStore().then(() => {
6944
+ void this.client.componentStages.refreshStore().then(() => {
6945
+ void this.client.components.refreshStore(overwrite).then(() => {
6946
+ void this.client.componentAttachments.refreshStore().then();
6947
+ });
6948
+ });
6949
+ void this.client.componentStageCompletions.refreshStore().then();
6423
6950
  });
6424
- void this.client.componentStageCompletions.refreshStore().then();
6425
6951
  });
6426
6952
  void this.client.userForms.refreshStore().then(() => {
6427
6953
  void this.client.userFormSubmissions.refreshStore().then();
6428
6954
  });
6429
6955
  }
6430
6956
  if (currentProjectId) {
6431
- const [_offlineAttachments, promise] = this.client.attachments.fetchAll(currentProjectId);
6432
- void promise.then((result) => {
6433
- const {
6434
- issue_attachments,
6435
- component_type_attachments,
6436
- component_attachments,
6437
- project_attachments,
6438
- document_attachments
6439
- } = result;
6440
- store.dispatch(setIssueAttachments(issue_attachments));
6441
- store.dispatch(setComponentAttachments(component_attachments));
6442
- store.dispatch(setComponentTypeAttachments(component_type_attachments));
6443
- store.dispatch(setProjectAttachments(project_attachments));
6444
- store.dispatch(setDocumentAttachments(document_attachments));
6957
+ void this.client.documents.refreshStore().then(() => {
6958
+ void this.client.documentAttachments.refreshStore().then();
6445
6959
  });
6446
- void this.client.documents.refreshStore();
6447
- void this.client.issueUpdates.refreshStore();
6448
6960
  }
6449
6961
  store.dispatch(setIsFetchingInitialData(false));
6450
6962
  if (overwrite) {
@@ -6608,6 +7120,48 @@ class ProjectFileService extends BaseApiService {
6608
7120
  });
6609
7121
  }
6610
7122
  }
7123
+ class ProjectAttachmentService extends BaseAttachmentService {
7124
+ constructor() {
7125
+ super(...arguments);
7126
+ __publicField(this, "attachmentModel", AttachmentModel.Project);
7127
+ }
7128
+ buildOfflineAttachment(data) {
7129
+ return offline({
7130
+ file: URL.createObjectURL(data.file),
7131
+ file_sha1: data.sha1,
7132
+ created_by: data.createdBy,
7133
+ file_name: data.file.name,
7134
+ file_type: data.file.type,
7135
+ submitted_at: data.submittedAt,
7136
+ description: data.description,
7137
+ project: data.modelId
7138
+ });
7139
+ }
7140
+ async attachFilesToProject(files, projectId) {
7141
+ return this.attachFiles(files, projectId, this.buildOfflineAttachment.bind(this), {
7142
+ addAttachments: addProjectAttachments,
7143
+ updateAttachments: updateProjectAttachments,
7144
+ removeAttachments: removeProjectAttachments
7145
+ });
7146
+ }
7147
+ deleteProjectAttachment(attachmentId) {
7148
+ return this.deleteAttachment(
7149
+ attachmentId,
7150
+ {
7151
+ setAttachment: setProjectAttachment,
7152
+ removeAttachment: removeProjectAttachment
7153
+ },
7154
+ {
7155
+ selectAttachment: selectProjectAttachment
7156
+ }
7157
+ );
7158
+ }
7159
+ async refreshStore() {
7160
+ return this.getAttachments({
7161
+ setAttachments: setProjectAttachments
7162
+ });
7163
+ }
7164
+ }
6611
7165
  class ProjectService extends BaseApiService {
6612
7166
  /**
6613
7167
  * Creates a new project. Due to the nature of project creation,
@@ -6809,7 +7363,7 @@ class UserFormService extends BaseApiService {
6809
7363
  ...revisionAttachmentPayload,
6810
7364
  file: URL.createObjectURL(image)
6811
7365
  };
6812
- store.dispatch(addUserFormRevisionAttachment(offlinePayload));
7366
+ store.dispatch(addFormRevisionAttachment(offlinePayload));
6813
7367
  return attach;
6814
7368
  });
6815
7369
  });
@@ -6845,8 +7399,8 @@ class UserFormService extends BaseApiService {
6845
7399
  submitted_at: submittedAt
6846
7400
  };
6847
7401
  const { store } = this.client;
6848
- store.dispatch(addUserForm(retForm));
6849
- store.dispatch(addUserFormRevision(retRevision));
7402
+ store.dispatch(addForm(retForm));
7403
+ store.dispatch(addFormRevision(retRevision));
6850
7404
  const formPromise = this.enqueueRequest({
6851
7405
  description: "Create form",
6852
7406
  method: HttpMethod.POST,
@@ -6864,8 +7418,8 @@ class UserFormService extends BaseApiService {
6864
7418
  });
6865
7419
  const attachImagesPromises = this.getAttachImagePromises(images, offlineRevisionPayload.offline_id);
6866
7420
  void formPromise.catch((e) => {
6867
- store.dispatch(deleteUserForm(retForm.offline_id));
6868
- store.dispatch(deleteUserFormRevision(retRevision.offline_id));
7421
+ store.dispatch(deleteForm(retForm.offline_id));
7422
+ store.dispatch(deleteFormRevision(retRevision.offline_id));
6869
7423
  throw e;
6870
7424
  });
6871
7425
  const settledPromise = Promise.all([formPromise, ...attachImagesPromises]).then(() => formPromise);
@@ -6908,7 +7462,7 @@ class UserFormService extends BaseApiService {
6908
7462
  form: formId2,
6909
7463
  submitted_at: (/* @__PURE__ */ new Date()).toISOString()
6910
7464
  };
6911
- store.dispatch(addUserFormRevision(fullRevision));
7465
+ store.dispatch(addFormRevision(fullRevision));
6912
7466
  const promise = this.enqueueRequest({
6913
7467
  description: "Create form revision",
6914
7468
  method: HttpMethod.PATCH,
@@ -6922,9 +7476,9 @@ class UserFormService extends BaseApiService {
6922
7476
  });
6923
7477
  const attachImagesPromises = this.getAttachImagePromises(images, offlineRevision.offline_id);
6924
7478
  void promise.then((result) => {
6925
- store.dispatch(addUserFormRevision(result));
7479
+ store.dispatch(setFormRevision(result));
6926
7480
  }).catch(() => {
6927
- store.dispatch(deleteUserFormRevision(fullRevision.offline_id));
7481
+ store.dispatch(deleteFormRevision(fullRevision.offline_id));
6928
7482
  });
6929
7483
  const settledPromise = Promise.all([promise, ...attachImagesPromises]).then(() => promise);
6930
7484
  return [fullRevision, settledPromise];
@@ -6966,19 +7520,19 @@ class UserFormService extends BaseApiService {
6966
7520
  async delete(formId2) {
6967
7521
  const { store } = this.client;
6968
7522
  const state = store.getState();
6969
- const userForm = selectUserForm(formId2)(state);
7523
+ const userForm = selectForm(formId2)(state);
6970
7524
  if (!userForm) {
6971
7525
  throw new Error("Expected userForm to exist");
6972
7526
  }
6973
- const userFormSubmissions = selectSubmissionsForForm(formId2)(state);
7527
+ const userFormSubmissions = selectFormSubmissionsOfForm(formId2)(state);
6974
7528
  if (userFormSubmissions && userFormSubmissions.length > 0) {
6975
- store.dispatch(deleteUserFormSubmissions(userFormSubmissions));
7529
+ store.dispatch(deleteFormSubmissions(userFormSubmissions.map(({ offline_id }) => offline_id)));
6976
7530
  }
6977
- const userFormRevisions = selectRevisionsForForm(formId2)(state);
7531
+ const userFormRevisions = selectFormRevisionsOfForm(formId2)(state);
6978
7532
  if (userFormRevisions && userFormRevisions.length > 0) {
6979
- store.dispatch(deleteUserFormRevisions(userFormRevisions));
7533
+ store.dispatch(deleteFormRevisions(userFormRevisions.map(({ offline_id }) => offline_id)));
6980
7534
  }
6981
- store.dispatch(deleteUserForm(formId2));
7535
+ store.dispatch(deleteForm(formId2));
6982
7536
  try {
6983
7537
  return await this.enqueueRequest({
6984
7538
  description: "Delete form",
@@ -6988,12 +7542,12 @@ class UserFormService extends BaseApiService {
6988
7542
  blocks: []
6989
7543
  });
6990
7544
  } catch (e) {
6991
- store.dispatch(addUserForm(userForm));
7545
+ store.dispatch(addForm(userForm));
6992
7546
  if (userFormRevisions && userFormRevisions.length > 0) {
6993
- store.dispatch(addUserFormRevisions(userFormRevisions));
7547
+ store.dispatch(addFormRevisions(userFormRevisions));
6994
7548
  }
6995
7549
  if (userFormSubmissions && userFormSubmissions.length > 0) {
6996
- store.dispatch(addUserFormSubmissions(userFormSubmissions));
7550
+ store.dispatch(addFormSubmissions(userFormSubmissions));
6997
7551
  }
6998
7552
  throw e;
6999
7553
  }
@@ -7007,16 +7561,15 @@ class UserFormService extends BaseApiService {
7007
7561
  blockers: [],
7008
7562
  blocks: []
7009
7563
  });
7010
- store.dispatch(addUserForms(Object.values(result.forms)));
7011
- store.dispatch(addUserFormRevisions(Object.values(result.revisions)));
7012
- store.dispatch(setUserFormRevisionAttachments(Object.values(result.attachments)));
7564
+ store.dispatch(setForms(Object.values(result.forms)));
7565
+ store.dispatch(setFormRevisions(Object.values(result.revisions)));
7566
+ store.dispatch(setFormRevisionAttachments(Object.values(result.attachments)));
7013
7567
  }
7014
7568
  }
7015
7569
  const isArrayOfFiles = (value) => {
7016
7570
  return Array.isArray(value) && value[0] instanceof File;
7017
7571
  };
7018
- const separateFilesFromValues = (payload) => {
7019
- const { values } = payload;
7572
+ const separateFilesFromValues = (values) => {
7020
7573
  const files = {};
7021
7574
  const newValues = {};
7022
7575
  for (const key in values) {
@@ -7031,17 +7584,13 @@ const separateFilesFromValues = (payload) => {
7031
7584
  newValues[key] = value;
7032
7585
  }
7033
7586
  }
7034
- const payloadWithoutFiles = {
7035
- ...payload,
7036
- values: newValues
7037
- };
7038
- return { payloadWithoutFiles, files };
7587
+ return { values: newValues, files };
7039
7588
  };
7040
7589
  class UserFormSubmissionService extends BaseApiService {
7041
7590
  constructor() {
7042
7591
  super(...arguments);
7043
7592
  // Attach files to submission, after uploading them to S3
7044
- __publicField(this, "getAttachFilesPromises", (files, payload) => {
7593
+ __publicField(this, "getAttachFilesPromises", (files, submission) => {
7045
7594
  const { store } = this.client;
7046
7595
  return Object.entries(files).map(async ([key, fileArray]) => {
7047
7596
  const attachResults = [];
@@ -7051,24 +7600,27 @@ class UserFormSubmissionService extends BaseApiService {
7051
7600
  const [fileProps] = await this.client.files.uploadFileToS3(sha1);
7052
7601
  const submissionAttachmentPayload = offline({
7053
7602
  ...fileProps,
7054
- submission: payload.offline_id,
7603
+ submission: submission.offline_id,
7055
7604
  field_identifier: key
7056
7605
  });
7057
7606
  const attach = await this.enqueueRequest({
7058
7607
  description: "Attach file to form submission",
7059
7608
  method: HttpMethod.POST,
7060
- url: `/forms/submission/${payload.offline_id}/attachments/`,
7609
+ url: `/forms/submission/${submission.offline_id}/attachments/`,
7061
7610
  payload: submissionAttachmentPayload,
7062
- blockers: [payload.component, payload.component_stage, payload.issue, payload.form_revision].filter(
7063
- (x) => x !== void 0
7064
- ),
7611
+ blockers: [
7612
+ submission.component,
7613
+ submission.component_stage,
7614
+ submission.issue,
7615
+ submission.form_revision
7616
+ ].filter((x) => x !== void 0),
7065
7617
  blocks: [submissionAttachmentPayload.offline_id]
7066
7618
  });
7067
7619
  const offlinePayload = {
7068
7620
  ...submissionAttachmentPayload,
7069
7621
  file: URL.createObjectURL(file)
7070
7622
  };
7071
- store.dispatch(addUserFormSubmissionAttachment(offlinePayload));
7623
+ store.dispatch(addFormSubmissionAttachment(offlinePayload));
7072
7624
  attachResults.push(attach);
7073
7625
  }
7074
7626
  return attachResults;
@@ -7082,71 +7634,168 @@ class UserFormSubmissionService extends BaseApiService {
7082
7634
  if (!activeProjectId) {
7083
7635
  throw new Error("Expected an active project");
7084
7636
  }
7085
- const { payloadWithoutFiles, files } = separateFilesFromValues(payload);
7637
+ const { values, files } = separateFilesFromValues(payload.values);
7638
+ const offlineSubmission = {
7639
+ ...payload,
7640
+ values,
7641
+ created_by: state.userReducer.currentUser.id,
7642
+ submitted_at: (/* @__PURE__ */ new Date()).toISOString()
7643
+ };
7086
7644
  const promise = this.enqueueRequest({
7087
7645
  description: "Respond to form",
7088
7646
  method: HttpMethod.POST,
7089
7647
  url: `/forms/revisions/${payload.form_revision}/respond/`,
7090
- payload: { ...payloadWithoutFiles, project: activeProjectId },
7648
+ payload: { ...offlineSubmission, project: activeProjectId },
7091
7649
  blockers: [payload.issue, payload.component, payload.component_stage, "add-form-entry"].filter(
7092
7650
  (x) => x !== void 0
7093
7651
  ),
7094
7652
  blocks: [payload.offline_id]
7095
7653
  });
7096
- const attachFilesPromises = this.getAttachFilesPromises(files, payload);
7097
- const now = (/* @__PURE__ */ new Date()).toISOString();
7098
- const fullOfflineResult = {
7099
- ...payload,
7100
- created_by: state.userReducer.currentUser.id,
7101
- created_at: now,
7102
- updated_at: now
7103
- };
7104
- const offlineResultWithoutFiles = {
7105
- ...fullOfflineResult,
7106
- ...payloadWithoutFiles
7107
- };
7108
- store.dispatch(updateOrCreateUserFormSubmission(offlineResultWithoutFiles));
7654
+ const attachFilesPromises = this.getAttachFilesPromises(files, offlineSubmission);
7655
+ store.dispatch(addFormSubmission(offlineSubmission));
7109
7656
  void promise.then((result) => {
7110
7657
  store.dispatch(addActiveProjectFormSubmissionsCount(1));
7111
- store.dispatch(updateOrCreateUserFormSubmission(result));
7658
+ store.dispatch(setFormSubmission(result));
7112
7659
  return result;
7113
7660
  }).catch(() => {
7114
- store.dispatch(deleteUserFormSubmission(payload.offline_id));
7661
+ store.dispatch(deleteFormSubmission(payload.offline_id));
7115
7662
  store.dispatch(addActiveProjectFormSubmissionsCount(-1));
7116
7663
  });
7117
7664
  const settledPromise = Promise.all([promise, ...attachFilesPromises]).then(() => promise);
7118
- return [fullOfflineResult, settledPromise];
7665
+ return [offlineSubmission, settledPromise];
7119
7666
  }
7120
- update(submission) {
7667
+ // Note currently the bulkAdd method is specific to form submissions for components
7668
+ // TODO: adapt the support bulk adding to any model type
7669
+ async bulkAdd(args) {
7670
+ const { formRevision, values: argsValues, componentOfflineIds } = args;
7121
7671
  const { store } = this.client;
7122
- const { payloadWithoutFiles, files } = separateFilesFromValues(submission);
7123
- if (!("created_by" in payloadWithoutFiles) || !("created_at" in payloadWithoutFiles)) {
7124
- throw new Error("Expected payloadWithoutFiles to have created_by and created_at fields.");
7672
+ const offlineSubmissions = [];
7673
+ const offlineAttachments = [];
7674
+ const submissionOfflineIds = [];
7675
+ const submissionsPayload = [];
7676
+ const attachmentsPayload = [];
7677
+ const { values, files } = separateFilesFromValues(argsValues);
7678
+ const submittedAt = (/* @__PURE__ */ new Date()).toISOString();
7679
+ const createdBy = store.getState().userReducer.currentUser.id;
7680
+ for (const component_id of componentOfflineIds) {
7681
+ const submission = offline({
7682
+ form_revision: formRevision,
7683
+ values,
7684
+ created_by: createdBy,
7685
+ submitted_at: submittedAt,
7686
+ component: component_id
7687
+ });
7688
+ submissionOfflineIds.push(submission.offline_id);
7689
+ submissionsPayload.push({ offline_id: submission.offline_id, component_id });
7690
+ offlineSubmissions.push(submission);
7691
+ for (const [fieldIdentifier, fileArray] of Object.entries(files)) {
7692
+ for (const file of fileArray) {
7693
+ const sha1 = await hashFile(file);
7694
+ await this.client.files.addCache(file, sha1);
7695
+ const offlineAttachment = offline({
7696
+ file_name: file.name,
7697
+ file_sha1: sha1,
7698
+ file: URL.createObjectURL(file),
7699
+ submission: submission.offline_id,
7700
+ field_identifier: fieldIdentifier
7701
+ });
7702
+ offlineAttachments.push(offlineAttachment);
7703
+ attachmentsPayload.push({
7704
+ offline_id: offlineAttachment.offline_id,
7705
+ submission_id: submission.offline_id,
7706
+ sha1,
7707
+ name: file.name,
7708
+ field_identifier: fieldIdentifier
7709
+ });
7710
+ }
7711
+ }
7712
+ }
7713
+ const filesRecord = {};
7714
+ for (const file of Object.values(files).flat()) {
7715
+ const sha1 = await hashFile(file);
7716
+ filesRecord[sha1] = {
7717
+ sha1,
7718
+ extension: file.name.split(".").pop() || "",
7719
+ file_type: file.type,
7720
+ size: file.size
7721
+ };
7125
7722
  }
7723
+ store.dispatch(addFormSubmissions(offlineSubmissions));
7724
+ store.dispatch(addFormSubmissionAttachments(offlineAttachments));
7725
+ const promise = this.enqueueRequest({
7726
+ description: "Bulk add form submissions",
7727
+ method: HttpMethod.POST,
7728
+ url: `/forms/revisions/${formRevision}/bulk-respond/`,
7729
+ payload: {
7730
+ form_data: values,
7731
+ submitted_at: submittedAt,
7732
+ submissions: submissionsPayload,
7733
+ attachments: attachmentsPayload,
7734
+ files: Object.values(filesRecord)
7735
+ },
7736
+ blockers: componentOfflineIds,
7737
+ blocks: submissionOfflineIds
7738
+ });
7739
+ promise.then(({ submissions, attachments, presigned_urls }) => {
7740
+ store.dispatch(updateFormSubmissions(submissions));
7741
+ store.dispatch(updateFormSubmissionAttachments(attachments));
7742
+ for (const [sha1, presigned_url] of Object.entries(presigned_urls)) {
7743
+ const file = filesRecord[sha1];
7744
+ if (!file)
7745
+ continue;
7746
+ void this.enqueueRequest({
7747
+ url: presigned_url.url,
7748
+ description: "Upload file",
7749
+ method: HttpMethod.POST,
7750
+ isExternalUrl: true,
7751
+ isAuthNeeded: false,
7752
+ attachmentHash: sha1,
7753
+ blockers: [`s3-${file.sha1}.${file.extension}`],
7754
+ blocks: [sha1],
7755
+ s3url: presigned_url
7756
+ });
7757
+ }
7758
+ }).catch(() => {
7759
+ store.dispatch(deleteFormSubmissions(submissionOfflineIds));
7760
+ store.dispatch(deleteFormSubmissionAttachments(offlineAttachments.map((x) => x.offline_id)));
7761
+ });
7762
+ return [offlineSubmissions, promise.then(({ submissions }) => submissions)];
7763
+ }
7764
+ update(submission) {
7765
+ const { store } = this.client;
7766
+ const { values, files } = separateFilesFromValues(submission.values);
7126
7767
  const attachFilesPromises = this.getAttachFilesPromises(files, submission);
7127
- const fullResult = {
7128
- ...payloadWithoutFiles,
7129
- updated_at: (/* @__PURE__ */ new Date()).toISOString()
7768
+ const offlineSubmission = {
7769
+ ...submission,
7770
+ values
7130
7771
  };
7131
- store.dispatch(updateOrCreateUserFormSubmission(fullResult));
7772
+ const submissionToBeUpdated = store.getState().formSubmissionReducer.formSubmissions[submission.offline_id];
7773
+ store.dispatch(updateFormSubmission(offlineSubmission));
7132
7774
  const promise = this.enqueueRequest({
7133
7775
  description: "Patch form submission",
7134
7776
  method: HttpMethod.PATCH,
7135
7777
  url: `/forms/submissions/${submission.offline_id}/`,
7136
- payload: fullResult,
7137
- blockers: [fullResult.issue, fullResult.component, fullResult.component_stage].filter(
7778
+ payload: offlineSubmission,
7779
+ blockers: [offlineSubmission.issue, offlineSubmission.component, offlineSubmission.component_stage].filter(
7138
7780
  (x) => x !== void 0
7139
7781
  ),
7140
- blocks: [fullResult.offline_id]
7782
+ blocks: [offlineSubmission.offline_id]
7783
+ });
7784
+ promise.then((createdSubmission) => {
7785
+ store.dispatch(setFormSubmission(createdSubmission));
7786
+ }).catch(() => {
7787
+ store.dispatch(setFormSubmission(submissionToBeUpdated));
7141
7788
  });
7142
- return Promise.all([promise, ...attachFilesPromises]).then(() => promise);
7789
+ return [offlineSubmission, Promise.all([promise, ...attachFilesPromises]).then(() => promise)];
7143
7790
  }
7144
7791
  async delete(submissionId) {
7145
7792
  const { store } = this.client;
7146
7793
  const state = store.getState();
7147
- const submission = state.userFormReducer.submissions[submissionId];
7148
- store.dispatch(deleteUserFormSubmission(submissionId));
7794
+ const submission = state.formSubmissionReducer.formSubmissions[submissionId];
7795
+ const submissionAttachments = selectAttachmentsOfFormSubmission(submissionId)(state);
7796
+ store.dispatch(deleteFormSubmission(submissionId));
7149
7797
  store.dispatch(addActiveProjectFormSubmissionsCount(-1));
7798
+ store.dispatch(deleteFormSubmissionAttachments(submissionAttachments.map((x) => x.offline_id)));
7150
7799
  try {
7151
7800
  return await this.enqueueRequest({
7152
7801
  description: "Delete user form submissions",
@@ -7156,10 +7805,9 @@ class UserFormSubmissionService extends BaseApiService {
7156
7805
  blocks: []
7157
7806
  });
7158
7807
  } catch (e) {
7159
- if (submission) {
7160
- store.dispatch(addActiveProjectFormSubmissionsCount(1));
7161
- store.dispatch(updateOrCreateUserFormSubmission(submission));
7162
- }
7808
+ store.dispatch(addActiveProjectFormSubmissionsCount(1));
7809
+ store.dispatch(addFormSubmission(submission));
7810
+ store.dispatch(addFormSubmissionAttachments(submissionAttachments));
7163
7811
  throw e;
7164
7812
  }
7165
7813
  }
@@ -7173,7 +7821,7 @@ class UserFormSubmissionService extends BaseApiService {
7173
7821
  blockers: [],
7174
7822
  blocks: []
7175
7823
  });
7176
- store.dispatch(setUserFormSubmissions(submissions));
7824
+ store.dispatch(setFormSubmissions(submissions));
7177
7825
  const attachments = await this.enqueueRequest({
7178
7826
  description: "Fetch form attachments",
7179
7827
  method: HttpMethod.GET,
@@ -7181,7 +7829,7 @@ class UserFormSubmissionService extends BaseApiService {
7181
7829
  blockers: [],
7182
7830
  blocks: []
7183
7831
  });
7184
- store.dispatch(setUserFormSubmissionAttachments(attachments));
7832
+ store.dispatch(setFormSubmissionAttachments(attachments));
7185
7833
  }
7186
7834
  }
7187
7835
  class WorkspaceService extends BaseApiService {
@@ -7873,6 +8521,48 @@ class DocumentService extends BaseApiService {
7873
8521
  store.dispatch(setDocuments(result));
7874
8522
  }
7875
8523
  }
8524
+ class DocumentAttachmentService extends BaseAttachmentService {
8525
+ constructor() {
8526
+ super(...arguments);
8527
+ __publicField(this, "attachmentModel", AttachmentModel.Document);
8528
+ }
8529
+ buildOfflineAttachment(data) {
8530
+ return offline({
8531
+ file: URL.createObjectURL(data.file),
8532
+ file_sha1: data.sha1,
8533
+ created_by: data.createdBy,
8534
+ file_name: data.file.name,
8535
+ file_type: data.file.type,
8536
+ submitted_at: data.submittedAt,
8537
+ description: data.description,
8538
+ document: data.modelId
8539
+ });
8540
+ }
8541
+ async attachFilesToDocument(files, documentId) {
8542
+ return this.attachFiles(files, documentId, this.buildOfflineAttachment.bind(this), {
8543
+ addAttachments: addDocumentAttachments,
8544
+ updateAttachments: updateDocumentAttachments,
8545
+ removeAttachments: removeDocumentAttachments
8546
+ });
8547
+ }
8548
+ deleteDocumentAttachment(attachmentId) {
8549
+ return this.deleteAttachment(
8550
+ attachmentId,
8551
+ {
8552
+ setAttachment: setDocumentAttachment,
8553
+ removeAttachment: removeDocumentAttachment
8554
+ },
8555
+ {
8556
+ selectAttachment: selectDocumentAttachment
8557
+ }
8558
+ );
8559
+ }
8560
+ async refreshStore() {
8561
+ return this.getAttachments({
8562
+ setAttachments: setDocumentAttachments
8563
+ });
8564
+ }
8565
+ }
7876
8566
  class AgentService extends BaseApiService {
7877
8567
  /**
7878
8568
  * Prompt the agent with a message.
@@ -7920,20 +8610,25 @@ class OvermapSDK {
7920
8610
  __publicField(this, "issues", new IssueService(this));
7921
8611
  __publicField(this, "issueComments", new IssueCommentService(this));
7922
8612
  __publicField(this, "issueUpdates", new IssueUpdateService(this));
8613
+ __publicField(this, "issueAttachments", new IssueAttachmentService(this));
7923
8614
  __publicField(this, "workspaces", new WorkspaceService(this));
7924
8615
  __publicField(this, "main", new MainService(this));
7925
8616
  __publicField(this, "components", new ComponentService(this));
8617
+ __publicField(this, "componentAttachments", new ComponentAttachmentService(this));
7926
8618
  __publicField(this, "componentTypes", new ComponentTypeService(this));
8619
+ __publicField(this, "componentTypeAttachments", new ComponentTypeAttachmentService(this));
7927
8620
  __publicField(this, "componentStages", new ComponentStageService(this));
7928
8621
  __publicField(this, "componentStageCompletions", new ComponentStageCompletionService(this));
7929
8622
  __publicField(this, "userForms", new UserFormService(this));
7930
8623
  __publicField(this, "userFormSubmissions", new UserFormSubmissionService(this));
7931
8624
  __publicField(this, "projects", new ProjectService(this));
7932
8625
  __publicField(this, "projectFiles", new ProjectFileService(this));
8626
+ __publicField(this, "projectAttachments", new ProjectAttachmentService(this));
7933
8627
  __publicField(this, "emailVerification", new EmailVerificationService(this));
7934
8628
  __publicField(this, "emailDomains", new EmailDomainsService(this));
7935
8629
  __publicField(this, "licenses", new LicenseService(this));
7936
8630
  __publicField(this, "documents", new DocumentService(this));
8631
+ __publicField(this, "documentAttachments", new DocumentAttachmentService(this));
7937
8632
  this.API_URL = apiUrl;
7938
8633
  this.store = store;
7939
8634
  }
@@ -14039,7 +14734,7 @@ const initialFormValues = (fields, values) => {
14039
14734
  };
14040
14735
  const useAttachImagesToFormRevisionFields = (revision) => {
14041
14736
  const { sdk } = useSDK();
14042
- const attachments = useAppSelector(selectRevisionAttachments((revision == null ? void 0 : revision.offline_id) ?? ""));
14737
+ const attachments = useAppSelector(selectAttachmentsOfFormRevision((revision == null ? void 0 : revision.offline_id) ?? ""));
14043
14738
  return useMemo(() => {
14044
14739
  if (!revision || !attachments)
14045
14740
  return revision;
@@ -14136,7 +14831,7 @@ const FormSubmissionViewer = memo(
14136
14831
  return formRevisionToSchema(revisionWithImages, { readonly: true });
14137
14832
  }, [revisionWithImages]);
14138
14833
  const submissionValuesWithAttachments = useMemo(() => {
14139
- const attachments = selectSubmissionAttachments(submission.offline_id)(sdk.store.getState()) ?? [];
14834
+ const attachments = selectAttachmentsOfFormSubmission(submission.offline_id)(sdk.store.getState()) ?? [];
14140
14835
  const downloadedAttachments = {};
14141
14836
  for (const attachment of attachments) {
14142
14837
  const promise = sdk.files.fetchFileFromUrl(attachment.file, attachment.file_sha1, attachment.file_name);
@@ -14186,8 +14881,8 @@ const FormBrowser = memo(
14186
14881
  }
14187
14882
  return ret;
14188
14883
  }, [filter, maxResults, ownerFilter]);
14189
- const userForms = useAppSelector(selectFilteredUserForms(ownerFilterOptions)) ?? [];
14190
- const userFormMapping = useAppSelector(selectUserFormMapping);
14884
+ const userForms = useAppSelector(selectFilteredForms(ownerFilterOptions)) ?? [];
14885
+ const userFormMapping = useAppSelector(selectFormMapping);
14191
14886
  const attachableUserForms = userForms.filter((form) => !form.component_type);
14192
14887
  const attachableUserFormMapping = Object.values(userFormMapping).filter(
14193
14888
  (form) => !form.component_type
@@ -14220,7 +14915,7 @@ const FormBrowser = memo(
14220
14915
  const handleChange = useCallback((e) => {
14221
14916
  setFilter(e.currentTarget.value);
14222
14917
  }, []);
14223
- const numberOfForms = useAppSelector(selectNumberOfGeneralUserForms) || 0;
14918
+ const numberOfForms = useAppSelector(selectGeneralFormCount) || 0;
14224
14919
  const numberOfHiddenForms = numberOfForms - attachableUserForms.length;
14225
14920
  const overflowMessage = attachableUserForms.length == maxResults && numberOfHiddenForms > 0 ? `Only the first ${maxResults} results are shown (${numberOfHiddenForms} hidden)` : numberOfHiddenForms > 0 && `${numberOfHiddenForms} hidden forms`;
14226
14921
  return /* @__PURE__ */ jsxs(Flex$1, { ref, direction: "column", gap: "2", children: [
@@ -14314,16 +15009,13 @@ const FormSubmissionBrowserEntry = memo((props) => {
14314
15009
  const { submission, onSubmissionClick, compact, labelType, rowDecorator } = props;
14315
15010
  const currentUser = useAppSelector(selectCurrentUser);
14316
15011
  const createdBy = useAppSelector(selectUser("created_by" in submission ? submission.created_by : currentUser.id));
14317
- const dateToUse = getCreatedAtOrSubmittedAtDate(submission);
14318
- const formattedDateTime = isToday(dateToUse) ? dateToUse.toLocaleTimeString([], {
14319
- hour: "2-digit",
14320
- minute: "2-digit"
14321
- }) : getLocalDateString(dateToUse);
15012
+ const dateToUse = submission.submitted_at;
15013
+ const formattedDateTime = getLocalDateString(dateToUse);
14322
15014
  const revision = useAppSelector(selectFormRevision(submission.form_revision));
14323
15015
  if (!revision) {
14324
15016
  throw new Error(`Could not find revision ${submission.form_revision} for submission ${submission.offline_id}.`);
14325
15017
  }
14326
- const latestRevisionNumber = (_a2 = useAppSelector(selectLatestFormRevision(revision.form))) == null ? void 0 : _a2.revision;
15018
+ const latestRevisionNumber = (_a2 = useAppSelector(selectLatestFormRevisionOfForm(revision.form))) == null ? void 0 : _a2.revision;
14327
15019
  const creatorProfileSrc = useFileSrc({
14328
15020
  file: (createdBy == null ? void 0 : createdBy.profile.file) ?? null,
14329
15021
  fileSha1: (createdBy == null ? void 0 : createdBy.profile.file_sha1) ?? null
@@ -14354,10 +15046,6 @@ const FormSubmissionBrowserEntry = memo((props) => {
14354
15046
  return row;
14355
15047
  });
14356
15048
  FormSubmissionBrowserEntry.displayName = "FormSubmissionBrowserEntry";
14357
- const getCreatedAtOrSubmittedAtDate = (submission) => {
14358
- const date = "created_at" in submission ? submission.created_at : submission.submitted_at;
14359
- return new Date(date);
14360
- };
14361
15049
  const FormSubmissionBrowser = memo((props) => {
14362
15050
  const {
14363
15051
  formId: formId2,
@@ -14371,10 +15059,10 @@ const FormSubmissionBrowser = memo((props) => {
14371
15059
  if (!!formId2 === !!propSubmissions) {
14372
15060
  throw new Error("Either formId or submissions must be provided, but not both.");
14373
15061
  }
14374
- const submissions = useAppSelector(propSubmissions ? () => propSubmissions : selectSubmissionsForForm(formId2));
15062
+ const submissions = useAppSelector(propSubmissions ? () => propSubmissions : selectFormSubmissionsOfForm(formId2));
14375
15063
  const sortedSubmissions = useMemo(
14376
15064
  () => submissions == null ? void 0 : submissions.sort((a, b) => {
14377
- return getCreatedAtOrSubmittedAtDate(b).getTime() - getCreatedAtOrSubmittedAtDate(a).getTime();
15065
+ return a.submitted_at.localeCompare(b.submitted_at);
14378
15066
  }),
14379
15067
  [submissions]
14380
15068
  );
@@ -15598,6 +16286,7 @@ const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePropert
15598
16286
  export {
15599
16287
  APIError,
15600
16288
  AgentService,
16289
+ AttachmentModel,
15601
16290
  AttachmentService,
15602
16291
  AuthService,
15603
16292
  BaseApiService,
@@ -15609,15 +16298,18 @@ export {
15609
16298
  ColorPicker,
15610
16299
  Colors,
15611
16300
  ColorsToString,
16301
+ ComponentAttachmentService,
15612
16302
  ComponentService,
15613
16303
  ComponentStageColors,
15614
16304
  ComponentStageCompletionService,
15615
16305
  ComponentStageService,
16306
+ ComponentTypeAttachmentService,
15616
16307
  ComponentTypeService,
15617
16308
  DEFAULT_ISSUE_PRIORITY,
15618
16309
  DEFAULT_ISSUE_STATUS,
15619
16310
  DateField,
15620
16311
  DateInput,
16312
+ DocumentAttachmentService,
15621
16313
  DocumentService,
15622
16314
  EmailDomainsService,
15623
16315
  EmailVerificationService,
@@ -15641,6 +16333,7 @@ export {
15641
16333
  InputWithHelpText,
15642
16334
  InputWithLabel,
15643
16335
  InputWithLabelAndHelpText,
16336
+ IssueAttachmentService,
15644
16337
  IssueCommentService,
15645
16338
  IssuePriority,
15646
16339
  IssueService,
@@ -15672,6 +16365,7 @@ export {
15672
16365
  PatchFormProvider,
15673
16366
  ProjectAccessLevel,
15674
16367
  ProjectAccessService,
16368
+ ProjectAttachmentService,
15675
16369
  ProjectFileService,
15676
16370
  ProjectService,
15677
16371
  ProjectType,
@@ -15692,6 +16386,7 @@ export {
15692
16386
  VerificationCodeType,
15693
16387
  WorkspaceService,
15694
16388
  YELLOW,
16389
+ _selectLatestFormRevision,
15695
16390
  _setLatestRetryTime,
15696
16391
  acceptProjectInvite,
15697
16392
  addActiveProjectFormSubmissionsCount,
@@ -15709,6 +16404,16 @@ export {
15709
16404
  addDocuments,
15710
16405
  addEmailDomain,
15711
16406
  addFavouriteProjectId,
16407
+ addForm,
16408
+ addFormRevision,
16409
+ addFormRevisionAttachment,
16410
+ addFormRevisionAttachments,
16411
+ addFormRevisions,
16412
+ addFormSubmission,
16413
+ addFormSubmissionAttachment,
16414
+ addFormSubmissionAttachments,
16415
+ addFormSubmissions,
16416
+ addForms,
15712
16417
  addIssue,
15713
16418
  addIssueAttachment,
15714
16419
  addIssueAttachments,
@@ -15729,13 +16434,6 @@ export {
15729
16434
  addStageCompletions,
15730
16435
  addStages,
15731
16436
  addToRecentIssues,
15732
- addUserForm,
15733
- addUserFormRevision,
15734
- addUserFormRevisionAttachment,
15735
- addUserFormRevisions,
15736
- addUserFormSubmissionAttachment,
15737
- addUserFormSubmissions,
15738
- addUserForms,
15739
16437
  addUsers,
15740
16438
  addWorkspace,
15741
16439
  areArraysEqual,
@@ -15756,6 +16454,7 @@ export {
15756
16454
  componentStageSlice,
15757
16455
  componentTypeReducer,
15758
16456
  componentTypeSlice,
16457
+ constructUploadedFilePayloads,
15759
16458
  coordinatesAreEqual,
15760
16459
  coordinatesToLiteral,
15761
16460
  coordinatesToPointGeometry,
@@ -15766,12 +16465,16 @@ export {
15766
16465
  defaultBadgeColor,
15767
16466
  defaultStore,
15768
16467
  deleteComponentType,
16468
+ deleteForm,
16469
+ deleteFormRevision,
16470
+ deleteFormRevisionAttachment,
16471
+ deleteFormRevisionAttachments,
16472
+ deleteFormRevisions,
16473
+ deleteFormSubmission,
16474
+ deleteFormSubmissionAttachment,
16475
+ deleteFormSubmissionAttachments,
16476
+ deleteFormSubmissions,
15769
16477
  deleteProject,
15770
- deleteUserForm,
15771
- deleteUserFormRevision,
15772
- deleteUserFormRevisions,
15773
- deleteUserFormSubmission,
15774
- deleteUserFormSubmissions,
15775
16478
  dequeue,
15776
16479
  deserialize,
15777
16480
  deserializeField,
@@ -15800,7 +16503,13 @@ export {
15800
16503
  fileSlice,
15801
16504
  fileToBlob,
15802
16505
  flipCoordinates,
16506
+ formReducer,
16507
+ formRevisionReducer,
15803
16508
  formRevisionToSchema,
16509
+ formRevisionsSlice,
16510
+ formSlice,
16511
+ formSubmissionReducer,
16512
+ formSubmissionSlice,
15804
16513
  index as forms,
15805
16514
  fullComponentMarkerSize,
15806
16515
  generateBadgeColors,
@@ -15876,6 +16585,7 @@ export {
15876
16585
  removeFavouriteProjectId,
15877
16586
  removeIssue,
15878
16587
  removeIssueAttachment,
16588
+ removeIssueAttachments,
15879
16589
  removeIssueComment,
15880
16590
  removeIssueComments,
15881
16591
  removeIssueUpdate,
@@ -15928,6 +16638,8 @@ export {
15928
16638
  selectAttachmentsOfComponentTypeByType,
15929
16639
  selectAttachmentsOfDocument,
15930
16640
  selectAttachmentsOfDocumentByType,
16641
+ selectAttachmentsOfFormRevision,
16642
+ selectAttachmentsOfFormSubmission,
15931
16643
  selectAttachmentsOfIssue,
15932
16644
  selectAttachmentsOfIssueByType,
15933
16645
  selectAttachmentsOfProject,
@@ -15945,11 +16657,9 @@ export {
15945
16657
  selectComponent,
15946
16658
  selectComponentAttachment,
15947
16659
  selectComponentAttachmentMapping,
15948
- selectComponentSubmissionMapping,
15949
16660
  selectComponentType,
15950
16661
  selectComponentTypeAttachment,
15951
16662
  selectComponentTypeAttachmentMapping,
15952
- selectComponentTypeForm,
15953
16663
  selectComponentTypeFromComponent,
15954
16664
  selectComponentTypeFromComponents,
15955
16665
  selectComponentTypeStagesMapping,
@@ -15979,8 +16689,24 @@ export {
15979
16689
  selectExpandedSections,
15980
16690
  selectFavouriteProjects,
15981
16691
  selectFileAttachmentsOfIssue,
15982
- selectFilteredUserForms,
16692
+ selectFilteredForms,
16693
+ selectForm,
16694
+ selectFormMapping,
16695
+ selectFormOfComponentType,
15983
16696
  selectFormRevision,
16697
+ selectFormRevisionMapping,
16698
+ selectFormRevisions,
16699
+ selectFormRevisionsOfForm,
16700
+ selectFormSubmission,
16701
+ selectFormSubmissionAttachmentsMapping,
16702
+ selectFormSubmissions,
16703
+ selectFormSubmissionsByComponents,
16704
+ selectFormSubmissionsMapping,
16705
+ selectFormSubmissionsOfComponent,
16706
+ selectFormSubmissionsOfForm,
16707
+ selectFormSubmissionsOfIssue,
16708
+ selectFormsCount,
16709
+ selectGeneralFormCount,
15984
16710
  selectHiddenCategoryCount,
15985
16711
  selectHiddenComponentTypeIds,
15986
16712
  selectIsFetchingInitialData,
@@ -15995,10 +16721,10 @@ export {
15995
16721
  selectIssueUpdateMapping,
15996
16722
  selectIssueUpdatesOfIssue,
15997
16723
  selectIssues,
15998
- selectLatestFormRevision,
16724
+ selectLatestFormRevisionByForm,
16725
+ selectLatestFormRevisionOfForm,
16726
+ selectLatestFormRevisionsOfComponentTypes,
15999
16727
  selectLatestRetryTime,
16000
- selectLatestRevisionByFormId,
16001
- selectLatestRevisionsFromComponentTypeIds,
16002
16728
  selectLicense,
16003
16729
  selectLicenseForProject,
16004
16730
  selectLicenses,
@@ -16007,8 +16733,6 @@ export {
16007
16733
  selectMapStyle,
16008
16734
  selectNumberOfComponentTypesMatchingCaseInsensitiveName,
16009
16735
  selectNumberOfComponentsOfComponentType,
16010
- selectNumberOfGeneralUserForms,
16011
- selectNumberOfUserForms,
16012
16736
  selectOrganization,
16013
16737
  selectOrganizationAccess,
16014
16738
  selectOrganizationAccessForUser,
@@ -16025,6 +16749,7 @@ export {
16025
16749
  selectProjectAccessForUser,
16026
16750
  selectProjectAccessUserMapping,
16027
16751
  selectProjectAccesses,
16752
+ selectProjectAttachment,
16028
16753
  selectProjectAttachmentMapping,
16029
16754
  selectProjectFileVisibility,
16030
16755
  selectProjectFiles,
@@ -16036,8 +16761,6 @@ export {
16036
16761
  selectRecentIssuesAsSearchResults,
16037
16762
  selectRecentProjects,
16038
16763
  selectRehydrated,
16039
- selectRevisionAttachments,
16040
- selectRevisionsForForm,
16041
16764
  selectRootDocuments,
16042
16765
  selectShowTooltips,
16043
16766
  selectSortedEmailDomains,
@@ -16052,16 +16775,10 @@ export {
16052
16775
  selectStagesFromComponentType,
16053
16776
  selectStagesFromComponentTypeIds,
16054
16777
  selectStagesFromStageIds,
16055
- selectSubmissionAttachments,
16056
- selectSubmissionsForComponent,
16057
- selectSubmissionsForForm,
16058
- selectSubmissionsForIssue,
16059
16778
  selectUploadUrl,
16060
16779
  selectUsedColors,
16061
16780
  selectUser,
16062
- selectUserForm,
16063
- selectUserFormMapping,
16064
- selectUserFormSubmission,
16781
+ selectUserFormRevisionAttachmentsMapping,
16065
16782
  selectUsersAsMapping,
16066
16783
  selectVisibleStatuses,
16067
16784
  selectVisibleUserIds,
@@ -16076,21 +16793,32 @@ export {
16076
16793
  setAppearance,
16077
16794
  setCategories,
16078
16795
  setCenterMapToProject,
16796
+ setComponentAttachment,
16079
16797
  setComponentAttachments,
16798
+ setComponentTypeAttachment,
16080
16799
  setComponentTypeAttachments,
16081
16800
  setComponentTypes,
16082
16801
  setComponents,
16083
16802
  setCreateProjectType,
16084
16803
  setCurrentUser,
16804
+ setDocumentAttachment,
16085
16805
  setDocumentAttachments,
16086
16806
  setDocuments,
16087
16807
  setEmailDomains,
16088
16808
  setEnableClustering,
16089
16809
  setEnableDuplicateIssues,
16090
16810
  setEnablePlacementMode,
16811
+ setFormRevision,
16812
+ setFormRevisionAttachments,
16813
+ setFormRevisions,
16814
+ setFormSubmission,
16815
+ setFormSubmissionAttachments,
16816
+ setFormSubmissions,
16817
+ setForms,
16091
16818
  setIsFetchingInitialData,
16092
16819
  setIsImportingProjectFile,
16093
16820
  setIsLoading,
16821
+ setIssueAttachment,
16094
16822
  setIssueAttachments,
16095
16823
  setIssueComment,
16096
16824
  setIssueComments,
@@ -16103,6 +16831,7 @@ export {
16103
16831
  setOrganizations,
16104
16832
  setProfilePicture,
16105
16833
  setProjectAccesses,
16834
+ setProjectAttachment,
16106
16835
  setProjectAttachments,
16107
16836
  setProjectFileVisible,
16108
16837
  setProjects,
@@ -16112,9 +16841,6 @@ export {
16112
16841
  setTokens,
16113
16842
  setTourStep,
16114
16843
  setUploadUrl,
16115
- setUserFormRevisionAttachments,
16116
- setUserFormSubmissionAttachments,
16117
- setUserFormSubmissions,
16118
16844
  setUsers,
16119
16845
  setVisibleStatuses,
16120
16846
  setVisibleUserIds,
@@ -16136,17 +16862,24 @@ export {
16136
16862
  updateActiveOrganization,
16137
16863
  updateComponent,
16138
16864
  updateComponentAttachment,
16865
+ updateComponentAttachments,
16139
16866
  updateComponentTypeAttachment,
16867
+ updateComponentTypeAttachments,
16140
16868
  updateDocumentAttachment,
16869
+ updateDocumentAttachments,
16141
16870
  updateDocuments,
16871
+ updateFormSubmission,
16872
+ updateFormSubmissionAttachments,
16873
+ updateFormSubmissions,
16142
16874
  updateIssue,
16143
16875
  updateIssueAttachment,
16876
+ updateIssueAttachments,
16144
16877
  updateLicense,
16145
16878
  updateOrCreateProject,
16146
- updateOrCreateUserFormSubmission,
16147
16879
  updateOrganizationAccess,
16148
16880
  updateProjectAccess,
16149
16881
  updateProjectAttachment,
16882
+ updateProjectAttachments,
16150
16883
  updateStages,
16151
16884
  useAppDispatch,
16152
16885
  useAppSelector,
@@ -16157,8 +16890,6 @@ export {
16157
16890
  useFormikInput,
16158
16891
  useMemoCompare,
16159
16892
  useSDK,
16160
- userFormReducer,
16161
- userFormSlice,
16162
16893
  userReducer,
16163
16894
  userSlice,
16164
16895
  valueIsFile,