@overmap-ai/core 1.0.49 → 1.0.50-bulk-form-submission.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -631,15 +631,15 @@ const wrapMigration = (migrator) => (state) => {
631
631
  };
632
632
  const migrations = [initialVersioning, signOut, signOut, createOutboxState];
633
633
  const manifest = Object.fromEntries(migrations.map((migration2, i) => [i, wrapMigration(migration2)]));
634
- const initialState$n = {
634
+ const initialState$p = {
635
635
  accessToken: "",
636
636
  refreshToken: "",
637
637
  isLoggedIn: false
638
638
  };
639
639
  const authSlice = createSlice({
640
640
  name: "auth",
641
- initialState: initialState$n,
642
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$n)),
641
+ initialState: initialState$p,
642
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$p)),
643
643
  reducers: {
644
644
  setTokens: (state, action) => {
645
645
  state.accessToken = action.payload.accessToken;
@@ -804,6 +804,19 @@ function downloadInMemoryFile(filename, text) {
804
804
  element.click();
805
805
  document.body.removeChild(element);
806
806
  }
807
+ const constructUploadedFilePayloads = async (files) => {
808
+ const filePayloads = {};
809
+ for (const file of files) {
810
+ const sha1 = await hashFile(file);
811
+ filePayloads[sha1] = {
812
+ sha1,
813
+ extension: file.name.split(".").pop() || "",
814
+ file_type: file.type,
815
+ size: file.size
816
+ };
817
+ }
818
+ return Object.values(filePayloads);
819
+ };
807
820
  const fileToBlob = async (dataUrl) => {
808
821
  return (await fetch(dataUrl)).blob();
809
822
  };
@@ -1370,7 +1383,7 @@ const getLocalRelativeDateString = memoize((date, min, max) => {
1370
1383
  return getLocalDateString(date);
1371
1384
  return relative.format(days, "days");
1372
1385
  });
1373
- const initialState$m = {
1386
+ const initialState$o = {
1374
1387
  categories: {},
1375
1388
  usedCategoryColors: [],
1376
1389
  categoryVisibility: {
@@ -1380,8 +1393,8 @@ const initialState$m = {
1380
1393
  };
1381
1394
  const categorySlice = createSlice({
1382
1395
  name: "categories",
1383
- initialState: initialState$m,
1384
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$m)),
1396
+ initialState: initialState$o,
1397
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$o)),
1385
1398
  reducers: {
1386
1399
  setCategories: (state, action) => {
1387
1400
  if (!Array.isArray(action.payload))
@@ -1515,6 +1528,7 @@ const selectHiddenCategoryCount = (state) => {
1515
1528
  };
1516
1529
  const categoryReducer = categorySlice.reducer;
1517
1530
  function setAttachments(state, action) {
1531
+ state.attachments = {};
1518
1532
  for (const attachment of action.payload) {
1519
1533
  state.attachments[attachment.offline_id] = attachment;
1520
1534
  }
@@ -1537,6 +1551,15 @@ function updateAttachment(state, action) {
1537
1551
  throw new Error(`Attachment ${action.payload.offline_id} does not exist.`);
1538
1552
  }
1539
1553
  }
1554
+ function updateAttachments(state, action) {
1555
+ for (const attachment of action.payload) {
1556
+ if (attachment.offline_id in state.attachments) {
1557
+ state.attachments[attachment.offline_id] = attachment;
1558
+ } else {
1559
+ throw new Error(`Attachment ${attachment.offline_id} does not exist.`);
1560
+ }
1561
+ }
1562
+ }
1540
1563
  function removeAttachment(state, action) {
1541
1564
  if (action.payload in state.attachments) {
1542
1565
  delete state.attachments[action.payload];
@@ -1549,14 +1572,14 @@ function removeAttachments(state, action) {
1549
1572
  delete state.attachments[attachmentId];
1550
1573
  }
1551
1574
  }
1552
- const initialState$l = {
1575
+ const initialState$n = {
1553
1576
  components: {},
1554
1577
  attachments: {}
1555
1578
  };
1556
1579
  const componentSlice = createSlice({
1557
1580
  name: "components",
1558
- initialState: initialState$l,
1559
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$l)),
1581
+ initialState: initialState$n,
1582
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$n)),
1560
1583
  reducers: {
1561
1584
  addComponent: (state, action) => {
1562
1585
  state.components[action.payload.offline_id] = action.payload;
@@ -1669,6 +1692,9 @@ const selectAllComponentAttachments = createSelector(
1669
1692
  [selectComponentAttachmentMapping],
1670
1693
  (mapping) => Object.values(mapping)
1671
1694
  );
1695
+ const selectComponentAttachment = (attachmentId) => (state) => {
1696
+ return state.componentReducer.attachments[attachmentId];
1697
+ };
1672
1698
  const selectAttachmentsOfComponent = restructureCreateSelectorWithArgs(
1673
1699
  createSelector(
1674
1700
  [selectAllComponentAttachments, (_state, componentId) => componentId],
@@ -1709,13 +1735,13 @@ const {
1709
1735
  removeAllComponentsOfType
1710
1736
  } = componentSlice.actions;
1711
1737
  const componentReducer = componentSlice.reducer;
1712
- const initialState$k = {
1738
+ const initialState$m = {
1713
1739
  completionsByComponentId: {}
1714
1740
  };
1715
1741
  const componentStageCompletionSlice = createSlice({
1716
1742
  name: "componentStageCompletions",
1717
- initialState: initialState$k,
1718
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$k)),
1743
+ initialState: initialState$m,
1744
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$m)),
1719
1745
  reducers: {
1720
1746
  addStageCompletion: (state, action) => {
1721
1747
  let stageToCompletionDateMapping = state.completionsByComponentId[action.payload.component];
@@ -1766,13 +1792,13 @@ const selectCompletedStageIdsForComponent = (component) => (state) => {
1766
1792
  return Object.keys(state.componentStageCompletionReducer.completionsByComponentId[component.offline_id] ?? {});
1767
1793
  };
1768
1794
  const componentStageCompletionReducer = componentStageCompletionSlice.reducer;
1769
- const initialState$j = {
1795
+ const initialState$l = {
1770
1796
  stages: {}
1771
1797
  };
1772
1798
  const componentStageSlice = createSlice({
1773
1799
  name: "componentStages",
1774
- initialState: initialState$j,
1775
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$j)),
1800
+ initialState: initialState$l,
1801
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$l)),
1776
1802
  reducers: {
1777
1803
  addStages: (state, action) => {
1778
1804
  Object.assign(state.stages, toOfflineIdRecord(action.payload));
@@ -1882,15 +1908,15 @@ const selectStageFormIdsFromStageIds = restructureCreateSelectorWithArgs(
1882
1908
  );
1883
1909
  const { addStages, updateStages, removeStages, linkStageToForm, unlinkStageToForm } = componentStageSlice.actions;
1884
1910
  const componentStageReducer = componentStageSlice.reducer;
1885
- const initialState$i = {
1911
+ const initialState$k = {
1886
1912
  componentTypes: {},
1887
1913
  hiddenComponentTypeIds: {},
1888
1914
  attachments: {}
1889
1915
  };
1890
1916
  const componentTypeSlice = createSlice({
1891
1917
  name: "componentTypes",
1892
- initialState: initialState$i,
1893
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$i)),
1918
+ initialState: initialState$k,
1919
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$k)),
1894
1920
  reducers: {
1895
1921
  addComponentType: (state, action) => {
1896
1922
  state.componentTypes[action.payload.offline_id] = action.payload;
@@ -1958,6 +1984,9 @@ const selectAllComponentTypeAttachments = createSelector(
1958
1984
  [selectComponentTypeAttachmentMapping],
1959
1985
  (mapping) => Object.values(mapping)
1960
1986
  );
1987
+ const selectComponentTypeAttachment = (attachmentId) => (state) => {
1988
+ return state.componentTypeReducer.attachments[attachmentId];
1989
+ };
1961
1990
  const selectAttachmentsOfComponentType = restructureCreateSelectorWithArgs(
1962
1991
  createSelector(
1963
1992
  [selectAllComponentTypeAttachments, (_state, componentTypeId) => componentTypeId],
@@ -1998,13 +2027,13 @@ const {
1998
2027
  deleteComponentType
1999
2028
  } = componentTypeSlice.actions;
2000
2029
  const componentTypeReducer = componentTypeSlice.reducer;
2001
- const initialState$h = {
2030
+ const initialState$j = {
2002
2031
  workspaces: {},
2003
2032
  activeWorkspaceId: null
2004
2033
  };
2005
2034
  const workspaceSlice = createSlice({
2006
2035
  name: "workspace",
2007
- initialState: initialState$h,
2036
+ initialState: initialState$j,
2008
2037
  // The `reducers` field lets us define reducers and generate associated actions
2009
2038
  reducers: {
2010
2039
  setWorkspaces: (state, action) => {
@@ -2061,7 +2090,7 @@ const selectPermittedWorkspaceIds = createSelector(
2061
2090
  );
2062
2091
  const workspaceReducer = workspaceSlice.reducer;
2063
2092
  const maxRecentIssues = 10;
2064
- const initialState$g = {
2093
+ const initialState$i = {
2065
2094
  issues: {},
2066
2095
  attachments: {},
2067
2096
  comments: {},
@@ -2073,9 +2102,9 @@ const initialState$g = {
2073
2102
  };
2074
2103
  const issueSlice = createSlice({
2075
2104
  name: "issues",
2076
- initialState: initialState$g,
2105
+ initialState: initialState$i,
2077
2106
  extraReducers: (builder) => builder.addCase("RESET", (state) => {
2078
- Object.assign(state, initialState$g);
2107
+ Object.assign(state, initialState$i);
2079
2108
  }),
2080
2109
  reducers: {
2081
2110
  setIssues: (state, action) => {
@@ -2130,6 +2159,7 @@ const issueSlice = createSlice({
2130
2159
  }
2131
2160
  },
2132
2161
  updateIssueAttachment: updateAttachment,
2162
+ updateIssueAttachments: updateAttachments,
2133
2163
  removeIssue: (state, action) => {
2134
2164
  if (action.payload in state.issues) {
2135
2165
  delete state.issues[action.payload];
@@ -2138,6 +2168,7 @@ const issueSlice = createSlice({
2138
2168
  }
2139
2169
  },
2140
2170
  removeIssueAttachment: removeAttachment,
2171
+ removeIssueAttachments: removeAttachments,
2141
2172
  removeIssueUpdate: (state, action) => {
2142
2173
  if (action.payload in state.updates) {
2143
2174
  delete state.updates[action.payload];
@@ -2247,6 +2278,7 @@ const {
2247
2278
  addToRecentIssues,
2248
2279
  cleanRecentIssues,
2249
2280
  removeIssueAttachment,
2281
+ removeIssueAttachments,
2250
2282
  removeAttachmentsOfIssue,
2251
2283
  removeIssue,
2252
2284
  removeIssueUpdate,
@@ -2260,6 +2292,7 @@ const {
2260
2292
  setVisibleStatuses,
2261
2293
  setVisibleUserIds,
2262
2294
  updateIssueAttachment,
2295
+ updateIssueAttachments,
2263
2296
  updateIssue,
2264
2297
  // Commments
2265
2298
  addIssueComment,
@@ -2352,6 +2385,9 @@ const selectAttachmentsOfIssue = restructureCreateSelectorWithArgs(
2352
2385
  }
2353
2386
  )
2354
2387
  );
2388
+ const selectIssueAttachment = (attachmentId) => (root) => {
2389
+ return root.issueReducer.attachments[attachmentId];
2390
+ };
2355
2391
  const selectAttachmentsOfIssueByType = restructureCreateSelectorWithArgs(
2356
2392
  createSelector(
2357
2393
  [selectIssueAttachments, (_state, issueId) => issueId],
@@ -2480,15 +2516,15 @@ const selectRecentIssuesAsSearchResults = createSelector(
2480
2516
  }
2481
2517
  );
2482
2518
  const issueReducer = issueSlice.reducer;
2483
- const initialState$f = {
2519
+ const initialState$h = {
2484
2520
  s3Urls: {}
2485
2521
  };
2486
2522
  const msPerHour = 1e3 * 60 * 60;
2487
2523
  const msPerWeek = msPerHour * 24 * 7;
2488
2524
  const fileSlice = createSlice({
2489
2525
  name: "file",
2490
- initialState: initialState$f,
2491
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$f)),
2526
+ initialState: initialState$h,
2527
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$h)),
2492
2528
  reducers: {
2493
2529
  setUploadUrl: (state, action) => {
2494
2530
  const { url, fields, sha1 } = action.payload;
@@ -2515,7 +2551,7 @@ const selectUploadUrl = (sha1) => (state) => {
2515
2551
  return url;
2516
2552
  };
2517
2553
  const fileReducer = fileSlice.reducer;
2518
- const initialState$e = {
2554
+ const initialState$g = {
2519
2555
  // TODO: Change first MapStyle.SATELLITE to MaptStyle.None when project creation map is fixed
2520
2556
  mapStyle: MapStyle.SATELLITE,
2521
2557
  showTooltips: false,
@@ -2523,8 +2559,8 @@ const initialState$e = {
2523
2559
  };
2524
2560
  const mapSlice = createSlice({
2525
2561
  name: "map",
2526
- initialState: initialState$e,
2527
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$e)),
2562
+ initialState: initialState$g,
2563
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$g)),
2528
2564
  reducers: {
2529
2565
  setMapStyle: (state, action) => {
2530
2566
  state.mapStyle = action.payload;
@@ -2593,7 +2629,7 @@ var LicenseStatus = /* @__PURE__ */ ((LicenseStatus2) => {
2593
2629
  LicenseStatus2[LicenseStatus2["PAST_DUE"] = 8] = "PAST_DUE";
2594
2630
  return LicenseStatus2;
2595
2631
  })(LicenseStatus || {});
2596
- const initialState$d = {
2632
+ const initialState$f = {
2597
2633
  users: {},
2598
2634
  currentUser: {
2599
2635
  id: 0,
@@ -2604,8 +2640,8 @@ const initialState$d = {
2604
2640
  };
2605
2641
  const userSlice = createSlice({
2606
2642
  name: "users",
2607
- initialState: initialState$d,
2608
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$d)),
2643
+ initialState: initialState$f,
2644
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$f)),
2609
2645
  reducers: {
2610
2646
  setUsers: (state, action) => {
2611
2647
  const usersMapping = {};
@@ -2667,13 +2703,13 @@ const selectUser = (userId) => (state) => {
2667
2703
  const selectUsersAsMapping = (state) => state.userReducer.users;
2668
2704
  const selectFavouriteProjects = (state) => state.userReducer.currentUser.profile.favourite_project_ids;
2669
2705
  const userReducer = userSlice.reducer;
2670
- const initialState$c = {
2706
+ const initialState$e = {
2671
2707
  organizationAccesses: {}
2672
2708
  };
2673
2709
  const organizationAccessSlice = createSlice({
2674
2710
  name: "organizationAccess",
2675
- initialState: initialState$c,
2676
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$c)),
2711
+ initialState: initialState$e,
2712
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$e)),
2677
2713
  reducers: {
2678
2714
  setOrganizationAccesses: (state, action) => {
2679
2715
  if (!Array.isArray(action.payload))
@@ -2736,13 +2772,13 @@ const selectOrganizationAccessUserMapping = (state) => {
2736
2772
  return organizationAccesses;
2737
2773
  };
2738
2774
  const organizationAccessReducer = organizationAccessSlice.reducer;
2739
- const initialState$b = {
2775
+ const initialState$d = {
2740
2776
  licenses: {}
2741
2777
  };
2742
2778
  const licenseSlice = createSlice({
2743
2779
  name: "license",
2744
- initialState: initialState$b,
2745
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$b)),
2780
+ initialState: initialState$d,
2781
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$d)),
2746
2782
  reducers: {
2747
2783
  setLicenses: (state, action) => {
2748
2784
  if (!Array.isArray(action.payload))
@@ -2787,13 +2823,13 @@ const selectLicensesForProjectsMapping = createSelector(
2787
2823
  (licenses) => Object.values(licenses).filter((license) => license.project).reduce((accum, license) => ({ ...accum, [license.project]: license }), {})
2788
2824
  );
2789
2825
  const licenseReducer = licenseSlice.reducer;
2790
- const initialState$a = {
2826
+ const initialState$c = {
2791
2827
  projectAccesses: {}
2792
2828
  };
2793
2829
  const projectAccessSlice = createSlice({
2794
2830
  name: "projectAccess",
2795
- initialState: initialState$a,
2796
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$a)),
2831
+ initialState: initialState$c,
2832
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$c)),
2797
2833
  reducers: {
2798
2834
  setProjectAccesses: (state, action) => {
2799
2835
  if (!Array.isArray(action.payload))
@@ -2861,7 +2897,7 @@ const selectProjectAccessUserMapping = (state) => {
2861
2897
  return projectAccesses;
2862
2898
  };
2863
2899
  const projectAccessReducer = projectAccessSlice.reducer;
2864
- const initialState$9 = {
2900
+ const initialState$b = {
2865
2901
  projects: {},
2866
2902
  activeProjectId: null,
2867
2903
  recentProjectIds: [],
@@ -2871,7 +2907,7 @@ const initialState$9 = {
2871
2907
  };
2872
2908
  const projectSlice = createSlice({
2873
2909
  name: "projects",
2874
- initialState: initialState$9,
2910
+ initialState: initialState$b,
2875
2911
  reducers: {
2876
2912
  setProjects: (state, action) => {
2877
2913
  const projectsMap = {};
@@ -3058,14 +3094,14 @@ const selectAttachmentsOfProjectByType = restructureCreateSelectorWithArgs(
3058
3094
  }
3059
3095
  )
3060
3096
  );
3061
- const initialState$8 = {
3097
+ const initialState$a = {
3062
3098
  organizations: {},
3063
3099
  activeOrganizationId: null
3064
3100
  };
3065
3101
  const organizationSlice = createSlice({
3066
3102
  name: "organizations",
3067
- initialState: initialState$8,
3068
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$8)),
3103
+ initialState: initialState$a,
3104
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$a)),
3069
3105
  reducers: {
3070
3106
  setOrganizations: (state, action) => {
3071
3107
  for (const org of action.payload) {
@@ -3184,14 +3220,14 @@ const createOfflineAction = (request2, baseUrl) => {
3184
3220
  }
3185
3221
  };
3186
3222
  };
3187
- const initialState$7 = {
3223
+ const initialState$9 = {
3188
3224
  deletedRequests: [],
3189
3225
  latestRetryTime: 0
3190
3226
  };
3191
3227
  const outboxSlice = createSlice({
3192
3228
  name: "outbox",
3193
- initialState: initialState$7,
3194
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$7)),
3229
+ initialState: initialState$9,
3230
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$9)),
3195
3231
  reducers: {
3196
3232
  // enqueueActions is a reducer that does nothing but enqueue API request to the Redux Offline outbox
3197
3233
  // Whenever an issue is being created, a reducer addIssue() is responsible for adding it to the offline store
@@ -3223,7 +3259,7 @@ const selectDeletedRequests = (state) => state.outboxReducer.deletedRequests;
3223
3259
  const selectLatestRetryTime = (state) => state.outboxReducer.latestRetryTime;
3224
3260
  const { enqueueRequest, markForDeletion, markAsDeleted, _setLatestRetryTime } = outboxSlice.actions;
3225
3261
  const outboxReducer = outboxSlice.reducer;
3226
- const initialState$6 = {
3262
+ const initialState$8 = {
3227
3263
  projectFiles: {},
3228
3264
  activeProjectFileId: null,
3229
3265
  isImportingProjectFile: false,
@@ -3231,8 +3267,8 @@ const initialState$6 = {
3231
3267
  };
3232
3268
  const projectFileSlice = createSlice({
3233
3269
  name: "projectFiles",
3234
- initialState: initialState$6,
3235
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$6)),
3270
+ initialState: initialState$8,
3271
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$8)),
3236
3272
  reducers: {
3237
3273
  addOrReplaceProjectFiles: (state, action) => {
3238
3274
  for (let fileObj of action.payload) {
@@ -3333,12 +3369,12 @@ const selectProjectFiles = createSelector(
3333
3369
  const selectActiveProjectFileId = (state) => state.projectFileReducer.activeProjectFileId;
3334
3370
  const selectIsImportingProjectFile = (state) => state.projectFileReducer.isImportingProjectFile;
3335
3371
  const projectFileReducer = projectFileSlice.reducer;
3336
- const initialState$5 = {
3372
+ const initialState$7 = {
3337
3373
  isRehydrated: false
3338
3374
  };
3339
3375
  const rehydratedSlice = createSlice({
3340
3376
  name: "rehydrated",
3341
- initialState: initialState$5,
3377
+ initialState: initialState$7,
3342
3378
  // The `reducers` field lets us define reducers and generate associated actions
3343
3379
  reducers: {
3344
3380
  setRehydrated: (state, action) => {
@@ -3348,7 +3384,7 @@ const rehydratedSlice = createSlice({
3348
3384
  });
3349
3385
  const selectRehydrated = (state) => state.rehydratedReducer.isRehydrated;
3350
3386
  const rehydratedReducer = rehydratedSlice.reducer;
3351
- const initialState$4 = {
3387
+ const initialState$6 = {
3352
3388
  useIssueTemplate: false,
3353
3389
  placementMode: false,
3354
3390
  enableClustering: false,
@@ -3365,8 +3401,8 @@ const initialState$4 = {
3365
3401
  };
3366
3402
  const settingSlice = createSlice({
3367
3403
  name: "settings",
3368
- initialState: initialState$4,
3369
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$4)),
3404
+ initialState: initialState$6,
3405
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$6)),
3370
3406
  reducers: {
3371
3407
  setEnableDuplicateIssues: (state, action) => {
3372
3408
  state.useIssueTemplate = action.payload;
@@ -3412,146 +3448,248 @@ const selectAppearance = (state) => state.settingReducer.appearance;
3412
3448
  const settingReducer = settingSlice.reducer;
3413
3449
  const selectIsFetchingInitialData = (state) => state.settingReducer.isFetchingInitialData;
3414
3450
  const selectIsLoading = (state) => state.settingReducer.isLoading;
3415
- const LATEST_REVISION_CACHE = {};
3416
- function considerCachingRevision(revision, formId2, preferPending = false) {
3451
+ const LATEST_FORM_REVISION_CACHE = {};
3452
+ function considerCachingFormRevision(formRevision, formId2, preferPending = false) {
3417
3453
  var _a2;
3418
- if (!revision) {
3454
+ if (!formRevision) {
3419
3455
  if (!formId2) {
3420
- throw new Error("If revision is null, formId is required.");
3456
+ throw new Error("If form revision is null, formId is required.");
3421
3457
  }
3422
- const currentLatestRevision = getLatestRevisionFromCache(formId2);
3423
- if (currentLatestRevision)
3458
+ const currentLatestFormRevision = getLatestFormRevisionFromCache(formId2);
3459
+ if (currentLatestFormRevision)
3424
3460
  return;
3425
- LATEST_REVISION_CACHE[formId2] = null;
3461
+ LATEST_FORM_REVISION_CACHE[formId2] = null;
3426
3462
  return;
3427
3463
  }
3428
- if (revision.revision === "Pending") {
3464
+ if (formRevision.revision === "Pending") {
3429
3465
  if (preferPending) {
3430
- LATEST_REVISION_CACHE[revision.form] = revision;
3466
+ LATEST_FORM_REVISION_CACHE[formRevision.form] = formRevision;
3431
3467
  }
3432
3468
  return;
3433
3469
  }
3434
- const cachedRevision = (_a2 = LATEST_REVISION_CACHE[revision.form]) == null ? void 0 : _a2.revision;
3435
- if (revision.revision > (typeof cachedRevision === "number" ? cachedRevision : -1)) {
3436
- LATEST_REVISION_CACHE[revision.form] = revision;
3470
+ const cachedFormRevision = (_a2 = LATEST_FORM_REVISION_CACHE[formRevision.form]) == null ? void 0 : _a2.revision;
3471
+ if (formRevision.revision > (typeof cachedFormRevision === "number" ? cachedFormRevision : -1)) {
3472
+ LATEST_FORM_REVISION_CACHE[formRevision.form] = formRevision;
3437
3473
  }
3438
3474
  }
3439
- function getLatestRevisionFromCache(formId2) {
3440
- return LATEST_REVISION_CACHE[formId2];
3475
+ function getLatestFormRevisionFromCache(formId2) {
3476
+ return LATEST_FORM_REVISION_CACHE[formId2];
3441
3477
  }
3442
- const initialState$3 = {
3443
- userForms: {},
3444
- revisions: {},
3445
- submissions: {},
3446
- submissionAttachments: {},
3447
- revisionAttachments: {}
3448
- };
3449
- const userFormSlice = createSlice({
3450
- name: "userForms",
3451
- initialState: initialState$3,
3452
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$3)),
3478
+ const initialState$5 = {
3479
+ formRevisions: {},
3480
+ attachments: {}
3481
+ };
3482
+ const formRevisionsSlice = createSlice({
3483
+ name: "formRevisions",
3484
+ initialState: initialState$5,
3485
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$5)),
3453
3486
  reducers: {
3454
- setUserForms: (state, action) => {
3455
- state.userForms = {};
3456
- action.payload.forEach((userForm) => {
3457
- state.userForms[userForm.offline_id] = userForm;
3458
- });
3459
- },
3460
- addUserForm: (state, action) => {
3461
- state.userForms[action.payload.offline_id] = action.payload;
3462
- },
3463
- addUserForms: (state, action) => {
3464
- action.payload.forEach((userForm) => {
3465
- state.userForms[userForm.offline_id] = userForm;
3466
- });
3487
+ // revision related actions
3488
+ setFormRevision: (state, action) => {
3489
+ state.formRevisions[action.payload.offline_id] = action.payload;
3490
+ considerCachingFormRevision(action.payload);
3467
3491
  },
3468
- addUserFormRevisions: (state, action) => {
3469
- action.payload.forEach((userFormRevision) => {
3470
- state.revisions[userFormRevision.offline_id] = userFormRevision;
3471
- considerCachingRevision(userFormRevision);
3472
- });
3473
- },
3474
- addUserFormRevision: (state, action) => {
3475
- state.revisions[action.payload.offline_id] = action.payload;
3476
- considerCachingRevision(action.payload);
3492
+ setFormRevisions: (state, action) => {
3493
+ state.formRevisions = {};
3494
+ for (const revision of action.payload) {
3495
+ state.formRevisions[revision.offline_id] = revision;
3496
+ considerCachingFormRevision(revision);
3497
+ }
3477
3498
  },
3478
- deleteUserFormRevision: (state, action) => {
3479
- delete state.revisions[action.payload];
3480
- delete LATEST_REVISION_CACHE[action.payload];
3499
+ addFormRevision: (state, action) => {
3500
+ if (state.formRevisions[action.payload.offline_id] !== void 0) {
3501
+ throw new Error(`Revision with offline_id ${action.payload.offline_id} already exists`);
3502
+ }
3503
+ state.formRevisions[action.payload.offline_id] = action.payload;
3504
+ considerCachingFormRevision(action.payload);
3481
3505
  },
3482
- deleteUserFormRevisions: (state, action) => {
3506
+ // TODO: @Audiopolis / Magnus - do we want to standardize using PayloadAction?
3507
+ addFormRevisions: (state, action) => {
3483
3508
  for (const userFormRevision of action.payload) {
3484
- delete state.revisions[userFormRevision.offline_id];
3485
- delete LATEST_REVISION_CACHE[userFormRevision.offline_id];
3509
+ if (state.formRevisions[userFormRevision.offline_id] !== void 0) {
3510
+ throw new Error(`Revision with offline_id ${userFormRevision.offline_id} already exists`);
3511
+ }
3512
+ }
3513
+ for (const userFormRevision of action.payload) {
3514
+ state.formRevisions[userFormRevision.offline_id] = userFormRevision;
3515
+ considerCachingFormRevision(userFormRevision);
3486
3516
  }
3487
3517
  },
3488
- updateOrCreateUserFormSubmission: (state, action) => {
3489
- state.submissions[action.payload.offline_id] = action.payload;
3490
- },
3491
- addUserFormSubmissionAttachment: (state, action) => {
3492
- const submissionId = action.payload.submission;
3493
- const submissionAttachments = state.submissionAttachments[submissionId];
3494
- if (submissionAttachments) {
3495
- submissionAttachments.push(action.payload);
3496
- } else {
3497
- state.submissionAttachments[submissionId] = [action.payload];
3518
+ // UserFormRevisions do not get updated
3519
+ deleteFormRevision: (state, action) => {
3520
+ if (state.formRevisions[action.payload] === void 0) {
3521
+ throw new Error(`Revision with offline_id ${action.payload} does not exist`);
3498
3522
  }
3523
+ delete state.formRevisions[action.payload];
3524
+ delete LATEST_FORM_REVISION_CACHE[action.payload];
3499
3525
  },
3500
- addUserFormRevisionAttachment: (state, action) => {
3501
- const revisionId = action.payload.revision;
3502
- const revisionAttachments = state.revisionAttachments[revisionId];
3503
- if (revisionAttachments) {
3504
- revisionAttachments.push(action.payload);
3505
- } else {
3506
- state.revisionAttachments[revisionId] = [action.payload];
3526
+ deleteFormRevisions: (state, action) => {
3527
+ for (const offlineId of action.payload) {
3528
+ if (state.formRevisions[offlineId] === void 0) {
3529
+ throw new Error(`Revision with offline_id ${offlineId} does not exist`);
3530
+ }
3531
+ }
3532
+ for (const offlineId of action.payload) {
3533
+ delete state.formRevisions[offlineId];
3534
+ delete LATEST_FORM_REVISION_CACHE[offlineId];
3507
3535
  }
3508
3536
  },
3509
- setUserFormSubmissionAttachments: (state, action) => {
3510
- state.submissionAttachments = {};
3537
+ // attachment related actions
3538
+ setFormRevisionAttachments: (state, action) => {
3539
+ state.attachments = {};
3511
3540
  for (const attachment of action.payload) {
3512
- const submissionId = attachment.submission;
3513
- const submissionAttachments = state.submissionAttachments[submissionId];
3514
- if (submissionAttachments) {
3515
- submissionAttachments.push(attachment);
3516
- } else {
3517
- state.submissionAttachments[submissionId] = [attachment];
3518
- }
3541
+ state.attachments[attachment.offline_id] = attachment;
3519
3542
  }
3520
3543
  },
3521
- setUserFormRevisionAttachments: (state, action) => {
3522
- state.revisionAttachments = {};
3544
+ addFormRevisionAttachment: (state, action) => {
3545
+ if (state.attachments[action.payload.offline_id] !== void 0) {
3546
+ throw new Error(`Attachment with offline_id ${action.payload.offline_id} already exists`);
3547
+ }
3548
+ state.attachments[action.payload.offline_id] = action.payload;
3549
+ },
3550
+ addFormRevisionAttachments: (state, action) => {
3523
3551
  for (const attachment of action.payload) {
3524
- const revisionId = attachment.revision;
3525
- const revisionAttachments = state.revisionAttachments[revisionId];
3526
- if (revisionAttachments) {
3527
- revisionAttachments.push(attachment);
3528
- } else {
3529
- state.revisionAttachments[revisionId] = [attachment];
3552
+ if (state.attachments[attachment.offline_id] !== void 0) {
3553
+ throw new Error(`Attachment with offline_id ${attachment.offline_id} already exists`);
3530
3554
  }
3531
3555
  }
3556
+ for (const attachment of action.payload) {
3557
+ state.attachments[attachment.offline_id] = attachment;
3558
+ }
3532
3559
  },
3533
- deleteUserFormSubmission: (state, action) => {
3534
- delete state.submissions[action.payload];
3535
- },
3536
- deleteUserFormSubmissions: (state, action) => {
3537
- for (const userFormSubmission of action.payload) {
3538
- delete state.submissions[userFormSubmission.offline_id];
3560
+ deleteFormRevisionAttachment: (state, action) => {
3561
+ if (state.attachments[action.payload] === void 0) {
3562
+ throw new Error(`Attachment with offline_id ${action.payload} does not exist`);
3539
3563
  }
3564
+ delete state.attachments[action.payload];
3540
3565
  },
3541
- addUserFormSubmissions: (state, action) => {
3542
- for (const submission of action.payload) {
3543
- state.submissions[submission.offline_id] = submission;
3566
+ deleteFormRevisionAttachments: (state, action) => {
3567
+ for (const offlineId of action.payload) {
3568
+ if (state.attachments[offlineId] === void 0) {
3569
+ throw new Error(`Attachment with offline_id ${offlineId} does not exist`);
3570
+ }
3571
+ }
3572
+ for (const offlineId of action.payload) {
3573
+ delete state.attachments[offlineId];
3544
3574
  }
3575
+ }
3576
+ }
3577
+ });
3578
+ const {
3579
+ setFormRevision,
3580
+ setFormRevisions,
3581
+ addFormRevision,
3582
+ addFormRevisions,
3583
+ deleteFormRevision,
3584
+ deleteFormRevisions,
3585
+ setFormRevisionAttachments,
3586
+ addFormRevisionAttachment,
3587
+ addFormRevisionAttachments,
3588
+ deleteFormRevisionAttachment,
3589
+ deleteFormRevisionAttachments
3590
+ } = formRevisionsSlice.actions;
3591
+ const selectFormRevisionMapping = (state) => state.formRevisionReducer.formRevisions;
3592
+ const selectFormRevisions = createSelector(
3593
+ [selectFormRevisionMapping],
3594
+ (formRevisions) => Object.values(formRevisions)
3595
+ );
3596
+ const selectFormRevision = (formRevisionId) => (state) => {
3597
+ return state.formRevisionReducer.formRevisions[formRevisionId];
3598
+ };
3599
+ const _selectLatestFormRevision = (formRevisions, formId2) => {
3600
+ let ret = null;
3601
+ for (const candidate of Object.values(formRevisions)) {
3602
+ if (candidate.form === formId2 && (!ret || ret.revision < candidate.revision)) {
3603
+ ret = candidate;
3604
+ }
3605
+ }
3606
+ if (!ret) {
3607
+ throw new Error("No form revision found for form " + formId2);
3608
+ }
3609
+ return ret;
3610
+ };
3611
+ const selectLatestFormRevisionOfForm = restructureCreateSelectorWithArgs(
3612
+ createSelector([selectFormRevisionMapping, (_state, formId2) => formId2], (revisions, formId2) => {
3613
+ if (!formId2) {
3614
+ throw new Error("formId is required");
3615
+ }
3616
+ return _selectLatestFormRevision(revisions, formId2);
3617
+ })
3618
+ );
3619
+ const selectFormRevisionsOfForm = restructureCreateSelectorWithArgs(
3620
+ createSelector([selectFormRevisions, (_state, formId2) => formId2], (revisions, formId2) => {
3621
+ return revisions.filter((revision) => {
3622
+ return revision.form === formId2;
3623
+ });
3624
+ })
3625
+ );
3626
+ const selectLatestFormRevisionsOfComponentTypes = restructureCreateSelectorWithArgs(
3627
+ createSelector(
3628
+ [
3629
+ (state) => state.formReducer.forms,
3630
+ selectFormRevisionMapping,
3631
+ (_state, componentTypeIds) => componentTypeIds
3632
+ ],
3633
+ (userForms, revisions, componentTypeIds) => {
3634
+ const componentTypeIdsSet = new Set(componentTypeIds);
3635
+ const ret = {};
3636
+ for (const form of Object.values(userForms)) {
3637
+ if (form.component_type && componentTypeIdsSet.has(form.component_type)) {
3638
+ ret[form.component_type] = _selectLatestFormRevision(revisions, form.offline_id);
3639
+ }
3640
+ }
3641
+ return ret;
3642
+ }
3643
+ )
3644
+ );
3645
+ const selectLatestFormRevisionByForm = createSelector([selectFormRevisionMapping], (revisions) => {
3646
+ const latestRevisions = {};
3647
+ for (const revision of Object.values(revisions)) {
3648
+ const formId2 = revision.form;
3649
+ const currentLatestRevision = latestRevisions[formId2];
3650
+ if (!currentLatestRevision || currentLatestRevision.revision < revision.revision) {
3651
+ latestRevisions[formId2] = revision;
3652
+ }
3653
+ }
3654
+ return latestRevisions;
3655
+ });
3656
+ const selectUserFormRevisionAttachmentsMapping = (state) => {
3657
+ return state.formRevisionReducer.attachments;
3658
+ };
3659
+ const selectAttachmentsOfFormRevision = restructureCreateSelectorWithArgs(
3660
+ createSelector(
3661
+ [selectUserFormRevisionAttachmentsMapping, (_state, revisionId) => revisionId],
3662
+ (attachments, revisionId) => {
3663
+ return Object.values(attachments).filter((attachment) => attachment.revision === revisionId);
3664
+ }
3665
+ )
3666
+ );
3667
+ const formRevisionReducer = formRevisionsSlice.reducer;
3668
+ const initialState$4 = {
3669
+ forms: {}
3670
+ };
3671
+ const formSlice = createSlice({
3672
+ name: "forms",
3673
+ initialState: initialState$4,
3674
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$4)),
3675
+ reducers: {
3676
+ setForms: (state, action) => {
3677
+ state.forms = {};
3678
+ action.payload.forEach((userForm) => {
3679
+ state.forms[userForm.offline_id] = userForm;
3680
+ });
3681
+ },
3682
+ addForm: (state, action) => {
3683
+ state.forms[action.payload.offline_id] = action.payload;
3545
3684
  },
3546
- setUserFormSubmissions: (state, action) => {
3547
- state.submissions = {};
3548
- action.payload.forEach((submission) => {
3549
- state.submissions[submission.offline_id] = submission;
3685
+ addForms: (state, action) => {
3686
+ action.payload.forEach((userForm) => {
3687
+ state.forms[userForm.offline_id] = userForm;
3550
3688
  });
3551
3689
  },
3552
3690
  favoriteForm: (state, action) => {
3553
3691
  const { formId: formId2 } = action.payload;
3554
- const form = state.userForms[formId2];
3692
+ const form = state.forms[formId2];
3555
3693
  if (!form) {
3556
3694
  throw new Error("No form exists with the id " + formId2);
3557
3695
  }
@@ -3559,48 +3697,23 @@ const userFormSlice = createSlice({
3559
3697
  },
3560
3698
  unfavoriteForm: (state, action) => {
3561
3699
  const { formId: formId2 } = action.payload;
3562
- const form = state.userForms[formId2];
3700
+ const form = state.forms[formId2];
3563
3701
  if (!form) {
3564
3702
  throw new Error("No form exists with the id " + formId2);
3565
3703
  }
3566
3704
  form.favorite = false;
3567
3705
  },
3568
- deleteUserForm: (state, action) => {
3569
- delete state.userForms[action.payload];
3706
+ deleteForm: (state, action) => {
3707
+ delete state.forms[action.payload];
3570
3708
  }
3571
3709
  }
3572
3710
  });
3573
- const {
3574
- addUserForm,
3575
- addUserForms,
3576
- addUserFormRevisions,
3577
- updateOrCreateUserFormSubmission,
3578
- addUserFormSubmissions,
3579
- deleteUserFormSubmission,
3580
- deleteUserFormSubmissions,
3581
- favoriteForm,
3582
- unfavoriteForm,
3583
- deleteUserForm,
3584
- deleteUserFormRevision,
3585
- deleteUserFormRevisions,
3586
- setUserFormSubmissions,
3587
- addUserFormRevision,
3588
- addUserFormSubmissionAttachment,
3589
- addUserFormRevisionAttachment,
3590
- setUserFormSubmissionAttachments,
3591
- setUserFormRevisionAttachments
3592
- } = userFormSlice.actions;
3593
- const selectSubmissionAttachments = (submissionId) => (state) => {
3594
- return state.userFormReducer.submissionAttachments[submissionId] || [];
3595
- };
3596
- const selectRevisionAttachments = (revisionId) => (state) => {
3597
- return state.userFormReducer.revisionAttachments[revisionId] || [];
3598
- };
3599
- const selectFilteredUserForms = restructureCreateSelectorWithArgs(
3711
+ const { setForms, addForm, addForms, favoriteForm, unfavoriteForm, deleteForm } = formSlice.actions;
3712
+ const selectFilteredForms = restructureCreateSelectorWithArgs(
3600
3713
  createSelector(
3601
3714
  [
3602
- (state) => state.userFormReducer.userForms,
3603
- (state) => state.userFormReducer.revisions,
3715
+ (state) => state.formReducer.forms,
3716
+ (state) => state.formRevisionReducer.formRevisions,
3604
3717
  (_state, search) => search
3605
3718
  ],
3606
3719
  (userForms, revisions, search) => {
@@ -3634,63 +3747,188 @@ const selectFilteredUserForms = restructureCreateSelectorWithArgs(
3634
3747
  { memoizeOptions: { equalityCheck: shallowEqual$1 } }
3635
3748
  )
3636
3749
  );
3637
- const selectFormRevision = (revisionId) => (state) => {
3638
- return state.userFormReducer.revisions[revisionId];
3750
+ const selectForm = (formId2) => (state) => {
3751
+ return state.formReducer.forms[formId2];
3639
3752
  };
3640
- const _selectLatestFormRevision = (revisions, formId2) => {
3641
- let ret = null;
3642
- for (const candidate of Object.values(revisions)) {
3643
- if (candidate.form === formId2 && (!ret || ret.revision < candidate.revision)) {
3644
- ret = candidate;
3645
- }
3646
- }
3647
- if (!ret) {
3648
- throw new Error("No revision found for form " + formId2);
3649
- }
3650
- return ret;
3753
+ const selectFormMapping = (state) => {
3754
+ return state.formReducer.forms;
3651
3755
  };
3652
- const selectLatestFormRevision = restructureCreateSelectorWithArgs(
3756
+ const selectFormOfComponentType = restructureCreateSelectorWithArgs(
3653
3757
  createSelector(
3654
- [(state) => state.userFormReducer.revisions, (_state, formId2) => formId2],
3655
- (revisions, formId2) => {
3656
- if (!formId2) {
3657
- throw new Error("formId is required");
3658
- }
3659
- return _selectLatestFormRevision(revisions, formId2);
3758
+ [selectFormMapping, (_state, componentTypeId) => componentTypeId],
3759
+ (userForms, componentTypeId) => {
3760
+ return Object.values(userForms).find((userForm) => userForm.component_type === componentTypeId);
3660
3761
  }
3661
3762
  )
3662
3763
  );
3663
- const selectUserForm = (formId2) => (state) => {
3664
- return state.userFormReducer.userForms[formId2];
3665
- };
3666
- const selectSubmissionMapping = (state) => state.userFormReducer.submissions;
3667
- const selectUserFormSubmission = (submissionId) => (state) => {
3668
- return state.userFormReducer.submissions[submissionId];
3669
- };
3670
- const selectSubmissions = createSelector([selectSubmissionMapping], (submissions) => Object.values(submissions));
3671
- const selectRevisionMapping = (state) => state.userFormReducer.revisions;
3672
- const selectRevisions = createSelector([selectRevisionMapping], (revisions) => Object.values(revisions));
3673
- const selectRevisionsForForm = restructureCreateSelectorWithArgs(
3674
- createSelector([selectRevisions, (_state, formId2) => formId2], (revisions, formId2) => {
3675
- return revisions.filter((revision) => {
3676
- return revision.form === formId2;
3677
- });
3678
- })
3764
+ const selectFormsCount = createSelector([selectFormMapping], (userForms) => {
3765
+ return Object.keys(userForms).length;
3766
+ });
3767
+ const selectGeneralFormCount = createSelector([selectFormMapping], (userForms) => {
3768
+ return Object.values(userForms).filter((form) => !form.component_type).length;
3769
+ });
3770
+ const formReducer = formSlice.reducer;
3771
+ const initialState$3 = {
3772
+ formSubmissions: {},
3773
+ attachments: {}
3774
+ };
3775
+ const formSubmissionSlice = createSlice({
3776
+ name: "formSubmissions",
3777
+ initialState: initialState$3,
3778
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$3)),
3779
+ reducers: {
3780
+ setFormSubmission: (state, action) => {
3781
+ state.formSubmissions[action.payload.offline_id] = action.payload;
3782
+ },
3783
+ setFormSubmissions: (state, action) => {
3784
+ state.formSubmissions = {};
3785
+ for (const submission of action.payload) {
3786
+ state.formSubmissions[submission.offline_id] = submission;
3787
+ }
3788
+ },
3789
+ addFormSubmission: (state, action) => {
3790
+ if (state.formSubmissions[action.payload.offline_id] !== void 0) {
3791
+ throw new Error(`Submission with offline_id ${action.payload.offline_id} already exists`);
3792
+ }
3793
+ state.formSubmissions[action.payload.offline_id] = action.payload;
3794
+ },
3795
+ addFormSubmissions: (state, action) => {
3796
+ for (const submission of action.payload) {
3797
+ if (state.formSubmissions[submission.offline_id] !== void 0) {
3798
+ throw new Error(`Submission with offline_id ${submission.offline_id} already exists`);
3799
+ }
3800
+ }
3801
+ for (const submission of action.payload) {
3802
+ state.formSubmissions[submission.offline_id] = submission;
3803
+ }
3804
+ },
3805
+ updateFormSubmission: (state, action) => {
3806
+ if (state.formSubmissions[action.payload.offline_id] === void 0) {
3807
+ throw new Error(`Submission with offline_id ${action.payload.offline_id} does not exist`);
3808
+ }
3809
+ state.formSubmissions[action.payload.offline_id] = action.payload;
3810
+ },
3811
+ updateFormSubmissions: (state, action) => {
3812
+ for (const submission of action.payload) {
3813
+ if (state.formSubmissions[submission.offline_id] === void 0) {
3814
+ throw new Error(`Submission with offline_id ${submission.offline_id} does not exist`);
3815
+ }
3816
+ }
3817
+ for (const submission of action.payload) {
3818
+ state.formSubmissions[submission.offline_id] = submission;
3819
+ }
3820
+ },
3821
+ deleteFormSubmission: (state, action) => {
3822
+ if (state.formSubmissions[action.payload] === void 0) {
3823
+ throw new Error(`Submission with offline_id ${action.payload} does not exist`);
3824
+ }
3825
+ delete state.formSubmissions[action.payload];
3826
+ },
3827
+ deleteFormSubmissions: (state, action) => {
3828
+ for (const offlineId of action.payload) {
3829
+ if (state.formSubmissions[offlineId] === void 0) {
3830
+ throw new Error(`Submission with offline_id ${offlineId} does not exist`);
3831
+ }
3832
+ delete state.formSubmissions[offlineId];
3833
+ }
3834
+ for (const offlineId of action.payload) {
3835
+ delete state.formSubmissions[offlineId];
3836
+ }
3837
+ },
3838
+ // Attachments
3839
+ addFormSubmissionAttachment: (state, action) => {
3840
+ if (state.attachments[action.payload.offline_id] !== void 0) {
3841
+ throw new Error(`Attachment with offline_id ${action.payload.offline_id} already exists`);
3842
+ }
3843
+ state.attachments[action.payload.offline_id] = action.payload;
3844
+ },
3845
+ addFormSubmissionAttachments: (state, action) => {
3846
+ for (const attachment of action.payload) {
3847
+ if (state.attachments[attachment.offline_id] !== void 0) {
3848
+ throw new Error(`Attachment with offline_id ${attachment.offline_id} already exists`);
3849
+ }
3850
+ }
3851
+ for (const attachment of action.payload) {
3852
+ state.attachments[attachment.offline_id] = attachment;
3853
+ }
3854
+ },
3855
+ // We only need a multi set for attachments because they are not updated, only added and deleted
3856
+ setFormSubmissionAttachments: (state, action) => {
3857
+ state.attachments = {};
3858
+ for (const attachment of action.payload) {
3859
+ state.attachments[attachment.offline_id] = attachment;
3860
+ }
3861
+ },
3862
+ updateFormSubmissionAttachments: (state, action) => {
3863
+ for (const attachment of action.payload) {
3864
+ if (state.attachments[attachment.offline_id] === void 0) {
3865
+ throw new Error(`Attachment with offline_id ${attachment.offline_id} does not exist`);
3866
+ }
3867
+ }
3868
+ for (const attachment of action.payload) {
3869
+ state.attachments[attachment.offline_id] = attachment;
3870
+ }
3871
+ },
3872
+ // The delete actions for UserFormSubmissionAttachments are not used in the app, but are included for completeness
3873
+ // Could be used if editing a submission is ever supported, will be applicable for supporting tip tap content in submissions
3874
+ deleteFormSubmissionAttachment: (state, action) => {
3875
+ if (state.attachments[action.payload] === void 0) {
3876
+ throw new Error(`Attachment with offline_id ${action.payload} does not exist`);
3877
+ }
3878
+ delete state.attachments[action.payload];
3879
+ },
3880
+ deleteFormSubmissionAttachments: (state, action) => {
3881
+ for (const offlineId of action.payload) {
3882
+ if (state.attachments[offlineId] === void 0) {
3883
+ throw new Error(`Attachment with offline_id ${offlineId} does not exist`);
3884
+ }
3885
+ delete state.attachments[offlineId];
3886
+ }
3887
+ }
3888
+ }
3889
+ });
3890
+ const {
3891
+ setFormSubmission,
3892
+ setFormSubmissions,
3893
+ addFormSubmission,
3894
+ addFormSubmissions,
3895
+ updateFormSubmission,
3896
+ updateFormSubmissions,
3897
+ deleteFormSubmission,
3898
+ deleteFormSubmissions,
3899
+ addFormSubmissionAttachment,
3900
+ addFormSubmissionAttachments,
3901
+ setFormSubmissionAttachments,
3902
+ updateFormSubmissionAttachments,
3903
+ deleteFormSubmissionAttachment,
3904
+ deleteFormSubmissionAttachments
3905
+ } = formSubmissionSlice.actions;
3906
+ const selectFormSubmissionsMapping = (state) => {
3907
+ return state.formSubmissionReducer.formSubmissions;
3908
+ };
3909
+ const selectFormSubmissions = createSelector(
3910
+ [selectFormSubmissionsMapping],
3911
+ (submissions) => {
3912
+ return Object.values(submissions);
3913
+ }
3679
3914
  );
3680
- const selectSubmissionsForForm = restructureCreateSelectorWithArgs(
3915
+ const selectFormSubmission = (submissionId) => (state) => {
3916
+ return state.formSubmissionReducer.formSubmissions[submissionId];
3917
+ };
3918
+ const selectFormSubmissionsOfForm = restructureCreateSelectorWithArgs(
3681
3919
  createSelector(
3682
- [selectSubmissions, selectRevisionMapping, (_state, formId2) => formId2],
3920
+ [selectFormSubmissions, selectFormRevisionMapping, (_state, formId2) => formId2],
3683
3921
  (submissions, revisionMapping, formId2) => {
3684
- return Object.values(submissions).filter((submission) => {
3922
+ return submissions.filter((submission) => {
3685
3923
  const revision = revisionMapping[submission.form_revision];
3686
3924
  return (revision == null ? void 0 : revision.form) === formId2;
3687
3925
  });
3688
3926
  }
3689
3927
  )
3690
3928
  );
3691
- const selectSubmissionsForIssue = restructureCreateSelectorWithArgs(
3929
+ const selectFormSubmissionsOfIssue = restructureCreateSelectorWithArgs(
3692
3930
  createSelector(
3693
- [(state) => state.userFormReducer.submissions, (_state, issueId) => issueId],
3931
+ [selectFormSubmissions, (_state, issueId) => issueId],
3694
3932
  (submissions, issueId) => {
3695
3933
  return Object.values(submissions).filter((submission) => {
3696
3934
  return submission.issue === issueId;
@@ -3698,9 +3936,9 @@ const selectSubmissionsForIssue = restructureCreateSelectorWithArgs(
3698
3936
  }
3699
3937
  )
3700
3938
  );
3701
- const selectSubmissionsForComponent = restructureCreateSelectorWithArgs(
3939
+ const selectFormSubmissionsOfComponent = restructureCreateSelectorWithArgs(
3702
3940
  createSelector(
3703
- [selectSubmissions, (_state, componentId) => componentId],
3941
+ [selectFormSubmissions, (_state, componentId) => componentId],
3704
3942
  (submissions, componentId) => {
3705
3943
  return submissions.filter((submission) => {
3706
3944
  return submission.component === componentId;
@@ -3708,8 +3946,8 @@ const selectSubmissionsForComponent = restructureCreateSelectorWithArgs(
3708
3946
  }
3709
3947
  )
3710
3948
  );
3711
- const selectComponentSubmissionMapping = createSelector(
3712
- [selectSubmissionMapping, selectComponentsMapping],
3949
+ const selectFormSubmissionsByComponents = createSelector(
3950
+ [selectFormSubmissionsMapping, selectComponentsMapping],
3713
3951
  (submissions, components) => {
3714
3952
  var _a2;
3715
3953
  const componentSubmissionMapping = {};
@@ -3725,54 +3963,18 @@ const selectComponentSubmissionMapping = createSelector(
3725
3963
  return componentSubmissionMapping;
3726
3964
  }
3727
3965
  );
3728
- const selectUserFormMapping = (state) => {
3729
- return state.userFormReducer.userForms;
3966
+ const selectFormSubmissionAttachmentsMapping = (state) => {
3967
+ return state.formSubmissionReducer.attachments;
3730
3968
  };
3731
- const selectComponentTypeForm = restructureCreateSelectorWithArgs(
3732
- createSelector(
3733
- [selectUserFormMapping, (_state, componentTypeId) => componentTypeId],
3734
- (userForms, componentTypeId) => {
3735
- return Object.values(userForms).find((userForm) => userForm.component_type === componentTypeId);
3736
- }
3737
- )
3738
- );
3739
- const selectLatestRevisionsFromComponentTypeIds = restructureCreateSelectorWithArgs(
3969
+ const selectAttachmentsOfFormSubmission = restructureCreateSelectorWithArgs(
3740
3970
  createSelector(
3741
- [
3742
- selectUserFormMapping,
3743
- selectRevisionMapping,
3744
- (_state, componentTypeIds) => componentTypeIds
3745
- ],
3746
- (userForms, revisions, componentTypeIds) => {
3747
- const componentTypeIdsSet = new Set(componentTypeIds);
3748
- const ret = {};
3749
- for (const form of Object.values(userForms)) {
3750
- if (form.component_type && componentTypeIdsSet.has(form.component_type)) {
3751
- ret[form.component_type] = _selectLatestFormRevision(revisions, form.offline_id);
3752
- }
3753
- }
3754
- return ret;
3971
+ [selectFormSubmissionAttachmentsMapping, (_state, submissionId) => submissionId],
3972
+ (attachmentsMapping, submissionId) => {
3973
+ return Object.values(attachmentsMapping).filter((attachment) => attachment.submission === submissionId);
3755
3974
  }
3756
3975
  )
3757
3976
  );
3758
- const selectLatestRevisionByFormId = createSelector([selectRevisionMapping], (revisions) => {
3759
- const latestRevisions = {};
3760
- for (const revision of Object.values(revisions)) {
3761
- const formId2 = revision.form;
3762
- const currentLatestRevision = latestRevisions[formId2];
3763
- if (!currentLatestRevision || currentLatestRevision.revision < revision.revision) {
3764
- latestRevisions[formId2] = revision;
3765
- }
3766
- }
3767
- return latestRevisions;
3768
- });
3769
- const selectNumberOfUserForms = createSelector([selectUserFormMapping], (userForms) => {
3770
- return Object.keys(userForms).length;
3771
- });
3772
- const selectNumberOfGeneralUserForms = createSelector([selectUserFormMapping], (userForms) => {
3773
- return Object.values(userForms).filter((form) => !form.component_type).length;
3774
- });
3775
- const userFormReducer = userFormSlice.reducer;
3977
+ const formSubmissionReducer = formSubmissionSlice.reducer;
3776
3978
  const initialState$2 = {
3777
3979
  emailDomains: {}
3778
3980
  };
@@ -3806,7 +4008,8 @@ const selectSortedEmailDomains = (state) => Object.values(state.emailDomainsRedu
3806
4008
  );
3807
4009
  const emailDomainsReducer = emailDomainsSlice.reducer;
3808
4010
  const initialState$1 = {
3809
- documents: {}
4011
+ documents: {},
4012
+ attachments: {}
3810
4013
  };
3811
4014
  const documentSlice = createSlice({
3812
4015
  name: "documents",
@@ -3943,10 +4146,28 @@ const documentSlice = createSlice({
3943
4146
  }
3944
4147
  delete state.documents[documentId];
3945
4148
  }
3946
- }
4149
+ },
4150
+ setDocumentAttachments: setAttachments,
4151
+ addDocumentAttachment: addAttachment,
4152
+ addDocumentAttachments: addAttachments,
4153
+ updateDocumentAttachment: updateAttachment,
4154
+ removeDocumentAttachment: removeAttachment,
4155
+ removeDocumentAttachments: removeAttachments
3947
4156
  }
3948
4157
  });
3949
- const { setDocuments, addDocuments, updateDocuments, moveDocument, removeDocuments } = documentSlice.actions;
4158
+ const {
4159
+ setDocuments,
4160
+ addDocuments,
4161
+ updateDocuments,
4162
+ moveDocument,
4163
+ removeDocuments,
4164
+ setDocumentAttachments,
4165
+ addDocumentAttachment,
4166
+ addDocumentAttachments,
4167
+ updateDocumentAttachment,
4168
+ removeDocumentAttachment,
4169
+ removeDocumentAttachments
4170
+ } = documentSlice.actions;
3950
4171
  const selectDocumentsMapping = (state) => state.documentsReducer.documents;
3951
4172
  const selectDocuments = createSelector(
3952
4173
  [selectDocumentsMapping],
@@ -3976,6 +4197,39 @@ const selectRootDocuments = createSelector(
3976
4197
  [selectDocuments],
3977
4198
  (documents) => documents.filter((document2) => !document2.parent_document)
3978
4199
  );
4200
+ const selectDocumentAttachmentMapping = (state) => state.documentsReducer.attachments;
4201
+ const selectAllDocumentAttachments = createSelector(
4202
+ [selectDocumentAttachmentMapping],
4203
+ (mapping) => Object.values(mapping)
4204
+ );
4205
+ const selectDocumentAttachment = (attachmentId) => (state) => {
4206
+ return state.documentsReducer.attachments[attachmentId];
4207
+ };
4208
+ const selectAttachmentsOfDocument = restructureCreateSelectorWithArgs(
4209
+ createSelector(
4210
+ [selectAllDocumentAttachments, (_state, documentId) => documentId],
4211
+ (attachments, documentId) => {
4212
+ return attachments.filter(({ document: document2 }) => documentId === document2);
4213
+ }
4214
+ )
4215
+ );
4216
+ const selectAttachmentsOfDocumentByType = restructureCreateSelectorWithArgs(
4217
+ createSelector(
4218
+ [selectAllDocumentAttachments, (_state, documentId) => documentId],
4219
+ (attachments, documentId) => {
4220
+ const attachmentsOfProject = attachments.filter(({ document: document2 }) => documentId === document2);
4221
+ const fileAttachments = attachmentsOfProject.filter(
4222
+ // this null check here is necessary, there are cases where file_type is null or undefined
4223
+ ({ file_type }) => !file_type || !file_type.startsWith("image/")
4224
+ );
4225
+ const imageAttachments = attachmentsOfProject.filter(
4226
+ // this null check here is necessary, there are cases where file_type is null or undefined
4227
+ ({ file_type }) => file_type && file_type.startsWith("image/")
4228
+ );
4229
+ return { fileAttachments, imageAttachments };
4230
+ }
4231
+ )
4232
+ );
3979
4233
  const documentsReducer = documentSlice.reducer;
3980
4234
  const initialState = {
3981
4235
  version: 0
@@ -4018,7 +4272,9 @@ const overmapReducers = {
4018
4272
  projectFileReducer,
4019
4273
  rehydratedReducer,
4020
4274
  settingReducer,
4021
- userFormReducer,
4275
+ formReducer,
4276
+ formRevisionReducer,
4277
+ formSubmissionReducer,
4022
4278
  userReducer,
4023
4279
  workspaceReducer,
4024
4280
  emailDomainsReducer,
@@ -4071,9 +4327,7 @@ function handleWorkspaceRemoval(draft, action) {
4071
4327
  throw new Error(`Failed to update index_workspace of issue ${issue.offline_id} to main workspace`);
4072
4328
  }
4073
4329
  }
4074
- const indexedForms = Object.values(draft.userFormReducer.userForms).filter(
4075
- (form) => form.index_workspace === workspaceId
4076
- );
4330
+ const indexedForms = Object.values(draft.formReducer.forms).filter((form) => form.index_workspace === workspaceId);
4077
4331
  for (const form of indexedForms) {
4078
4332
  form.index_workspace = mainWorkspace.offline_id;
4079
4333
  }
@@ -4551,6 +4805,22 @@ class BaseApiService {
4551
4805
  }
4552
4806
  }
4553
4807
  class AttachmentService extends BaseApiService {
4808
+ processPresignedUrls(presignedUrls) {
4809
+ for (const [sha1, presignedUrl] of Object.entries(presignedUrls)) {
4810
+ void this.enqueueRequest({
4811
+ url: presignedUrl.url,
4812
+ description: "Upload file",
4813
+ method: HttpMethod.POST,
4814
+ isExternalUrl: true,
4815
+ isAuthNeeded: false,
4816
+ attachmentHash: sha1,
4817
+ // TODO: can we use the sha1 as the blocker?
4818
+ blockers: [`s3-${sha1}`],
4819
+ blocks: [sha1],
4820
+ s3url: presignedUrl
4821
+ });
4822
+ }
4823
+ }
4554
4824
  fetchAll(projectId) {
4555
4825
  const promise = this.enqueueRequest({
4556
4826
  description: "Fetch attachments",
@@ -4564,7 +4834,8 @@ class AttachmentService extends BaseApiService {
4564
4834
  issue_attachments: Object.values(state.issueReducer.attachments),
4565
4835
  component_attachments: Object.values(state.componentReducer.attachments),
4566
4836
  component_type_attachments: Object.values(state.componentTypeReducer.attachments),
4567
- project_attachments: Object.values(state.projectReducer.attachments)
4837
+ project_attachments: Object.values(state.projectReducer.attachments),
4838
+ document_attachments: Object.values(state.documentsReducer.attachments)
4568
4839
  };
4569
4840
  return [allAttachments, promise];
4570
4841
  }
@@ -4576,6 +4847,7 @@ class AttachmentService extends BaseApiService {
4576
4847
  }
4577
4848
  const offlineAttachment = {
4578
4849
  ...attachmentPayload,
4850
+ // TODO: just handle creating the objectURL in here, then the front end doesn't need to worry about it
4579
4851
  file: attachmentPayload.file.objectURL,
4580
4852
  file_name: attachmentPayload.file.name,
4581
4853
  file_type: attachmentPayload.file.type,
@@ -4609,6 +4881,7 @@ class AttachmentService extends BaseApiService {
4609
4881
  }
4610
4882
  const offlineAttachment = {
4611
4883
  ...attachmentPayload,
4884
+ // TODO: just handle creating the objectURL in here, then the front end doesn't need to worry about it
4612
4885
  file: attachmentPayload.file.objectURL,
4613
4886
  file_name: attachmentPayload.file.name,
4614
4887
  file_type: attachmentPayload.file.type,
@@ -4642,6 +4915,7 @@ class AttachmentService extends BaseApiService {
4642
4915
  }
4643
4916
  const offlineAttachment = {
4644
4917
  ...attachmentPayload,
4918
+ // TODO: just handle creating the objectURL in here, then the front end doesn't need to worry about it
4645
4919
  file: attachmentPayload.file.objectURL,
4646
4920
  file_name: attachmentPayload.file.name,
4647
4921
  file_type: attachmentPayload.file.type,
@@ -4668,8 +4942,8 @@ class AttachmentService extends BaseApiService {
4668
4942
  });
4669
4943
  return [offlineAttachment, promise];
4670
4944
  }
4671
- async addProjectAttachment(attachmentPayload) {
4672
- const { description: description2, project, file_sha1, offline_id } = attachmentPayload;
4945
+ async addDocumentAttachment(attachmentPayload) {
4946
+ const { description: description2, document: document2, file_sha1, offline_id } = attachmentPayload;
4673
4947
  if (!attachmentPayload.file.objectURL) {
4674
4948
  throw new Error("Expected attachmentPayload.file.objectURL to be defined.");
4675
4949
  }
@@ -4682,24 +4956,24 @@ class AttachmentService extends BaseApiService {
4682
4956
  created_by: this.client.store.getState().userReducer.currentUser.id
4683
4957
  };
4684
4958
  await this.client.files.addCache(attachmentPayload.file, file_sha1);
4685
- this.client.store.dispatch(addProjectAttachment(offlineAttachment));
4959
+ this.client.store.dispatch(addDocumentAttachment(offlineAttachment));
4686
4960
  const [fileProps] = await this.client.files.uploadFileToS3(file_sha1);
4687
4961
  const promise = this.enqueueRequest({
4688
4962
  description: "Create attachment",
4689
4963
  method: HttpMethod.POST,
4690
- url: `/projects/${project}/attach/`,
4691
- blocks: [offline_id, project.toString()],
4964
+ url: `/documents/${document2}/attach/`,
4965
+ blocks: [offline_id, document2],
4692
4966
  blockers: [file_sha1],
4693
4967
  payload: {
4694
4968
  offline_id,
4695
- project,
4969
+ document: document2,
4696
4970
  description: description2 ?? "",
4697
4971
  submitted_at: (/* @__PURE__ */ new Date()).toISOString(),
4698
4972
  ...fileProps
4699
4973
  }
4700
4974
  });
4701
4975
  promise.catch((error2) => {
4702
- this.client.store.dispatch(removeProjectAttachment(offlineAttachment.offline_id));
4976
+ this.client.store.dispatch(removeDocumentAttachment(offlineAttachment.offline_id));
4703
4977
  throw error2;
4704
4978
  });
4705
4979
  return [offlineAttachment, promise];
@@ -4707,26 +4981,54 @@ class AttachmentService extends BaseApiService {
4707
4981
  /** the outer Promise is needed to await the hashing of each file, which is required before offline use. If wanting to
4708
4982
  * attach promise handlers to the request to add the attachment in the backend, apply it on the promise returned from the
4709
4983
  * OptimisticModelResult. */
4710
- attachFilesToIssue(filesToSubmit, issueId) {
4711
- return filesToSubmit.map((file) => {
4712
- if (!(file instanceof File)) {
4713
- throw new Error("Expected a File instance.");
4714
- }
4715
- const photoAttachmentPromise = async (file2) => {
4716
- const hash = await hashFile(file2);
4717
- const attachment = offline({
4718
- file: file2,
4719
- file_name: file2.name,
4720
- file_type: file2.type,
4721
- issue: issueId,
4722
- file_sha1: hash,
4723
- submitted_at: (/* @__PURE__ */ new Date()).toISOString(),
4724
- created_by: this.client.store.getState().userReducer.currentUser.id
4725
- });
4726
- return this.addIssueAttachment(attachment);
4727
- };
4728
- return photoAttachmentPromise(file);
4984
+ // note the method is only marked as async since files needs to be hashed
4985
+ async attachFilesToIssue(files, issueId) {
4986
+ const { store } = this.client;
4987
+ const offlineAttachments = [];
4988
+ const attachmentsPayload = [];
4989
+ const currentUser = store.getState().userReducer.currentUser;
4990
+ const submittedAt = (/* @__PURE__ */ new Date()).toISOString();
4991
+ for (const file of files) {
4992
+ const attachment = offline({
4993
+ file: URL.createObjectURL(file),
4994
+ file_name: file.name,
4995
+ file_type: file.type,
4996
+ file_sha1: await hashFile(file),
4997
+ description: "",
4998
+ submitted_at: submittedAt,
4999
+ created_by: currentUser.id,
5000
+ issue: issueId
5001
+ });
5002
+ attachmentsPayload.push({
5003
+ offline_id: attachment.offline_id,
5004
+ name: attachment.file_name,
5005
+ sha1: attachment.file_sha1,
5006
+ description: attachment.description,
5007
+ created_by: attachment.created_by,
5008
+ issue_id: attachment.issue
5009
+ });
5010
+ offlineAttachments.push(attachment);
5011
+ }
5012
+ store.dispatch(addIssueAttachments(offlineAttachments));
5013
+ const promise = this.enqueueRequest({
5014
+ description: "Attach files to issue",
5015
+ method: HttpMethod.POST,
5016
+ url: `/issues/${issueId}/attach/`,
5017
+ payload: {
5018
+ submitted_at: submittedAt,
5019
+ attachments: attachmentsPayload,
5020
+ files: await constructUploadedFilePayloads(files)
5021
+ },
5022
+ blocks: offlineAttachments.map((attachment) => attachment.offline_id),
5023
+ blockers: offlineAttachments.map((attachment) => attachment.file_sha1)
5024
+ });
5025
+ promise.then(({ attachments, presigned_urls }) => {
5026
+ store.dispatch(updateIssueAttachments(attachments));
5027
+ this.processPresignedUrls(presigned_urls);
5028
+ }).catch(() => {
5029
+ store.dispatch(removeIssueAttachments(offlineAttachments.map((attachment) => attachment.offline_id)));
4729
5030
  });
5031
+ return [offlineAttachments, promise.then(({ attachments }) => attachments)];
4730
5032
  }
4731
5033
  attachFilesToComponent(filesToSubmit, componentId) {
4732
5034
  return filesToSubmit.map((file) => {
@@ -4770,7 +5072,7 @@ class AttachmentService extends BaseApiService {
4770
5072
  return photoAttachmentPromise(file);
4771
5073
  });
4772
5074
  }
4773
- attachFilesToProject(filesToSubmit, projectId) {
5075
+ attachFilesToDocument(filesToSubmit, documentId) {
4774
5076
  return filesToSubmit.map((file) => {
4775
5077
  if (!(file instanceof File)) {
4776
5078
  throw new Error("Expected a File instance.");
@@ -4781,12 +5083,12 @@ class AttachmentService extends BaseApiService {
4781
5083
  file: file2,
4782
5084
  file_name: file2.name,
4783
5085
  file_type: file2.type,
4784
- project: projectId,
5086
+ document: documentId,
4785
5087
  file_sha1: hash,
4786
5088
  submitted_at: (/* @__PURE__ */ new Date()).toISOString(),
4787
5089
  created_by: this.client.store.getState().userReducer.currentUser.id
4788
5090
  });
4789
- return this.addProjectAttachment(attachment);
5091
+ return this.addDocumentAttachment(attachment);
4790
5092
  };
4791
5093
  return photoAttachmentPromise(file);
4792
5094
  });
@@ -4966,9 +5268,9 @@ class AttachmentService extends BaseApiService {
4966
5268
  const promise = performRequest2();
4967
5269
  return [offlineAttachment, promise];
4968
5270
  }
4969
- async replaceProjectAttachmentFile(attachmentId, newFile) {
5271
+ async replaceDocumentAttachmentFile(attachmentId, newFile) {
4970
5272
  const { store } = this.client;
4971
- const attachment = store.getState().projectReducer.attachments[attachmentId];
5273
+ const attachment = store.getState().documentsReducer.attachments[attachmentId];
4972
5274
  if (!attachment)
4973
5275
  throw new Error(`Attachment ${attachmentId} not found`);
4974
5276
  let oldFile = void 0;
@@ -4982,7 +5284,7 @@ class AttachmentService extends BaseApiService {
4982
5284
  throw new Error(`newFile["objectURL"] is unexpectedly ${newFile.objectURL}`);
4983
5285
  }
4984
5286
  store.dispatch(
4985
- updateProjectAttachment({
5287
+ updateDocumentAttachment({
4986
5288
  ...attachment,
4987
5289
  file_sha1: newSha1,
4988
5290
  file: URL.createObjectURL(newFile)
@@ -4990,13 +5292,13 @@ class AttachmentService extends BaseApiService {
4990
5292
  );
4991
5293
  await this.client.files.addCache(newFile, newSha1);
4992
5294
  const [fileProps] = await this.client.files.uploadFileToS3(newSha1).catch((e) => {
4993
- store.dispatch(updateProjectAttachment(attachment));
5295
+ store.dispatch(updateDocumentAttachment(attachment));
4994
5296
  throw e;
4995
5297
  });
4996
5298
  const promise2 = this.enqueueRequest({
4997
5299
  description: "Edit attachment",
4998
5300
  method: HttpMethod.PATCH,
4999
- url: `/attachments/projects/${attachment.offline_id}/`,
5301
+ url: `/attachments/documents/${attachment.offline_id}/`,
5000
5302
  isResponseBlob: false,
5001
5303
  payload: fileProps,
5002
5304
  blockers: [attachmentId, newSha1],
@@ -5009,7 +5311,7 @@ class AttachmentService extends BaseApiService {
5009
5311
  } catch (e) {
5010
5312
  if (oldFile) {
5011
5313
  store.dispatch(
5012
- updateProjectAttachment({
5314
+ updateDocumentAttachment({
5013
5315
  ...attachment,
5014
5316
  file_sha1: attachment.file_sha1,
5015
5317
  file: URL.createObjectURL(oldFile)
@@ -5079,20 +5381,20 @@ class AttachmentService extends BaseApiService {
5079
5381
  blocks: [componentTypeAttachmentId]
5080
5382
  });
5081
5383
  }
5082
- deleteProjectAttachment(projectAttachmentId) {
5384
+ deleteDocumentAttachment(documentAttachmentId) {
5083
5385
  const { store } = this.client;
5084
- const attachment = selectProjectAttachmentMapping(store.getState())[projectAttachmentId];
5386
+ const attachment = store.getState().documentsReducer.attachments[documentAttachmentId];
5085
5387
  if (!attachment) {
5086
- throw new Error(`Attachment ${projectAttachmentId} not found`);
5388
+ throw new Error(`Attachment ${documentAttachmentId} not found`);
5087
5389
  }
5088
- store.dispatch(removeProjectAttachment(projectAttachmentId));
5390
+ store.dispatch(removeDocumentAttachment(documentAttachmentId));
5089
5391
  void this.client.files.removeCache(attachment.file_sha1);
5090
5392
  return this.enqueueRequest({
5091
- description: "Delete attachment",
5393
+ description: "Delete document attachment",
5092
5394
  method: HttpMethod.DELETE,
5093
- url: `/attachments/projects/${projectAttachmentId}/`,
5094
- blockers: [projectAttachmentId],
5095
- blocks: [projectAttachmentId]
5395
+ url: `/attachments/documents/${documentAttachmentId}/`,
5396
+ blockers: [documentAttachmentId],
5397
+ blocks: [documentAttachmentId]
5096
5398
  });
5097
5399
  }
5098
5400
  }
@@ -6330,11 +6632,18 @@ class MainService extends BaseApiService {
6330
6632
  if (currentProjectId) {
6331
6633
  const [_offlineAttachments, promise] = this.client.attachments.fetchAll(currentProjectId);
6332
6634
  void promise.then((result) => {
6333
- const { issue_attachments, component_type_attachments, component_attachments, project_attachments } = result;
6635
+ const {
6636
+ issue_attachments,
6637
+ component_type_attachments,
6638
+ component_attachments,
6639
+ project_attachments,
6640
+ document_attachments
6641
+ } = result;
6334
6642
  store.dispatch(setIssueAttachments(issue_attachments));
6335
6643
  store.dispatch(setComponentAttachments(component_attachments));
6336
6644
  store.dispatch(setComponentTypeAttachments(component_type_attachments));
6337
6645
  store.dispatch(setProjectAttachments(project_attachments));
6646
+ store.dispatch(setDocumentAttachments(document_attachments));
6338
6647
  });
6339
6648
  void this.client.documents.refreshStore();
6340
6649
  void this.client.issueUpdates.refreshStore();
@@ -6702,7 +7011,7 @@ class UserFormService extends BaseApiService {
6702
7011
  ...revisionAttachmentPayload,
6703
7012
  file: URL.createObjectURL(image)
6704
7013
  };
6705
- store.dispatch(addUserFormRevisionAttachment(offlinePayload));
7014
+ store.dispatch(addFormRevisionAttachment(offlinePayload));
6706
7015
  return attach;
6707
7016
  });
6708
7017
  });
@@ -6736,8 +7045,8 @@ class UserFormService extends BaseApiService {
6736
7045
  revision: 0
6737
7046
  };
6738
7047
  const { store } = this.client;
6739
- store.dispatch(addUserForm(retForm));
6740
- store.dispatch(addUserFormRevision(retRevision));
7048
+ store.dispatch(addForm(retForm));
7049
+ store.dispatch(addFormRevision(retRevision));
6741
7050
  const formPromise = this.enqueueRequest({
6742
7051
  description: "Create form",
6743
7052
  method: HttpMethod.POST,
@@ -6755,8 +7064,8 @@ class UserFormService extends BaseApiService {
6755
7064
  });
6756
7065
  const attachImagesPromises = this.getAttachImagePromises(images, offlineRevisionPayload.offline_id);
6757
7066
  void formPromise.catch((e) => {
6758
- store.dispatch(deleteUserForm(retForm.offline_id));
6759
- store.dispatch(deleteUserFormRevision(retRevision.offline_id));
7067
+ store.dispatch(deleteForm(retForm.offline_id));
7068
+ store.dispatch(deleteFormRevision(retRevision.offline_id));
6760
7069
  throw e;
6761
7070
  });
6762
7071
  const settledPromise = Promise.all([formPromise, ...attachImagesPromises]).then(() => formPromise);
@@ -6798,7 +7107,7 @@ class UserFormService extends BaseApiService {
6798
7107
  revision: "Pending",
6799
7108
  form: formId2
6800
7109
  };
6801
- store.dispatch(addUserFormRevision(fullRevision));
7110
+ store.dispatch(addFormRevision(fullRevision));
6802
7111
  const promise = this.enqueueRequest({
6803
7112
  description: "Create form revision",
6804
7113
  method: HttpMethod.PATCH,
@@ -6812,9 +7121,9 @@ class UserFormService extends BaseApiService {
6812
7121
  });
6813
7122
  const attachImagesPromises = this.getAttachImagePromises(images, offlineRevision.offline_id);
6814
7123
  void promise.then((result) => {
6815
- store.dispatch(addUserFormRevision(result));
7124
+ store.dispatch(setFormRevision(result));
6816
7125
  }).catch(() => {
6817
- store.dispatch(deleteUserFormRevision(fullRevision.offline_id));
7126
+ store.dispatch(deleteFormRevision(fullRevision.offline_id));
6818
7127
  });
6819
7128
  const settledPromise = Promise.all([promise, ...attachImagesPromises]).then(() => promise);
6820
7129
  return [fullRevision, settledPromise];
@@ -6856,19 +7165,19 @@ class UserFormService extends BaseApiService {
6856
7165
  async delete(formId2) {
6857
7166
  const { store } = this.client;
6858
7167
  const state = store.getState();
6859
- const userForm = selectUserForm(formId2)(state);
7168
+ const userForm = selectForm(formId2)(state);
6860
7169
  if (!userForm) {
6861
7170
  throw new Error("Expected userForm to exist");
6862
7171
  }
6863
- const userFormSubmissions = selectSubmissionsForForm(formId2)(state);
7172
+ const userFormSubmissions = selectFormSubmissionsOfForm(formId2)(state);
6864
7173
  if (userFormSubmissions && userFormSubmissions.length > 0) {
6865
- store.dispatch(deleteUserFormSubmissions(userFormSubmissions));
7174
+ store.dispatch(deleteFormSubmissions(userFormSubmissions.map(({ offline_id }) => offline_id)));
6866
7175
  }
6867
- const userFormRevisions = selectRevisionsForForm(formId2)(state);
7176
+ const userFormRevisions = selectFormRevisionsOfForm(formId2)(state);
6868
7177
  if (userFormRevisions && userFormRevisions.length > 0) {
6869
- store.dispatch(deleteUserFormRevisions(userFormRevisions));
7178
+ store.dispatch(deleteFormRevisions(userFormRevisions.map(({ offline_id }) => offline_id)));
6870
7179
  }
6871
- store.dispatch(deleteUserForm(formId2));
7180
+ store.dispatch(deleteForm(formId2));
6872
7181
  try {
6873
7182
  return await this.enqueueRequest({
6874
7183
  description: "Delete form",
@@ -6878,12 +7187,12 @@ class UserFormService extends BaseApiService {
6878
7187
  blocks: []
6879
7188
  });
6880
7189
  } catch (e) {
6881
- store.dispatch(addUserForm(userForm));
7190
+ store.dispatch(addForm(userForm));
6882
7191
  if (userFormRevisions && userFormRevisions.length > 0) {
6883
- store.dispatch(addUserFormRevisions(userFormRevisions));
7192
+ store.dispatch(addFormRevisions(userFormRevisions));
6884
7193
  }
6885
7194
  if (userFormSubmissions && userFormSubmissions.length > 0) {
6886
- store.dispatch(addUserFormSubmissions(userFormSubmissions));
7195
+ store.dispatch(addFormSubmissions(userFormSubmissions));
6887
7196
  }
6888
7197
  throw e;
6889
7198
  }
@@ -6897,16 +7206,15 @@ class UserFormService extends BaseApiService {
6897
7206
  blockers: [],
6898
7207
  blocks: []
6899
7208
  });
6900
- store.dispatch(addUserForms(Object.values(result.forms)));
6901
- store.dispatch(addUserFormRevisions(Object.values(result.revisions)));
6902
- store.dispatch(setUserFormRevisionAttachments(Object.values(result.attachments)));
7209
+ store.dispatch(setForms(Object.values(result.forms)));
7210
+ store.dispatch(setFormRevisions(Object.values(result.revisions)));
7211
+ store.dispatch(setFormRevisionAttachments(Object.values(result.attachments)));
6903
7212
  }
6904
7213
  }
6905
7214
  const isArrayOfFiles = (value) => {
6906
7215
  return Array.isArray(value) && value[0] instanceof File;
6907
7216
  };
6908
- const separateFilesFromValues = (payload) => {
6909
- const { values } = payload;
7217
+ const separateFilesFromValues = (values) => {
6910
7218
  const files = {};
6911
7219
  const newValues = {};
6912
7220
  for (const key in values) {
@@ -6921,17 +7229,13 @@ const separateFilesFromValues = (payload) => {
6921
7229
  newValues[key] = value;
6922
7230
  }
6923
7231
  }
6924
- const payloadWithoutFiles = {
6925
- ...payload,
6926
- values: newValues
6927
- };
6928
- return { payloadWithoutFiles, files };
7232
+ return { values: newValues, files };
6929
7233
  };
6930
7234
  class UserFormSubmissionService extends BaseApiService {
6931
7235
  constructor() {
6932
7236
  super(...arguments);
6933
7237
  // Attach files to submission, after uploading them to S3
6934
- __publicField(this, "getAttachFilesPromises", (files, payload) => {
7238
+ __publicField(this, "getAttachFilesPromises", (files, submission) => {
6935
7239
  const { store } = this.client;
6936
7240
  return Object.entries(files).map(async ([key, fileArray]) => {
6937
7241
  const attachResults = [];
@@ -6941,24 +7245,27 @@ class UserFormSubmissionService extends BaseApiService {
6941
7245
  const [fileProps] = await this.client.files.uploadFileToS3(sha1);
6942
7246
  const submissionAttachmentPayload = offline({
6943
7247
  ...fileProps,
6944
- submission: payload.offline_id,
7248
+ submission: submission.offline_id,
6945
7249
  field_identifier: key
6946
7250
  });
6947
7251
  const attach = await this.enqueueRequest({
6948
7252
  description: "Attach file to form submission",
6949
7253
  method: HttpMethod.POST,
6950
- url: `/forms/submission/${payload.offline_id}/attachments/`,
7254
+ url: `/forms/submission/${submission.offline_id}/attachments/`,
6951
7255
  payload: submissionAttachmentPayload,
6952
- blockers: [payload.component, payload.component_stage, payload.issue, payload.form_revision].filter(
6953
- (x) => x !== void 0
6954
- ),
7256
+ blockers: [
7257
+ submission.component,
7258
+ submission.component_stage,
7259
+ submission.issue,
7260
+ submission.form_revision
7261
+ ].filter((x) => x !== void 0),
6955
7262
  blocks: [submissionAttachmentPayload.offline_id]
6956
7263
  });
6957
7264
  const offlinePayload = {
6958
7265
  ...submissionAttachmentPayload,
6959
7266
  file: URL.createObjectURL(file)
6960
7267
  };
6961
- store.dispatch(addUserFormSubmissionAttachment(offlinePayload));
7268
+ store.dispatch(addFormSubmissionAttachment(offlinePayload));
6962
7269
  attachResults.push(attach);
6963
7270
  }
6964
7271
  return attachResults;
@@ -6972,70 +7279,165 @@ class UserFormSubmissionService extends BaseApiService {
6972
7279
  if (!activeProjectId) {
6973
7280
  throw new Error("Expected an active project");
6974
7281
  }
6975
- const { payloadWithoutFiles, files } = separateFilesFromValues(payload);
7282
+ const { values, files } = separateFilesFromValues(payload.values);
7283
+ const offlineSubmission = {
7284
+ ...payload,
7285
+ values,
7286
+ created_by: state.userReducer.currentUser.id,
7287
+ submitted_at: (/* @__PURE__ */ new Date()).toISOString()
7288
+ };
6976
7289
  const promise = this.enqueueRequest({
6977
7290
  description: "Respond to form",
6978
7291
  method: HttpMethod.POST,
6979
7292
  url: `/forms/revisions/${payload.form_revision}/respond/`,
6980
- payload: { ...payloadWithoutFiles, project: activeProjectId },
7293
+ payload: { ...offlineSubmission, project: activeProjectId },
6981
7294
  blockers: [payload.issue, payload.component, payload.component_stage, "add-form-entry"].filter(
6982
7295
  (x) => x !== void 0
6983
7296
  ),
6984
7297
  blocks: [payload.offline_id]
6985
7298
  });
6986
- const attachFilesPromises = this.getAttachFilesPromises(files, payload);
6987
- const now = (/* @__PURE__ */ new Date()).toISOString();
6988
- const fullOfflineResult = {
6989
- ...payload,
6990
- created_by: state.userReducer.currentUser.id,
6991
- created_at: now,
6992
- updated_at: now
6993
- };
6994
- const offlineResultWithoutFiles = {
6995
- ...fullOfflineResult,
6996
- ...payloadWithoutFiles
6997
- };
6998
- store.dispatch(updateOrCreateUserFormSubmission(offlineResultWithoutFiles));
7299
+ const attachFilesPromises = this.getAttachFilesPromises(files, offlineSubmission);
7300
+ store.dispatch(addFormSubmission(offlineSubmission));
6999
7301
  void promise.then((result) => {
7000
7302
  store.dispatch(addActiveProjectFormSubmissionsCount(1));
7001
- store.dispatch(updateOrCreateUserFormSubmission(result));
7303
+ store.dispatch(setFormSubmission(result));
7002
7304
  return result;
7003
7305
  }).catch(() => {
7004
- store.dispatch(deleteUserFormSubmission(payload.offline_id));
7306
+ store.dispatch(deleteFormSubmission(payload.offline_id));
7005
7307
  store.dispatch(addActiveProjectFormSubmissionsCount(-1));
7006
7308
  });
7007
7309
  const settledPromise = Promise.all([promise, ...attachFilesPromises]).then(() => promise);
7008
- return [fullOfflineResult, settledPromise];
7310
+ return [offlineSubmission, settledPromise];
7009
7311
  }
7010
- update(submission) {
7312
+ // Note currently the bulkAdd method is specific to form submissions for components
7313
+ // TODO: adapt the support bulk adding to any model type
7314
+ async bulkAdd(args) {
7315
+ const { form_revision, values: argsValues, componentOfflineIds } = args;
7011
7316
  const { store } = this.client;
7012
- const { payloadWithoutFiles, files } = separateFilesFromValues(submission);
7013
- if (!("created_by" in payloadWithoutFiles) || !("created_at" in payloadWithoutFiles)) {
7014
- throw new Error("Expected payloadWithoutFiles to have created_by and created_at fields.");
7317
+ const offlineSubmissions = [];
7318
+ const offlineAttachments = [];
7319
+ const submissionOfflineIds = [];
7320
+ const submissionsPayload = [];
7321
+ const attachmentsPayload = [];
7322
+ const { values, files } = separateFilesFromValues(argsValues);
7323
+ const submittedAt = (/* @__PURE__ */ new Date()).toISOString();
7324
+ const createdBy = store.getState().userReducer.currentUser.id;
7325
+ for (const component_id of componentOfflineIds) {
7326
+ const submission = offline({
7327
+ form_revision,
7328
+ values,
7329
+ created_by: createdBy,
7330
+ submitted_at: submittedAt,
7331
+ component: component_id
7332
+ });
7333
+ submissionOfflineIds.push(submission.offline_id);
7334
+ submissionsPayload.push({ offline_id: submission.offline_id, component_id });
7335
+ offlineSubmissions.push(submission);
7336
+ for (const [fieldIdentifier, fileArray] of Object.entries(files)) {
7337
+ for (const file of fileArray) {
7338
+ const sha1 = await hashFile(file);
7339
+ await this.client.files.addCache(file, sha1);
7340
+ const offlineAttachment = offline({
7341
+ file_name: file.name,
7342
+ file_sha1: sha1,
7343
+ file: URL.createObjectURL(file),
7344
+ submission: submission.offline_id,
7345
+ field_identifier: fieldIdentifier
7346
+ });
7347
+ offlineAttachments.push(offlineAttachment);
7348
+ attachmentsPayload.push({
7349
+ offline_id: offlineAttachment.offline_id,
7350
+ submission_id: submission.offline_id,
7351
+ sha1,
7352
+ name: file.name,
7353
+ field_identifier: fieldIdentifier
7354
+ });
7355
+ }
7356
+ }
7015
7357
  }
7358
+ const filesRecord = {};
7359
+ for (const file of Object.values(files).flat()) {
7360
+ const sha1 = await hashFile(file);
7361
+ filesRecord[sha1] = {
7362
+ sha1,
7363
+ extension: file.name.split(".").pop() || "",
7364
+ file_type: file.type,
7365
+ size: file.size
7366
+ };
7367
+ }
7368
+ store.dispatch(addFormSubmissions(offlineSubmissions));
7369
+ store.dispatch(addFormSubmissionAttachments(offlineAttachments));
7370
+ const promise = this.enqueueRequest({
7371
+ description: "Bulk add form submissions",
7372
+ method: HttpMethod.POST,
7373
+ url: `/forms/revisions/${form_revision}/bulk-respond/`,
7374
+ payload: {
7375
+ form_data: values,
7376
+ submitted_at: submittedAt,
7377
+ submissions: submissionsPayload,
7378
+ attachments: attachmentsPayload,
7379
+ files: Object.values(filesRecord)
7380
+ },
7381
+ blockers: componentOfflineIds,
7382
+ blocks: submissionOfflineIds
7383
+ });
7384
+ promise.then(({ submissions, attachments, presigned_urls }) => {
7385
+ store.dispatch(updateFormSubmissions(submissions));
7386
+ store.dispatch(updateFormSubmissionAttachments(attachments));
7387
+ for (const [sha1, presigned_url] of Object.entries(presigned_urls)) {
7388
+ const file = filesRecord[sha1];
7389
+ if (!file)
7390
+ continue;
7391
+ void this.enqueueRequest({
7392
+ url: presigned_url.url,
7393
+ description: "Upload file",
7394
+ method: HttpMethod.POST,
7395
+ isExternalUrl: true,
7396
+ isAuthNeeded: false,
7397
+ attachmentHash: sha1,
7398
+ blockers: [`s3-${file.sha1}.${file.extension}`],
7399
+ blocks: [sha1],
7400
+ s3url: presigned_url
7401
+ });
7402
+ }
7403
+ }).catch(() => {
7404
+ store.dispatch(deleteFormSubmissions(submissionOfflineIds));
7405
+ store.dispatch(deleteFormSubmissionAttachments(offlineAttachments.map((x) => x.offline_id)));
7406
+ });
7407
+ return [offlineSubmissions, promise.then(({ submissions }) => submissions)];
7408
+ }
7409
+ update(submission) {
7410
+ const { store } = this.client;
7411
+ const { values, files } = separateFilesFromValues(submission.values);
7016
7412
  const attachFilesPromises = this.getAttachFilesPromises(files, submission);
7017
- const fullResult = {
7018
- ...payloadWithoutFiles,
7019
- updated_at: (/* @__PURE__ */ new Date()).toISOString()
7413
+ const offlineSubmission = {
7414
+ ...submission,
7415
+ values
7020
7416
  };
7021
- store.dispatch(updateOrCreateUserFormSubmission(fullResult));
7417
+ const submissionToBeUpdated = store.getState().formSubmissionReducer.formSubmissions[submission.offline_id];
7418
+ store.dispatch(updateFormSubmission(offlineSubmission));
7022
7419
  const promise = this.enqueueRequest({
7023
7420
  description: "Patch form submission",
7024
7421
  method: HttpMethod.PATCH,
7025
7422
  url: `/forms/submissions/${submission.offline_id}/`,
7026
- payload: fullResult,
7027
- blockers: [fullResult.issue, fullResult.component, fullResult.component_stage].filter(
7423
+ payload: offlineSubmission,
7424
+ blockers: [offlineSubmission.issue, offlineSubmission.component, offlineSubmission.component_stage].filter(
7028
7425
  (x) => x !== void 0
7029
7426
  ),
7030
- blocks: [fullResult.offline_id]
7427
+ blocks: [offlineSubmission.offline_id]
7428
+ });
7429
+ promise.then((createdSubmission) => {
7430
+ store.dispatch(setFormSubmission(createdSubmission));
7431
+ }).catch(() => {
7432
+ store.dispatch(setFormSubmission(submissionToBeUpdated));
7031
7433
  });
7032
- return Promise.all([promise, ...attachFilesPromises]).then(() => promise);
7434
+ return [offlineSubmission, Promise.all([promise, ...attachFilesPromises]).then(() => promise)];
7033
7435
  }
7034
7436
  async delete(submissionId) {
7035
7437
  const { store } = this.client;
7036
7438
  const state = store.getState();
7037
- const submission = state.userFormReducer.submissions[submissionId];
7038
- store.dispatch(deleteUserFormSubmission(submissionId));
7439
+ const submission = state.formSubmissionReducer.formSubmissions[submissionId];
7440
+ store.dispatch(deleteFormSubmission(submissionId));
7039
7441
  store.dispatch(addActiveProjectFormSubmissionsCount(-1));
7040
7442
  try {
7041
7443
  return await this.enqueueRequest({
@@ -7046,10 +7448,8 @@ class UserFormSubmissionService extends BaseApiService {
7046
7448
  blocks: []
7047
7449
  });
7048
7450
  } catch (e) {
7049
- if (submission) {
7050
- store.dispatch(addActiveProjectFormSubmissionsCount(1));
7051
- store.dispatch(updateOrCreateUserFormSubmission(submission));
7052
- }
7451
+ store.dispatch(addActiveProjectFormSubmissionsCount(1));
7452
+ store.dispatch(addFormSubmission(submission));
7053
7453
  throw e;
7054
7454
  }
7055
7455
  }
@@ -7063,7 +7463,7 @@ class UserFormSubmissionService extends BaseApiService {
7063
7463
  blockers: [],
7064
7464
  blocks: []
7065
7465
  });
7066
- store.dispatch(setUserFormSubmissions(submissions));
7466
+ store.dispatch(setFormSubmissions(submissions));
7067
7467
  const attachments = await this.enqueueRequest({
7068
7468
  description: "Fetch form attachments",
7069
7469
  method: HttpMethod.GET,
@@ -7071,7 +7471,7 @@ class UserFormSubmissionService extends BaseApiService {
7071
7471
  blockers: [],
7072
7472
  blocks: []
7073
7473
  });
7074
- store.dispatch(setUserFormSubmissionAttachments(attachments));
7474
+ store.dispatch(setFormSubmissionAttachments(attachments));
7075
7475
  }
7076
7476
  }
7077
7477
  class WorkspaceService extends BaseApiService {
@@ -13930,7 +14330,7 @@ const initialFormValues = (fields, values) => {
13930
14330
  };
13931
14331
  const useAttachImagesToFormRevisionFields = (revision) => {
13932
14332
  const { sdk } = useSDK();
13933
- const attachments = useAppSelector(selectRevisionAttachments((revision == null ? void 0 : revision.offline_id) ?? ""));
14333
+ const attachments = useAppSelector(selectAttachmentsOfFormRevision((revision == null ? void 0 : revision.offline_id) ?? ""));
13934
14334
  return useMemo(() => {
13935
14335
  if (!revision || !attachments)
13936
14336
  return revision;
@@ -14027,7 +14427,7 @@ const FormSubmissionViewer = memo(
14027
14427
  return formRevisionToSchema(revisionWithImages, { readonly: true });
14028
14428
  }, [revisionWithImages]);
14029
14429
  const submissionValuesWithAttachments = useMemo(() => {
14030
- const attachments = selectSubmissionAttachments(submission.offline_id)(sdk.store.getState()) ?? [];
14430
+ const attachments = selectAttachmentsOfFormSubmission(submission.offline_id)(sdk.store.getState()) ?? [];
14031
14431
  const downloadedAttachments = {};
14032
14432
  for (const attachment of attachments) {
14033
14433
  const promise = sdk.files.fetchFileFromUrl(attachment.file, attachment.file_sha1, attachment.file_name);
@@ -14077,8 +14477,8 @@ const FormBrowser = memo(
14077
14477
  }
14078
14478
  return ret;
14079
14479
  }, [filter, maxResults, ownerFilter]);
14080
- const userForms = useAppSelector(selectFilteredUserForms(ownerFilterOptions)) ?? [];
14081
- const userFormMapping = useAppSelector(selectUserFormMapping);
14480
+ const userForms = useAppSelector(selectFilteredForms(ownerFilterOptions)) ?? [];
14481
+ const userFormMapping = useAppSelector(selectFormMapping);
14082
14482
  const attachableUserForms = userForms.filter((form) => !form.component_type);
14083
14483
  const attachableUserFormMapping = Object.values(userFormMapping).filter(
14084
14484
  (form) => !form.component_type
@@ -14111,7 +14511,7 @@ const FormBrowser = memo(
14111
14511
  const handleChange = useCallback((e) => {
14112
14512
  setFilter(e.currentTarget.value);
14113
14513
  }, []);
14114
- const numberOfForms = useAppSelector(selectNumberOfGeneralUserForms) || 0;
14514
+ const numberOfForms = useAppSelector(selectGeneralFormCount) || 0;
14115
14515
  const numberOfHiddenForms = numberOfForms - attachableUserForms.length;
14116
14516
  const overflowMessage = attachableUserForms.length == maxResults && numberOfHiddenForms > 0 ? `Only the first ${maxResults} results are shown (${numberOfHiddenForms} hidden)` : numberOfHiddenForms > 0 && `${numberOfHiddenForms} hidden forms`;
14117
14517
  return /* @__PURE__ */ jsxs(Flex$1, { ref, direction: "column", gap: "2", children: [
@@ -14205,16 +14605,13 @@ const FormSubmissionBrowserEntry = memo((props) => {
14205
14605
  const { submission, onSubmissionClick, compact, labelType, rowDecorator } = props;
14206
14606
  const currentUser = useAppSelector(selectCurrentUser);
14207
14607
  const createdBy = useAppSelector(selectUser("created_by" in submission ? submission.created_by : currentUser.id));
14208
- const dateToUse = getCreatedAtOrSubmittedAtDate(submission);
14209
- const formattedDateTime = isToday(dateToUse) ? dateToUse.toLocaleTimeString([], {
14210
- hour: "2-digit",
14211
- minute: "2-digit"
14212
- }) : getLocalDateString(dateToUse);
14608
+ const dateToUse = submission.submitted_at;
14609
+ const formattedDateTime = getLocalDateString(dateToUse);
14213
14610
  const revision = useAppSelector(selectFormRevision(submission.form_revision));
14214
14611
  if (!revision) {
14215
14612
  throw new Error(`Could not find revision ${submission.form_revision} for submission ${submission.offline_id}.`);
14216
14613
  }
14217
- const latestRevisionNumber = (_a2 = useAppSelector(selectLatestFormRevision(revision.form))) == null ? void 0 : _a2.revision;
14614
+ const latestRevisionNumber = (_a2 = useAppSelector(selectLatestFormRevisionOfForm(revision.form))) == null ? void 0 : _a2.revision;
14218
14615
  const creatorProfileSrc = useFileSrc({
14219
14616
  file: (createdBy == null ? void 0 : createdBy.profile.file) ?? null,
14220
14617
  fileSha1: (createdBy == null ? void 0 : createdBy.profile.file_sha1) ?? null
@@ -14245,10 +14642,6 @@ const FormSubmissionBrowserEntry = memo((props) => {
14245
14642
  return row;
14246
14643
  });
14247
14644
  FormSubmissionBrowserEntry.displayName = "FormSubmissionBrowserEntry";
14248
- const getCreatedAtOrSubmittedAtDate = (submission) => {
14249
- const date = "created_at" in submission ? submission.created_at : submission.submitted_at;
14250
- return new Date(date);
14251
- };
14252
14645
  const FormSubmissionBrowser = memo((props) => {
14253
14646
  const {
14254
14647
  formId: formId2,
@@ -14262,10 +14655,10 @@ const FormSubmissionBrowser = memo((props) => {
14262
14655
  if (!!formId2 === !!propSubmissions) {
14263
14656
  throw new Error("Either formId or submissions must be provided, but not both.");
14264
14657
  }
14265
- const submissions = useAppSelector(propSubmissions ? () => propSubmissions : selectSubmissionsForForm(formId2));
14658
+ const submissions = useAppSelector(propSubmissions ? () => propSubmissions : selectFormSubmissionsOfForm(formId2));
14266
14659
  const sortedSubmissions = useMemo(
14267
14660
  () => submissions == null ? void 0 : submissions.sort((a, b) => {
14268
- return getCreatedAtOrSubmittedAtDate(b).getTime() - getCreatedAtOrSubmittedAtDate(a).getTime();
14661
+ return a.submitted_at.localeCompare(b.submitted_at);
14269
14662
  }),
14270
14663
  [submissions]
14271
14664
  );
@@ -15583,6 +15976,7 @@ export {
15583
15976
  VerificationCodeType,
15584
15977
  WorkspaceService,
15585
15978
  YELLOW,
15979
+ _selectLatestFormRevision,
15586
15980
  _setLatestRetryTime,
15587
15981
  acceptProjectInvite,
15588
15982
  addActiveProjectFormSubmissionsCount,
@@ -15595,9 +15989,21 @@ export {
15595
15989
  addComponentTypeAttachment,
15596
15990
  addComponentTypeAttachments,
15597
15991
  addComponentsInBatches,
15992
+ addDocumentAttachment,
15993
+ addDocumentAttachments,
15598
15994
  addDocuments,
15599
15995
  addEmailDomain,
15600
15996
  addFavouriteProjectId,
15997
+ addForm,
15998
+ addFormRevision,
15999
+ addFormRevisionAttachment,
16000
+ addFormRevisionAttachments,
16001
+ addFormRevisions,
16002
+ addFormSubmission,
16003
+ addFormSubmissionAttachment,
16004
+ addFormSubmissionAttachments,
16005
+ addFormSubmissions,
16006
+ addForms,
15601
16007
  addIssue,
15602
16008
  addIssueAttachment,
15603
16009
  addIssueAttachments,
@@ -15618,13 +16024,6 @@ export {
15618
16024
  addStageCompletions,
15619
16025
  addStages,
15620
16026
  addToRecentIssues,
15621
- addUserForm,
15622
- addUserFormRevision,
15623
- addUserFormRevisionAttachment,
15624
- addUserFormRevisions,
15625
- addUserFormSubmissionAttachment,
15626
- addUserFormSubmissions,
15627
- addUserForms,
15628
16027
  addUsers,
15629
16028
  addWorkspace,
15630
16029
  areArraysEqual,
@@ -15645,6 +16044,7 @@ export {
15645
16044
  componentStageSlice,
15646
16045
  componentTypeReducer,
15647
16046
  componentTypeSlice,
16047
+ constructUploadedFilePayloads,
15648
16048
  coordinatesAreEqual,
15649
16049
  coordinatesToLiteral,
15650
16050
  coordinatesToPointGeometry,
@@ -15655,12 +16055,16 @@ export {
15655
16055
  defaultBadgeColor,
15656
16056
  defaultStore,
15657
16057
  deleteComponentType,
16058
+ deleteForm,
16059
+ deleteFormRevision,
16060
+ deleteFormRevisionAttachment,
16061
+ deleteFormRevisionAttachments,
16062
+ deleteFormRevisions,
16063
+ deleteFormSubmission,
16064
+ deleteFormSubmissionAttachment,
16065
+ deleteFormSubmissionAttachments,
16066
+ deleteFormSubmissions,
15658
16067
  deleteProject,
15659
- deleteUserForm,
15660
- deleteUserFormRevision,
15661
- deleteUserFormRevisions,
15662
- deleteUserFormSubmission,
15663
- deleteUserFormSubmissions,
15664
16068
  dequeue,
15665
16069
  deserialize,
15666
16070
  deserializeField,
@@ -15689,7 +16093,13 @@ export {
15689
16093
  fileSlice,
15690
16094
  fileToBlob,
15691
16095
  flipCoordinates,
16096
+ formReducer,
16097
+ formRevisionReducer,
15692
16098
  formRevisionToSchema,
16099
+ formRevisionsSlice,
16100
+ formSlice,
16101
+ formSubmissionReducer,
16102
+ formSubmissionSlice,
15693
16103
  index as forms,
15694
16104
  fullComponentMarkerSize,
15695
16105
  generateBadgeColors,
@@ -15758,11 +16168,14 @@ export {
15758
16168
  removeComponentAttachments,
15759
16169
  removeComponentTypeAttachment,
15760
16170
  removeComponentTypeAttachments,
16171
+ removeDocumentAttachment,
16172
+ removeDocumentAttachments,
15761
16173
  removeDocuments,
15762
16174
  removeEmailDomain,
15763
16175
  removeFavouriteProjectId,
15764
16176
  removeIssue,
15765
16177
  removeIssueAttachment,
16178
+ removeIssueAttachments,
15766
16179
  removeIssueComment,
15767
16180
  removeIssueComments,
15768
16181
  removeIssueUpdate,
@@ -15805,6 +16218,7 @@ export {
15805
16218
  selectAllAttachments,
15806
16219
  selectAllComponentAttachments,
15807
16220
  selectAllComponentTypeAttachments,
16221
+ selectAllDocumentAttachments,
15808
16222
  selectAllProjectAttachments,
15809
16223
  selectAncestorIdsOfDocument,
15810
16224
  selectAppearance,
@@ -15812,6 +16226,10 @@ export {
15812
16226
  selectAttachmentsOfComponentByType,
15813
16227
  selectAttachmentsOfComponentType,
15814
16228
  selectAttachmentsOfComponentTypeByType,
16229
+ selectAttachmentsOfDocument,
16230
+ selectAttachmentsOfDocumentByType,
16231
+ selectAttachmentsOfFormRevision,
16232
+ selectAttachmentsOfFormSubmission,
15815
16233
  selectAttachmentsOfIssue,
15816
16234
  selectAttachmentsOfIssueByType,
15817
16235
  selectAttachmentsOfProject,
@@ -15827,11 +16245,11 @@ export {
15827
16245
  selectCompletedStageIdsForComponent,
15828
16246
  selectCompletedStages,
15829
16247
  selectComponent,
16248
+ selectComponentAttachment,
15830
16249
  selectComponentAttachmentMapping,
15831
- selectComponentSubmissionMapping,
15832
16250
  selectComponentType,
16251
+ selectComponentTypeAttachment,
15833
16252
  selectComponentTypeAttachmentMapping,
15834
- selectComponentTypeForm,
15835
16253
  selectComponentTypeFromComponent,
15836
16254
  selectComponentTypeFromComponents,
15837
16255
  selectComponentTypeStagesMapping,
@@ -15847,6 +16265,8 @@ export {
15847
16265
  selectCurrentUser,
15848
16266
  selectDeletedRequests,
15849
16267
  selectDocument,
16268
+ selectDocumentAttachment,
16269
+ selectDocumentAttachmentMapping,
15850
16270
  selectDocuments,
15851
16271
  selectDocumentsMapping,
15852
16272
  selectEmailDomainsAsMapping,
@@ -15859,8 +16279,24 @@ export {
15859
16279
  selectExpandedSections,
15860
16280
  selectFavouriteProjects,
15861
16281
  selectFileAttachmentsOfIssue,
15862
- selectFilteredUserForms,
16282
+ selectFilteredForms,
16283
+ selectForm,
16284
+ selectFormMapping,
16285
+ selectFormOfComponentType,
15863
16286
  selectFormRevision,
16287
+ selectFormRevisionMapping,
16288
+ selectFormRevisions,
16289
+ selectFormRevisionsOfForm,
16290
+ selectFormSubmission,
16291
+ selectFormSubmissionAttachmentsMapping,
16292
+ selectFormSubmissions,
16293
+ selectFormSubmissionsByComponents,
16294
+ selectFormSubmissionsMapping,
16295
+ selectFormSubmissionsOfComponent,
16296
+ selectFormSubmissionsOfForm,
16297
+ selectFormSubmissionsOfIssue,
16298
+ selectFormsCount,
16299
+ selectGeneralFormCount,
15864
16300
  selectHiddenCategoryCount,
15865
16301
  selectHiddenComponentTypeIds,
15866
16302
  selectIsFetchingInitialData,
@@ -15868,16 +16304,17 @@ export {
15868
16304
  selectIsLoading,
15869
16305
  selectIsLoggedIn,
15870
16306
  selectIssue,
16307
+ selectIssueAttachment,
15871
16308
  selectIssueAttachmentMapping,
15872
16309
  selectIssueAttachments,
15873
16310
  selectIssueMapping,
15874
16311
  selectIssueUpdateMapping,
15875
16312
  selectIssueUpdatesOfIssue,
15876
16313
  selectIssues,
15877
- selectLatestFormRevision,
16314
+ selectLatestFormRevisionByForm,
16315
+ selectLatestFormRevisionOfForm,
16316
+ selectLatestFormRevisionsOfComponentTypes,
15878
16317
  selectLatestRetryTime,
15879
- selectLatestRevisionByFormId,
15880
- selectLatestRevisionsFromComponentTypeIds,
15881
16318
  selectLicense,
15882
16319
  selectLicenseForProject,
15883
16320
  selectLicenses,
@@ -15886,8 +16323,6 @@ export {
15886
16323
  selectMapStyle,
15887
16324
  selectNumberOfComponentTypesMatchingCaseInsensitiveName,
15888
16325
  selectNumberOfComponentsOfComponentType,
15889
- selectNumberOfGeneralUserForms,
15890
- selectNumberOfUserForms,
15891
16326
  selectOrganization,
15892
16327
  selectOrganizationAccess,
15893
16328
  selectOrganizationAccessForUser,
@@ -15915,8 +16350,6 @@ export {
15915
16350
  selectRecentIssuesAsSearchResults,
15916
16351
  selectRecentProjects,
15917
16352
  selectRehydrated,
15918
- selectRevisionAttachments,
15919
- selectRevisionsForForm,
15920
16353
  selectRootDocuments,
15921
16354
  selectShowTooltips,
15922
16355
  selectSortedEmailDomains,
@@ -15931,16 +16364,10 @@ export {
15931
16364
  selectStagesFromComponentType,
15932
16365
  selectStagesFromComponentTypeIds,
15933
16366
  selectStagesFromStageIds,
15934
- selectSubmissionAttachments,
15935
- selectSubmissionsForComponent,
15936
- selectSubmissionsForForm,
15937
- selectSubmissionsForIssue,
15938
16367
  selectUploadUrl,
15939
16368
  selectUsedColors,
15940
16369
  selectUser,
15941
- selectUserForm,
15942
- selectUserFormMapping,
15943
- selectUserFormSubmission,
16370
+ selectUserFormRevisionAttachmentsMapping,
15944
16371
  selectUsersAsMapping,
15945
16372
  selectVisibleStatuses,
15946
16373
  selectVisibleUserIds,
@@ -15961,11 +16388,19 @@ export {
15961
16388
  setComponents,
15962
16389
  setCreateProjectType,
15963
16390
  setCurrentUser,
16391
+ setDocumentAttachments,
15964
16392
  setDocuments,
15965
16393
  setEmailDomains,
15966
16394
  setEnableClustering,
15967
16395
  setEnableDuplicateIssues,
15968
16396
  setEnablePlacementMode,
16397
+ setFormRevision,
16398
+ setFormRevisionAttachments,
16399
+ setFormRevisions,
16400
+ setFormSubmission,
16401
+ setFormSubmissionAttachments,
16402
+ setFormSubmissions,
16403
+ setForms,
15969
16404
  setIsFetchingInitialData,
15970
16405
  setIsImportingProjectFile,
15971
16406
  setIsLoading,
@@ -15990,9 +16425,6 @@ export {
15990
16425
  setTokens,
15991
16426
  setTourStep,
15992
16427
  setUploadUrl,
15993
- setUserFormRevisionAttachments,
15994
- setUserFormSubmissionAttachments,
15995
- setUserFormSubmissions,
15996
16428
  setUsers,
15997
16429
  setVisibleStatuses,
15998
16430
  setVisibleUserIds,
@@ -16015,12 +16447,16 @@ export {
16015
16447
  updateComponent,
16016
16448
  updateComponentAttachment,
16017
16449
  updateComponentTypeAttachment,
16450
+ updateDocumentAttachment,
16018
16451
  updateDocuments,
16452
+ updateFormSubmission,
16453
+ updateFormSubmissionAttachments,
16454
+ updateFormSubmissions,
16019
16455
  updateIssue,
16020
16456
  updateIssueAttachment,
16457
+ updateIssueAttachments,
16021
16458
  updateLicense,
16022
16459
  updateOrCreateProject,
16023
- updateOrCreateUserFormSubmission,
16024
16460
  updateOrganizationAccess,
16025
16461
  updateProjectAccess,
16026
16462
  updateProjectAttachment,
@@ -16034,8 +16470,6 @@ export {
16034
16470
  useFormikInput,
16035
16471
  useMemoCompare,
16036
16472
  useSDK,
16037
- userFormReducer,
16038
- userFormSlice,
16039
16473
  userReducer,
16040
16474
  userSlice,
16041
16475
  valueIsFile,